| // 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 <algorithm> |
| #include <limits> |
| |
| #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" |
| #include "starboard/types.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 <typename T> |
| 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<uint8_t>((*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<uint8_t>((*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<size_t> 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<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. 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<const char*>(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<int64_t> 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<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::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<ct::SignedCertificateTimestamp>& 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<const char*>(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 |