crypto_framer.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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;
12
13namespace net {
14
15namespace {
16
17const size_t kCryptoTagSize = sizeof(uint32);
18const size_t kNumEntriesSize = sizeof(uint16);
19const size_t kValueLenSize = sizeof(uint16);
20
21// OneShotVisitor is a framer visitor that records a single handshake message.
22class OneShotVisitor : public CryptoFramerVisitorInterface {
23 public:
24  explicit OneShotVisitor(CryptoHandshakeMessage* out)
25      : out_(out),
26        error_(false) {
27  }
28
29  virtual void OnError(CryptoFramer* framer) OVERRIDE {
30    error_ = true;
31  }
32
33  virtual void OnHandshakeMessage(
34      const CryptoHandshakeMessage& message) OVERRIDE {
35    *out_ = message;
36  }
37
38  bool error() const {
39    return error_;
40  }
41
42 private:
43  CryptoHandshakeMessage* const out_;
44  bool error_;
45};
46
47}  // namespace
48
49CryptoFramer::CryptoFramer()
50    : visitor_(NULL),
51      message_tag_(0),
52      num_entries_(0),
53      values_len_(0) {
54  Clear();
55}
56
57CryptoFramer::~CryptoFramer() {}
58
59// static
60CryptoHandshakeMessage* CryptoFramer::ParseMessage(StringPiece in) {
61  scoped_ptr<CryptoHandshakeMessage> msg(new CryptoHandshakeMessage);
62  OneShotVisitor visitor(msg.get());
63  CryptoFramer framer;
64
65  framer.set_visitor(&visitor);
66  if (!framer.ProcessInput(in) ||
67      visitor.error() ||
68      framer.InputBytesRemaining()) {
69    return NULL;
70  }
71
72  return msg.release();
73}
74
75bool CryptoFramer::ProcessInput(StringPiece input) {
76  DCHECK_EQ(QUIC_NO_ERROR, error_);
77  if (error_ != QUIC_NO_ERROR) {
78    return false;
79  }
80  // Add this data to the buffer.
81  buffer_.append(input.data(), input.length());
82  QuicDataReader reader(buffer_.data(), buffer_.length());
83
84  switch (state_) {
85    case STATE_READING_TAG:
86      if (reader.BytesRemaining() < kCryptoTagSize) {
87        break;
88      }
89      reader.ReadUInt32(&message_tag_);
90      state_ = STATE_READING_NUM_ENTRIES;
91    case STATE_READING_NUM_ENTRIES:
92      if (reader.BytesRemaining() < kNumEntriesSize) {
93        break;
94      }
95      reader.ReadUInt16(&num_entries_);
96      if (num_entries_ > kMaxEntries) {
97        error_ = QUIC_CRYPTO_TOO_MANY_ENTRIES;
98        return false;
99      }
100      state_ = STATE_READING_KEY_TAGS;
101    case STATE_READING_KEY_TAGS:
102      if (reader.BytesRemaining() < num_entries_ * kCryptoTagSize) {
103        break;
104      }
105      for (int i = 0; i < num_entries_; ++i) {
106        CryptoTag tag;
107        reader.ReadUInt32(&tag);
108        if (i > 0 && tag <= tags_.back()) {
109          error_ = QUIC_CRYPTO_TAGS_OUT_OF_ORDER;
110          return false;
111        }
112        tags_.push_back(tag);
113      }
114      state_ = STATE_READING_LENGTHS;
115    case STATE_READING_LENGTHS: {
116      size_t expected_bytes = num_entries_ * kValueLenSize;
117      bool has_padding = (num_entries_ % 2 == 1);
118      if (has_padding) {
119        expected_bytes += kValueLenSize;
120      }
121      if (reader.BytesRemaining() < expected_bytes) {
122        break;
123      }
124      values_len_ = 0;
125      for (int i = 0; i < num_entries_; ++i) {
126        uint16 len;
127        reader.ReadUInt16(&len);
128        tag_length_map_[tags_[i]] = len;
129        values_len_ += len;
130      }
131      // Possible padding
132      if (has_padding) {
133        uint16 len;
134        reader.ReadUInt16(&len);
135        if (len != 0) {
136          error_ = QUIC_CRYPTO_INVALID_VALUE_LENGTH;
137          return false;
138        }
139      }
140      state_ = STATE_READING_VALUES;
141    }
142    case STATE_READING_VALUES:
143      if (reader.BytesRemaining() < values_len_) {
144        break;
145      }
146      for (int i = 0; i < num_entries_; ++i) {
147        StringPiece value;
148        reader.ReadStringPiece(&value, tag_length_map_[tags_[i]]);
149        tag_value_map_[tags_[i]] = value.as_string();
150      }
151      CryptoHandshakeMessage message;
152      message.tag = message_tag_;
153      message.tag_value_map.swap(tag_value_map_);
154      visitor_->OnHandshakeMessage(message);
155      Clear();
156      state_ = STATE_READING_TAG;
157      break;
158  }
159  // Save any remaining data.
160  buffer_ = reader.PeekRemainingPayload().as_string();
161  return true;
162}
163
164// static
165QuicData* CryptoFramer::ConstructHandshakeMessage(
166    const CryptoHandshakeMessage& message) {
167  if (message.tag_value_map.size() > kMaxEntries) {
168    return NULL;
169  }
170  size_t len = sizeof(uint32);  // message tag
171  len += sizeof(uint16);  // number of map entries
172  CryptoTagValueMap::const_iterator it = message.tag_value_map.begin();
173  while (it != message.tag_value_map.end()) {
174    len += sizeof(uint32);  // tag
175    len += sizeof(uint16);  // value len
176    len += it->second.length(); // value
177    ++it;
178  }
179  if (message.tag_value_map.size() % 2 == 1) {
180    len += sizeof(uint16);  // padding
181  }
182
183  QuicDataWriter writer(len);
184  if (!writer.WriteUInt32(message.tag)) {
185    DCHECK(false) << "Failed to write message tag.";
186    return NULL;
187  }
188  if (!writer.WriteUInt16(message.tag_value_map.size())) {
189    DCHECK(false) << "Failed to write size.";
190    return NULL;
191  }
192  // Tags
193  for (it = message.tag_value_map.begin();
194       it != message.tag_value_map.end(); ++it) {
195    if (!writer.WriteUInt32(it->first)) {
196      DCHECK(false) << "Failed to write tag.";
197      return NULL;
198    }
199  }
200  // Lengths
201  for (it = message.tag_value_map.begin();
202       it != message.tag_value_map.end(); ++it) {
203    if (!writer.WriteUInt16(it->second.length())) {
204      DCHECK(false) << "Failed to write length.";
205      return NULL;
206    }
207  }
208  // Possible padding
209  if (message.tag_value_map.size() % 2 == 1) {
210    if (!writer.WriteUInt16(0)) {
211      DCHECK(false) << "Failed to write padding.";
212      return NULL;
213    }
214  }
215  // Values
216  for (it = message.tag_value_map.begin();
217       it != message.tag_value_map.end(); ++it) {
218    if (!writer.WriteBytes(it->second.data(), it->second.length())) {
219      DCHECK(false) << "Failed to write value.";
220      return NULL;
221    }
222  }
223  return new QuicData(writer.take(), len, true);
224}
225
226void CryptoFramer::Clear() {
227  tag_value_map_.clear();
228  tag_length_map_.clear();
229  tags_.clear();
230  error_ = QUIC_NO_ERROR;
231  state_ = STATE_READING_TAG;
232}
233
234}  // namespace net
235