Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(40)

Side by Side Diff: net/cert/ct_serialization.cc

Issue 1575543002: Minor improvements to ct_serialization.cc (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Addresses review comments Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/cert/ct_serialization.h" 5 #include "net/cert/ct_serialization.h"
6 6
7 #include <stdint.h> 7 #include <stdint.h>
8 8
9 #include <algorithm>
9 #include <limits> 10 #include <limits>
10 11
11 #include "base/logging.h" 12 #include "base/logging.h"
13 #include "base/numerics/safe_math.h"
12 14
13 namespace net { 15 namespace net {
14 16
15 namespace ct { 17 namespace ct {
16 18
17 namespace { 19 namespace {
18 20
19 // Note: length is always specified in bytes. 21 // Note: length is always specified in bytes.
20 // Signed Certificate Timestamp (SCT) Version length 22 // Signed Certificate Timestamp (SCT) Version length
21 const size_t kVersionLength = 1; 23 const size_t kVersionLength = 1;
(...skipping 16 matching lines...) Expand all
38 const size_t kSerializedSCTLengthBytes = 2; 40 const size_t kSerializedSCTLengthBytes = 2;
39 41
40 // Members of digitally-signed struct of a STH 42 // Members of digitally-signed struct of a STH
41 const size_t kTreeSizeLength = 8; 43 const size_t kTreeSizeLength = 8;
42 44
43 enum SignatureType { 45 enum SignatureType {
44 SIGNATURE_TYPE_CERTIFICATE_TIMESTAMP = 0, 46 SIGNATURE_TYPE_CERTIFICATE_TIMESTAMP = 0,
45 TREE_HASH = 1, 47 TREE_HASH = 1,
46 }; 48 };
47 49
50 // Returns the maximum value of an unsigned integer that is |length| bytes long.
51 // |length| can be up to 8.
52 uint64_t GetUintMaxValue(size_t length) {
53 DCHECK_LE(length, 8u);
54
55 // If length == 8, we can't calculate the max value because that would involve
56 // shifting a uint64_t left by 64 bits, which invokes undefined behaviour (see
57 // https://www.securecoding.cert.org/confluence/x/IRE). Instead, we just hard-
58 // code the result for that case.
Ryan Sleevi 2016/01/22 23:32:16 https://groups.google.com/a/chromium.org/forum/#!t
Rob Percival 2016/01/25 16:42:29 Acknowledged.
59 return length == 8 ? 0xffffffffffffffff : (uint64_t(1) << (length * 8)) - 1;
60 }
61
48 // Reads a TLS-encoded variable length unsigned integer from |in|. 62 // Reads a TLS-encoded variable length unsigned integer from |in|.
49 // The integer is expected to be in big-endian order, which is used by TLS. 63 // The integer is expected to be in big-endian order, which is used by TLS.
50 // The bytes read from |in| are discarded (i.e. |in|'s prefix removed) 64 // The bytes read from |in| are discarded (i.e. |in|'s prefix removed)
51 // |length| indicates the size (in bytes) of the integer. On success, returns 65 // |length| indicates the size (in bytes) of the integer. On success, returns
52 // true and stores the result in |*out|. 66 // true and stores the result in |*out|.
53 template <typename T> 67 template <typename T>
54 bool ReadUint(size_t length, base::StringPiece* in, T* out) { 68 bool ReadUint(size_t length, base::StringPiece* in, T* out) {
55 if (in->size() < length) 69 if (in->size() < length)
56 return false; 70 return false;
71 DCHECK_NE(length, 0u);
57 DCHECK_LE(length, sizeof(T)); 72 DCHECK_LE(length, sizeof(T));
58 73
59 T result = 0; 74 T result = static_cast<uint8_t>((*in)[0]);
60 for (size_t i = 0; i < length; ++i) { 75 // This loop only executes if sizeof(T) > 1, because the first operation is
61 result = (result << 8) | static_cast<unsigned char>((*in)[i]); 76 // to shift left by 1 byte, which is undefined behaviour if T is a 1 byte
77 // integer.
78 for (size_t i = 1; i < length; ++i) {
79 result = (result << 8) | static_cast<uint8_t>((*in)[i]);
62 } 80 }
63 in->remove_prefix(length); 81 in->remove_prefix(length);
64 *out = result; 82 *out = result;
65 return true; 83 return true;
66 } 84 }
67 85
68 // Reads a TLS-encoded field length from |in|. 86 // Reads a TLS-encoded field length from |in|.
69 // The bytes read from |in| are discarded (i.e. |in|'s prefix removed) 87 // The bytes read from |in| are discarded (i.e. |in|'s prefix removed).
70 // |prefix_length| indicates the bytes needed to represent the length (e.g. 3) 88 // |prefix_length| indicates the bytes needed to represent the length (e.g. 3).
89 // Max |prefix_length| is 8.
71 // success, returns true and stores the result in |*out|. 90 // success, returns true and stores the result in |*out|.
72 bool ReadLength(size_t prefix_length, base::StringPiece* in, size_t* out) { 91 bool ReadLength(size_t prefix_length, base::StringPiece* in, size_t* out) {
73 size_t length; 92 uint64_t length = 0;
74 if (!ReadUint(prefix_length, in, &length)) 93 if (!ReadUint(prefix_length, in, &length))
75 return false; 94 return false;
76 *out = length; 95 base::CheckedNumeric<size_t> checked_length = length;
96 if (!checked_length.IsValid())
97 return false;
98 *out = checked_length.ValueOrDie();
77 return true; 99 return true;
78 } 100 }
79 101
80 // Reads |length| bytes from |*in|. If |*in| is too small, returns false. 102 // Reads |length| bytes from |*in|. If |*in| is too small, returns false.
81 // The bytes read from |in| are discarded (i.e. |in|'s prefix removed) 103 // The bytes read from |in| are discarded (i.e. |in|'s prefix removed)
82 bool ReadFixedBytes(size_t length, 104 bool ReadFixedBytes(size_t length,
83 base::StringPiece* in, 105 base::StringPiece* in,
84 base::StringPiece* out) { 106 base::StringPiece* out) {
85 if (in->length() < length) 107 if (in->length() < length)
86 return false; 108 return false;
87 out->set(in->data(), length); 109 out->set(in->data(), length);
88 in->remove_prefix(length); 110 in->remove_prefix(length);
89 return true; 111 return true;
90 } 112 }
91 113
92 // Reads a length-prefixed variable amount of bytes from |in|, updating |out| 114 // Reads a length-prefixed variable amount of bytes from |in|, updating |out|
93 // on success. |prefix_length| indicates the number of bytes needed to represent 115 // on success. |prefix_length| indicates the number of bytes needed to represent
94 // the length. 116 // the length.
95 // The bytes read from |in| are discarded (i.e. |in|'s prefix removed) 117 // The bytes read from |in| are discarded (i.e. |in|'s prefix removed)
96 bool ReadVariableBytes(size_t prefix_length, 118 bool ReadVariableBytes(size_t prefix_length,
97 base::StringPiece* in, 119 base::StringPiece* in,
98 base::StringPiece* out) { 120 base::StringPiece* out) {
99 size_t length; 121 size_t length = 0;
100 if (!ReadLength(prefix_length, in, &length)) 122 if (!ReadLength(prefix_length, in, &length))
101 return false; 123 return false;
102 return ReadFixedBytes(length, in, out); 124 return ReadFixedBytes(length, in, out);
103 } 125 }
104 126
105 // Reads a variable-length list that has been TLS encoded. 127 // Reads a variable-length list that has been TLS encoded.
106 // The bytes read from |in| are discarded (i.e. |in|'s prefix removed) 128 // The bytes read from |in| are discarded (i.e. |in|'s prefix removed)
107 // |max_list_length| contains the overall length of the encoded list. 129 // |max_list_length| contains the overall length of the encoded list.
108 // |max_item_length| contains the maximum length of a single item. 130 // |max_item_length| contains the maximum length of a single item.
109 // On success, returns true and updates |*out| with the encoded list. 131 // On success, returns true and updates |*out| with the encoded list.
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
169 case DigitallySigned::SIG_ALGO_ECDSA: 191 case DigitallySigned::SIG_ALGO_ECDSA:
170 break; 192 break;
171 default: 193 default:
172 return false; 194 return false;
173 } 195 }
174 *out = static_cast<DigitallySigned::SignatureAlgorithm>(in); 196 *out = static_cast<DigitallySigned::SignatureAlgorithm>(in);
175 return true; 197 return true;
176 } 198 }
177 199
178 // Writes a TLS-encoded variable length unsigned integer to |output|. 200 // Writes a TLS-encoded variable length unsigned integer to |output|.
179 // |length| indicates the size (in bytes) of the integer. 201 // |length| indicates the size (in bytes) of the integer. This must be able to
202 // accomodate |value|.
180 // |value| the value itself to be written. 203 // |value| the value itself to be written.
181 template <typename T> 204 void WriteUint(size_t length, uint64_t value, std::string* output) {
182 void WriteUint(size_t length, T value, std::string* output) { 205 // Check that |value| fits into |length| bytes.
183 DCHECK_LE(length, sizeof(T)); 206 DCHECK(length >= sizeof(value) || value >> (length * 8) == 0);
184 DCHECK(length == sizeof(T) || value >> (length * 8) == 0);
185 207
186 for (; length > 0; --length) { 208 for (; length > 0; --length) {
187 output->push_back((value >> ((length - 1)* 8)) & 0xFF); 209 output->push_back((value >> ((length - 1) * 8)) & 0xFF);
188 } 210 }
189 } 211 }
190 212
191 // Writes an array to |output| from |input|. 213 // Writes an array to |output| from |input|.
192 // Should be used in one of two cases: 214 // Should be used in one of two cases:
193 // * The length of |input| has already been encoded into the |output| stream. 215 // * The length of |input| has already been encoded into the |output| stream.
194 // * The length of |input| is fixed and the reader is expected to specify that 216 // * The length of |input| is fixed and the reader is expected to specify that
195 // length when reading. 217 // length when reading.
196 // If the length of |input| is dynamic and data is expected to follow it, 218 // If the length of |input| is dynamic and data is expected to follow it,
197 // WriteVariableBytes must be used. 219 // WriteVariableBytes must be used.
198 void WriteEncodedBytes(const base::StringPiece& input, std::string* output) { 220 // Returns the number of bytes written (the length of |input|).
221 size_t WriteEncodedBytes(const base::StringPiece& input, std::string* output) {
199 input.AppendToString(output); 222 input.AppendToString(output);
223 return input.size();
200 } 224 }
201 225
202 // Writes a variable-length array to |output|. 226 // Writes a variable-length array to |output|.
203 // |prefix_length| indicates the number of bytes needed to represnt the length. 227 // |prefix_length| indicates the number of bytes needed to represent the length.
204 // |input| is the array itself. 228 // |input| is the array itself.
205 // If the size of |input| is less than 2^|prefix_length| - 1, encode the 229 // If 1 <= |prefix_length| <= 8 and the size of |input| is less than
206 // length and data and return true. Otherwise, return false. 230 // 2^|prefix_length| - 1, encode the length and data and return true.
231 // Otherwise, return false.
207 bool WriteVariableBytes(size_t prefix_length, 232 bool WriteVariableBytes(size_t prefix_length,
208 const base::StringPiece& input, 233 const base::StringPiece& input,
209 std::string* output) { 234 std::string* output) {
210 size_t input_size = input.size(); 235 size_t input_size = input.size();
211 size_t max_allowed_input_size = 236 size_t max_input_size =
212 static_cast<size_t>(((1 << (prefix_length * 8)) - 1)); 237 GetUintMaxValue(std::min(sizeof(size_t), prefix_length));
Ryan Sleevi 2016/01/22 23:32:16 I don't see why this is necessary. You already kno
Rob Percival 2016/01/25 16:42:29 Done.
213 if (input_size > max_allowed_input_size) 238
239 if (input_size > max_input_size)
214 return false; 240 return false;
215 241
216 WriteUint(prefix_length, input.size(), output); 242 WriteUint(prefix_length, input_size, output);
217 WriteEncodedBytes(input, output); 243 WriteEncodedBytes(input, output);
218 244
219 return true; 245 return true;
220 } 246 }
221 247
222 // Writes a LogEntry of type X.509 cert to |output|. 248 // Writes a LogEntry of type X.509 cert to |output|.
223 // |input| is the LogEntry containing the certificate. 249 // |input| is the LogEntry containing the certificate.
224 // Returns true if the leaf_certificate in the LogEntry does not exceed 250 // Returns true if the leaf_certificate in the LogEntry does not exceed
225 // kMaxAsn1CertificateLength and so can be written to |output|. 251 // kMaxAsn1CertificateLength and so can be written to |output|.
226 bool EncodeAsn1CertLogEntry(const LogEntry& input, std::string* output) { 252 bool EncodeAsn1CertLogEntry(const LogEntry& input, std::string* output) {
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
284 WriteUint(kLogEntryTypeLength, input.type, output); 310 WriteUint(kLogEntryTypeLength, input.type, output);
285 switch (input.type) { 311 switch (input.type) {
286 case LogEntry::LOG_ENTRY_TYPE_X509: 312 case LogEntry::LOG_ENTRY_TYPE_X509:
287 return EncodeAsn1CertLogEntry(input, output); 313 return EncodeAsn1CertLogEntry(input, output);
288 case LogEntry::LOG_ENTRY_TYPE_PRECERT: 314 case LogEntry::LOG_ENTRY_TYPE_PRECERT:
289 return EncodePrecertLogEntry(input, output); 315 return EncodePrecertLogEntry(input, output);
290 } 316 }
291 return false; 317 return false;
292 } 318 }
293 319
320 static bool ReadTimeSinceEpoch(base::StringPiece* input, base::Time* output) {
Ryan Sleevi 2016/01/22 23:32:16 Is this abstraction necessary? Will it ever be reu
Rob Percival 2016/01/25 16:42:29 Yes, it is reused in one of my next patches: https
321 uint64_t time_since_epoch = 0;
322 if (!ReadUint(kTimestampLength, input, &time_since_epoch)) {
323 return false;
324 }
Ryan Sleevi 2016/01/22 23:32:16 no braces
Rob Percival 2016/01/25 16:42:29 Done.
325
326 base::CheckedNumeric<int64_t> time_since_epoch_signed = time_since_epoch;
327
328 if (!time_since_epoch_signed.IsValid()) {
329 DVLOG(1) << "Timestamp value too big to cast to int64_t: "
330 << time_since_epoch;
331 return false;
332 }
333
334 *output =
335 base::Time::UnixEpoch() +
336 base::TimeDelta::FromMilliseconds(time_since_epoch_signed.ValueOrDie());
337
338 return true;
339 }
340
294 static void WriteTimeSinceEpoch(const base::Time& timestamp, 341 static void WriteTimeSinceEpoch(const base::Time& timestamp,
295 std::string* output) { 342 std::string* output) {
296 base::TimeDelta time_since_epoch = timestamp - base::Time::UnixEpoch(); 343 base::TimeDelta time_since_epoch = timestamp - base::Time::UnixEpoch();
297 WriteUint(kTimestampLength, time_since_epoch.InMilliseconds(), output); 344 WriteUint(kTimestampLength, time_since_epoch.InMilliseconds(), output);
298 } 345 }
299 346
300 bool EncodeV1SCTSignedData(const base::Time& timestamp, 347 bool EncodeV1SCTSignedData(const base::Time& timestamp,
301 const std::string& serialized_log_entry, 348 const std::string& serialized_log_entry,
302 const std::string& extensions, 349 const std::string& extensions,
303 std::string* output) { 350 std::string* output) {
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
344 new SignedCertificateTimestamp()); 391 new SignedCertificateTimestamp());
345 unsigned version; 392 unsigned version;
346 if (!ReadUint(kVersionLength, input, &version)) 393 if (!ReadUint(kVersionLength, input, &version))
347 return false; 394 return false;
348 if (version != SignedCertificateTimestamp::SCT_VERSION_1) { 395 if (version != SignedCertificateTimestamp::SCT_VERSION_1) {
349 DVLOG(1) << "Unsupported/invalid version " << version; 396 DVLOG(1) << "Unsupported/invalid version " << version;
350 return false; 397 return false;
351 } 398 }
352 399
353 result->version = SignedCertificateTimestamp::SCT_VERSION_1; 400 result->version = SignedCertificateTimestamp::SCT_VERSION_1;
354 uint64_t timestamp;
355 base::StringPiece log_id; 401 base::StringPiece log_id;
356 base::StringPiece extensions; 402 base::StringPiece extensions;
357 if (!ReadFixedBytes(kLogIdLength, input, &log_id) || 403 if (!ReadFixedBytes(kLogIdLength, input, &log_id) ||
358 !ReadUint(kTimestampLength, input, &timestamp) || 404 !ReadTimeSinceEpoch(input, &result->timestamp) ||
359 !ReadVariableBytes(kExtensionsLengthBytes, input, 405 !ReadVariableBytes(kExtensionsLengthBytes, input, &extensions) ||
360 &extensions) ||
361 !DecodeDigitallySigned(input, &result->signature)) { 406 !DecodeDigitallySigned(input, &result->signature)) {
362 return false; 407 return false;
363 } 408 }
364 409
365 if (timestamp > static_cast<uint64_t>(std::numeric_limits<int64_t>::max())) {
366 DVLOG(1) << "Timestamp value too big to cast to int64_t: " << timestamp;
367 return false;
368 }
369
370 log_id.CopyToString(&result->log_id); 410 log_id.CopyToString(&result->log_id);
371 extensions.CopyToString(&result->extensions); 411 extensions.CopyToString(&result->extensions);
372 result->timestamp =
373 base::Time::UnixEpoch() +
374 base::TimeDelta::FromMilliseconds(static_cast<int64_t>(timestamp));
375
376 output->swap(result); 412 output->swap(result);
377 return true; 413 return true;
378 } 414 }
379 415
380 bool EncodeSCTListForTesting(const base::StringPiece& sct, 416 bool EncodeSCTListForTesting(const base::StringPiece& sct,
381 std::string* output) { 417 std::string* output) {
382 std::string encoded_sct; 418 std::string encoded_sct;
383 return WriteVariableBytes(kSerializedSCTLengthBytes, sct, &encoded_sct) && 419 return WriteVariableBytes(kSerializedSCTLengthBytes, sct, &encoded_sct) &&
384 WriteVariableBytes(kSCTListLengthBytes, encoded_sct, output); 420 WriteVariableBytes(kSCTListLengthBytes, encoded_sct, output);
385 } 421 }
386 422
387 } // namespace ct 423 } // namespace ct
388 424
389 } // namespace net 425 } // namespace net
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698