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 // Note: length is always specified in bytes. | |
| 17 // Signed Certificate Timestamp (SCT) Version length | |
| 18 const size_t kVersionLength = 1; | |
| 19 | |
| 20 // Members of a V1 SCT | |
| 21 const size_t kLogIdLength = 32; | |
| 22 const size_t kTimestampLength = 8; | |
| 23 const size_t kExtensionsLengthBytes = 2; | |
| 24 const size_t kHashAlgorithmLength = 1; | |
| 25 const size_t kSigAlgorithmLength = 1; | |
| 26 const size_t kSignatureLengthBytes = 2; | |
| 27 | |
| 28 // Members of the digitally-signed struct of a V1 SCT | |
| 29 const size_t kSignatureTypeLength = 1; | |
| 30 const size_t kLogEntryTypeLength = 2; | |
| 31 const size_t kAsn1CertificateLengthBytes = 3; | |
| 32 const size_t kTbsCertificateLengthBytes = 3; | |
| 33 | |
| 34 const size_t kSCTListLengthBytes = 2; | |
| 35 const size_t kSerializedSCTLengthBytes = 2; | |
| 36 | |
| 37 enum SignatureType { | |
| 38 SIGNATURE_TYPE_CERTIFICATE_TIMESTAMP = 0, | |
| 39 TREE_HASH = 1, | |
| 40 }; | |
| 41 | |
| 42 // Reads a TLS-encoded variable length unsigned integer from |in|. | |
| 43 // The integer is expected to be in big-endian order, which is used by TLS. | |
| 44 // The bytes read from |in| are discarded (i.e. |in|'s prefix removed) | |
| 45 // |length| indicates the size (in bytes) of the integer. On success, returns | |
| 46 // true and stores the result in |*out|. | |
| 47 template <typename T> | |
| 48 bool ReadUint(size_t length, base::StringPiece* in, T* out) { | |
| 49 if (in->size() < length) | |
| 50 return false; | |
| 51 DCHECK_LE(length, sizeof(T)); | |
| 52 | |
| 53 T result = 0; | |
| 54 for (size_t i = 0; i < length; ++i) { | |
| 55 result = (result << 8) | static_cast<unsigned char>((*in)[i]); | |
| 56 } | |
| 57 in->remove_prefix(length); | |
| 58 *out = result; | |
| 59 return true; | |
| 60 } | |
| 61 | |
| 62 // Reads a TLS-encoded field length from |in|. | |
| 63 // The bytes read from |in| are discarded (i.e. |in|'s prefix removed) | |
| 64 // |max_length| indicates the maximum length supported (eg: 2^24 - 1). On | |
|
Ryan Sleevi
2013/10/30 20:48:27
update comment
Eran M. (Google)
2013/10/31 11:37:37
Done, same for all other comments where |max_lengt
| |
| 65 // success, returns true and stores the result in |*out|. | |
| 66 bool ReadLength(size_t prefix_length, base::StringPiece* in, size_t* out) { | |
| 67 size_t length; | |
| 68 if (!ReadUint(prefix_length, in, &length)) | |
| 69 return false; | |
| 70 *out = length; | |
| 71 return true; | |
| 72 } | |
| 73 | |
| 74 // Reads |length| bytes from |*in|. If |*in| is too small, returns false. | |
| 75 // The bytes read from |in| are discarded (i.e. |in|'s prefix removed) | |
| 76 bool ReadFixedBytes(size_t length, | |
| 77 base::StringPiece* in, | |
| 78 base::StringPiece* out) { | |
| 79 if (in->length() < length) | |
| 80 return false; | |
| 81 out->set(in->data(), length); | |
| 82 in->remove_prefix(length); | |
| 83 return true; | |
| 84 } | |
| 85 | |
| 86 // Reads a length-prefixed variable amount of bytes from |in|, updating |out| | |
| 87 // on success. |max_length| indicates the maximum length of the field. | |
| 88 // The bytes read from |in| are discarded (i.e. |in|'s prefix removed) | |
| 89 bool ReadVariableBytes(size_t prefix_length, | |
| 90 base::StringPiece* in, | |
| 91 base::StringPiece* out) { | |
| 92 size_t length; | |
| 93 if (!ReadLength(prefix_length, in, &length)) | |
| 94 return false; | |
| 95 return ReadFixedBytes(length, in, out); | |
| 96 } | |
| 97 | |
| 98 // Reads a variable-length list that has been TLS encoded. | |
| 99 // The bytes read from |in| are discarded (i.e. |in|'s prefix removed) | |
| 100 // |max_list_length| contains the overall length of the encoded list. | |
| 101 // |max_item_length| contains the maximum length of a single item. | |
| 102 // On success, returns true and updates |*out| with the encoded list. | |
| 103 bool ReadList(size_t max_list_length, | |
| 104 size_t max_item_length, | |
| 105 base::StringPiece* in, | |
| 106 std::vector<base::StringPiece>* out) { | |
| 107 std::vector<base::StringPiece> result; | |
| 108 | |
| 109 base::StringPiece list_data; | |
| 110 if (!ReadVariableBytes(max_list_length, in, &list_data)) | |
| 111 return false; | |
| 112 | |
| 113 while (!list_data.empty()) { | |
| 114 base::StringPiece list_item; | |
| 115 if (!ReadVariableBytes(max_item_length, &list_data, &list_item)) { | |
| 116 DVLOG(1) << "Failed to read item in list."; | |
| 117 return false; | |
| 118 } | |
| 119 if (list_item.empty()) { | |
| 120 DVLOG(1) << "Empty item in list"; | |
| 121 return false; | |
| 122 } | |
| 123 result.push_back(list_item); | |
| 124 } | |
| 125 | |
| 126 result.swap(*out); | |
| 127 return true; | |
| 128 } | |
| 129 | |
| 130 // Checks and converts a hash algorithm. | |
| 131 // |in| is the numeric representation of the algorithm. | |
| 132 // If the hash algorithm value is in a set of known values, fills in |out| and | |
| 133 // returns true. Otherwise, returns false. | |
| 134 bool ConvertHashAlgorithm(unsigned in, DigitallySigned::HashAlgorithm* out) { | |
| 135 switch (in) { | |
| 136 case DigitallySigned::HASH_ALGO_NONE: | |
| 137 case DigitallySigned::HASH_ALGO_MD5: | |
| 138 case DigitallySigned::HASH_ALGO_SHA1: | |
| 139 case DigitallySigned::HASH_ALGO_SHA224: | |
| 140 case DigitallySigned::HASH_ALGO_SHA256: | |
| 141 case DigitallySigned::HASH_ALGO_SHA384: | |
| 142 case DigitallySigned::HASH_ALGO_SHA512: | |
| 143 break; | |
| 144 default: | |
| 145 return false; | |
| 146 } | |
| 147 *out = static_cast<DigitallySigned::HashAlgorithm>(in); | |
| 148 return true; | |
| 149 } | |
| 150 | |
| 151 // Checks and converts a signing algorithm. | |
| 152 // |in| is the numeric representation of the algorithm. | |
| 153 // If the signing algorithm value is in a set of known values, fills in |out| | |
| 154 // and returns true. Otherwise, returns false. | |
| 155 bool ConvertSignatureAlgorithm( | |
| 156 unsigned in, | |
| 157 DigitallySigned::SignatureAlgorithm* out) { | |
| 158 switch (in) { | |
| 159 case DigitallySigned::SIG_ALGO_ANONYMOUS: | |
| 160 case DigitallySigned::SIG_ALGO_RSA: | |
| 161 case DigitallySigned::SIG_ALGO_DSA: | |
| 162 case DigitallySigned::SIG_ALGO_ECDSA: | |
| 163 break; | |
| 164 default: | |
| 165 return false; | |
| 166 } | |
| 167 *out = static_cast<DigitallySigned::SignatureAlgorithm>(in); | |
| 168 return true; | |
| 169 } | |
| 170 | |
| 171 // Checks and converts a log entry type. | |
| 172 // |in| the numeric representation of the log type. | |
| 173 // If the log type is 0 (X.509 cert) or 1 (PreCertificate), fills in |out| and | |
| 174 // returns true. Otherwise, returns false. | |
| 175 bool ConvertLogEntryType(int in, LogEntry::Type* out) { | |
| 176 switch (in) { | |
| 177 case LogEntry::LOG_ENTRY_TYPE_X509: | |
| 178 case LogEntry::LOG_ENTRY_TYPE_PRECERT: | |
| 179 break; | |
| 180 default: | |
| 181 return false; | |
| 182 } | |
| 183 *out = static_cast<LogEntry::Type>(in); | |
| 184 return true; | |
| 185 } | |
| 186 | |
| 187 // Writes a TLS-encoded variable length unsigned integer to |output|. | |
| 188 // |length| indicates the size (in bytes) of the integer. | |
| 189 // |value| the value itself to be written. | |
| 190 template <typename T> | |
| 191 void WriteUint(size_t length, T value, std::string* output) { | |
| 192 DCHECK_LE(length, sizeof(T)); | |
| 193 DCHECK(length == sizeof(T) || value >> (length * 8) == 0); | |
| 194 | |
| 195 for (; length > 0; --length) { | |
| 196 output->push_back((value >> ((length - 1)* 8)) & 0xFF); | |
| 197 } | |
| 198 } | |
| 199 | |
| 200 // Writes an array to |output| from |input|. | |
| 201 // Should be used in one of two cases: | |
| 202 // * The length of |input| has already been encoded into the |output| stream. | |
| 203 // * The length of |input| is fixed and the reader is expected to specify that | |
| 204 // length when reading. | |
| 205 // If the length of |input| is dynamic and data is expected to follow it, | |
| 206 // WriteVariableBytes must be used. | |
| 207 void WriteEncodedBytes(const base::StringPiece& input, std::string* output) { | |
| 208 input.AppendToString(output); | |
| 209 } | |
| 210 | |
| 211 // Writes a variable-length array to |output|. | |
| 212 // |max_length| indicates the length (in bytes) of the array. | |
| 213 // |input| is the array itself. | |
| 214 // If the size of |input| is less than |max_length|, encode the | |
| 215 // length and data and return true. Otherwise, return false. | |
| 216 bool WriteVariableBytes(size_t prefix_length, | |
| 217 const base::StringPiece& input, | |
| 218 std::string* output) { | |
| 219 if (input.size() > ((1 << (prefix_length * 8)) - 1)) | |
| 220 return false; | |
| 221 | |
| 222 WriteUint(prefix_length, input.size(), output); | |
| 223 WriteEncodedBytes(input, output); | |
| 224 | |
| 225 return true; | |
| 226 } | |
| 227 | |
| 228 // Writes a LogEntry of type X.509 cert to |output|. | |
| 229 // |input| is the LogEntry containing the certificate. | |
| 230 // Returns true if the leaf_certificate in the LogEntry does not exceed | |
| 231 // kMaxAsn1CertificateLength and so can be written to |output|. | |
| 232 bool EncodeAsn1CertLogEntry(const LogEntry& input, std::string* output) { | |
| 233 return WriteVariableBytes(kAsn1CertificateLengthBytes, | |
| 234 input.leaf_certificate, output); | |
| 235 } | |
| 236 | |
| 237 // Writes a LogEntry of type PreCertificate to |output|. | |
| 238 // |input| is the LogEntry containing the TBSCertificate and issuer key hash. | |
| 239 // Returns true if the TBSCertificate component in the LogEntry does not | |
| 240 // exceed kMaxTbsCertificateLength and so can be written to |output|. | |
| 241 bool EncodePrecertLogEntry(const LogEntry& input, std::string* output) { | |
| 242 WriteEncodedBytes( | |
| 243 base::StringPiece( | |
| 244 reinterpret_cast<const char*>(input.issuer_key_hash.data), | |
| 245 kLogIdLength), | |
| 246 output); | |
| 247 return WriteVariableBytes(kTbsCertificateLengthBytes, | |
| 248 input.tbs_certificate, output); | |
| 249 } | |
| 250 | |
| 251 } // namespace | |
| 252 | |
| 253 bool EncodeDigitallySigned(const DigitallySigned& input, | |
| 254 std::string* output) { | |
| 255 WriteUint(kHashAlgorithmLength, input.hash_algorithm, output); | |
| 256 WriteUint(kSigAlgorithmLength, input.signature_algorithm, | |
| 257 output); | |
| 258 return WriteVariableBytes(kSignatureLengthBytes, input.signature_data, | |
| 259 output); | |
| 260 } | |
| 261 | |
| 262 bool DecodeDigitallySigned(base::StringPiece* input, | |
| 263 DigitallySigned* output) { | |
| 264 unsigned hash_algo; | |
| 265 unsigned sig_algo; | |
| 266 base::StringPiece sig_data; | |
| 267 | |
| 268 if (!ReadUint(kHashAlgorithmLength, input, &hash_algo) || | |
| 269 !ReadUint(kSigAlgorithmLength, input, &sig_algo) || | |
| 270 !ReadVariableBytes(kSignatureLengthBytes, input, &sig_data)) { | |
| 271 return false; | |
| 272 } | |
| 273 | |
| 274 DigitallySigned result; | |
| 275 if (!ConvertHashAlgorithm(hash_algo, &result.hash_algorithm)) { | |
| 276 DVLOG(1) << "Invalid hash algorithm " << hash_algo; | |
| 277 return false; | |
| 278 } | |
| 279 if (!ConvertSignatureAlgorithm(sig_algo, &result.signature_algorithm)) { | |
| 280 DVLOG(1) << "Invalid signature algorithm " << sig_algo; | |
| 281 return false; | |
| 282 } | |
| 283 sig_data.CopyToString(&result.signature_data); | |
| 284 | |
| 285 // XXX(rsleevi): Declare a swap specialization to avoid the extra copy | |
|
Ryan Sleevi
2013/10/30 20:48:27
Just remove this comment
Eran M. (Google)
2013/10/31 11:37:37
Done.
| |
| 286 // of |result.signature_data|. | |
| 287 *output = result; | |
| 288 return true; | |
| 289 } | |
| 290 | |
| 291 bool EncodeLogEntry(const LogEntry& input, std::string* output) { | |
| 292 WriteUint(kLogEntryTypeLength, input.type, output); | |
| 293 switch (input.type) { | |
| 294 case LogEntry::LOG_ENTRY_TYPE_X509: | |
| 295 return EncodeAsn1CertLogEntry(input, output); | |
| 296 case LogEntry::LOG_ENTRY_TYPE_PRECERT: | |
| 297 return EncodePrecertLogEntry(input, output); | |
| 298 } | |
| 299 return false; | |
| 300 } | |
| 301 | |
| 302 bool EncodeV1SCTSignedData(const base::Time& timestamp, | |
| 303 const std::string& serialized_log_entry, | |
| 304 const std::string& extensions, | |
| 305 std::string* output) { | |
| 306 WriteUint(kVersionLength, SignedCertificateTimestamp::SCT_VERSION_1, | |
| 307 output); | |
| 308 WriteUint(kSignatureTypeLength, SIGNATURE_TYPE_CERTIFICATE_TIMESTAMP, | |
| 309 output); | |
| 310 base::TimeDelta time_since_epoch = timestamp - base::Time::UnixEpoch(); | |
| 311 WriteUint(kTimestampLength, time_since_epoch.InMilliseconds(), | |
| 312 output); | |
| 313 // NOTE: serialized_log_entry must already be serialized and contain the | |
| 314 // length as the prefix. | |
| 315 WriteEncodedBytes(serialized_log_entry, output); | |
| 316 return WriteVariableBytes(kExtensionsLengthBytes, extensions, output); | |
| 317 } | |
| 318 | |
| 319 bool DecodeSCTList(base::StringPiece* input, | |
| 320 std::vector<base::StringPiece>* output) { | |
| 321 std::vector<base::StringPiece> result; | |
| 322 if (!ReadList(kSCTListLengthBytes, kSerializedSCTLengthBytes, | |
| 323 input, &result)) { | |
| 324 return false; | |
| 325 } | |
| 326 | |
| 327 if (!input->empty() || result.empty()) | |
| 328 return false; | |
| 329 output->swap(result); | |
| 330 return true; | |
| 331 } | |
| 332 | |
| 333 bool DecodeSignedCertificateTimestamp(base::StringPiece* input, | |
| 334 SignedCertificateTimestamp* output) { | |
| 335 SignedCertificateTimestamp result; | |
| 336 unsigned version; | |
| 337 if (!ReadUint(kVersionLength, input, &version)) | |
| 338 return false; | |
| 339 if (version != SignedCertificateTimestamp::SCT_VERSION_1) { | |
| 340 DVLOG(1) << "Unsupported/invalid version " << version; | |
| 341 return false; | |
| 342 } | |
| 343 | |
| 344 result.version = SignedCertificateTimestamp::SCT_VERSION_1; | |
| 345 uint64 timestamp; | |
| 346 base::StringPiece log_id; | |
| 347 base::StringPiece extensions; | |
| 348 if (!ReadFixedBytes(kLogIdLength, input, &log_id) || | |
| 349 !ReadUint(kTimestampLength, input, ×tamp) || | |
| 350 !ReadVariableBytes(kExtensionsLengthBytes, input, | |
| 351 &extensions) || | |
| 352 !DecodeDigitallySigned(input, &result.signature)) { | |
| 353 return false; | |
| 354 } | |
| 355 | |
| 356 if (timestamp > static_cast<uint64>(kint64max)) { | |
| 357 DVLOG(1) << "Timestamp value too big to cast to int64: " << timestamp; | |
| 358 return false; | |
| 359 } | |
| 360 | |
| 361 log_id.CopyToString(&result.log_id); | |
| 362 extensions.CopyToString(&result.extensions); | |
| 363 result.timestamp = | |
| 364 base::Time::UnixEpoch() + | |
| 365 base::TimeDelta::FromMilliseconds(static_cast<int64>(timestamp)); | |
| 366 | |
| 367 // XXX(rsleevi): Declare a swap specialization to avoid the extra copy. | |
|
Ryan Sleevi
2013/10/30 20:48:27
same here re: comment
Eran M. (Google)
2013/10/31 11:37:37
Done.
| |
| 368 *output = result; | |
| 369 return true; | |
| 370 } | |
| 371 | |
| 372 } // namespace ct | |
| 373 | |
| 374 } // namespace net | |
| OLD | NEW |