Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2013 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/cert/ct_serialization.h" | |
| 6 | |
| 7 #include "base/basictypes.h" | |
| 8 #include "base/logging.h" | |
| 9 | |
| 10 namespace net { | |
| 11 | |
| 12 namespace ct { | |
| 13 | |
| 14 namespace { | |
| 15 | |
| 16 // SCT Version length | |
|
wtc
2013/10/24 23:14:23
Nit: define "SCT".
Eran M. (Google)
2013/10/30 18:00:08
Done.
| |
| 17 const size_t kVersionLengthInBytes = 1; | |
|
wtc
2013/10/24 23:14:23
Since all of the lengths in this file are in bytes
Eran M. (Google)
2013/10/30 18:00:08
Done.
| |
| 18 | |
| 19 // Members of a V1 SCT | |
| 20 const size_t kLogIdLengthInBytes = 32; | |
| 21 const size_t kTimestampLengthInBytes = 8; | |
| 22 const size_t kMaxExtensionsLengthInBytes = (1 << 16) - 1; | |
| 23 const size_t kHashAlgorithmLengthInBytes = 1; | |
| 24 const size_t kSigAlgorithmLengthInBytes = 1; | |
| 25 const size_t kMaxSignatureLengthInBytes = (1 << 16) - 1; | |
| 26 | |
| 27 // Members of the digitally-signed struct of a V1 SCT | |
| 28 const size_t kSignatureTypeLengthInBytes = 1; | |
| 29 const size_t kLogEntryTypeLengthInBytes = 2; | |
| 30 const size_t kMaxAsn1CertificateLengthInBytes = (1 << 24) - 1; | |
| 31 const size_t kMaxTbsCertificateLengthInBytes = (1 << 24) - 1; | |
| 32 | |
| 33 const size_t kMaxSCTListLengthInBytes = (1 << 16) - 1; | |
| 34 const size_t kMaxSerializedSCTLengthInBytes = (1 << 16) - 1; | |
| 35 | |
| 36 enum SignatureType { | |
| 37 SIGNATURE_TYPE_CERTIFICATE_TIMESTAMP = 0, | |
|
wtc
2013/10/24 23:14:23
Nit: I suggest also include "tree_hash(1)" for com
Eran M. (Google)
2013/10/30 18:00:08
Done.
| |
| 38 }; | |
| 39 | |
| 40 // Returns the size, in bytes, of the length prefix needed for a field | |
| 41 // capable of being |max_field_length| long. For example, a field that can | |
| 42 // be 2^16 - 1 bytes needs 2 bytes to encode the length. | |
| 43 // This is essentially log2(max_field_length) | |
|
wtc
2013/10/24 23:14:23
It should be (log2(max_field_length) + 7)/8 becaus
Eran M. (Google)
2013/10/30 18:00:08
Good idea - switched the constants to the number o
| |
| 44 size_t PrefixLength(size_t max_field_length) { | |
| 45 CHECK_GT(max_field_length, 0U); | |
| 46 size_t prefix_length = 0; | |
| 47 | |
| 48 for (; max_field_length > 0; max_field_length >>= 8) | |
| 49 ++prefix_length; | |
| 50 | |
| 51 return prefix_length; | |
| 52 } | |
| 53 | |
| 54 // Reads a TLS-encoded variable length unsigned integer from |in|. | |
| 55 // |length| indicates the size (in bytes) of the integer. On success, returns | |
| 56 // true and stores the result in |*out|. | |
|
wtc
2013/10/24 23:14:23
Document how |in| is updated. You may want to poin
Eran M. (Google)
2013/10/30 18:00:08
Done, also added the description of the modificati
| |
| 57 template <typename T> | |
| 58 bool ReadUint(size_t length, base::StringPiece* in, T* out) { | |
|
wtc
2013/10/24 23:14:23
It would be nice to assert that length <= sizeof(T
Eran M. (Google)
2013/10/30 18:00:08
Done.
| |
| 59 if (in->size() < length) | |
| 60 return false; | |
| 61 T result = 0; | |
| 62 for (size_t i = 0; i < length; ++i) { | |
| 63 result = (result << 8) | static_cast<unsigned char>((*in)[i]); | |
| 64 } | |
|
wtc
2013/10/24 23:14:23
Nit: omit curly braces.
Eran M. (Google)
2013/10/30 18:00:08
I prefer to keep them in this context, as the body
| |
| 65 in->remove_prefix(length); | |
| 66 *out = result; | |
| 67 return true; | |
| 68 } | |
| 69 | |
| 70 // Reads a TLS-encoded field length from |in|. | |
| 71 // |max_length| indicates the maximum length supported (eg: 2^24 - 1). On | |
| 72 // success, returns true and stores the result in |*out|. | |
|
wtc
2013/10/24 23:14:23
Document how |in| is updated.
Eran M. (Google)
2013/10/30 18:00:08
Done.
| |
| 73 bool ReadLength(size_t max_length, base::StringPiece* in, size_t* out) { | |
| 74 size_t prefix_length = PrefixLength(max_length); | |
| 75 size_t length; | |
| 76 if (!ReadUint(prefix_length, in, &length) || length > max_length) | |
|
wtc
2013/10/24 23:14:23
Since we only pass constants of the form 2^n - 1 (
Eran M. (Google)
2013/10/30 18:00:08
Done.
| |
| 77 return false; | |
| 78 *out = length; | |
| 79 return true; | |
| 80 } | |
| 81 | |
| 82 // Reads |length| bytes from |*in|. If |*in| is too small, returns false. | |
| 83 bool ReadFixedBytes(size_t length, | |
| 84 base::StringPiece* in, | |
| 85 base::StringPiece* out) { | |
| 86 if (in->length() < length) | |
| 87 return false; | |
| 88 out->set(in->data(), length); | |
| 89 in->remove_prefix(length); | |
| 90 return true; | |
| 91 } | |
| 92 | |
| 93 // Reads a length-prefixed variable amount of bytes from |in|, updating |out| | |
| 94 // on success. |max_length| indicates the maximum length of the field. | |
| 95 bool ReadVariableBytes(size_t max_length, | |
| 96 base::StringPiece* in, | |
| 97 base::StringPiece* out) { | |
| 98 size_t length; | |
| 99 if (!ReadLength(max_length, in, &length)) | |
| 100 return false; | |
| 101 return ReadFixedBytes(length, in, out); | |
| 102 } | |
| 103 | |
| 104 // Reads a variable-length list that has been TLS encoded. | |
| 105 // |max_list_length| contains the overall length of the encoded list. | |
| 106 // |max_item_length| contains the maximum length of a single item. | |
| 107 // On success, returns true and updates |*out| with the encoded list. | |
| 108 bool ReadList(size_t max_list_length, | |
| 109 size_t max_item_length, | |
| 110 base::StringPiece* in, | |
| 111 std::vector<base::StringPiece>* out) { | |
| 112 std::vector<base::StringPiece> result; | |
| 113 | |
| 114 base::StringPiece list_data; | |
| 115 if (!ReadVariableBytes(max_list_length, in, &list_data)) | |
| 116 return false; | |
| 117 | |
| 118 while (!list_data.empty()) { | |
| 119 base::StringPiece list_item; | |
| 120 if (!ReadVariableBytes(max_item_length, &list_data, &list_item)) { | |
| 121 return false; | |
| 122 } | |
|
wtc
2013/10/24 23:14:23
Nit: omit curly braces.
Eran M. (Google)
2013/10/30 18:00:08
Added a DVLOG statement, so keeping them (to be co
| |
| 123 if (list_item.empty()) { | |
|
wtc
2013/10/24 23:14:23
Some types may be zero-length. But I agree it does
Eran M. (Google)
2013/10/30 18:00:08
This specifically is used to parse SCT lists, an S
| |
| 124 DVLOG(1) << "Empty item in list"; | |
| 125 return false; | |
| 126 } | |
| 127 result.push_back(list_item); | |
| 128 } | |
| 129 | |
| 130 result.swap(*out); | |
| 131 return true; | |
| 132 } | |
| 133 | |
| 134 // Checks and converts a hash algorithm. | |
| 135 // |in| is the numeric representation of the algorithm. | |
| 136 // If the hash algorithm value is in a set of known values, fills in |out| and | |
| 137 // returns true. Otherwise, returns false. | |
| 138 bool ConvertHashAlgorithm(int in, DigitallySigned::HashAlgorithm* out) { | |
| 139 switch (in) { | |
| 140 case DigitallySigned::HASH_ALGO_NONE: | |
| 141 case DigitallySigned::HASH_ALGO_MD5: | |
| 142 case DigitallySigned::HASH_ALGO_SHA1: | |
| 143 case DigitallySigned::HASH_ALGO_SHA224: | |
| 144 case DigitallySigned::HASH_ALGO_SHA256: | |
| 145 case DigitallySigned::HASH_ALGO_SHA384: | |
| 146 case DigitallySigned::HASH_ALGO_SHA512: | |
| 147 break; | |
| 148 default: | |
| 149 return false; | |
| 150 } | |
| 151 *out = static_cast<DigitallySigned::HashAlgorithm>(in); | |
| 152 return true; | |
| 153 } | |
| 154 | |
| 155 // Checks and converts a signing algorithm. | |
| 156 // |in| is the numeric representation of the algorithm. | |
| 157 // If the signing algorithm value is in a set of known values, fills in |out| | |
| 158 // and returns true. Otherwise, returns false. | |
| 159 bool ConvertSigAlgorithm(int in, DigitallySigned::SignatureAlgorithm* out) { | |
|
wtc
2013/10/24 23:14:23
Nit: Sig => Signature
Eran M. (Google)
2013/10/30 18:00:08
Done.
| |
| 160 switch (in) { | |
| 161 case DigitallySigned::SIG_ALGO_ANONYMOUS: | |
| 162 case DigitallySigned::SIG_ALGO_RSA: | |
| 163 case DigitallySigned::SIG_ALGO_DSA: | |
| 164 case DigitallySigned::SIG_ALGO_ECDSA: | |
| 165 break; | |
| 166 default: | |
| 167 return false; | |
| 168 } | |
| 169 *out = static_cast<DigitallySigned::SignatureAlgorithm>(in); | |
| 170 return true; | |
| 171 } | |
| 172 | |
| 173 // Checks and converts a log entry type. | |
| 174 // |in| the numeric representation of the log type. | |
| 175 // If the log type is 0 (X509 cert) or 1 (PreCertificate), fills in |out| and | |
| 176 // returns true. Otherwise, returns false. | |
| 177 bool ConvertLogEntryType(int in, LogEntry::Type* out) { | |
| 178 switch (in) { | |
| 179 case LogEntry::LOG_ENTRY_TYPE_X509: | |
| 180 case LogEntry::LOG_ENTRY_TYPE_PRECERT: | |
| 181 break; | |
| 182 default: | |
| 183 return false; | |
| 184 } | |
| 185 *out = static_cast<LogEntry::Type>(in); | |
| 186 return true; | |
| 187 } | |
| 188 | |
| 189 // Writes a TLS-encoded variable length unsigned integer to |output|. | |
| 190 // |max_length| indicates the size (in bytes) of the integer. | |
| 191 // |value| the value itself to be written. | |
| 192 template <typename T> | |
| 193 void WriteUint(size_t max_length, T value, std::string* output) { | |
|
wtc
2013/10/24 23:14:23
"max_length" is a poor parameter name because it h
Eran M. (Google)
2013/10/30 18:00:08
Done.
| |
| 194 DCHECK_LE(max_length, sizeof(T)); | |
| 195 DCHECK(max_length == sizeof(T) || value >> (max_length * 8) == 0); | |
| 196 | |
| 197 for (; max_length > 0; --max_length) { | |
| 198 output->push_back( | |
| 199 ((value & (static_cast<T>(0xFF) << ((max_length - 1) * 8))) | |
| 200 >> ((max_length - 1) * 8))); | |
|
wtc
2013/10/24 23:14:23
You don't need the left shift of 0xFF. You can do
Eran M. (Google)
2013/10/30 18:00:08
Done.
| |
| 201 } | |
| 202 } | |
| 203 | |
| 204 // Writes a fixed-length array to |output| from |input|. | |
| 205 void WriteFixedBytes(const base::StringPiece& input, std::string* output) { | |
| 206 input.AppendToString(output); | |
| 207 } | |
| 208 | |
| 209 // Writes a variable-length array to |output|. | |
| 210 // |max_field_length| indicates the length (in bytes) of the array. | |
| 211 // |input| is the array itself. | |
| 212 // If the size of |input| is less than |max_field_length|, encode the | |
| 213 // length and data and return true. Otherwise, return false. | |
| 214 bool WriteVariableBytes(size_t max_field_length, | |
|
wtc
2013/10/24 23:14:23
Nit: this parameter's name should ideally match th
Eran M. (Google)
2013/10/30 18:00:08
Done.
| |
| 215 const base::StringPiece& input, | |
| 216 std::string* output) { | |
| 217 if (input.size() > max_field_length) | |
| 218 return false; | |
| 219 | |
| 220 size_t prefix_length = PrefixLength(max_field_length); | |
| 221 WriteUint(prefix_length, input.size(), output); | |
| 222 WriteFixedBytes(input, output); | |
| 223 | |
| 224 return true; | |
| 225 } | |
| 226 | |
| 227 // Writes a LogEntry of type X509 cert to |output|. | |
|
wtc
2013/10/24 23:14:23
Nit: X509 => X.509
Eran M. (Google)
2013/10/30 18:00:08
Done.
| |
| 228 // |input| is the LogEntry containing the certificate. | |
| 229 // Returns true if the leaf_cert in the LogEntry does not exceed | |
| 230 // kMaxAsn1CertificateLengthInBytes and so can be written to |output|. | |
| 231 bool EncodeAsn1CertLogEntry(const LogEntry& input, std::string* output) { | |
| 232 return WriteVariableBytes(kMaxAsn1CertificateLengthInBytes, | |
| 233 input.leaf_cert, output); | |
| 234 } | |
| 235 | |
| 236 // Writes a LogEntry of type PreCertificate to |output|. | |
| 237 // |input| is the LogEntry containing the TBSCertificate and issuer key hash. | |
| 238 // Returns true if the TBSCertificate component in the LogEntry does not | |
| 239 // exceed kMaxTbsCertificateLengthInBytes and so can be written to |output|. | |
| 240 bool EncodePrecertLogEntry(const LogEntry& input, std::string* output) { | |
| 241 WriteFixedBytes( | |
| 242 base::StringPiece( | |
| 243 reinterpret_cast<const char*>(input.issuer_key_hash.data), | |
| 244 kLogIdLengthInBytes), | |
| 245 output); | |
| 246 return WriteVariableBytes(kMaxTbsCertificateLengthInBytes, | |
| 247 input.tbs_certificate, output); | |
| 248 } | |
| 249 | |
| 250 } // namespace | |
| 251 | |
| 252 // If |input| is less than kMaxSignatureLengthInBytes, encodes the |input| | |
|
wtc
2013/10/24 23:14:23
Nit: If |input| is => If |input.signature_data| is
Eran M. (Google)
2013/10/30 18:00:08
Done.
| |
| 253 // to |output and returns true. Otherwise, returns false. | |
| 254 bool EncodeDigitallySigned(const DigitallySigned& input, | |
| 255 std::string* output) { | |
| 256 WriteUint(kHashAlgorithmLengthInBytes, input.hash_algorithm, output); | |
| 257 WriteUint(kSigAlgorithmLengthInBytes, input.signature_algorithm, | |
| 258 output); | |
| 259 return WriteVariableBytes(kMaxSignatureLengthInBytes, input.signature_data, | |
| 260 output); | |
| 261 } | |
| 262 | |
| 263 // Reads and decodes a DigitallySigned object from |input|. | |
| 264 // Returns true and fills |output| if all fields can be read, false otherwise. | |
| 265 bool DecodeDigitallySigned(base::StringPiece* input, | |
| 266 DigitallySigned* output) { | |
| 267 int hash_algo; | |
| 268 int sig_algo; | |
| 269 base::StringPiece sig_data; | |
| 270 | |
| 271 if (!ReadUint(kHashAlgorithmLengthInBytes, input, &hash_algo) || | |
| 272 !ReadUint(kSigAlgorithmLengthInBytes, input, &sig_algo) || | |
|
wtc
2013/10/24 23:14:23
The third argument of ReadUint (as the function na
Eran M. (Google)
2013/10/30 18:00:08
Done - changed to unsigned.
| |
| 273 !ReadVariableBytes(kMaxSignatureLengthInBytes, input, &sig_data)) { | |
| 274 return false; | |
| 275 } | |
| 276 | |
| 277 DigitallySigned result; | |
| 278 if (!ConvertHashAlgorithm(hash_algo, &result.hash_algorithm)) { | |
| 279 DVLOG(1) << "Invalid hash algorithm " << hash_algo; | |
| 280 return false; | |
| 281 } | |
| 282 if (!ConvertSigAlgorithm(sig_algo, &result.signature_algorithm)) { | |
| 283 DVLOG(1) << "Invalid signature algorithm " << sig_algo; | |
| 284 return false; | |
| 285 } | |
| 286 sig_data.CopyToString(&result.signature_data); | |
| 287 | |
| 288 // XXX(rsleevi): Declare a swap specialization to avoid the extra copy | |
| 289 // of |result.signature|. | |
|
wtc
2013/10/24 23:14:23
Nit: |result.signature| => |result.signature_data|
Eran M. (Google)
2013/10/30 18:00:08
Done.
| |
| 290 *output = result; | |
| 291 return true; | |
| 292 } | |
| 293 | |
| 294 // Encodes the |input| LogEntry to |output|. Returns true if the entry size | |
| 295 // does not exceed allowed size in RFC6962, false otherwise. | |
| 296 bool EncodeLogEntry(const LogEntry& input, std::string* output) { | |
| 297 WriteUint(kLogEntryTypeLengthInBytes, input.type, output); | |
| 298 switch (input.type) { | |
| 299 case LogEntry::LOG_ENTRY_TYPE_X509: | |
| 300 return EncodeAsn1CertLogEntry(input, output); | |
| 301 case LogEntry::LOG_ENTRY_TYPE_PRECERT: | |
| 302 return EncodePrecertLogEntry(input, output); | |
| 303 } | |
| 304 return false; | |
| 305 } | |
| 306 | |
| 307 // Encodes the data signed by an SCT into |output|. The signature included in | |
| 308 // the SCT is then verified over these bytes. | |
| 309 // |timestamp| timestamp from the SCT. | |
| 310 // |serialized_log_entry| the log entry signed by the SCT. | |
| 311 // |extensions| CT extensions. | |
| 312 // Returns true if the extensions' length does not exceed | |
| 313 // kMaxExtensionsLengthInBytes, false otherwise. | |
| 314 bool EncodeV1SCTSignedData(const base::Time& timestamp, | |
| 315 const std::string& serialized_log_entry, | |
| 316 const std::string& extensions, | |
| 317 std::string* output) { | |
| 318 WriteUint(kVersionLengthInBytes, SignedCertificateTimestamp::SCT_VERSION_1, | |
| 319 output); | |
| 320 WriteUint(kSignatureTypeLengthInBytes, SIGNATURE_TYPE_CERTIFICATE_TIMESTAMP, | |
| 321 output); | |
| 322 base::TimeDelta time_since_epoch = timestamp - base::Time::UnixEpoch(); | |
| 323 WriteUint(kTimestampLengthInBytes, time_since_epoch.InMilliseconds(), | |
| 324 output); | |
| 325 WriteFixedBytes(serialized_log_entry, output); | |
|
wtc
2013/10/24 23:14:23
Using the function name "WriteFixedBytes" is a lit
Eran M. (Google)
2013/10/30 18:00:08
Done - renamed to WriteEncodedBytes (and updated t
| |
| 326 return WriteVariableBytes(kMaxExtensionsLengthInBytes, extensions, output); | |
| 327 } | |
| 328 | |
| 329 // Decode a list of SCTs: from a single string in |input| to a vector of | |
| 330 // individually-encoded SCTs |output|. This list is typically obtained | |
| 331 // from the CT extension in a certificate. | |
| 332 // Returns true if the list could be read and decoded successfully, false | |
| 333 // otherwise (note that the validity of each individual SCT should be checked | |
| 334 // separately). | |
| 335 bool DecodeSCTList(base::StringPiece* input, | |
| 336 std::vector<base::StringPiece>* output) { | |
| 337 std::vector<base::StringPiece> result; | |
| 338 if (!ReadList(kMaxSCTListLengthInBytes, kMaxSerializedSCTLengthInBytes, | |
| 339 input, &result)) { | |
| 340 return false; | |
| 341 } | |
| 342 | |
| 343 if (!input->empty() || result.empty()) | |
| 344 return false; | |
| 345 output->swap(result); | |
| 346 return true; | |
| 347 } | |
| 348 | |
| 349 // Decodes a single SCT from |input| to |output|. | |
| 350 // Returns true if all fields in the SCT could be read and decoded, false | |
| 351 // otherwise. | |
| 352 bool DecodeSignedCertificateTimestamp(base::StringPiece* input, | |
| 353 SignedCertificateTimestamp* output) { | |
| 354 SignedCertificateTimestamp result; | |
| 355 int version; | |
| 356 if (!ReadUint(kVersionLengthInBytes, input, &version)) | |
|
wtc
2013/10/24 23:14:23
|version| is a signed int, but ReadUint implies we
Eran M. (Google)
2013/10/30 18:00:08
Done.
| |
| 357 return false; | |
| 358 if (version != SignedCertificateTimestamp::SCT_VERSION_1) { | |
| 359 DVLOG(1) << "Unsupported/invalid version " << version; | |
| 360 return false; | |
| 361 } | |
| 362 | |
| 363 result.version = SignedCertificateTimestamp::SCT_VERSION_1; | |
| 364 uint64 timestamp; | |
| 365 base::StringPiece log_id; | |
| 366 base::StringPiece extensions; | |
| 367 if (!ReadFixedBytes(kLogIdLengthInBytes, input, &log_id) || | |
| 368 !ReadUint(kTimestampLengthInBytes, input, ×tamp) || | |
| 369 !ReadVariableBytes(kMaxExtensionsLengthInBytes, input, | |
| 370 &extensions) || | |
| 371 !DecodeDigitallySigned(input, &result.signature)) { | |
| 372 return false; | |
| 373 } | |
| 374 log_id.CopyToString(&result.log_id); | |
| 375 extensions.CopyToString(&result.extensions); | |
| 376 result.timestamp = | |
| 377 base::Time::UnixEpoch() + | |
| 378 base::TimeDelta::FromMilliseconds(static_cast<int64>(timestamp)); | |
|
wtc
2013/10/24 23:14:23
Should we validate |timestamp| before casting it t
Eran M. (Google)
2013/10/30 18:00:08
Done - verified that the value does not exceed max
| |
| 379 | |
| 380 // XXX(rsleevi): Declare a swap specialization to avoid the extra copy. | |
| 381 *output = result; | |
| 382 return true; | |
| 383 } | |
| 384 | |
| 385 } // namespace ct | |
| 386 | |
| 387 } // namespace net | |
| OLD | NEW |