1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/quic/crypto/crypto_framer.h"
6
7#include "net/quic/crypto/crypto_protocol.h"
8#include "net/quic/quic_data_reader.h"
9#include "net/quic/quic_data_writer.h"
10
11using base::StringPiece;
12using std::make_pair;
13using std::pair;
14using std::vector;
15
16namespace net {
17
18namespace {
19
20const size_t kQuicTagSize = sizeof(uint32);
21const size_t kCryptoEndOffsetSize = sizeof(uint32);
22const size_t kNumEntriesSize = sizeof(uint16);
23
24// OneShotVisitor is a framer visitor that records a single handshake message.
25class OneShotVisitor : public CryptoFramerVisitorInterface {
26 public:
27  OneShotVisitor() : error_(false) {}
28
29  virtual void OnError(CryptoFramer* framer) OVERRIDE { error_ = true; }
30
31  virtual void OnHandshakeMessage(
32      const CryptoHandshakeMessage& message) OVERRIDE {
33    out_.reset(new CryptoHandshakeMessage(message));
34  }
35
36  bool error() const { return error_; }
37
38  CryptoHandshakeMessage* release() { return out_.release(); }
39
40 private:
41  scoped_ptr<CryptoHandshakeMessage> out_;
42  bool error_;
43};
44
45}  // namespace
46
47CryptoFramer::CryptoFramer()
48    : visitor_(NULL),
49      num_entries_(0),
50      values_len_(0) {
51  Clear();
52}
53
54CryptoFramer::~CryptoFramer() {}
55
56// static
57CryptoHandshakeMessage* CryptoFramer::ParseMessage(StringPiece in) {
58  OneShotVisitor visitor;
59  CryptoFramer framer;
60
61  framer.set_visitor(&visitor);
62  if (!framer.ProcessInput(in) || visitor.error() ||
63      framer.InputBytesRemaining()) {
64    return NULL;
65  }
66
67  return visitor.release();
68}
69
70bool CryptoFramer::ProcessInput(StringPiece input) {
71  DCHECK_EQ(QUIC_NO_ERROR, error_);
72  if (error_ != QUIC_NO_ERROR) {
73    return false;
74  }
75  error_ = Process(input);
76  if (error_ != QUIC_NO_ERROR) {
77    visitor_->OnError(this);
78    return false;
79  }
80
81  return true;
82}
83
84// static
85QuicData* CryptoFramer::ConstructHandshakeMessage(
86    const CryptoHandshakeMessage& message) {
87  size_t num_entries = message.tag_value_map().size();
88  size_t pad_length = 0;
89  bool need_pad_tag = false;
90  bool need_pad_value = false;
91
92  size_t len = message.size();
93  if (len < message.minimum_size()) {
94    need_pad_tag = true;
95    need_pad_value = true;
96    num_entries++;
97
98    size_t delta = message.minimum_size() - len;
99    const size_t overhead = kQuicTagSize + kCryptoEndOffsetSize;
100    if (delta > overhead) {
101      pad_length = delta - overhead;
102    }
103    len += overhead + pad_length;
104  }
105
106  if (num_entries > kMaxEntries) {
107    return NULL;
108  }
109
110
111  QuicDataWriter writer(len);
112  if (!writer.WriteUInt32(message.tag())) {
113    DCHECK(false) << "Failed to write message tag.";
114    return NULL;
115  }
116  if (!writer.WriteUInt16(num_entries)) {
117    DCHECK(false) << "Failed to write size.";
118    return NULL;
119  }
120  if (!writer.WriteUInt16(0)) {
121    DCHECK(false) << "Failed to write padding.";
122    return NULL;
123  }
124
125  uint32 end_offset = 0;
126  // Tags and offsets
127  for (QuicTagValueMap::const_iterator it = message.tag_value_map().begin();
128       it != message.tag_value_map().end(); ++it) {
129    if (it->first == kPAD && need_pad_tag) {
130      // Existing PAD tags are only checked when padding needs to be added
131      // because parts of the code may need to reserialize received messages
132      // and those messages may, legitimately include padding.
133      DCHECK(false) << "Message needed padding but already contained a PAD tag";
134      return NULL;
135    }
136
137    if (it->first > kPAD && need_pad_tag) {
138      need_pad_tag = false;
139      if (!WritePadTag(&writer, pad_length, &end_offset)) {
140        return NULL;
141      }
142    }
143
144    if (!writer.WriteUInt32(it->first)) {
145      DCHECK(false) << "Failed to write tag.";
146      return NULL;
147    }
148    end_offset += it->second.length();
149    if (!writer.WriteUInt32(end_offset)) {
150      DCHECK(false) << "Failed to write end offset.";
151      return NULL;
152    }
153  }
154
155  if (need_pad_tag) {
156    if (!WritePadTag(&writer, pad_length, &end_offset)) {
157      return NULL;
158    }
159  }
160
161  // Values
162  for (QuicTagValueMap::const_iterator it = message.tag_value_map().begin();
163       it != message.tag_value_map().end(); ++it) {
164    if (it->first > kPAD && need_pad_value) {
165      need_pad_value = false;
166      if (!writer.WriteRepeatedByte('-', pad_length)) {
167        DCHECK(false) << "Failed to write padding.";
168        return NULL;
169      }
170    }
171
172    if (!writer.WriteBytes(it->second.data(), it->second.length())) {
173      DCHECK(false) << "Failed to write value.";
174      return NULL;
175    }
176  }
177
178  if (need_pad_value) {
179    if (!writer.WriteRepeatedByte('-', pad_length)) {
180      DCHECK(false) << "Failed to write padding.";
181      return NULL;
182    }
183  }
184
185  return new QuicData(writer.take(), len, true);
186}
187
188void CryptoFramer::Clear() {
189  message_.Clear();
190  tags_and_lengths_.clear();
191  error_ = QUIC_NO_ERROR;
192  state_ = STATE_READING_TAG;
193}
194
195QuicErrorCode CryptoFramer::Process(StringPiece input) {
196  // Add this data to the buffer.
197  buffer_.append(input.data(), input.length());
198  QuicDataReader reader(buffer_.data(), buffer_.length());
199
200  switch (state_) {
201    case STATE_READING_TAG:
202      if (reader.BytesRemaining() < kQuicTagSize) {
203        break;
204      }
205      QuicTag message_tag;
206      reader.ReadUInt32(&message_tag);
207      message_.set_tag(message_tag);
208      state_ = STATE_READING_NUM_ENTRIES;
209    case STATE_READING_NUM_ENTRIES:
210      if (reader.BytesRemaining() < kNumEntriesSize + sizeof(uint16)) {
211        break;
212      }
213      reader.ReadUInt16(&num_entries_);
214      if (num_entries_ > kMaxEntries) {
215        return QUIC_CRYPTO_TOO_MANY_ENTRIES;
216      }
217      uint16 padding;
218      reader.ReadUInt16(&padding);
219
220      tags_and_lengths_.reserve(num_entries_);
221      state_ = STATE_READING_TAGS_AND_LENGTHS;
222      values_len_ = 0;
223    case STATE_READING_TAGS_AND_LENGTHS: {
224      if (reader.BytesRemaining() <
225              num_entries_ * (kQuicTagSize + kCryptoEndOffsetSize)) {
226        break;
227      }
228
229      uint32 last_end_offset = 0;
230      for (unsigned i = 0; i < num_entries_; ++i) {
231        QuicTag tag;
232        reader.ReadUInt32(&tag);
233        if (i > 0 && tag <= tags_and_lengths_[i-1].first) {
234          if (tag == tags_and_lengths_[i-1].first) {
235            return QUIC_CRYPTO_DUPLICATE_TAG;
236          }
237          return QUIC_CRYPTO_TAGS_OUT_OF_ORDER;
238        }
239
240        uint32 end_offset;
241        reader.ReadUInt32(&end_offset);
242
243        if (end_offset < last_end_offset) {
244          return QUIC_CRYPTO_TAGS_OUT_OF_ORDER;
245        }
246        tags_and_lengths_.push_back(
247            make_pair(tag, static_cast<size_t>(end_offset - last_end_offset)));
248        last_end_offset = end_offset;
249      }
250      values_len_ = last_end_offset;
251      state_ = STATE_READING_VALUES;
252    }
253    case STATE_READING_VALUES:
254      if (reader.BytesRemaining() < values_len_) {
255        break;
256      }
257      for (vector<pair<QuicTag, size_t> >::const_iterator
258           it = tags_and_lengths_.begin(); it != tags_and_lengths_.end();
259           it++) {
260        StringPiece value;
261        reader.ReadStringPiece(&value, it->second);
262        message_.SetStringPiece(it->first, value);
263      }
264      visitor_->OnHandshakeMessage(message_);
265      Clear();
266      state_ = STATE_READING_TAG;
267      break;
268  }
269  // Save any remaining data.
270  buffer_ = reader.PeekRemainingPayload().as_string();
271  return QUIC_NO_ERROR;
272}
273
274// static
275bool CryptoFramer::WritePadTag(QuicDataWriter* writer,
276                               size_t pad_length,
277                               uint32* end_offset) {
278  if (!writer->WriteUInt32(kPAD)) {
279    DCHECK(false) << "Failed to write tag.";
280    return false;
281  }
282  *end_offset += pad_length;
283  if (!writer->WriteUInt32(*end_offset)) {
284    DCHECK(false) << "Failed to write end offset.";
285    return false;
286  }
287  return true;
288}
289
290}  // namespace net
291