| // Copyright 2013 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "net/cert/ct_serialization.h" | 
 |  | 
 | #include "base/basictypes.h" | 
 | #include "base/logging.h" | 
 |  | 
 | namespace net { | 
 |  | 
 | namespace ct { | 
 |  | 
 | namespace { | 
 |  | 
 | // Note: length is always specified in bytes. | 
 | // Signed Certificate Timestamp (SCT) Version length | 
 | const size_t kVersionLength = 1; | 
 |  | 
 | // Members of a V1 SCT | 
 | const size_t kLogIdLength = 32; | 
 | const size_t kTimestampLength = 8; | 
 | const size_t kExtensionsLengthBytes = 2; | 
 | const size_t kHashAlgorithmLength = 1; | 
 | const size_t kSigAlgorithmLength = 1; | 
 | const size_t kSignatureLengthBytes = 2; | 
 |  | 
 | // Members of the digitally-signed struct of a V1 SCT | 
 | const size_t kSignatureTypeLength = 1; | 
 | const size_t kLogEntryTypeLength = 2; | 
 | const size_t kAsn1CertificateLengthBytes = 3; | 
 | const size_t kTbsCertificateLengthBytes = 3; | 
 |  | 
 | const size_t kSCTListLengthBytes = 2; | 
 | const size_t kSerializedSCTLengthBytes = 2; | 
 |  | 
 | // Members of digitally-signed struct of a STH | 
 | const size_t kTreeSizeLength = 8; | 
 |  | 
 | enum SignatureType { | 
 |   SIGNATURE_TYPE_CERTIFICATE_TIMESTAMP = 0, | 
 |   TREE_HASH = 1, | 
 | }; | 
 |  | 
 | // Reads a TLS-encoded variable length unsigned integer from |in|. | 
 | // The integer is expected to be in big-endian order, which is used by TLS. | 
 | // The bytes read from |in| are discarded (i.e. |in|'s prefix removed) | 
 | // |length| indicates the size (in bytes) of the integer. On success, returns | 
 | // true and stores the result in |*out|. | 
 | template <typename T> | 
 | bool ReadUint(size_t length, base::StringPiece* in, T* out) { | 
 |   if (in->size() < length) | 
 |     return false; | 
 |   DCHECK_LE(length, sizeof(T)); | 
 |  | 
 |   T result = 0; | 
 |   for (size_t i = 0; i < length; ++i) { | 
 |     result = (result << 8) | static_cast<unsigned char>((*in)[i]); | 
 |   } | 
 |   in->remove_prefix(length); | 
 |   *out = result; | 
 |   return true; | 
 | } | 
 |  | 
 | // Reads a TLS-encoded field length from |in|. | 
 | // The bytes read from |in| are discarded (i.e. |in|'s prefix removed) | 
 | // |prefix_length| indicates the bytes needed to represent the length (e.g. 3) | 
 | // success, returns true and stores the result in |*out|. | 
 | bool ReadLength(size_t prefix_length, base::StringPiece* in, size_t* out) { | 
 |   size_t length; | 
 |   if (!ReadUint(prefix_length, in, &length)) | 
 |     return false; | 
 |   *out = length; | 
 |   return true; | 
 | } | 
 |  | 
 | // Reads |length| bytes from |*in|. If |*in| is too small, returns false. | 
 | // The bytes read from |in| are discarded (i.e. |in|'s prefix removed) | 
 | bool ReadFixedBytes(size_t length, | 
 |                     base::StringPiece* in, | 
 |                     base::StringPiece* out) { | 
 |   if (in->length() < length) | 
 |     return false; | 
 |   out->set(in->data(), length); | 
 |   in->remove_prefix(length); | 
 |   return true; | 
 | } | 
 |  | 
 | // Reads a length-prefixed variable amount of bytes from |in|, updating |out| | 
 | // on success. |prefix_length| indicates the number of bytes needed to represent | 
 | // the length. | 
 | // The bytes read from |in| are discarded (i.e. |in|'s prefix removed) | 
 | bool ReadVariableBytes(size_t prefix_length, | 
 |                        base::StringPiece* in, | 
 |                        base::StringPiece* out) { | 
 |   size_t length; | 
 |   if (!ReadLength(prefix_length, in, &length)) | 
 |     return false; | 
 |   return ReadFixedBytes(length, in, out); | 
 | } | 
 |  | 
 | // Reads a variable-length list that has been TLS encoded. | 
 | // The bytes read from |in| are discarded (i.e. |in|'s prefix removed) | 
 | // |max_list_length| contains the overall length of the encoded list. | 
 | // |max_item_length| contains the maximum length of a single item. | 
 | // On success, returns true and updates |*out| with the encoded list. | 
 | bool ReadList(size_t max_list_length, | 
 |               size_t max_item_length, | 
 |               base::StringPiece* in, | 
 |               std::vector<base::StringPiece>* out) { | 
 |   std::vector<base::StringPiece> result; | 
 |  | 
 |   base::StringPiece list_data; | 
 |   if (!ReadVariableBytes(max_list_length, in, &list_data)) | 
 |     return false; | 
 |  | 
 |   while (!list_data.empty()) { | 
 |     base::StringPiece list_item; | 
 |     if (!ReadVariableBytes(max_item_length, &list_data, &list_item)) { | 
 |       DVLOG(1) << "Failed to read item in list."; | 
 |       return false; | 
 |     } | 
 |     if (list_item.empty()) { | 
 |       DVLOG(1) << "Empty item in list"; | 
 |       return false; | 
 |     } | 
 |     result.push_back(list_item); | 
 |   } | 
 |  | 
 |   result.swap(*out); | 
 |   return true; | 
 | } | 
 |  | 
 | // Checks and converts a hash algorithm. | 
 | // |in| is the numeric representation of the algorithm. | 
 | // If the hash algorithm value is in a set of known values, fills in |out| and | 
 | // returns true. Otherwise, returns false. | 
 | bool ConvertHashAlgorithm(unsigned in, DigitallySigned::HashAlgorithm* out) { | 
 |   switch (in) { | 
 |     case DigitallySigned::HASH_ALGO_NONE: | 
 |     case DigitallySigned::HASH_ALGO_MD5: | 
 |     case DigitallySigned::HASH_ALGO_SHA1: | 
 |     case DigitallySigned::HASH_ALGO_SHA224: | 
 |     case DigitallySigned::HASH_ALGO_SHA256: | 
 |     case DigitallySigned::HASH_ALGO_SHA384: | 
 |     case DigitallySigned::HASH_ALGO_SHA512: | 
 |       break; | 
 |     default: | 
 |       return false; | 
 |   } | 
 |   *out = static_cast<DigitallySigned::HashAlgorithm>(in); | 
 |   return true; | 
 | } | 
 |  | 
 | // Checks and converts a signing algorithm. | 
 | // |in| is the numeric representation of the algorithm. | 
 | // If the signing algorithm value is in a set of known values, fills in |out| | 
 | // and returns true. Otherwise, returns false. | 
 | bool ConvertSignatureAlgorithm( | 
 |     unsigned in, | 
 |     DigitallySigned::SignatureAlgorithm* out) { | 
 |   switch (in) { | 
 |     case DigitallySigned::SIG_ALGO_ANONYMOUS: | 
 |     case DigitallySigned::SIG_ALGO_RSA: | 
 |     case DigitallySigned::SIG_ALGO_DSA: | 
 |     case DigitallySigned::SIG_ALGO_ECDSA: | 
 |       break; | 
 |     default: | 
 |       return false; | 
 |   } | 
 |   *out = static_cast<DigitallySigned::SignatureAlgorithm>(in); | 
 |   return true; | 
 | } | 
 |  | 
 | // Writes a TLS-encoded variable length unsigned integer to |output|. | 
 | // |length| indicates the size (in bytes) of the integer. | 
 | // |value| the value itself to be written. | 
 | template <typename T> | 
 | void WriteUint(size_t length, T value, std::string* output) { | 
 |   DCHECK_LE(length, sizeof(T)); | 
 |   DCHECK(length == sizeof(T) || value >> (length * 8) == 0); | 
 |  | 
 |   for (; length > 0; --length) { | 
 |     output->push_back((value >> ((length - 1)* 8)) & 0xFF); | 
 |   } | 
 | } | 
 |  | 
 | // Writes an array to |output| from |input|. | 
 | // Should be used in one of two cases: | 
 | // * The length of |input| has already been encoded into the |output| stream. | 
 | // * The length of |input| is fixed and the reader is expected to specify that | 
 | // length when reading. | 
 | // If the length of |input| is dynamic and data is expected to follow it, | 
 | // WriteVariableBytes must be used. | 
 | void WriteEncodedBytes(const base::StringPiece& input, std::string* output) { | 
 |   input.AppendToString(output); | 
 | } | 
 |  | 
 | // Writes a variable-length array to |output|. | 
 | // |prefix_length| indicates the number of bytes needed to represnt the length. | 
 | // |input| is the array itself. | 
 | // If the size of |input| is less than 2^|prefix_length| - 1, encode the | 
 | // length and data and return true. Otherwise, return false. | 
 | bool WriteVariableBytes(size_t prefix_length, | 
 |                         const base::StringPiece& input, | 
 |                         std::string* output) { | 
 |   size_t input_size = input.size(); | 
 |   size_t max_allowed_input_size = | 
 |       static_cast<size_t>(((1 << (prefix_length * 8)) - 1)); | 
 |   if (input_size > max_allowed_input_size) | 
 |     return false; | 
 |  | 
 |   WriteUint(prefix_length, input.size(), output); | 
 |   WriteEncodedBytes(input, output); | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | // Writes a LogEntry of type X.509 cert to |output|. | 
 | // |input| is the LogEntry containing the certificate. | 
 | // Returns true if the leaf_certificate in the LogEntry does not exceed | 
 | // kMaxAsn1CertificateLength and so can be written to |output|. | 
 | bool EncodeAsn1CertLogEntry(const LogEntry& input, std::string* output) { | 
 |   return WriteVariableBytes(kAsn1CertificateLengthBytes, | 
 |                             input.leaf_certificate, output); | 
 | } | 
 |  | 
 | // Writes a LogEntry of type PreCertificate to |output|. | 
 | // |input| is the LogEntry containing the TBSCertificate and issuer key hash. | 
 | // Returns true if the TBSCertificate component in the LogEntry does not | 
 | // exceed kMaxTbsCertificateLength and so can be written to |output|. | 
 | bool EncodePrecertLogEntry(const LogEntry& input, std::string* output) { | 
 |   WriteEncodedBytes( | 
 |       base::StringPiece( | 
 |           reinterpret_cast<const char*>(input.issuer_key_hash.data), | 
 |           kLogIdLength), | 
 |       output); | 
 |   return WriteVariableBytes(kTbsCertificateLengthBytes, | 
 |                             input.tbs_certificate, output); | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | bool EncodeDigitallySigned(const DigitallySigned& input, | 
 |                            std::string* output) { | 
 |   WriteUint(kHashAlgorithmLength, input.hash_algorithm, output); | 
 |   WriteUint(kSigAlgorithmLength, input.signature_algorithm, | 
 |             output); | 
 |   return WriteVariableBytes(kSignatureLengthBytes, input.signature_data, | 
 |                             output); | 
 | } | 
 |  | 
 | bool DecodeDigitallySigned(base::StringPiece* input, | 
 |                            DigitallySigned* output) { | 
 |   unsigned hash_algo; | 
 |   unsigned sig_algo; | 
 |   base::StringPiece sig_data; | 
 |  | 
 |   if (!ReadUint(kHashAlgorithmLength, input, &hash_algo) || | 
 |       !ReadUint(kSigAlgorithmLength, input, &sig_algo) || | 
 |       !ReadVariableBytes(kSignatureLengthBytes, input, &sig_data)) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   DigitallySigned result; | 
 |   if (!ConvertHashAlgorithm(hash_algo, &result.hash_algorithm)) { | 
 |     DVLOG(1) << "Invalid hash algorithm " << hash_algo; | 
 |     return false; | 
 |   } | 
 |   if (!ConvertSignatureAlgorithm(sig_algo, &result.signature_algorithm)) { | 
 |     DVLOG(1) << "Invalid signature algorithm " << sig_algo; | 
 |     return false; | 
 |   } | 
 |   sig_data.CopyToString(&result.signature_data); | 
 |  | 
 |   *output = result; | 
 |   return true; | 
 | } | 
 |  | 
 | bool EncodeLogEntry(const LogEntry& input, std::string* output) { | 
 |   WriteUint(kLogEntryTypeLength, input.type, output); | 
 |   switch (input.type) { | 
 |     case LogEntry::LOG_ENTRY_TYPE_X509: | 
 |       return EncodeAsn1CertLogEntry(input, output); | 
 |     case LogEntry::LOG_ENTRY_TYPE_PRECERT: | 
 |       return EncodePrecertLogEntry(input, output); | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | static void WriteTimeSinceEpoch(const base::Time& timestamp, | 
 |                                 std::string* output) { | 
 |   base::TimeDelta time_since_epoch = timestamp - base::Time::UnixEpoch(); | 
 |   WriteUint(kTimestampLength, time_since_epoch.InMilliseconds(), output); | 
 | } | 
 |  | 
 | bool EncodeV1SCTSignedData(const base::Time& timestamp, | 
 |                            const std::string& serialized_log_entry, | 
 |                            const std::string& extensions, | 
 |                            std::string* output) { | 
 |   WriteUint(kVersionLength, SignedCertificateTimestamp::SCT_VERSION_1, | 
 |             output); | 
 |   WriteUint(kSignatureTypeLength, SIGNATURE_TYPE_CERTIFICATE_TIMESTAMP, | 
 |             output); | 
 |   WriteTimeSinceEpoch(timestamp, output); | 
 |   // NOTE: serialized_log_entry must already be serialized and contain the | 
 |   // length as the prefix. | 
 |   WriteEncodedBytes(serialized_log_entry, output); | 
 |   return WriteVariableBytes(kExtensionsLengthBytes, extensions, output); | 
 | } | 
 |  | 
 | void EncodeTreeHeadSignature(const SignedTreeHead& signed_tree_head, | 
 |                              std::string* output) { | 
 |   WriteUint(kVersionLength, signed_tree_head.version, output); | 
 |   WriteUint(kSignatureTypeLength, TREE_HASH, output); | 
 |   WriteTimeSinceEpoch(signed_tree_head.timestamp, output); | 
 |   WriteUint(kTreeSizeLength, signed_tree_head.tree_size, output); | 
 |   WriteEncodedBytes( | 
 |       base::StringPiece(signed_tree_head.sha256_root_hash, kSthRootHashLength), | 
 |       output); | 
 | } | 
 |  | 
 | bool DecodeSCTList(base::StringPiece* input, | 
 |                    std::vector<base::StringPiece>* output) { | 
 |   std::vector<base::StringPiece> result; | 
 |   if (!ReadList(kSCTListLengthBytes, kSerializedSCTLengthBytes, | 
 |                 input, &result)) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   if (!input->empty() || result.empty()) | 
 |     return false; | 
 |   output->swap(result); | 
 |   return true; | 
 | } | 
 |  | 
 | bool DecodeSignedCertificateTimestamp( | 
 |     base::StringPiece* input, | 
 |     scoped_refptr<SignedCertificateTimestamp>* output) { | 
 |   scoped_refptr<SignedCertificateTimestamp> result( | 
 |       new SignedCertificateTimestamp()); | 
 |   unsigned version; | 
 |   if (!ReadUint(kVersionLength, input, &version)) | 
 |     return false; | 
 |   if (version != SignedCertificateTimestamp::SCT_VERSION_1) { | 
 |     DVLOG(1) << "Unsupported/invalid version " << version; | 
 |     return false; | 
 |   } | 
 |  | 
 |   result->version = SignedCertificateTimestamp::SCT_VERSION_1; | 
 |   uint64 timestamp; | 
 |   base::StringPiece log_id; | 
 |   base::StringPiece extensions; | 
 |   if (!ReadFixedBytes(kLogIdLength, input, &log_id) || | 
 |       !ReadUint(kTimestampLength, input, ×tamp) || | 
 |       !ReadVariableBytes(kExtensionsLengthBytes, input, | 
 |                          &extensions) || | 
 |       !DecodeDigitallySigned(input, &result->signature)) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   if (timestamp > static_cast<uint64>(kint64max)) { | 
 |     DVLOG(1) << "Timestamp value too big to cast to int64: " << timestamp; | 
 |     return false; | 
 |   } | 
 |  | 
 |   log_id.CopyToString(&result->log_id); | 
 |   extensions.CopyToString(&result->extensions); | 
 |   result->timestamp = | 
 |       base::Time::UnixEpoch() + | 
 |       base::TimeDelta::FromMilliseconds(static_cast<int64>(timestamp)); | 
 |  | 
 |   output->swap(result); | 
 |   return true; | 
 | } | 
 |  | 
 | bool EncodeSCTListForTesting(const base::StringPiece& sct, | 
 |                              std::string* output) { | 
 |   std::string encoded_sct; | 
 |   return WriteVariableBytes(kSerializedSCTLengthBytes, sct, &encoded_sct) && | 
 |       WriteVariableBytes(kSCTListLengthBytes, encoded_sct, output); | 
 | } | 
 |  | 
 | }  // namespace ct | 
 |  | 
 | }  // namespace net |