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