tor-browser

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

CTLogVerifier.cpp (8618B)


      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 "CTLogVerifier.h"
      8 
      9 #include <stdint.h>
     10 
     11 #include "CTSerialization.h"
     12 #include "CertVerifier.h"
     13 #include "hasht.h"
     14 #include "mozpkix/Result.h"
     15 #include "mozpkix/pkixnss.h"
     16 #include "mozpkix/pkixutil.h"
     17 #include "mozilla/glean/SecurityCtMetrics.h"
     18 
     19 using namespace mozilla::pkix;
     20 
     21 namespace mozilla {
     22 namespace ct {
     23 
     24 // A TrustDomain used to extract the SCT log signature parameters
     25 // given its subjectPublicKeyInfo.
     26 // Only RSASSA-PKCS1v15 with SHA-256 and ECDSA (using the NIST P-256 curve)
     27 // with SHA-256 are allowed.
     28 // RSA keys must be at least 2048 bits.
     29 // See See RFC 6962, Section 2.1.4.
     30 class SignatureParamsTrustDomain final : public TrustDomain {
     31 public:
     32  SignatureParamsTrustDomain()
     33      : mSignatureAlgorithm(DigitallySigned::SignatureAlgorithm::Anonymous) {}
     34 
     35  pkix::Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input,
     36                            TrustLevel&) override {
     37    return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
     38  }
     39 
     40  pkix::Result FindIssuer(Input, IssuerChecker&, Time) override {
     41    return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
     42  }
     43 
     44  pkix::Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
     45                               const Input*, const Input*) override {
     46    return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
     47  }
     48 
     49  pkix::Result IsChainValid(const DERArray&, Time,
     50                            const CertPolicyId&) override {
     51    return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
     52  }
     53 
     54  pkix::Result DigestBuf(Input, DigestAlgorithm, uint8_t*, size_t) override {
     55    return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
     56  }
     57 
     58  pkix::Result CheckSignatureDigestAlgorithm(DigestAlgorithm, EndEntityOrCA,
     59                                             Time) override {
     60    return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
     61  }
     62 
     63  pkix::Result CheckECDSACurveIsAcceptable(EndEntityOrCA,
     64                                           NamedCurve curve) override {
     65    assert(mSignatureAlgorithm ==
     66           DigitallySigned::SignatureAlgorithm::Anonymous);
     67    if (curve != NamedCurve::secp256r1) {
     68      return pkix::Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE;
     69    }
     70    mSignatureAlgorithm = DigitallySigned::SignatureAlgorithm::ECDSA;
     71    return Success;
     72  }
     73 
     74  pkix::Result VerifyECDSASignedData(Input, DigestAlgorithm, Input,
     75                                     Input) override {
     76    return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
     77  }
     78 
     79  pkix::Result CheckRSAPublicKeyModulusSizeInBits(
     80      EndEntityOrCA, unsigned int modulusSizeInBits) override {
     81    assert(mSignatureAlgorithm ==
     82           DigitallySigned::SignatureAlgorithm::Anonymous);
     83    // Require RSA keys of at least 2048 bits. See RFC 6962, Section 2.1.4.
     84    if (modulusSizeInBits < 2048) {
     85      return pkix::Result::ERROR_INADEQUATE_KEY_SIZE;
     86    }
     87    mSignatureAlgorithm = DigitallySigned::SignatureAlgorithm::RSA;
     88    return Success;
     89  }
     90 
     91  pkix::Result VerifyRSAPKCS1SignedData(Input, DigestAlgorithm, Input,
     92                                        Input) override {
     93    return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
     94  }
     95 
     96  pkix::Result VerifyRSAPSSSignedData(Input, DigestAlgorithm, Input,
     97                                      Input) override {
     98    return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
     99  }
    100 
    101  pkix::Result CheckValidityIsAcceptable(Time, Time, EndEntityOrCA,
    102                                         KeyPurposeId) override {
    103    return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
    104  }
    105 
    106  void NoteAuxiliaryExtension(AuxiliaryExtension, Input) override {}
    107 
    108  DigitallySigned::SignatureAlgorithm mSignatureAlgorithm;
    109 };
    110 
    111 CTLogVerifier::CTLogVerifier(CTLogOperatorId operatorId, CTLogState state,
    112                             CTLogFormat format, uint64_t timestamp)
    113    : mSignatureAlgorithm(DigitallySigned::SignatureAlgorithm::Anonymous),
    114      mOperatorId(operatorId),
    115      mState(state),
    116      mFormat(format),
    117      mTimestamp(timestamp) {}
    118 
    119 pkix::Result CTLogVerifier::Init(Input subjectPublicKeyInfo) {
    120  SignatureParamsTrustDomain trustDomain;
    121  pkix::Result rv = CheckSubjectPublicKeyInfo(subjectPublicKeyInfo, trustDomain,
    122                                              EndEntityOrCA::MustBeEndEntity);
    123  if (rv != Success) {
    124    return rv;
    125  }
    126  mSignatureAlgorithm = trustDomain.mSignatureAlgorithm;
    127 
    128  InputToBuffer(subjectPublicKeyInfo, mSubjectPublicKeyInfo);
    129 
    130  mKeyId.resize(SHA256_LENGTH);
    131  rv = DigestBufNSS(subjectPublicKeyInfo, DigestAlgorithm::sha256,
    132                    mKeyId.data(), mKeyId.size());
    133  if (rv != Success) {
    134    return rv;
    135  }
    136 
    137  return Success;
    138 }
    139 
    140 pkix::Result CTLogVerifier::Verify(const LogEntry& entry,
    141                                   const SignedCertificateTimestamp& sct,
    142                                   SignatureCache* signatureCache) {
    143  if (mKeyId.empty() || sct.logId != mKeyId || !signatureCache) {
    144    return pkix::Result::FATAL_ERROR_INVALID_ARGS;
    145  }
    146  if (!SignatureParametersMatch(sct.signature)) {
    147    return pkix::Result::FATAL_ERROR_INVALID_ARGS;
    148  }
    149 
    150  Buffer serializedLogEntry;
    151  pkix::Result rv = EncodeLogEntry(entry, serializedLogEntry);
    152  if (rv != Success) {
    153    return rv;
    154  }
    155 
    156  Input logEntryInput;
    157  rv = BufferToInput(serializedLogEntry, logEntryInput);
    158  if (rv != Success) {
    159    return rv;
    160  }
    161 
    162  // sct.extensions may be empty.  If it is, sctExtensionsInput will remain in
    163  // its default state, which is valid but of length 0.
    164  Input sctExtensionsInput;
    165  if (!sct.extensions.empty()) {
    166    rv = sctExtensionsInput.Init(sct.extensions.data(), sct.extensions.size());
    167    if (rv != Success) {
    168      return rv;
    169    }
    170  }
    171 
    172  Buffer serializedData;
    173  rv = EncodeV1SCTSignedData(sct.timestamp, logEntryInput, sctExtensionsInput,
    174                             serializedData);
    175  if (rv != Success) {
    176    return rv;
    177  }
    178  return VerifySignature(serializedData, sct.signature.signatureData,
    179                         signatureCache);
    180 }
    181 
    182 bool CTLogVerifier::SignatureParametersMatch(const DigitallySigned& signature) {
    183  return signature.SignatureParametersMatch(
    184      DigitallySigned::HashAlgorithm::SHA256, mSignatureAlgorithm);
    185 }
    186 
    187 pkix::Result CTLogVerifier::VerifySignature(Input data, Input signature,
    188                                            SignatureCache* signatureCache) {
    189  Input spki;
    190  pkix::Result rv = BufferToInput(mSubjectPublicKeyInfo, spki);
    191  if (rv != Success) {
    192    return rv;
    193  }
    194 
    195  switch (mSignatureAlgorithm) {
    196    case DigitallySigned::SignatureAlgorithm::RSA:
    197      rv = psm::VerifySignedDataWithCache(
    198          der::PublicKeyAlgorithm::RSA_PKCS1,
    199          mozilla::glean::sct_signature_cache::total,
    200          mozilla::glean::sct_signature_cache::hits, data,
    201          DigestAlgorithm::sha256, signature, spki, signatureCache, nullptr);
    202      break;
    203    case DigitallySigned::SignatureAlgorithm::ECDSA:
    204      rv = psm::VerifySignedDataWithCache(
    205          der::PublicKeyAlgorithm::ECDSA,
    206          mozilla::glean::sct_signature_cache::total,
    207          mozilla::glean::sct_signature_cache::hits, data,
    208          DigestAlgorithm::sha256, signature, spki, signatureCache, nullptr);
    209      break;
    210    // We do not expect new values added to this enum any time soon,
    211    // so just listing all the available ones seems to be the easiest way
    212    // to suppress warning C4061 on MSVC (which expects all values of the
    213    // enum to be explicitly handled).
    214    case DigitallySigned::SignatureAlgorithm::Anonymous:
    215    case DigitallySigned::SignatureAlgorithm::DSA:
    216    default:
    217      assert(false);
    218      return pkix::Result::FATAL_ERROR_INVALID_ARGS;
    219  }
    220  if (rv != Success) {
    221    if (IsFatalError(rv)) {
    222      return rv;
    223    }
    224    // If the error is non-fatal, we assume the signature was invalid.
    225    return pkix::Result::ERROR_BAD_SIGNATURE;
    226  }
    227  return Success;
    228 }
    229 
    230 pkix::Result CTLogVerifier::VerifySignature(const Buffer& data,
    231                                            const Buffer& signature,
    232                                            SignatureCache* signatureCache) {
    233  Input dataInput;
    234  pkix::Result rv = BufferToInput(data, dataInput);
    235  if (rv != Success) {
    236    return rv;
    237  }
    238  Input signatureInput;
    239  rv = BufferToInput(signature, signatureInput);
    240  if (rv != Success) {
    241    return rv;
    242  }
    243  return VerifySignature(dataInput, signatureInput, signatureCache);
    244 }
    245 
    246 }  // namespace ct
    247 }  // namespace mozilla