// 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 #include #include #include "base/logging.h" #include "base/numerics/safe_math.h" #include "crypto/sha2.h" #include "net/cert/merkle_tree_leaf.h" #include "net/cert/signed_certificate_timestamp.h" #include "net/cert/signed_tree_head.h" namespace net { namespace ct { namespace { // Note: length is always specified in bytes. // CT protocol version length const size_t kVersionLength = 1; // Common V1 struct members const size_t kTimestampLength = 8; const size_t kSignedEntryTypeLength = 2; const size_t kAsn1CertificateLengthBytes = 3; const size_t kTbsCertificateLengthBytes = 3; const size_t kExtensionsLengthBytes = 2; // Members of a V1 SCT const size_t kLogIdLength = crypto::kSHA256Length; 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 kSCTListLengthBytes = 2; const size_t kSerializedSCTLengthBytes = 2; // Members of digitally-signed struct of a STH const size_t kTreeSizeLength = 8; // Members of a V1 MerkleTreeLeaf const size_t kMerkleLeafTypeLength = 1; const size_t kIssuerKeyHashLength = crypto::kSHA256Length; 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 bool ReadUint(size_t length, base::StringPiece* in, T* out) { if (in->size() < length) return false; DCHECK_NE(length, 0u); DCHECK_LE(length, sizeof(T)); T result = static_cast((*in)[0]); // This loop only executes if sizeof(T) > 1, because the first operation is // to shift left by 1 byte, which is undefined behaviour if T is a 1 byte // integer. for (size_t i = 1; i < length; ++i) { result = (result << 8) | static_cast((*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). // Max |prefix_length| is 8. // success, returns true and stores the result in |*out|. bool ReadLength(size_t prefix_length, base::StringPiece* in, size_t* out) { uint64_t length = 0; if (!ReadUint(prefix_length, in, &length)) return false; base::CheckedNumeric checked_length = length; if (!checked_length.IsValid()) return false; *out = checked_length.ValueOrDie(); 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 = 0; 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* out) { std::vector 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(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(in); return true; } // Writes a TLS-encoded variable length unsigned integer to |output|. // |length| indicates the size (in bytes) of the integer. This must be able to // accomodate |value|. // |value| the value itself to be written. void WriteUint(size_t length, uint64_t value, std::string* output) { // Check that |value| fits into |length| bytes. DCHECK(length >= sizeof(value) || 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. // Returns the number of bytes written (the length of |input|). size_t WriteEncodedBytes(const base::StringPiece& input, std::string* output) { input.AppendToString(output); return input.size(); } // Writes a variable-length array to |output|. // |prefix_length| indicates the number of bytes needed to represent the length. // |input| is the array itself. // If 1 <= |prefix_length| <= 8 and 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) { DCHECK_GE(prefix_length, 1u); DCHECK_LE(prefix_length, 8u); uint64_t input_size = input.size(); uint64_t max_input_size = (prefix_length == 8) ? UINT64_MAX : ((UINT64_C(1) << (prefix_length * 8)) - 1); if (input_size > max_input_size) return false; WriteUint(prefix_length, input_size, output); WriteEncodedBytes(input, output); return true; } // Writes a SignedEntryData of type X.509 cert to |output|. // |input| is the SignedEntryData containing the certificate. // Returns true if the leaf_certificate in the SignedEntryData does not exceed // kMaxAsn1CertificateLength and so can be written to |output|. bool EncodeAsn1CertSignedEntry(const SignedEntryData& input, std::string* output) { return WriteVariableBytes(kAsn1CertificateLengthBytes, input.leaf_certificate, output); } // Writes a SignedEntryData of type PreCertificate to |output|. // |input| is the SignedEntryData containing the TBSCertificate and issuer key // hash. Returns true if the TBSCertificate component in the SignedEntryData // does not exceed kMaxTbsCertificateLength and so can be written to |output|. bool EncodePrecertSignedEntry(const SignedEntryData& input, std::string* output) { WriteEncodedBytes( base::StringPiece( reinterpret_cast(input.issuer_key_hash.data), kIssuerKeyHashLength), 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 EncodeSignedEntry(const SignedEntryData& input, std::string* output) { WriteUint(kSignedEntryTypeLength, input.type, output); switch (input.type) { case SignedEntryData::LOG_ENTRY_TYPE_X509: return EncodeAsn1CertSignedEntry(input, output); case SignedEntryData::LOG_ENTRY_TYPE_PRECERT: return EncodePrecertSignedEntry(input, output); } return false; } static bool ReadTimeSinceEpoch(base::StringPiece* input, base::Time* output) { uint64_t time_since_epoch = 0; if (!ReadUint(kTimestampLength, input, &time_since_epoch)) return false; base::CheckedNumeric time_since_epoch_signed = time_since_epoch; if (!time_since_epoch_signed.IsValid()) { DVLOG(1) << "Timestamp value too big to cast to int64_t: " << time_since_epoch; return false; } *output = base::Time::UnixEpoch() + base::TimeDelta::FromMilliseconds(time_since_epoch_signed.ValueOrDie()); return true; } 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 EncodeTreeLeaf(const MerkleTreeLeaf& leaf, std::string* output) { WriteUint(kVersionLength, 0, output); // version: 1 WriteUint(kMerkleLeafTypeLength, 0, output); // type: timestamped entry WriteTimeSinceEpoch(leaf.timestamp, output); if (!EncodeSignedEntry(leaf.signed_entry, output)) return false; if (!WriteVariableBytes(kExtensionsLengthBytes, leaf.extensions, output)) return false; return true; } bool EncodeV1SCTSignedData(const base::Time& timestamp, const std::string& serialized_log_entry, const std::string& extensions, std::string* output) { WriteUint(kVersionLength, SignedCertificateTimestamp::V1, 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* output) { std::vector 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* output) { scoped_refptr result( new SignedCertificateTimestamp()); unsigned version; if (!ReadUint(kVersionLength, input, &version)) return false; if (version != SignedCertificateTimestamp::V1) { DVLOG(1) << "Unsupported/invalid version " << version; return false; } result->version = SignedCertificateTimestamp::V1; base::StringPiece log_id; base::StringPiece extensions; if (!ReadFixedBytes(kLogIdLength, input, &log_id) || !ReadTimeSinceEpoch(input, &result->timestamp) || !ReadVariableBytes(kExtensionsLengthBytes, input, &extensions) || !DecodeDigitallySigned(input, &result->signature)) { return false; } log_id.CopyToString(&result->log_id); extensions.CopyToString(&result->extensions); output->swap(result); return true; } void EncodeSignedCertificateTimestamp( const scoped_refptr& input, std::string* output) { // This function only supports serialization of V1 SCTs. DCHECK_EQ(SignedCertificateTimestamp::V1, input->version); WriteUint(kVersionLength, input->version, output); DCHECK_EQ(kLogIdLength, input->log_id.size()); WriteEncodedBytes( base::StringPiece(reinterpret_cast(input->log_id.data()), kLogIdLength), output); WriteTimeSinceEpoch(input->timestamp, output); WriteVariableBytes(kExtensionsLengthBytes, input->extensions, output); EncodeDigitallySigned(input->signature, output); } 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