tor-browser

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

CTObjectsExtractor.cpp (12607B)


      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 "CTObjectsExtractor.h"
      8 
      9 #include <limits>
     10 #include <vector>
     11 
     12 #include "hasht.h"
     13 #include "mozpkix/pkixnss.h"
     14 #include "mozpkix/pkixutil.h"
     15 
     16 namespace mozilla {
     17 namespace ct {
     18 
     19 using namespace mozilla::pkix;
     20 
     21 // Holds a non-owning pointer to a byte buffer and allows writing chunks of data
     22 // to the buffer, placing the later chunks after the earlier ones
     23 // in a stream-like fashion.
     24 // Note that writing to Output always succeeds. If the internal buffer
     25 // overflows, an error flag is turned on and you won't be able to retrieve
     26 // the final data.
     27 class Output {
     28 public:
     29  Output(uint8_t* buffer, size_t length)
     30      : begin(buffer),
     31        end(buffer + length),
     32        current(begin),
     33        overflowed(false) {}
     34 
     35  template <size_t N>
     36  explicit Output(uint8_t (&buffer)[N]) : Output(buffer, N) {}
     37 
     38  void Write(Input data) { Write(data.UnsafeGetData(), data.GetLength()); }
     39 
     40  void Write(uint8_t b) { Write(&b, 1); }
     41 
     42  bool IsOverflowed() const { return overflowed; }
     43 
     44  Result GetInput(/*out*/ Input& input) const {
     45    if (overflowed || current < begin) {
     46      return Result::FATAL_ERROR_INVALID_STATE;
     47    }
     48    size_t length = static_cast<size_t>(current - begin);
     49    return input.Init(begin, length);
     50  }
     51 
     52 private:
     53  uint8_t* begin;
     54  uint8_t* end;
     55  uint8_t* current;
     56  bool overflowed;
     57 
     58  Output(const Output&) = delete;
     59  void operator=(const Output&) = delete;
     60 
     61  void Write(const uint8_t* data, size_t length) {
     62    if (end < current) {
     63      overflowed = true;
     64    }
     65    size_t available = static_cast<size_t>(end - current);
     66    if (available < length) {
     67      overflowed = true;
     68    }
     69    if (overflowed) {
     70      return;
     71    }
     72    memcpy(current, data, length);
     73    current += length;
     74  }
     75 };
     76 
     77 // For reference:
     78 //
     79 // Certificate  ::=  SEQUENCE  {
     80 //      tbsCertificate       TBSCertificate,
     81 //      signatureAlgorithm   AlgorithmIdentifier,
     82 //      signatureValue       BIT STRING  }
     83 //
     84 // TBSCertificate  ::=  SEQUENCE  {
     85 //      version         [0]  EXPLICIT Version DEFAULT v1,
     86 //      serialNumber         CertificateSerialNumber,
     87 //      signature            AlgorithmIdentifier,
     88 //      issuer               Name,
     89 //      validity             Validity,
     90 //      subject              Name,
     91 //      subjectPublicKeyInfo SubjectPublicKeyInfo,
     92 //      issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,
     93 //                           -- If present, version MUST be v2 or v3
     94 //      subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,
     95 //                           -- If present, version MUST be v2 or v3
     96 //      extensions      [3]  EXPLICIT Extensions OPTIONAL
     97 //                           -- If present, version MUST be v3
     98 //      }
     99 
    100 // python DottedOIDToCode.py id-embeddedSctList 1.3.6.1.4.1.11129.2.4.2
    101 // See Section 3.3 of RFC 6962.
    102 static const uint8_t EMBEDDED_SCT_LIST_OID[] = {0x2b, 0x06, 0x01, 0x04, 0x01,
    103                                                0xd6, 0x79, 0x02, 0x04, 0x02};
    104 // Maximum length of DER TLV header
    105 static const size_t MAX_TLV_HEADER_LENGTH = 4;
    106 // DER tag of the "extensions [3]" field from TBSCertificate
    107 static const uint8_t EXTENSIONS_CONTEXT_TAG =
    108    der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 3;
    109 
    110 Result CheckForInputSizeTypeOverflow(size_t length) {
    111  if (length > std::numeric_limits<Input::size_type>::max()) {
    112    return Result::FATAL_ERROR_INVALID_STATE;
    113  }
    114  return Success;
    115 }
    116 
    117 // Given a leaf certificate, extracts the DER-encoded TBSCertificate component
    118 // of the corresponding Precertificate.
    119 // Basically, the extractor needs to remove the embedded SCTs extension
    120 // from the certificate and return its TBSCertificate. We do it in an ad hoc
    121 // manner by breaking the source DER into several parts and then joining
    122 // the right parts, taking care to update the relevant TLV headers.
    123 // See WriteOutput for more details on the parts involved.
    124 class PrecertTBSExtractor {
    125 public:
    126  // |buffer| is the buffer to be used for writing the output. Since the
    127  // required buffer size is not generally known in advance, it's best
    128  // to use at least the size of the input certificate DER.
    129  PrecertTBSExtractor(Input der, uint8_t* buffer, size_t bufferLength)
    130      : mDER(der), mOutput(buffer, bufferLength) {}
    131 
    132  // Performs the extraction.
    133  Result Init() {
    134    Reader tbsReader;
    135    Result rv = GetTBSCertificate(tbsReader);
    136    if (rv != Success) {
    137      return rv;
    138    }
    139 
    140    rv = ExtractTLVsBeforeExtensions(tbsReader);
    141    if (rv != Success) {
    142      return rv;
    143    }
    144 
    145    rv = ExtractOptionalExtensionsExceptSCTs(tbsReader);
    146    if (rv != Success) {
    147      return rv;
    148    }
    149 
    150    return WriteOutput();
    151  }
    152 
    153  // Use to retrieve the result after a successful call to Init.
    154  // The returned Input points to the buffer supplied in the constructor.
    155  Input GetPrecertTBS() { return mPrecertTBS; }
    156 
    157 private:
    158  Result GetTBSCertificate(Reader& tbsReader) {
    159    Reader certificateReader;
    160    Result rv =
    161        der::ExpectTagAndGetValueAtEnd(mDER, der::SEQUENCE, certificateReader);
    162    if (rv != Success) {
    163      return rv;
    164    }
    165    return ExpectTagAndGetValue(certificateReader, der::SEQUENCE, tbsReader);
    166  }
    167 
    168  Result ExtractTLVsBeforeExtensions(Reader& tbsReader) {
    169    Reader::Mark tbsBegin = tbsReader.GetMark();
    170    while (!tbsReader.AtEnd()) {
    171      if (tbsReader.Peek(EXTENSIONS_CONTEXT_TAG)) {
    172        break;
    173      }
    174      uint8_t tag;
    175      Input tagValue;
    176      Result rv = der::ReadTagAndGetValue(tbsReader, tag, tagValue);
    177      if (rv != Success) {
    178        return rv;
    179      }
    180    }
    181    return tbsReader.GetInput(tbsBegin, mTLVsBeforeExtensions);
    182  }
    183 
    184  Result ExtractOptionalExtensionsExceptSCTs(Reader& tbsReader) {
    185    if (!tbsReader.Peek(EXTENSIONS_CONTEXT_TAG)) {
    186      return Success;
    187    }
    188 
    189    Reader extensionsContextReader;
    190    Result rv = der::ExpectTagAndGetValueAtEnd(
    191        tbsReader, EXTENSIONS_CONTEXT_TAG, extensionsContextReader);
    192    if (rv != Success) {
    193      return rv;
    194    }
    195 
    196    Reader extensionsReader;
    197    rv = der::ExpectTagAndGetValueAtEnd(extensionsContextReader, der::SEQUENCE,
    198                                        extensionsReader);
    199    if (rv != Success) {
    200      return rv;
    201    }
    202 
    203    while (!extensionsReader.AtEnd()) {
    204      Reader::Mark extensionTLVBegin = extensionsReader.GetMark();
    205      Reader extension;
    206      rv =
    207          der::ExpectTagAndGetValue(extensionsReader, der::SEQUENCE, extension);
    208      if (rv != Success) {
    209        return rv;
    210      }
    211      Reader extensionID;
    212      rv = der::ExpectTagAndGetValue(extension, der::OIDTag, extensionID);
    213      if (rv != Success) {
    214        return rv;
    215      }
    216      if (!extensionID.MatchRest(EMBEDDED_SCT_LIST_OID)) {
    217        Input extensionTLV;
    218        rv = extensionsReader.GetInput(extensionTLVBegin, extensionTLV);
    219        if (rv != Success) {
    220          return rv;
    221        }
    222        mExtensionTLVs.push_back(std::move(extensionTLV));
    223      }
    224    }
    225    return Success;
    226  }
    227 
    228  Result WriteOutput() {
    229    // What should be written here:
    230    //
    231    // TBSCertificate ::= SEQUENCE (TLV with header |tbsHeader|)
    232    //   dump of |mTLVsBeforeExtensions|
    233    //   extensions [3] OPTIONAL (TLV with header |extensionsContextHeader|)
    234    //     SEQUENCE (TLV with with header |extensionsHeader|)
    235    //       dump of |mExtensionTLVs|
    236 
    237    Result rv;
    238    if (!mExtensionTLVs.empty()) {
    239      uint8_t tbsHeaderBuffer[MAX_TLV_HEADER_LENGTH];
    240      uint8_t extensionsContextHeaderBuffer[MAX_TLV_HEADER_LENGTH];
    241      uint8_t extensionsHeaderBuffer[MAX_TLV_HEADER_LENGTH];
    242 
    243      Input tbsHeader;
    244      Input extensionsContextHeader;
    245      Input extensionsHeader;
    246 
    247      // Count the total size of the extensions. Note that since
    248      // the extensions data is contained within mDER (an Input),
    249      // their combined length won't overflow Input::size_type.
    250      Input::size_type extensionsValueLength = 0;
    251      for (auto& extensionTLV : mExtensionTLVs) {
    252        extensionsValueLength += extensionTLV.GetLength();
    253      }
    254 
    255      rv = MakeTLVHeader(der::SEQUENCE, extensionsValueLength,
    256                         extensionsHeaderBuffer, extensionsHeader);
    257      if (rv != Success) {
    258        return rv;
    259      }
    260      // Since we're getting these extensions from a certificate that has
    261      // already fit in an Input, this shouldn't overflow.
    262      size_t extensionsContextLengthAsSizeT =
    263          static_cast<size_t>(extensionsHeader.GetLength()) +
    264          static_cast<size_t>(extensionsValueLength);
    265      rv = CheckForInputSizeTypeOverflow(extensionsContextLengthAsSizeT);
    266      if (rv != Success) {
    267        return rv;
    268      }
    269      Input::size_type extensionsContextLength =
    270          static_cast<Input::size_type>(extensionsContextLengthAsSizeT);
    271      rv =
    272          MakeTLVHeader(EXTENSIONS_CONTEXT_TAG, extensionsContextLength,
    273                        extensionsContextHeaderBuffer, extensionsContextHeader);
    274      if (rv != Success) {
    275        return rv;
    276      }
    277      size_t tbsLengthAsSizeT =
    278          static_cast<size_t>(mTLVsBeforeExtensions.GetLength()) +
    279          static_cast<size_t>(extensionsContextHeader.GetLength()) +
    280          static_cast<size_t>(extensionsHeader.GetLength()) +
    281          static_cast<size_t>(extensionsValueLength);
    282      rv = CheckForInputSizeTypeOverflow(tbsLengthAsSizeT);
    283      if (rv != Success) {
    284        return rv;
    285      }
    286      Input::size_type tbsLength =
    287          static_cast<Input::size_type>(tbsLengthAsSizeT);
    288      rv = MakeTLVHeader(der::SEQUENCE, tbsLength, tbsHeaderBuffer, tbsHeader);
    289      if (rv != Success) {
    290        return rv;
    291      }
    292 
    293      mOutput.Write(tbsHeader);
    294      mOutput.Write(mTLVsBeforeExtensions);
    295      mOutput.Write(extensionsContextHeader);
    296      mOutput.Write(extensionsHeader);
    297      for (auto& extensionTLV : mExtensionTLVs) {
    298        mOutput.Write(extensionTLV);
    299      }
    300    } else {
    301      uint8_t tbsHeaderBuffer[MAX_TLV_HEADER_LENGTH];
    302      Input tbsHeader;
    303      rv = MakeTLVHeader(der::SEQUENCE, mTLVsBeforeExtensions.GetLength(),
    304                         tbsHeaderBuffer, tbsHeader);
    305      if (rv != Success) {
    306        return rv;
    307      }
    308      mOutput.Write(tbsHeader);
    309      mOutput.Write(mTLVsBeforeExtensions);
    310    }
    311 
    312    return mOutput.GetInput(mPrecertTBS);
    313  }
    314 
    315  Result MakeTLVHeader(uint8_t tag, size_t length,
    316                       uint8_t (&buffer)[MAX_TLV_HEADER_LENGTH],
    317                       /*out*/ Input& header) {
    318    Output output(buffer);
    319    output.Write(tag);
    320    if (length < 128) {
    321      output.Write(static_cast<uint8_t>(length));
    322    } else if (length < 256) {
    323      output.Write(0x81u);
    324      output.Write(static_cast<uint8_t>(length));
    325    } else if (length < 65536) {
    326      output.Write(0x82u);
    327      output.Write(static_cast<uint8_t>(length / 256));
    328      output.Write(static_cast<uint8_t>(length % 256));
    329    } else {
    330      return Result::FATAL_ERROR_INVALID_ARGS;
    331    }
    332    return output.GetInput(header);
    333  }
    334 
    335  Input mDER;
    336  Input mTLVsBeforeExtensions;
    337  std::vector<Input> mExtensionTLVs;
    338  Output mOutput;
    339  Input mPrecertTBS;
    340 };
    341 
    342 Result GetPrecertLogEntry(Input leafCertificate,
    343                          Input issuerSubjectPublicKeyInfo, LogEntry& output) {
    344  assert(leafCertificate.GetLength() > 0);
    345  assert(issuerSubjectPublicKeyInfo.GetLength() > 0);
    346  output.Reset();
    347 
    348  Buffer precertTBSBuffer;
    349  precertTBSBuffer.resize(leafCertificate.GetLength());
    350 
    351  PrecertTBSExtractor extractor(leafCertificate, precertTBSBuffer.data(),
    352                                precertTBSBuffer.size());
    353  Result rv = extractor.Init();
    354  if (rv != Success) {
    355    return rv;
    356  }
    357  Input precertTBS(extractor.GetPrecertTBS());
    358  assert(precertTBS.UnsafeGetData() == precertTBSBuffer.data());
    359  assert(precertTBS.GetLength() <= precertTBSBuffer.size());
    360  precertTBSBuffer.resize(precertTBS.GetLength());
    361 
    362  output.type = LogEntry::Type::Precert;
    363  output.tbsCertificate = std::move(precertTBSBuffer);
    364 
    365  output.issuerKeyHash.resize(SHA256_LENGTH);
    366  return DigestBufNSS(issuerSubjectPublicKeyInfo, DigestAlgorithm::sha256,
    367                      output.issuerKeyHash.data(), output.issuerKeyHash.size());
    368 }
    369 
    370 void GetX509LogEntry(Input leafCertificate, LogEntry& output) {
    371  assert(leafCertificate.GetLength() > 0);
    372  output.Reset();
    373  output.type = LogEntry::Type::X509;
    374  InputToBuffer(leafCertificate, output.leafCertificate);
    375 }
    376 
    377 }  // namespace ct
    378 }  // namespace mozilla