Home | History | Annotate | Download | only in cert
      1 // Copyright 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 // |prefix_length| indicates the bytes needed to represent the length (e.g. 3)
     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. |prefix_length| indicates the number of bytes needed to represent
     88 // the length.
     89 // The bytes read from |in| are discarded (i.e. |in|'s prefix removed)
     90 bool ReadVariableBytes(size_t prefix_length,
     91                        base::StringPiece* in,
     92                        base::StringPiece* out) {
     93   size_t length;
     94   if (!ReadLength(prefix_length, in, &length))
     95     return false;
     96   return ReadFixedBytes(length, in, out);
     97 }
     98 
     99 // Reads a variable-length list that has been TLS encoded.
    100 // The bytes read from |in| are discarded (i.e. |in|'s prefix removed)
    101 // |max_list_length| contains the overall length of the encoded list.
    102 // |max_item_length| contains the maximum length of a single item.
    103 // On success, returns true and updates |*out| with the encoded list.
    104 bool ReadList(size_t max_list_length,
    105               size_t max_item_length,
    106               base::StringPiece* in,
    107               std::vector<base::StringPiece>* out) {
    108   std::vector<base::StringPiece> result;
    109 
    110   base::StringPiece list_data;
    111   if (!ReadVariableBytes(max_list_length, in, &list_data))
    112     return false;
    113 
    114   while (!list_data.empty()) {
    115     base::StringPiece list_item;
    116     if (!ReadVariableBytes(max_item_length, &list_data, &list_item)) {
    117       DVLOG(1) << "Failed to read item in list.";
    118       return false;
    119     }
    120     if (list_item.empty()) {
    121       DVLOG(1) << "Empty item in list";
    122       return false;
    123     }
    124     result.push_back(list_item);
    125   }
    126 
    127   result.swap(*out);
    128   return true;
    129 }
    130 
    131 // Checks and converts a hash algorithm.
    132 // |in| is the numeric representation of the algorithm.
    133 // If the hash algorithm value is in a set of known values, fills in |out| and
    134 // returns true. Otherwise, returns false.
    135 bool ConvertHashAlgorithm(unsigned in, DigitallySigned::HashAlgorithm* out) {
    136   switch (in) {
    137     case DigitallySigned::HASH_ALGO_NONE:
    138     case DigitallySigned::HASH_ALGO_MD5:
    139     case DigitallySigned::HASH_ALGO_SHA1:
    140     case DigitallySigned::HASH_ALGO_SHA224:
    141     case DigitallySigned::HASH_ALGO_SHA256:
    142     case DigitallySigned::HASH_ALGO_SHA384:
    143     case DigitallySigned::HASH_ALGO_SHA512:
    144       break;
    145     default:
    146       return false;
    147   }
    148   *out = static_cast<DigitallySigned::HashAlgorithm>(in);
    149   return true;
    150 }
    151 
    152 // Checks and converts a signing algorithm.
    153 // |in| is the numeric representation of the algorithm.
    154 // If the signing algorithm value is in a set of known values, fills in |out|
    155 // and returns true. Otherwise, returns false.
    156 bool ConvertSignatureAlgorithm(
    157     unsigned in,
    158     DigitallySigned::SignatureAlgorithm* out) {
    159   switch (in) {
    160     case DigitallySigned::SIG_ALGO_ANONYMOUS:
    161     case DigitallySigned::SIG_ALGO_RSA:
    162     case DigitallySigned::SIG_ALGO_DSA:
    163     case DigitallySigned::SIG_ALGO_ECDSA:
    164       break;
    165     default:
    166       return false;
    167   }
    168   *out = static_cast<DigitallySigned::SignatureAlgorithm>(in);
    169   return true;
    170 }
    171 
    172 // Writes a TLS-encoded variable length unsigned integer to |output|.
    173 // |length| indicates the size (in bytes) of the integer.
    174 // |value| the value itself to be written.
    175 template <typename T>
    176 void WriteUint(size_t length, T value, std::string* output) {
    177   DCHECK_LE(length, sizeof(T));
    178   DCHECK(length == sizeof(T) || value >> (length * 8) == 0);
    179 
    180   for (; length > 0; --length) {
    181     output->push_back((value >> ((length - 1)* 8)) & 0xFF);
    182   }
    183 }
    184 
    185 // Writes an array to |output| from |input|.
    186 // Should be used in one of two cases:
    187 // * The length of |input| has already been encoded into the |output| stream.
    188 // * The length of |input| is fixed and the reader is expected to specify that
    189 // length when reading.
    190 // If the length of |input| is dynamic and data is expected to follow it,
    191 // WriteVariableBytes must be used.
    192 void WriteEncodedBytes(const base::StringPiece& input, std::string* output) {
    193   input.AppendToString(output);
    194 }
    195 
    196 // Writes a variable-length array to |output|.
    197 // |prefix_length| indicates the number of bytes needed to represnt the length.
    198 // |input| is the array itself.
    199 // If the size of |input| is less than 2^|prefix_length| - 1, encode the
    200 // length and data and return true. Otherwise, return false.
    201 bool WriteVariableBytes(size_t prefix_length,
    202                         const base::StringPiece& input,
    203                         std::string* output) {
    204   size_t input_size = input.size();
    205   size_t max_allowed_input_size =
    206       static_cast<size_t>(((1 << (prefix_length * 8)) - 1));
    207   if (input_size > max_allowed_input_size)
    208     return false;
    209 
    210   WriteUint(prefix_length, input.size(), output);
    211   WriteEncodedBytes(input, output);
    212 
    213   return true;
    214 }
    215 
    216 // Writes a LogEntry of type X.509 cert to |output|.
    217 // |input| is the LogEntry containing the certificate.
    218 // Returns true if the leaf_certificate in the LogEntry does not exceed
    219 // kMaxAsn1CertificateLength and so can be written to |output|.
    220 bool EncodeAsn1CertLogEntry(const LogEntry& input, std::string* output) {
    221   return WriteVariableBytes(kAsn1CertificateLengthBytes,
    222                             input.leaf_certificate, output);
    223 }
    224 
    225 // Writes a LogEntry of type PreCertificate to |output|.
    226 // |input| is the LogEntry containing the TBSCertificate and issuer key hash.
    227 // Returns true if the TBSCertificate component in the LogEntry does not
    228 // exceed kMaxTbsCertificateLength and so can be written to |output|.
    229 bool EncodePrecertLogEntry(const LogEntry& input, std::string* output) {
    230   WriteEncodedBytes(
    231       base::StringPiece(
    232           reinterpret_cast<const char*>(input.issuer_key_hash.data),
    233           kLogIdLength),
    234       output);
    235   return WriteVariableBytes(kTbsCertificateLengthBytes,
    236                             input.tbs_certificate, output);
    237 }
    238 
    239 }  // namespace
    240 
    241 bool EncodeDigitallySigned(const DigitallySigned& input,
    242                            std::string* output) {
    243   WriteUint(kHashAlgorithmLength, input.hash_algorithm, output);
    244   WriteUint(kSigAlgorithmLength, input.signature_algorithm,
    245             output);
    246   return WriteVariableBytes(kSignatureLengthBytes, input.signature_data,
    247                             output);
    248 }
    249 
    250 bool DecodeDigitallySigned(base::StringPiece* input,
    251                            DigitallySigned* output) {
    252   unsigned hash_algo;
    253   unsigned sig_algo;
    254   base::StringPiece sig_data;
    255 
    256   if (!ReadUint(kHashAlgorithmLength, input, &hash_algo) ||
    257       !ReadUint(kSigAlgorithmLength, input, &sig_algo) ||
    258       !ReadVariableBytes(kSignatureLengthBytes, input, &sig_data)) {
    259     return false;
    260   }
    261 
    262   DigitallySigned result;
    263   if (!ConvertHashAlgorithm(hash_algo, &result.hash_algorithm)) {
    264     DVLOG(1) << "Invalid hash algorithm " << hash_algo;
    265     return false;
    266   }
    267   if (!ConvertSignatureAlgorithm(sig_algo, &result.signature_algorithm)) {
    268     DVLOG(1) << "Invalid signature algorithm " << sig_algo;
    269     return false;
    270   }
    271   sig_data.CopyToString(&result.signature_data);
    272 
    273   *output = result;
    274   return true;
    275 }
    276 
    277 bool EncodeLogEntry(const LogEntry& input, std::string* output) {
    278   WriteUint(kLogEntryTypeLength, input.type, output);
    279   switch (input.type) {
    280     case LogEntry::LOG_ENTRY_TYPE_X509:
    281       return EncodeAsn1CertLogEntry(input, output);
    282     case LogEntry::LOG_ENTRY_TYPE_PRECERT:
    283       return EncodePrecertLogEntry(input, output);
    284   }
    285   return false;
    286 }
    287 
    288 bool EncodeV1SCTSignedData(const base::Time& timestamp,
    289                            const std::string& serialized_log_entry,
    290                            const std::string& extensions,
    291                            std::string* output) {
    292   WriteUint(kVersionLength, SignedCertificateTimestamp::SCT_VERSION_1,
    293             output);
    294   WriteUint(kSignatureTypeLength, SIGNATURE_TYPE_CERTIFICATE_TIMESTAMP,
    295             output);
    296   base::TimeDelta time_since_epoch = timestamp - base::Time::UnixEpoch();
    297   WriteUint(kTimestampLength, time_since_epoch.InMilliseconds(),
    298             output);
    299   // NOTE: serialized_log_entry must already be serialized and contain the
    300   // length as the prefix.
    301   WriteEncodedBytes(serialized_log_entry, output);
    302   return WriteVariableBytes(kExtensionsLengthBytes, extensions, output);
    303 }
    304 
    305 bool DecodeSCTList(base::StringPiece* input,
    306                    std::vector<base::StringPiece>* output) {
    307   std::vector<base::StringPiece> result;
    308   if (!ReadList(kSCTListLengthBytes, kSerializedSCTLengthBytes,
    309                 input, &result)) {
    310     return false;
    311   }
    312 
    313   if (!input->empty() || result.empty())
    314     return false;
    315   output->swap(result);
    316   return true;
    317 }
    318 
    319 bool DecodeSignedCertificateTimestamp(
    320     base::StringPiece* input,
    321     scoped_refptr<SignedCertificateTimestamp>* output) {
    322   scoped_refptr<SignedCertificateTimestamp> result(
    323       new SignedCertificateTimestamp());
    324   unsigned version;
    325   if (!ReadUint(kVersionLength, input, &version))
    326     return false;
    327   if (version != SignedCertificateTimestamp::SCT_VERSION_1) {
    328     DVLOG(1) << "Unsupported/invalid version " << version;
    329     return false;
    330   }
    331 
    332   result->version = SignedCertificateTimestamp::SCT_VERSION_1;
    333   uint64 timestamp;
    334   base::StringPiece log_id;
    335   base::StringPiece extensions;
    336   if (!ReadFixedBytes(kLogIdLength, input, &log_id) ||
    337       !ReadUint(kTimestampLength, input, &timestamp) ||
    338       !ReadVariableBytes(kExtensionsLengthBytes, input,
    339                          &extensions) ||
    340       !DecodeDigitallySigned(input, &result->signature)) {
    341     return false;
    342   }
    343 
    344   if (timestamp > static_cast<uint64>(kint64max)) {
    345     DVLOG(1) << "Timestamp value too big to cast to int64: " << timestamp;
    346     return false;
    347   }
    348 
    349   log_id.CopyToString(&result->log_id);
    350   extensions.CopyToString(&result->extensions);
    351   result->timestamp =
    352       base::Time::UnixEpoch() +
    353       base::TimeDelta::FromMilliseconds(static_cast<int64>(timestamp));
    354 
    355   output->swap(result);
    356   return true;
    357 }
    358 
    359 bool EncodeSCTListForTesting(const base::StringPiece& sct,
    360                              std::string* output) {
    361   std::string encoded_sct;
    362   return WriteVariableBytes(kSerializedSCTLengthBytes, sct, &encoded_sct) &&
    363       WriteVariableBytes(kSCTListLengthBytes, encoded_sct, output);
    364 }
    365 
    366 }  // namespace ct
    367 
    368 }  // namespace net
    369