tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

CTSerialization.cpp (12963B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "CTSerialization.h"
      8 #include "CTUtils.h"
      9 
     10 #include <stdint.h>
     11 #include <type_traits>
     12 
     13 namespace mozilla {
     14 namespace ct {
     15 
     16 using namespace mozilla::pkix;
     17 
     18 typedef mozilla::pkix::Result Result;
     19 
     20 // Note: length is always specified in bytes.
     21 // Signed Certificate Timestamp (SCT) Version length
     22 static const size_t kVersionLength = 1;
     23 
     24 // Members of a V1 SCT
     25 static const size_t kLogIdLength = 32;
     26 static const size_t kTimestampLength = 8;
     27 static const size_t kExtensionsLengthBytes = 2;
     28 static const size_t kHashAlgorithmLength = 1;
     29 static const size_t kSigAlgorithmLength = 1;
     30 static const size_t kSignatureLengthBytes = 2;
     31 
     32 // Members of the digitally-signed struct of a V1 SCT
     33 static const size_t kSignatureTypeLength = 1;
     34 static const size_t kLogEntryTypeLength = 2;
     35 static const size_t kAsn1CertificateLengthBytes = 3;
     36 static const size_t kTbsCertificateLengthBytes = 3;
     37 
     38 static const size_t kSCTListLengthBytes = 2;
     39 static const size_t kSerializedSCTLengthBytes = 2;
     40 
     41 enum class SignatureType {
     42  CertificateTimestamp = 0,
     43  TreeHash = 1,
     44 };
     45 
     46 // Reads a serialized hash algorithm.
     47 static Result ReadHashAlgorithm(Reader& in,
     48                                DigitallySigned::HashAlgorithm& out) {
     49  unsigned int value;
     50  Result rv = ReadUint<kHashAlgorithmLength>(in, value);
     51  if (rv != Success) {
     52    return rv;
     53  }
     54  DigitallySigned::HashAlgorithm algo =
     55      static_cast<DigitallySigned::HashAlgorithm>(value);
     56  switch (algo) {
     57    case DigitallySigned::HashAlgorithm::None:
     58    case DigitallySigned::HashAlgorithm::MD5:
     59    case DigitallySigned::HashAlgorithm::SHA1:
     60    case DigitallySigned::HashAlgorithm::SHA224:
     61    case DigitallySigned::HashAlgorithm::SHA256:
     62    case DigitallySigned::HashAlgorithm::SHA384:
     63    case DigitallySigned::HashAlgorithm::SHA512:
     64      out = algo;
     65      return Success;
     66  }
     67  return Result::ERROR_BAD_DER;
     68 }
     69 
     70 // Reads a serialized signature algorithm.
     71 static Result ReadSignatureAlgorithm(Reader& in,
     72                                     DigitallySigned::SignatureAlgorithm& out) {
     73  unsigned int value;
     74  Result rv = ReadUint<kSigAlgorithmLength>(in, value);
     75  if (rv != Success) {
     76    return rv;
     77  }
     78  DigitallySigned::SignatureAlgorithm algo =
     79      static_cast<DigitallySigned::SignatureAlgorithm>(value);
     80  switch (algo) {
     81    case DigitallySigned::SignatureAlgorithm::Anonymous:
     82    case DigitallySigned::SignatureAlgorithm::RSA:
     83    case DigitallySigned::SignatureAlgorithm::DSA:
     84    case DigitallySigned::SignatureAlgorithm::ECDSA:
     85      out = algo;
     86      return Success;
     87  }
     88  return Result::ERROR_BAD_DER;
     89 }
     90 
     91 // Reads a serialized version enum.
     92 static Result ReadVersion(Reader& in,
     93                          SignedCertificateTimestamp::Version& out) {
     94  unsigned int value;
     95  Result rv = ReadUint<kVersionLength>(in, value);
     96  if (rv != Success) {
     97    return rv;
     98  }
     99  SignedCertificateTimestamp::Version version =
    100      static_cast<SignedCertificateTimestamp::Version>(value);
    101  switch (version) {
    102    case SignedCertificateTimestamp::Version::V1:
    103      out = version;
    104      return Success;
    105  }
    106  return Result::ERROR_BAD_DER;
    107 }
    108 
    109 // Writes a TLS-encoded variable length unsigned integer to |output|.
    110 // Note: range/overflow checks are not performed on the input parameters.
    111 // |length| indicates the size (in bytes) of the integer to be written.
    112 // |value| the value itself to be written.
    113 static Result UncheckedWriteUint(size_t length, uint64_t value,
    114                                 Buffer& output) {
    115  output.reserve(length + output.size());
    116  for (; length > 0; --length) {
    117    uint8_t nextByte = (value >> ((length - 1) * 8)) & 0xFF;
    118    output.push_back(nextByte);
    119  }
    120  return Success;
    121 }
    122 
    123 // Performs sanity checks on T and calls UncheckedWriteUint.
    124 template <size_t length, typename T>
    125 static inline Result WriteUint(T value, Buffer& output) {
    126  static_assert(length <= 8, "At most 8 byte integers can be written");
    127  static_assert(sizeof(T) >= length, "T must be able to hold <length> bytes");
    128  if (std::is_signed<T>::value) {
    129    // We accept signed integer types assuming the actual value is non-negative.
    130    if (value < 0) {
    131      return Result::FATAL_ERROR_INVALID_ARGS;
    132    }
    133  }
    134  if (sizeof(T) > length) {
    135    // We allow the value variable to take more bytes than is written,
    136    // but the unwritten bytes must be zero.
    137    // Note: when "sizeof(T) == length" holds, "value >> (length * 8)" is
    138    // undefined since the shift is too big. On some compilers, this would
    139    // produce a warning even though the actual code is unreachable.
    140    if (value >> (length * 8 - 1) > 1) {
    141      return Result::FATAL_ERROR_INVALID_ARGS;
    142    }
    143  }
    144  return UncheckedWriteUint(length, static_cast<uint64_t>(value), output);
    145 }
    146 
    147 // Writes an array to |output| from |input|.
    148 // Should be used in one of two cases:
    149 // * The length of |input| has already been encoded into the |output| stream.
    150 // * The length of |input| is fixed and the reader is expected to specify that
    151 // length when reading.
    152 // If the length of |input| is dynamic and data is expected to follow it,
    153 // WriteVariableBytes must be used.
    154 static void WriteEncodedBytes(Input input, Buffer& output) {
    155  output.insert(output.end(), input.UnsafeGetData(),
    156                input.UnsafeGetData() + input.GetLength());
    157 }
    158 
    159 // Same as above, but the source data is in a Buffer.
    160 static void WriteEncodedBytes(const Buffer& source, Buffer& output) {
    161  output.insert(output.end(), source.begin(), source.end());
    162 }
    163 
    164 // A variable-length byte array is prefixed by its length when serialized.
    165 // This writes the length prefix.
    166 // |prefixLength| indicates the number of bytes needed to represent the length.
    167 // |dataLength| is the length of the byte array following the prefix.
    168 // Fails if |dataLength| is more than 2^|prefixLength| - 1.
    169 template <size_t prefixLength>
    170 static Result WriteVariableBytesPrefix(size_t dataLength, Buffer& output) {
    171  const size_t maxAllowedInputSize =
    172      static_cast<size_t>(((1 << (prefixLength * 8)) - 1));
    173  if (dataLength > maxAllowedInputSize) {
    174    return Result::FATAL_ERROR_INVALID_ARGS;
    175  }
    176 
    177  return WriteUint<prefixLength>(dataLength, output);
    178 }
    179 
    180 // Writes a variable-length array to |output|.
    181 // |prefixLength| indicates the number of bytes needed to represent the length.
    182 // |input| is the array itself.
    183 // Fails if the size of |input| is more than 2^|prefixLength| - 1.
    184 template <size_t prefixLength>
    185 static Result WriteVariableBytes(Input input, Buffer& output) {
    186  Result rv = WriteVariableBytesPrefix<prefixLength>(input.GetLength(), output);
    187  if (rv != Success) {
    188    return rv;
    189  }
    190  WriteEncodedBytes(input, output);
    191  return Success;
    192 }
    193 
    194 // Same as above, but the source data is in a Buffer.
    195 template <size_t prefixLength>
    196 static Result WriteVariableBytes(const Buffer& source, Buffer& output) {
    197  Input input;
    198  Result rv = BufferToInput(source, input);
    199  if (rv != Success) {
    200    return rv;
    201  }
    202  return WriteVariableBytes<prefixLength>(input, output);
    203 }
    204 
    205 // Writes a LogEntry of type X.509 cert to |output|.
    206 // |input| is the LogEntry containing the certificate.
    207 static Result EncodeAsn1CertLogEntry(const LogEntry& entry, Buffer& output) {
    208  return WriteVariableBytes<kAsn1CertificateLengthBytes>(entry.leafCertificate,
    209                                                         output);
    210 }
    211 
    212 // Writes a LogEntry of type PreCertificate to |output|.
    213 // |input| is the LogEntry containing the TBSCertificate and issuer key hash.
    214 static Result EncodePrecertLogEntry(const LogEntry& entry, Buffer& output) {
    215  if (entry.issuerKeyHash.size() != kLogIdLength) {
    216    return Result::FATAL_ERROR_INVALID_ARGS;
    217  }
    218  WriteEncodedBytes(entry.issuerKeyHash, output);
    219  return WriteVariableBytes<kTbsCertificateLengthBytes>(entry.tbsCertificate,
    220                                                        output);
    221 }
    222 
    223 Result EncodeDigitallySigned(const DigitallySigned& data, Buffer& output) {
    224  Result rv = WriteUint<kHashAlgorithmLength>(
    225      static_cast<unsigned int>(data.hashAlgorithm), output);
    226  if (rv != Success) {
    227    return rv;
    228  }
    229  rv = WriteUint<kSigAlgorithmLength>(
    230      static_cast<unsigned int>(data.signatureAlgorithm), output);
    231  if (rv != Success) {
    232    return rv;
    233  }
    234  return WriteVariableBytes<kSignatureLengthBytes>(data.signatureData, output);
    235 }
    236 
    237 Result DecodeDigitallySigned(Reader& reader, DigitallySigned& output) {
    238  DigitallySigned result;
    239 
    240  Result rv = ReadHashAlgorithm(reader, result.hashAlgorithm);
    241  if (rv != Success) {
    242    return rv;
    243  }
    244  rv = ReadSignatureAlgorithm(reader, result.signatureAlgorithm);
    245  if (rv != Success) {
    246    return rv;
    247  }
    248 
    249  Input signatureData;
    250  rv = ReadVariableBytes<kSignatureLengthBytes>(reader, signatureData);
    251  if (rv != Success) {
    252    return rv;
    253  }
    254  InputToBuffer(signatureData, result.signatureData);
    255 
    256  output = std::move(result);
    257  return Success;
    258 }
    259 
    260 Result EncodeLogEntry(const LogEntry& entry, Buffer& output) {
    261  Result rv = WriteUint<kLogEntryTypeLength>(
    262      static_cast<unsigned int>(entry.type), output);
    263  if (rv != Success) {
    264    return rv;
    265  }
    266  switch (entry.type) {
    267    case LogEntry::Type::X509:
    268      return EncodeAsn1CertLogEntry(entry, output);
    269    case LogEntry::Type::Precert:
    270      return EncodePrecertLogEntry(entry, output);
    271    default:
    272      assert(false);
    273  }
    274  return Result::ERROR_BAD_DER;
    275 }
    276 
    277 static Result WriteTimeSinceEpoch(uint64_t timestamp, Buffer& output) {
    278  return WriteUint<kTimestampLength>(timestamp, output);
    279 }
    280 
    281 Result EncodeV1SCTSignedData(uint64_t timestamp, Input serializedLogEntry,
    282                             Input extensions, Buffer& output) {
    283  Result rv = WriteUint<kVersionLength>(
    284      static_cast<unsigned int>(SignedCertificateTimestamp::Version::V1),
    285      output);
    286  if (rv != Success) {
    287    return rv;
    288  }
    289  rv = WriteUint<kSignatureTypeLength>(
    290      static_cast<unsigned int>(SignatureType::CertificateTimestamp), output);
    291  if (rv != Success) {
    292    return rv;
    293  }
    294  rv = WriteTimeSinceEpoch(timestamp, output);
    295  if (rv != Success) {
    296    return rv;
    297  }
    298  // NOTE: serializedLogEntry must already be serialized and contain the
    299  // length as the prefix.
    300  WriteEncodedBytes(serializedLogEntry, output);
    301  return WriteVariableBytes<kExtensionsLengthBytes>(extensions, output);
    302 }
    303 
    304 Result DecodeSCTList(Input input, Reader& listReader) {
    305  Reader inputReader(input);
    306  Input listData;
    307  Result rv = ReadVariableBytes<kSCTListLengthBytes>(inputReader, listData);
    308  if (rv != Success) {
    309    return rv;
    310  }
    311  return listReader.Init(listData);
    312 }
    313 
    314 Result ReadSCTListItem(Reader& listReader, Input& output) {
    315  if (listReader.AtEnd()) {
    316    return Result::FATAL_ERROR_INVALID_ARGS;
    317  }
    318 
    319  Result rv = ReadVariableBytes<kSerializedSCTLengthBytes>(listReader, output);
    320  if (rv != Success) {
    321    return rv;
    322  }
    323  if (output.GetLength() == 0) {
    324    return Result::ERROR_BAD_DER;
    325  }
    326  return Success;
    327 }
    328 
    329 Result DecodeSignedCertificateTimestamp(Reader& reader,
    330                                        SignedCertificateTimestamp& output) {
    331  SignedCertificateTimestamp result;
    332 
    333  Result rv = ReadVersion(reader, result.version);
    334  if (rv != Success) {
    335    return rv;
    336  }
    337 
    338  uint64_t timestamp;
    339  Input logId;
    340  Input extensions;
    341 
    342  rv = ReadFixedBytes(kLogIdLength, reader, logId);
    343  if (rv != Success) {
    344    return rv;
    345  }
    346  rv = ReadUint<kTimestampLength>(reader, timestamp);
    347  if (rv != Success) {
    348    return rv;
    349  }
    350  rv = ReadVariableBytes<kExtensionsLengthBytes>(reader, extensions);
    351  if (rv != Success) {
    352    return rv;
    353  }
    354  rv = DecodeDigitallySigned(reader, result.signature);
    355  if (rv != Success) {
    356    return rv;
    357  }
    358 
    359  InputToBuffer(logId, result.logId);
    360  InputToBuffer(extensions, result.extensions);
    361  result.timestamp = timestamp;
    362 
    363  rv = result.DecodeExtensions();
    364  if (rv != Success) {
    365    return rv;
    366  }
    367 
    368  output = std::move(result);
    369  return Success;
    370 }
    371 
    372 Result EncodeSCTList(const std::vector<pkix::Input>& scts, Buffer& output) {
    373  // Find out the total size of the SCT list to be written so we can
    374  // write the prefix for the list before writing its contents.
    375  size_t sctListLength = 0;
    376  for (auto& sct : scts) {
    377    sctListLength +=
    378        /* data size */ sct.GetLength() +
    379        /* length prefix size */ kSerializedSCTLengthBytes;
    380  }
    381 
    382  output.reserve(kSCTListLengthBytes + sctListLength);
    383 
    384  // Write the prefix for the SCT list.
    385  Result rv =
    386      WriteVariableBytesPrefix<kSCTListLengthBytes>(sctListLength, output);
    387  if (rv != Success) {
    388    return rv;
    389  }
    390  // Now write each SCT from the list.
    391  for (auto& sct : scts) {
    392    rv = WriteVariableBytes<kSerializedSCTLengthBytes>(sct, output);
    393    if (rv != Success) {
    394      return rv;
    395    }
    396  }
    397  return Success;
    398 }
    399 
    400 }  // namespace ct
    401 }  // namespace mozilla