tor-browser

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

MultiLogCTVerifier.cpp (5886B)


      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 "MultiLogCTVerifier.h"
      8 
      9 #include "CTObjectsExtractor.h"
     10 #include "CTSerialization.h"
     11 #include "mozilla/StaticPrefs_security.h"
     12 
     13 namespace mozilla {
     14 namespace ct {
     15 
     16 using namespace mozilla::pkix;
     17 
     18 MultiLogCTVerifier::MultiLogCTVerifier()
     19    : mSignatureCache(signature_cache_new(
     20                          StaticPrefs::security_pki_sct_signature_cache_size()),
     21                      signature_cache_free) {}
     22 
     23 void MultiLogCTVerifier::AddLog(CTLogVerifier&& log) {
     24  mLogs.push_back(std::move(log));
     25 }
     26 
     27 pkix::Result MultiLogCTVerifier::Verify(
     28    Input cert, Input issuerSubjectPublicKeyInfo, Input sctListFromCert,
     29    Input sctListFromOCSPResponse, Input sctListFromTLSExtension, Time time,
     30    Maybe<Time> distrustAfterTime, CTVerifyResult& result) {
     31  assert(cert.GetLength() > 0);
     32  result.Reset();
     33 
     34  pkix::Result rv;
     35 
     36  // Verify embedded SCTs
     37  if (issuerSubjectPublicKeyInfo.GetLength() > 0 &&
     38      sctListFromCert.GetLength() > 0) {
     39    LogEntry precertEntry;
     40    rv = GetPrecertLogEntry(cert, issuerSubjectPublicKeyInfo, precertEntry);
     41    if (rv != Success) {
     42      return rv;
     43    }
     44    rv = VerifySCTs(sctListFromCert, precertEntry, SCTOrigin::Embedded, time,
     45                    distrustAfterTime, result);
     46    if (rv != Success) {
     47      return rv;
     48    }
     49  }
     50 
     51  LogEntry x509Entry;
     52  GetX509LogEntry(cert, x509Entry);
     53 
     54  // Verify SCTs from a stapled OCSP response
     55  if (sctListFromOCSPResponse.GetLength() > 0) {
     56    rv = VerifySCTs(sctListFromOCSPResponse, x509Entry, SCTOrigin::OCSPResponse,
     57                    time, distrustAfterTime, result);
     58    if (rv != Success) {
     59      return rv;
     60    }
     61  }
     62 
     63  // Verify SCTs from a TLS extension
     64  if (sctListFromTLSExtension.GetLength() > 0) {
     65    rv = VerifySCTs(sctListFromTLSExtension, x509Entry, SCTOrigin::TLSExtension,
     66                    time, distrustAfterTime, result);
     67    if (rv != Success) {
     68      return rv;
     69    }
     70  }
     71  return Success;
     72 }
     73 
     74 void DecodeSCTs(Input encodedSctList,
     75                std::vector<SignedCertificateTimestamp>& decodedSCTs,
     76                size_t& decodingErrors) {
     77  decodedSCTs.clear();
     78 
     79  Reader listReader;
     80  pkix::Result rv = DecodeSCTList(encodedSctList, listReader);
     81  if (rv != Success) {
     82    decodingErrors++;
     83    return;
     84  }
     85 
     86  while (!listReader.AtEnd()) {
     87    Input encodedSct;
     88    rv = ReadSCTListItem(listReader, encodedSct);
     89    if (rv != Success) {
     90      decodingErrors++;
     91      return;
     92    }
     93 
     94    Reader encodedSctReader(encodedSct);
     95    SignedCertificateTimestamp sct;
     96    rv = DecodeSignedCertificateTimestamp(encodedSctReader, sct);
     97    if (rv != Success) {
     98      decodingErrors++;
     99      continue;
    100    }
    101    decodedSCTs.push_back(std::move(sct));
    102  }
    103 }
    104 
    105 pkix::Result MultiLogCTVerifier::VerifySCTs(Input encodedSctList,
    106                                            const LogEntry& expectedEntry,
    107                                            SCTOrigin origin, Time time,
    108                                            Maybe<Time> distrustAfterTime,
    109                                            CTVerifyResult& result) {
    110  std::vector<SignedCertificateTimestamp> decodedSCTs;
    111  DecodeSCTs(encodedSctList, decodedSCTs, result.decodingErrors);
    112  for (auto sct : decodedSCTs) {
    113    pkix::Result rv = VerifySingleSCT(std::move(sct), expectedEntry, origin,
    114                                      time, distrustAfterTime, result);
    115    if (rv != Success) {
    116      return rv;
    117    }
    118  }
    119  return Success;
    120 }
    121 
    122 pkix::Result MultiLogCTVerifier::VerifySingleSCT(
    123    SignedCertificateTimestamp&& sct, const LogEntry& expectedEntry,
    124    SCTOrigin origin, Time time, Maybe<Time> distrustAfterTime,
    125    CTVerifyResult& result) {
    126  switch (origin) {
    127    case SCTOrigin::Embedded:
    128      result.embeddedSCTs++;
    129      break;
    130    case SCTOrigin::TLSExtension:
    131      result.sctsFromTLSHandshake++;
    132      break;
    133    case SCTOrigin::OCSPResponse:
    134      result.sctsFromOCSP++;
    135      break;
    136  }
    137 
    138  CTLogVerifier* matchingLog = nullptr;
    139  for (auto& log : mLogs) {
    140    if (log.keyId() == sct.logId) {
    141      matchingLog = &log;
    142      break;
    143    }
    144  }
    145 
    146  if (!matchingLog) {
    147    // SCT does not match any known log.
    148    result.sctsFromUnknownLogs++;
    149    return Success;
    150  }
    151 
    152  if (!matchingLog->SignatureParametersMatch(sct.signature)) {
    153    // SCT signature parameters do not match the log's.
    154    result.sctsWithInvalidSignatures++;
    155    return Success;
    156  }
    157 
    158  pkix::Result rv =
    159      matchingLog->Verify(expectedEntry, sct, mSignatureCache.get());
    160  if (rv != Success) {
    161    if (rv == pkix::Result::ERROR_BAD_SIGNATURE) {
    162      result.sctsWithInvalidSignatures++;
    163      return Success;
    164    }
    165    return rv;
    166  }
    167 
    168  // Make sure the timestamp is legitimate (not in the future).
    169  // SCT's |timestamp| is measured in milliseconds since the epoch,
    170  // ignoring leap seconds. When converting it to a second-level precision
    171  // pkix::Time, we round down.
    172  Time sctTime = TimeFromEpochInSeconds(sct.timestamp / 1000u);
    173  if (sctTime > time) {
    174    result.sctsWithInvalidTimestamps++;
    175    return Success;
    176  }
    177 
    178  // If the root has a distrustAfter time, ensure that the SCT's timestamp is
    179  // not after that time.
    180  if (distrustAfterTime.isSome() && sctTime > distrustAfterTime.value()) {
    181    result.sctsWithDistrustedTimestamps++;
    182    return Success;
    183  }
    184 
    185  VerifiedSCT verifiedSct(std::move(sct), origin, matchingLog->operatorId(),
    186                          matchingLog->state(), matchingLog->format(),
    187                          matchingLog->timestamp());
    188  result.verifiedScts.push_back(std::move(verifiedSct));
    189  return Success;
    190 }
    191 
    192 }  // namespace ct
    193 }  // namespace mozilla