tor-browser

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

WebTransportCertificateVerifier.cpp (10128B)


      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "WebTransportCertificateVerifier.h"
      7 #include "ScopedNSSTypes.h"
      8 #include "nss/mozpkix/pkixutil.h"
      9 #include "nss/mozpkix/pkixcheck.h"
     10 #include "hasht.h"
     11 
     12 namespace mozilla::psm {
     13 
     14 class ServerCertHashesTrustDomain : public mozilla::pkix::TrustDomain {
     15 public:
     16  ServerCertHashesTrustDomain() = default;
     17 
     18  virtual mozilla::pkix::Result FindIssuer(
     19      mozilla::pkix::Input encodedIssuerName, IssuerChecker& checker,
     20      mozilla::pkix::Time time) override;
     21 
     22  virtual mozilla::pkix::Result GetCertTrust(
     23      mozilla::pkix::EndEntityOrCA endEntityOrCA,
     24      const mozilla::pkix::CertPolicyId& policy,
     25      mozilla::pkix::Input candidateCertDER,
     26      /*out*/ mozilla::pkix::TrustLevel& trustLevel) override;
     27 
     28  virtual mozilla::pkix::Result CheckSignatureDigestAlgorithm(
     29      mozilla::pkix::DigestAlgorithm digestAlg,
     30      mozilla::pkix::EndEntityOrCA endEntityOrCA,
     31      mozilla::pkix::Time notBefore) override;
     32 
     33  virtual mozilla::pkix::Result CheckRSAPublicKeyModulusSizeInBits(
     34      mozilla::pkix::EndEntityOrCA endEntityOrCA,
     35      unsigned int modulusSizeInBits) override;
     36 
     37  virtual mozilla::pkix::Result VerifyRSAPKCS1SignedData(
     38      mozilla::pkix::Input data, mozilla::pkix::DigestAlgorithm digestAlgorithm,
     39      mozilla::pkix::Input signature,
     40      mozilla::pkix::Input subjectPublicKeyInfo) override;
     41 
     42  virtual mozilla::pkix::Result VerifyRSAPSSSignedData(
     43      mozilla::pkix::Input data, mozilla::pkix::DigestAlgorithm digestAlgorithm,
     44      mozilla::pkix::Input signature,
     45      mozilla::pkix::Input subjectPublicKeyInfo) override;
     46 
     47  virtual mozilla::pkix::Result CheckECDSACurveIsAcceptable(
     48      mozilla::pkix::EndEntityOrCA endEntityOrCA,
     49      mozilla::pkix::NamedCurve curve) override;
     50 
     51  virtual mozilla::pkix::Result VerifyECDSASignedData(
     52      mozilla::pkix::Input data, mozilla::pkix::DigestAlgorithm digestAlgorithm,
     53      mozilla::pkix::Input signature,
     54      mozilla::pkix::Input subjectPublicKeyInfo) override;
     55 
     56  virtual mozilla::pkix::Result DigestBuf(
     57      mozilla::pkix::Input item, mozilla::pkix::DigestAlgorithm digestAlg,
     58      /*out*/ uint8_t* digestBuf, size_t digestBufLen) override;
     59 
     60  virtual mozilla::pkix::Result CheckValidityIsAcceptable(
     61      mozilla::pkix::Time notBefore, mozilla::pkix::Time notAfter,
     62      mozilla::pkix::EndEntityOrCA endEntityOrCA,
     63      mozilla::pkix::KeyPurposeId keyPurpose) override;
     64 
     65  virtual mozilla::pkix::Result CheckRevocation(
     66      mozilla::pkix::EndEntityOrCA endEntityOrCA,
     67      const mozilla::pkix::CertID& certID, mozilla::pkix::Time time,
     68      mozilla::pkix::Duration validityDuration,
     69      /*optional*/ const mozilla::pkix::Input* stapledOCSPResponse,
     70      /*optional*/ const mozilla::pkix::Input* aiaExtension) override;
     71 
     72  virtual mozilla::pkix::Result IsChainValid(
     73      const mozilla::pkix::DERArray& certChain, mozilla::pkix::Time time,
     74      const mozilla::pkix::CertPolicyId& requiredPolicy) override;
     75 
     76  virtual void NoteAuxiliaryExtension(
     77      mozilla::pkix::AuxiliaryExtension extension,
     78      mozilla::pkix::Input extensionData) override;
     79 };
     80 
     81 mozilla::pkix::Result ServerCertHashesTrustDomain::FindIssuer(
     82    mozilla::pkix::Input encodedIssuerName, IssuerChecker& checker,
     83    mozilla::pkix::Time time) {
     84  MOZ_ASSERT_UNREACHABLE("not expecting this to be called");
     85 
     86  return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
     87 }
     88 
     89 mozilla::pkix::Result ServerCertHashesTrustDomain::GetCertTrust(
     90    mozilla::pkix::EndEntityOrCA endEntityOrCA,
     91    const mozilla::pkix::CertPolicyId& policy,
     92    mozilla::pkix::Input candidateCertDER,
     93    /*out*/ mozilla::pkix::TrustLevel& trustLevel) {
     94  MOZ_ASSERT_UNREACHABLE("not expecting this to be called");
     95 
     96  return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
     97 }
     98 
     99 mozilla::pkix::Result
    100 ServerCertHashesTrustDomain::CheckSignatureDigestAlgorithm(
    101    mozilla::pkix::DigestAlgorithm digestAlg,
    102    mozilla::pkix::EndEntityOrCA endEntityOrCA, mozilla::pkix::Time notBefore) {
    103  MOZ_ASSERT_UNREACHABLE("not expecting this to be called");
    104 
    105  return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
    106 }
    107 
    108 mozilla::pkix::Result
    109 ServerCertHashesTrustDomain::CheckRSAPublicKeyModulusSizeInBits(
    110    mozilla::pkix::EndEntityOrCA endEntityOrCA,
    111    unsigned int modulusSizeInBits) {
    112  return mozilla::pkix::Result::
    113      ERROR_UNSUPPORTED_KEYALG;  // RSA is not supported for
    114                                 // serverCertificateHashes,
    115  // Chromium does only support it for an intermediate period due to spec
    116  // change, we do not support it.
    117 }
    118 
    119 mozilla::pkix::Result ServerCertHashesTrustDomain::VerifyRSAPKCS1SignedData(
    120    mozilla::pkix::Input data, mozilla::pkix::DigestAlgorithm digestAlgorithm,
    121    mozilla::pkix::Input signature, mozilla::pkix::Input subjectPublicKeyInfo) {
    122  MOZ_ASSERT_UNREACHABLE("not expecting this to be called");
    123 
    124  return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
    125 }
    126 
    127 mozilla::pkix::Result ServerCertHashesTrustDomain::VerifyRSAPSSSignedData(
    128    mozilla::pkix::Input data, mozilla::pkix::DigestAlgorithm digestAlgorithm,
    129    mozilla::pkix::Input signature, mozilla::pkix::Input subjectPublicKeyInfo) {
    130  MOZ_ASSERT_UNREACHABLE("not expecting this to be called");
    131 
    132  return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
    133 }
    134 
    135 mozilla::pkix::Result ServerCertHashesTrustDomain::CheckECDSACurveIsAcceptable(
    136    mozilla::pkix::EndEntityOrCA endEntityOrCA,
    137    mozilla::pkix::NamedCurve curve) {
    138  return mozilla::pkix::Result::Success;
    139 }
    140 
    141 mozilla::pkix::Result ServerCertHashesTrustDomain::VerifyECDSASignedData(
    142    mozilla::pkix::Input data, mozilla::pkix::DigestAlgorithm digestAlgorithm,
    143    mozilla::pkix::Input signature, mozilla::pkix::Input subjectPublicKeyInfo) {
    144  MOZ_ASSERT_UNREACHABLE("not expecting this to be called");
    145 
    146  return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
    147 }
    148 
    149 mozilla::pkix::Result ServerCertHashesTrustDomain::DigestBuf(
    150    mozilla::pkix::Input item, mozilla::pkix::DigestAlgorithm digestAlg,
    151    /*out*/ uint8_t* digestBuf, size_t digestBufLen) {
    152  MOZ_ASSERT_UNREACHABLE("not expecting this to be called");
    153 
    154  return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
    155 }
    156 
    157 mozilla::pkix::Result ServerCertHashesTrustDomain::CheckValidityIsAcceptable(
    158    mozilla::pkix::Time notBefore, mozilla::pkix::Time notAfter,
    159    mozilla::pkix::EndEntityOrCA endEntityOrCA,
    160    mozilla::pkix::KeyPurposeId keyPurpose) {
    161  MOZ_ASSERT_UNREACHABLE("not expecting this to be called");
    162 
    163  return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
    164 }
    165 
    166 mozilla::pkix::Result ServerCertHashesTrustDomain::CheckRevocation(
    167    mozilla::pkix::EndEntityOrCA endEntityOrCA,
    168    const mozilla::pkix::CertID& certID, mozilla::pkix::Time time,
    169    mozilla::pkix::Duration validityDuration,
    170    /*optional*/ const mozilla::pkix::Input* stapledOCSPResponse,
    171    /*optional*/ const mozilla::pkix::Input* aiaExtension) {
    172  MOZ_ASSERT_UNREACHABLE("not expecting this to be called");
    173 
    174  return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
    175 }
    176 
    177 mozilla::pkix::Result ServerCertHashesTrustDomain::IsChainValid(
    178    const mozilla::pkix::DERArray& certChain, mozilla::pkix::Time time,
    179    const mozilla::pkix::CertPolicyId& requiredPolicy) {
    180  MOZ_ASSERT_UNREACHABLE("not expecting this to be called");
    181 
    182  return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
    183 }
    184 
    185 void ServerCertHashesTrustDomain::NoteAuxiliaryExtension(
    186    mozilla::pkix::AuxiliaryExtension extension,
    187    mozilla::pkix::Input extensionData) {
    188  MOZ_ASSERT_UNREACHABLE("not expecting this to be called");
    189 }
    190 
    191 }  // namespace mozilla::psm
    192 
    193 namespace mozilla::net {
    194 // Does certificate verificate as required for serverCertificateHashes
    195 // This function is currently only used for Quic, but may be used later also for
    196 // http/2
    197 mozilla::pkix::Result AuthCertificateWithServerCertificateHashes(
    198    nsTArray<uint8_t>& peerCert,
    199    const nsTArray<RefPtr<nsIWebTransportHash>>& aServerCertHashes) {
    200  using namespace mozilla::pkix;
    201  Input certDER;
    202  mozilla::pkix::Result rv =
    203      certDER.Init(peerCert.Elements(), peerCert.Length());
    204  if (rv != Success) {
    205    return rv;
    206  }
    207  BackCert cert(certDER, EndEntityOrCA::MustBeEndEntity, nullptr);
    208  rv = cert.Init();
    209  if (rv != Success) {
    210    return rv;
    211  }
    212 
    213  Time notBefore(Time::uninitialized);
    214  Time notAfter(Time::uninitialized);
    215  rv = ParseValidity(cert.GetValidity(), &notBefore, &notAfter);
    216  if (rv != Success) {
    217    return rv;
    218  }
    219  // now we check that validity is not greater than 14 days
    220  Duration certDuration(notBefore, notAfter);
    221  if (certDuration > Duration(60 * 60 * 24 * 14)) {
    222    return mozilla::pkix::Result::ERROR_VALIDITY_TOO_LONG;
    223  }
    224  Time now = Now();
    225  // and if the certificate is actually valid?
    226  rv = CheckValidity(now, notBefore, notAfter);
    227  if (rv != Success) {
    228    return rv;
    229  }
    230 
    231  mozilla::psm::ServerCertHashesTrustDomain trustDomain;
    232  rv = CheckSubjectPublicKeyInfo(
    233      cert.GetSubjectPublicKeyInfo(), trustDomain,
    234      EndEntityOrCA::MustBeEndEntity /* should be ignored*/);
    235  if (rv != Success) {
    236    return rv;
    237  }
    238 
    239  // ok now the final check, calculate the hash and compare it:
    240  // https://w3c.github.io/webtransport/#compute-a-certificate-hash
    241  nsTArray<uint8_t> certHash;
    242  if (NS_FAILED(Digest::DigestBuf(SEC_OID_SHA256, peerCert, certHash)) ||
    243      certHash.Length() != SHA256_LENGTH) {
    244    return mozilla::pkix::Result::ERROR_INVALID_ALGORITHM;
    245  }
    246 
    247  // https://w3c.github.io/webtransport/#verify-a-certificate-hash
    248  for (const auto& hash : aServerCertHashes) {
    249    nsCString algorithm;
    250    if (NS_FAILED(hash->GetAlgorithm(algorithm)) || algorithm != "sha-256") {
    251      continue;
    252    }
    253 
    254    nsTArray<uint8_t> value;
    255    if (NS_FAILED(hash->GetValue(value))) {
    256      continue;
    257    }
    258 
    259    if (certHash == value) {
    260      return Success;
    261    }
    262  }
    263  return mozilla::pkix::Result::ERROR_UNTRUSTED_CERT;
    264 }
    265 }  // namespace mozilla::net