xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_framer.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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 "quiche/quic/core/crypto/crypto_framer.h"
6 
7 #include <string>
8 #include <utility>
9 
10 #include "absl/base/attributes.h"
11 #include "absl/strings/str_cat.h"
12 #include "absl/strings/string_view.h"
13 #include "quiche/quic/core/crypto/crypto_protocol.h"
14 #include "quiche/quic/core/quic_data_reader.h"
15 #include "quiche/quic/core/quic_data_writer.h"
16 #include "quiche/quic/core/quic_packets.h"
17 #include "quiche/quic/platform/api/quic_logging.h"
18 #include "quiche/common/quiche_endian.h"
19 
20 namespace quic {
21 
22 namespace {
23 
24 const size_t kQuicTagSize = sizeof(QuicTag);
25 const size_t kCryptoEndOffsetSize = sizeof(uint32_t);
26 const size_t kNumEntriesSize = sizeof(uint16_t);
27 
28 // OneShotVisitor is a framer visitor that records a single handshake message.
29 class OneShotVisitor : public CryptoFramerVisitorInterface {
30  public:
OneShotVisitor()31   OneShotVisitor() : error_(false) {}
32 
OnError(CryptoFramer *)33   void OnError(CryptoFramer* /*framer*/) override { error_ = true; }
34 
OnHandshakeMessage(const CryptoHandshakeMessage & message)35   void OnHandshakeMessage(const CryptoHandshakeMessage& message) override {
36     out_ = std::make_unique<CryptoHandshakeMessage>(message);
37   }
38 
error() const39   bool error() const { return error_; }
40 
release()41   std::unique_ptr<CryptoHandshakeMessage> release() { return std::move(out_); }
42 
43  private:
44   std::unique_ptr<CryptoHandshakeMessage> out_;
45   bool error_;
46 };
47 
48 }  // namespace
49 
CryptoFramer()50 CryptoFramer::CryptoFramer()
51     : visitor_(nullptr),
52       error_detail_(""),
53       num_entries_(0),
54       values_len_(0),
55       process_truncated_messages_(false) {
56   Clear();
57 }
58 
~CryptoFramer()59 CryptoFramer::~CryptoFramer() {}
60 
61 // static
ParseMessage(absl::string_view in)62 std::unique_ptr<CryptoHandshakeMessage> CryptoFramer::ParseMessage(
63     absl::string_view in) {
64   OneShotVisitor visitor;
65   CryptoFramer framer;
66 
67   framer.set_visitor(&visitor);
68   if (!framer.ProcessInput(in) || visitor.error() ||
69       framer.InputBytesRemaining()) {
70     return nullptr;
71   }
72 
73   return visitor.release();
74 }
75 
error() const76 QuicErrorCode CryptoFramer::error() const { return error_; }
77 
error_detail() const78 const std::string& CryptoFramer::error_detail() const { return error_detail_; }
79 
ProcessInput(absl::string_view input,EncryptionLevel)80 bool CryptoFramer::ProcessInput(absl::string_view input,
81                                 EncryptionLevel /*level*/) {
82   return ProcessInput(input);
83 }
84 
ProcessInput(absl::string_view input)85 bool CryptoFramer::ProcessInput(absl::string_view input) {
86   QUICHE_DCHECK_EQ(QUIC_NO_ERROR, error_);
87   if (error_ != QUIC_NO_ERROR) {
88     return false;
89   }
90   error_ = Process(input);
91   if (error_ != QUIC_NO_ERROR) {
92     QUICHE_DCHECK(!error_detail_.empty());
93     visitor_->OnError(this);
94     return false;
95   }
96 
97   return true;
98 }
99 
InputBytesRemaining() const100 size_t CryptoFramer::InputBytesRemaining() const { return buffer_.length(); }
101 
HasTag(QuicTag tag) const102 bool CryptoFramer::HasTag(QuicTag tag) const {
103   if (state_ != STATE_READING_VALUES) {
104     return false;
105   }
106   for (const auto& it : tags_and_lengths_) {
107     if (it.first == tag) {
108       return true;
109     }
110   }
111   return false;
112 }
113 
ForceHandshake()114 void CryptoFramer::ForceHandshake() {
115   QuicDataReader reader(buffer_.data(), buffer_.length(),
116                         quiche::HOST_BYTE_ORDER);
117   for (const std::pair<QuicTag, size_t>& item : tags_and_lengths_) {
118     absl::string_view value;
119     if (reader.BytesRemaining() < item.second) {
120       break;
121     }
122     reader.ReadStringPiece(&value, item.second);
123     message_.SetStringPiece(item.first, value);
124   }
125   visitor_->OnHandshakeMessage(message_);
126 }
127 
128 // static
ConstructHandshakeMessage(const CryptoHandshakeMessage & message)129 std::unique_ptr<QuicData> CryptoFramer::ConstructHandshakeMessage(
130     const CryptoHandshakeMessage& message) {
131   size_t num_entries = message.tag_value_map().size();
132   size_t pad_length = 0;
133   bool need_pad_tag = false;
134   bool need_pad_value = false;
135 
136   size_t len = message.size();
137   if (len < message.minimum_size()) {
138     need_pad_tag = true;
139     need_pad_value = true;
140     num_entries++;
141 
142     size_t delta = message.minimum_size() - len;
143     const size_t overhead = kQuicTagSize + kCryptoEndOffsetSize;
144     if (delta > overhead) {
145       pad_length = delta - overhead;
146     }
147     len += overhead + pad_length;
148   }
149 
150   if (num_entries > kMaxEntries) {
151     return nullptr;
152   }
153 
154   std::unique_ptr<char[]> buffer(new char[len]);
155   QuicDataWriter writer(len, buffer.get(), quiche::HOST_BYTE_ORDER);
156   if (!writer.WriteTag(message.tag())) {
157     QUICHE_DCHECK(false) << "Failed to write message tag.";
158     return nullptr;
159   }
160   if (!writer.WriteUInt16(static_cast<uint16_t>(num_entries))) {
161     QUICHE_DCHECK(false) << "Failed to write size.";
162     return nullptr;
163   }
164   if (!writer.WriteUInt16(0)) {
165     QUICHE_DCHECK(false) << "Failed to write padding.";
166     return nullptr;
167   }
168 
169   uint32_t end_offset = 0;
170   // Tags and offsets
171   for (auto it = message.tag_value_map().begin();
172        it != message.tag_value_map().end(); ++it) {
173     if (it->first == kPAD && need_pad_tag) {
174       // Existing PAD tags are only checked when padding needs to be added
175       // because parts of the code may need to reserialize received messages
176       // and those messages may, legitimately include padding.
177       QUICHE_DCHECK(false)
178           << "Message needed padding but already contained a PAD tag";
179       return nullptr;
180     }
181 
182     if (it->first > kPAD && need_pad_tag) {
183       need_pad_tag = false;
184       if (!WritePadTag(&writer, pad_length, &end_offset)) {
185         return nullptr;
186       }
187     }
188 
189     if (!writer.WriteTag(it->first)) {
190       QUICHE_DCHECK(false) << "Failed to write tag.";
191       return nullptr;
192     }
193     end_offset += it->second.length();
194     if (!writer.WriteUInt32(end_offset)) {
195       QUICHE_DCHECK(false) << "Failed to write end offset.";
196       return nullptr;
197     }
198   }
199 
200   if (need_pad_tag) {
201     if (!WritePadTag(&writer, pad_length, &end_offset)) {
202       return nullptr;
203     }
204   }
205 
206   // Values
207   for (auto it = message.tag_value_map().begin();
208        it != message.tag_value_map().end(); ++it) {
209     if (it->first > kPAD && need_pad_value) {
210       need_pad_value = false;
211       if (!writer.WriteRepeatedByte('-', pad_length)) {
212         QUICHE_DCHECK(false) << "Failed to write padding.";
213         return nullptr;
214       }
215     }
216 
217     if (!writer.WriteBytes(it->second.data(), it->second.length())) {
218       QUICHE_DCHECK(false) << "Failed to write value.";
219       return nullptr;
220     }
221   }
222 
223   if (need_pad_value) {
224     if (!writer.WriteRepeatedByte('-', pad_length)) {
225       QUICHE_DCHECK(false) << "Failed to write padding.";
226       return nullptr;
227     }
228   }
229 
230   return std::make_unique<QuicData>(buffer.release(), len, true);
231 }
232 
Clear()233 void CryptoFramer::Clear() {
234   message_.Clear();
235   tags_and_lengths_.clear();
236   error_ = QUIC_NO_ERROR;
237   error_detail_ = "";
238   state_ = STATE_READING_TAG;
239 }
240 
Process(absl::string_view input)241 QuicErrorCode CryptoFramer::Process(absl::string_view input) {
242   // Add this data to the buffer.
243   buffer_.append(input.data(), input.length());
244   QuicDataReader reader(buffer_.data(), buffer_.length(),
245                         quiche::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       ABSL_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_ = absl::StrCat(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       ABSL_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_ = absl::StrCat("Duplicate tag:", tag);
286             return QUIC_CRYPTO_DUPLICATE_TAG;
287           }
288           error_detail_ = absl::StrCat("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               absl::StrCat("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       ABSL_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         absl::string_view value;
318         if (!reader.ReadStringPiece(&value, item.second)) {
319           QUICHE_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.
332   buffer_ = std::string(reader.PeekRemainingPayload());
333   return QUIC_NO_ERROR;
334 }
335 
336 // static
WritePadTag(QuicDataWriter * writer,size_t pad_length,uint32_t * end_offset)337 bool CryptoFramer::WritePadTag(QuicDataWriter* writer, size_t pad_length,
338                                uint32_t* end_offset) {
339   if (!writer->WriteTag(kPAD)) {
340     QUICHE_DCHECK(false) << "Failed to write tag.";
341     return false;
342   }
343   *end_offset += pad_length;
344   if (!writer->WriteUInt32(*end_offset)) {
345     QUICHE_DCHECK(false) << "Failed to write end offset.";
346     return false;
347   }
348   return true;
349 }
350 
351 }  // namespace quic
352