QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 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/third_party/quiche/src/quic/core/crypto/crypto_framer.h" |
| 6 | |
| 7 | #include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" |
| 8 | #include "net/third_party/quiche/src/quic/core/quic_data_reader.h" |
| 9 | #include "net/third_party/quiche/src/quic/core/quic_data_writer.h" |
| 10 | #include "net/third_party/quiche/src/quic/core/quic_packets.h" |
| 11 | #include "net/third_party/quiche/src/quic/platform/api/quic_fallthrough.h" |
| 12 | #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" |
| 13 | #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" |
| 14 | #include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" |
| 15 | #include "net/third_party/quiche/src/quic/platform/api/quic_string.h" |
| 16 | #include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" |
| 17 | |
| 18 | namespace quic { |
| 19 | |
| 20 | namespace { |
| 21 | |
| 22 | const size_t kQuicTagSize = sizeof(QuicTag); |
| 23 | const size_t kCryptoEndOffsetSize = sizeof(uint32_t); |
| 24 | const size_t kNumEntriesSize = sizeof(uint16_t); |
| 25 | |
| 26 | // OneShotVisitor is a framer visitor that records a single handshake message. |
| 27 | class OneShotVisitor : public CryptoFramerVisitorInterface { |
| 28 | public: |
| 29 | OneShotVisitor() : error_(false) {} |
| 30 | |
| 31 | void OnError(CryptoFramer* framer) override { error_ = true; } |
| 32 | |
| 33 | void OnHandshakeMessage(const CryptoHandshakeMessage& message) override { |
| 34 | out_ = QuicMakeUnique<CryptoHandshakeMessage>(message); |
| 35 | } |
| 36 | |
| 37 | bool error() const { return error_; } |
| 38 | |
| 39 | std::unique_ptr<CryptoHandshakeMessage> release() { return std::move(out_); } |
| 40 | |
| 41 | private: |
| 42 | std::unique_ptr<CryptoHandshakeMessage> out_; |
| 43 | bool error_; |
| 44 | }; |
| 45 | |
| 46 | } // namespace |
| 47 | |
| 48 | CryptoFramer::CryptoFramer() |
| 49 | : visitor_(nullptr), |
| 50 | error_detail_(""), |
| 51 | num_entries_(0), |
| 52 | values_len_(0), |
| 53 | process_truncated_messages_(false) { |
| 54 | Clear(); |
| 55 | } |
| 56 | |
| 57 | CryptoFramer::~CryptoFramer() {} |
| 58 | |
| 59 | // static |
| 60 | std::unique_ptr<CryptoHandshakeMessage> CryptoFramer::ParseMessage( |
| 61 | QuicStringPiece in) { |
| 62 | OneShotVisitor visitor; |
| 63 | CryptoFramer framer; |
| 64 | |
| 65 | framer.set_visitor(&visitor); |
| 66 | if (!framer.ProcessInput(in) || visitor.error() || |
| 67 | framer.InputBytesRemaining()) { |
| 68 | return nullptr; |
| 69 | } |
| 70 | |
| 71 | return visitor.release(); |
| 72 | } |
| 73 | |
| 74 | QuicErrorCode CryptoFramer::error() const { |
| 75 | return error_; |
| 76 | } |
| 77 | |
vasilvv | c48c871 | 2019-03-11 13:38:16 -0700 | [diff] [blame^] | 78 | const std::string& CryptoFramer::error_detail() const { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 79 | return error_detail_; |
| 80 | } |
| 81 | |
| 82 | bool CryptoFramer::ProcessInput(QuicStringPiece input, EncryptionLevel level) { |
| 83 | return ProcessInput(input); |
| 84 | } |
| 85 | |
| 86 | bool CryptoFramer::ProcessInput(QuicStringPiece input) { |
| 87 | DCHECK_EQ(QUIC_NO_ERROR, error_); |
| 88 | if (error_ != QUIC_NO_ERROR) { |
| 89 | return false; |
| 90 | } |
| 91 | error_ = Process(input); |
| 92 | if (error_ != QUIC_NO_ERROR) { |
| 93 | DCHECK(!error_detail_.empty()); |
| 94 | visitor_->OnError(this); |
| 95 | return false; |
| 96 | } |
| 97 | |
| 98 | return true; |
| 99 | } |
| 100 | |
| 101 | size_t CryptoFramer::InputBytesRemaining() const { |
| 102 | return buffer_.length(); |
| 103 | } |
| 104 | |
| 105 | bool CryptoFramer::HasTag(QuicTag tag) const { |
| 106 | if (state_ != STATE_READING_VALUES) { |
| 107 | return false; |
| 108 | } |
| 109 | for (const auto& it : tags_and_lengths_) { |
| 110 | if (it.first == tag) { |
| 111 | return true; |
| 112 | } |
| 113 | } |
| 114 | return false; |
| 115 | } |
| 116 | |
| 117 | void CryptoFramer::ForceHandshake() { |
| 118 | QuicDataReader reader(buffer_.data(), buffer_.length(), HOST_BYTE_ORDER); |
| 119 | for (const std::pair<QuicTag, size_t>& item : tags_and_lengths_) { |
| 120 | QuicStringPiece value; |
| 121 | if (reader.BytesRemaining() < item.second) { |
| 122 | break; |
| 123 | } |
| 124 | reader.ReadStringPiece(&value, item.second); |
| 125 | message_.SetStringPiece(item.first, value); |
| 126 | } |
| 127 | visitor_->OnHandshakeMessage(message_); |
| 128 | } |
| 129 | |
| 130 | // static |
| 131 | QuicData* CryptoFramer::ConstructHandshakeMessage( |
| 132 | const CryptoHandshakeMessage& message) { |
| 133 | size_t num_entries = message.tag_value_map().size(); |
| 134 | size_t pad_length = 0; |
| 135 | bool need_pad_tag = false; |
| 136 | bool need_pad_value = false; |
| 137 | |
| 138 | size_t len = message.size(); |
| 139 | if (len < message.minimum_size()) { |
| 140 | need_pad_tag = true; |
| 141 | need_pad_value = true; |
| 142 | num_entries++; |
| 143 | |
| 144 | size_t delta = message.minimum_size() - len; |
| 145 | const size_t overhead = kQuicTagSize + kCryptoEndOffsetSize; |
| 146 | if (delta > overhead) { |
| 147 | pad_length = delta - overhead; |
| 148 | } |
| 149 | len += overhead + pad_length; |
| 150 | } |
| 151 | |
| 152 | if (num_entries > kMaxEntries) { |
| 153 | return nullptr; |
| 154 | } |
| 155 | |
| 156 | std::unique_ptr<char[]> buffer(new char[len]); |
| 157 | QuicDataWriter writer(len, buffer.get(), HOST_BYTE_ORDER); |
| 158 | if (!writer.WriteTag(message.tag())) { |
| 159 | DCHECK(false) << "Failed to write message tag."; |
| 160 | return nullptr; |
| 161 | } |
| 162 | if (!writer.WriteUInt16(static_cast<uint16_t>(num_entries))) { |
| 163 | DCHECK(false) << "Failed to write size."; |
| 164 | return nullptr; |
| 165 | } |
| 166 | if (!writer.WriteUInt16(0)) { |
| 167 | DCHECK(false) << "Failed to write padding."; |
| 168 | return nullptr; |
| 169 | } |
| 170 | |
| 171 | uint32_t end_offset = 0; |
| 172 | // Tags and offsets |
| 173 | for (auto it = message.tag_value_map().begin(); |
| 174 | it != message.tag_value_map().end(); ++it) { |
| 175 | if (it->first == kPAD && need_pad_tag) { |
| 176 | // Existing PAD tags are only checked when padding needs to be added |
| 177 | // because parts of the code may need to reserialize received messages |
| 178 | // and those messages may, legitimately include padding. |
| 179 | DCHECK(false) << "Message needed padding but already contained a PAD tag"; |
| 180 | return nullptr; |
| 181 | } |
| 182 | |
| 183 | if (it->first > kPAD && need_pad_tag) { |
| 184 | need_pad_tag = false; |
| 185 | if (!WritePadTag(&writer, pad_length, &end_offset)) { |
| 186 | return nullptr; |
| 187 | } |
| 188 | } |
| 189 | |
| 190 | if (!writer.WriteTag(it->first)) { |
| 191 | DCHECK(false) << "Failed to write tag."; |
| 192 | return nullptr; |
| 193 | } |
| 194 | end_offset += it->second.length(); |
| 195 | if (!writer.WriteUInt32(end_offset)) { |
| 196 | DCHECK(false) << "Failed to write end offset."; |
| 197 | return nullptr; |
| 198 | } |
| 199 | } |
| 200 | |
| 201 | if (need_pad_tag) { |
| 202 | if (!WritePadTag(&writer, pad_length, &end_offset)) { |
| 203 | return nullptr; |
| 204 | } |
| 205 | } |
| 206 | |
| 207 | // Values |
| 208 | for (auto it = message.tag_value_map().begin(); |
| 209 | it != message.tag_value_map().end(); ++it) { |
| 210 | if (it->first > kPAD && need_pad_value) { |
| 211 | need_pad_value = false; |
| 212 | if (!writer.WriteRepeatedByte('-', pad_length)) { |
| 213 | DCHECK(false) << "Failed to write padding."; |
| 214 | return nullptr; |
| 215 | } |
| 216 | } |
| 217 | |
| 218 | if (!writer.WriteBytes(it->second.data(), it->second.length())) { |
| 219 | DCHECK(false) << "Failed to write value."; |
| 220 | return nullptr; |
| 221 | } |
| 222 | } |
| 223 | |
| 224 | if (need_pad_value) { |
| 225 | if (!writer.WriteRepeatedByte('-', pad_length)) { |
| 226 | DCHECK(false) << "Failed to write padding."; |
| 227 | return nullptr; |
| 228 | } |
| 229 | } |
| 230 | |
| 231 | return new QuicData(buffer.release(), len, true); |
| 232 | } |
| 233 | |
| 234 | void CryptoFramer::Clear() { |
| 235 | message_.Clear(); |
| 236 | tags_and_lengths_.clear(); |
| 237 | error_ = QUIC_NO_ERROR; |
| 238 | error_detail_ = ""; |
| 239 | state_ = STATE_READING_TAG; |
| 240 | } |
| 241 | |
| 242 | QuicErrorCode CryptoFramer::Process(QuicStringPiece input) { |
| 243 | // Add this data to the buffer. |
| 244 | buffer_.append(input.data(), input.length()); |
| 245 | QuicDataReader reader(buffer_.data(), buffer_.length(), HOST_BYTE_ORDER); |
| 246 | |
| 247 | switch (state_) { |
| 248 | case STATE_READING_TAG: |
| 249 | if (reader.BytesRemaining() < kQuicTagSize) { |
| 250 | break; |
| 251 | } |
| 252 | QuicTag message_tag; |
| 253 | reader.ReadTag(&message_tag); |
| 254 | message_.set_tag(message_tag); |
| 255 | state_ = STATE_READING_NUM_ENTRIES; |
| 256 | QUIC_FALLTHROUGH_INTENDED; |
| 257 | case STATE_READING_NUM_ENTRIES: |
| 258 | if (reader.BytesRemaining() < kNumEntriesSize + sizeof(uint16_t)) { |
| 259 | break; |
| 260 | } |
| 261 | reader.ReadUInt16(&num_entries_); |
| 262 | if (num_entries_ > kMaxEntries) { |
| 263 | error_detail_ = QuicStrCat(num_entries_, " entries"); |
| 264 | return QUIC_CRYPTO_TOO_MANY_ENTRIES; |
| 265 | } |
| 266 | uint16_t padding; |
| 267 | reader.ReadUInt16(&padding); |
| 268 | |
| 269 | tags_and_lengths_.reserve(num_entries_); |
| 270 | state_ = STATE_READING_TAGS_AND_LENGTHS; |
| 271 | values_len_ = 0; |
| 272 | QUIC_FALLTHROUGH_INTENDED; |
| 273 | case STATE_READING_TAGS_AND_LENGTHS: { |
| 274 | if (reader.BytesRemaining() < |
| 275 | num_entries_ * (kQuicTagSize + kCryptoEndOffsetSize)) { |
| 276 | break; |
| 277 | } |
| 278 | |
| 279 | uint32_t last_end_offset = 0; |
| 280 | for (unsigned i = 0; i < num_entries_; ++i) { |
| 281 | QuicTag tag; |
| 282 | reader.ReadTag(&tag); |
| 283 | if (i > 0 && tag <= tags_and_lengths_[i - 1].first) { |
| 284 | if (tag == tags_and_lengths_[i - 1].first) { |
| 285 | error_detail_ = QuicStrCat("Duplicate tag:", tag); |
| 286 | return QUIC_CRYPTO_DUPLICATE_TAG; |
| 287 | } |
| 288 | error_detail_ = QuicStrCat("Tag ", tag, " out of order"); |
| 289 | return QUIC_CRYPTO_TAGS_OUT_OF_ORDER; |
| 290 | } |
| 291 | |
| 292 | uint32_t end_offset; |
| 293 | reader.ReadUInt32(&end_offset); |
| 294 | |
| 295 | if (end_offset < last_end_offset) { |
| 296 | error_detail_ = |
| 297 | QuicStrCat("End offset: ", end_offset, " vs ", last_end_offset); |
| 298 | return QUIC_CRYPTO_TAGS_OUT_OF_ORDER; |
| 299 | } |
| 300 | tags_and_lengths_.push_back(std::make_pair( |
| 301 | tag, static_cast<size_t>(end_offset - last_end_offset))); |
| 302 | last_end_offset = end_offset; |
| 303 | } |
| 304 | values_len_ = last_end_offset; |
| 305 | state_ = STATE_READING_VALUES; |
| 306 | QUIC_FALLTHROUGH_INTENDED; |
| 307 | } |
| 308 | case STATE_READING_VALUES: |
| 309 | if (reader.BytesRemaining() < values_len_) { |
| 310 | if (!process_truncated_messages_) { |
| 311 | break; |
| 312 | } |
| 313 | QUIC_LOG(ERROR) << "Trunacted message. Missing " |
| 314 | << values_len_ - reader.BytesRemaining() << " bytes."; |
| 315 | } |
| 316 | for (const std::pair<QuicTag, size_t>& item : tags_and_lengths_) { |
| 317 | QuicStringPiece value; |
| 318 | if (!reader.ReadStringPiece(&value, item.second)) { |
| 319 | DCHECK(process_truncated_messages_); |
| 320 | // Store an empty value. |
| 321 | message_.SetStringPiece(item.first, ""); |
| 322 | continue; |
| 323 | } |
| 324 | message_.SetStringPiece(item.first, value); |
| 325 | } |
| 326 | visitor_->OnHandshakeMessage(message_); |
| 327 | Clear(); |
| 328 | state_ = STATE_READING_TAG; |
| 329 | break; |
| 330 | } |
| 331 | // Save any remaining data. |
vasilvv | c48c871 | 2019-03-11 13:38:16 -0700 | [diff] [blame^] | 332 | buffer_ = std::string(reader.PeekRemainingPayload()); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 333 | return QUIC_NO_ERROR; |
| 334 | } |
| 335 | |
| 336 | // static |
| 337 | bool CryptoFramer::WritePadTag(QuicDataWriter* writer, |
| 338 | size_t pad_length, |
| 339 | uint32_t* end_offset) { |
| 340 | if (!writer->WriteTag(kPAD)) { |
| 341 | DCHECK(false) << "Failed to write tag."; |
| 342 | return false; |
| 343 | } |
| 344 | *end_offset += pad_length; |
| 345 | if (!writer->WriteUInt32(*end_offset)) { |
| 346 | DCHECK(false) << "Failed to write end offset."; |
| 347 | return false; |
| 348 | } |
| 349 | return true; |
| 350 | } |
| 351 | |
| 352 | } // namespace quic |