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(), ¬Before, ¬After); 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