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