summaryrefslogtreecommitdiffstats
path: root/netwerk/protocol/http/WebTransportCertificateVerifier.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'netwerk/protocol/http/WebTransportCertificateVerifier.cpp')
-rw-r--r--netwerk/protocol/http/WebTransportCertificateVerifier.cpp280
1 files changed, 280 insertions, 0 deletions
diff --git a/netwerk/protocol/http/WebTransportCertificateVerifier.cpp b/netwerk/protocol/http/WebTransportCertificateVerifier.cpp
new file mode 100644
index 0000000000..cc778640a1
--- /dev/null
+++ b/netwerk/protocol/http/WebTransportCertificateVerifier.cpp
@@ -0,0 +1,280 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WebTransportCertificateVerifier.h"
+#include "ScopedNSSTypes.h"
+#include "nss/mozpkix/pkixutil.h"
+#include "nss/mozpkix/pkixcheck.h"
+#include "hasht.h"
+
+namespace mozilla::psm {
+
+class ServerCertHashesTrustDomain : public mozilla::pkix::TrustDomain {
+ public:
+ ServerCertHashesTrustDomain() = default;
+
+ virtual mozilla::pkix::Result FindIssuer(
+ mozilla::pkix::Input encodedIssuerName, IssuerChecker& checker,
+ mozilla::pkix::Time time) override;
+
+ virtual mozilla::pkix::Result GetCertTrust(
+ mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ const mozilla::pkix::CertPolicyId& policy,
+ mozilla::pkix::Input candidateCertDER,
+ /*out*/ mozilla::pkix::TrustLevel& trustLevel) override;
+
+ virtual mozilla::pkix::Result CheckSignatureDigestAlgorithm(
+ mozilla::pkix::DigestAlgorithm digestAlg,
+ mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ mozilla::pkix::Time notBefore) override;
+
+ virtual mozilla::pkix::Result CheckRSAPublicKeyModulusSizeInBits(
+ mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ unsigned int modulusSizeInBits) override;
+
+ virtual mozilla::pkix::Result VerifyRSAPKCS1SignedData(
+ mozilla::pkix::Input data, mozilla::pkix::DigestAlgorithm digestAlgorithm,
+ mozilla::pkix::Input signature,
+ mozilla::pkix::Input subjectPublicKeyInfo) override;
+
+ virtual mozilla::pkix::Result VerifyRSAPSSSignedData(
+ mozilla::pkix::Input data, mozilla::pkix::DigestAlgorithm digestAlgorithm,
+ mozilla::pkix::Input signature,
+ mozilla::pkix::Input subjectPublicKeyInfo) override;
+
+ virtual mozilla::pkix::Result CheckECDSACurveIsAcceptable(
+ mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ mozilla::pkix::NamedCurve curve) override;
+
+ virtual mozilla::pkix::Result VerifyECDSASignedData(
+ mozilla::pkix::Input data, mozilla::pkix::DigestAlgorithm digestAlgorithm,
+ mozilla::pkix::Input signature,
+ mozilla::pkix::Input subjectPublicKeyInfo) override;
+
+ virtual mozilla::pkix::Result DigestBuf(
+ mozilla::pkix::Input item, mozilla::pkix::DigestAlgorithm digestAlg,
+ /*out*/ uint8_t* digestBuf, size_t digestBufLen) override;
+
+ virtual mozilla::pkix::Result CheckValidityIsAcceptable(
+ mozilla::pkix::Time notBefore, mozilla::pkix::Time notAfter,
+ mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ mozilla::pkix::KeyPurposeId keyPurpose) override;
+
+ virtual mozilla::pkix::Result NetscapeStepUpMatchesServerAuth(
+ mozilla::pkix::Time notBefore,
+ /*out*/ bool& matches) override;
+
+ virtual mozilla::pkix::Result CheckRevocation(
+ mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ const mozilla::pkix::CertID& certID, mozilla::pkix::Time time,
+ mozilla::pkix::Duration validityDuration,
+ /*optional*/ const mozilla::pkix::Input* stapledOCSPResponse,
+ /*optional*/ const mozilla::pkix::Input* aiaExtension,
+ /*optional*/ const mozilla::pkix::Input* sctExtension) override;
+
+ virtual mozilla::pkix::Result IsChainValid(
+ const mozilla::pkix::DERArray& certChain, mozilla::pkix::Time time,
+ const mozilla::pkix::CertPolicyId& requiredPolicy) override;
+
+ virtual void NoteAuxiliaryExtension(
+ mozilla::pkix::AuxiliaryExtension extension,
+ mozilla::pkix::Input extensionData) override;
+};
+
+mozilla::pkix::Result ServerCertHashesTrustDomain::FindIssuer(
+ mozilla::pkix::Input encodedIssuerName, IssuerChecker& checker,
+ mozilla::pkix::Time time) {
+ MOZ_ASSERT_UNREACHABLE("not expecting this to be called");
+
+ return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
+}
+
+mozilla::pkix::Result ServerCertHashesTrustDomain::GetCertTrust(
+ mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ const mozilla::pkix::CertPolicyId& policy,
+ mozilla::pkix::Input candidateCertDER,
+ /*out*/ mozilla::pkix::TrustLevel& trustLevel) {
+ MOZ_ASSERT_UNREACHABLE("not expecting this to be called");
+
+ return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
+}
+
+mozilla::pkix::Result
+ServerCertHashesTrustDomain::CheckSignatureDigestAlgorithm(
+ mozilla::pkix::DigestAlgorithm digestAlg,
+ mozilla::pkix::EndEntityOrCA endEntityOrCA, mozilla::pkix::Time notBefore) {
+ MOZ_ASSERT_UNREACHABLE("not expecting this to be called");
+
+ return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
+}
+
+mozilla::pkix::Result
+ServerCertHashesTrustDomain::CheckRSAPublicKeyModulusSizeInBits(
+ mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ unsigned int modulusSizeInBits) {
+ return mozilla::pkix::Result::
+ ERROR_UNSUPPORTED_KEYALG; // RSA is not supported for
+ // serverCertificateHashes,
+ // Chromium does only support it for an intermediate period due to spec
+ // change, we do not support it.
+}
+
+mozilla::pkix::Result ServerCertHashesTrustDomain::VerifyRSAPKCS1SignedData(
+ mozilla::pkix::Input data, mozilla::pkix::DigestAlgorithm digestAlgorithm,
+ mozilla::pkix::Input signature, mozilla::pkix::Input subjectPublicKeyInfo) {
+ MOZ_ASSERT_UNREACHABLE("not expecting this to be called");
+
+ return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
+}
+
+mozilla::pkix::Result ServerCertHashesTrustDomain::VerifyRSAPSSSignedData(
+ mozilla::pkix::Input data, mozilla::pkix::DigestAlgorithm digestAlgorithm,
+ mozilla::pkix::Input signature, mozilla::pkix::Input subjectPublicKeyInfo) {
+ MOZ_ASSERT_UNREACHABLE("not expecting this to be called");
+
+ return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
+}
+
+mozilla::pkix::Result ServerCertHashesTrustDomain::CheckECDSACurveIsAcceptable(
+ mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ mozilla::pkix::NamedCurve curve) {
+ return mozilla::pkix::Result::Success;
+}
+
+mozilla::pkix::Result ServerCertHashesTrustDomain::VerifyECDSASignedData(
+ mozilla::pkix::Input data, mozilla::pkix::DigestAlgorithm digestAlgorithm,
+ mozilla::pkix::Input signature, mozilla::pkix::Input subjectPublicKeyInfo) {
+ MOZ_ASSERT_UNREACHABLE("not expecting this to be called");
+
+ return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
+}
+
+mozilla::pkix::Result ServerCertHashesTrustDomain::DigestBuf(
+ mozilla::pkix::Input item, mozilla::pkix::DigestAlgorithm digestAlg,
+ /*out*/ uint8_t* digestBuf, size_t digestBufLen) {
+ MOZ_ASSERT_UNREACHABLE("not expecting this to be called");
+
+ return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
+}
+
+mozilla::pkix::Result ServerCertHashesTrustDomain::CheckValidityIsAcceptable(
+ mozilla::pkix::Time notBefore, mozilla::pkix::Time notAfter,
+ mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ mozilla::pkix::KeyPurposeId keyPurpose) {
+ MOZ_ASSERT_UNREACHABLE("not expecting this to be called");
+
+ return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
+}
+
+mozilla::pkix::Result
+ServerCertHashesTrustDomain::NetscapeStepUpMatchesServerAuth(
+ mozilla::pkix::Time notBefore,
+ /*out*/ bool& matches) {
+ MOZ_ASSERT_UNREACHABLE("not expecting this to be called");
+
+ return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
+}
+
+mozilla::pkix::Result ServerCertHashesTrustDomain::CheckRevocation(
+ mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ const mozilla::pkix::CertID& certID, mozilla::pkix::Time time,
+ mozilla::pkix::Duration validityDuration,
+ /*optional*/ const mozilla::pkix::Input* stapledOCSPResponse,
+ /*optional*/ const mozilla::pkix::Input* aiaExtension,
+ /*optional*/ const mozilla::pkix::Input* sctExtension) {
+ MOZ_ASSERT_UNREACHABLE("not expecting this to be called");
+
+ return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
+}
+
+mozilla::pkix::Result ServerCertHashesTrustDomain::IsChainValid(
+ const mozilla::pkix::DERArray& certChain, mozilla::pkix::Time time,
+ const mozilla::pkix::CertPolicyId& requiredPolicy) {
+ MOZ_ASSERT_UNREACHABLE("not expecting this to be called");
+
+ return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
+}
+
+void ServerCertHashesTrustDomain::NoteAuxiliaryExtension(
+ mozilla::pkix::AuxiliaryExtension extension,
+ mozilla::pkix::Input extensionData) {
+ MOZ_ASSERT_UNREACHABLE("not expecting this to be called");
+}
+
+} // namespace mozilla::psm
+
+namespace mozilla::net {
+// Does certificate verificate as required for serverCertificateHashes
+// This function is currently only used for Quic, but may be used later also for
+// http/2
+mozilla::pkix::Result AuthCertificateWithServerCertificateHashes(
+ nsTArray<uint8_t>& peerCert,
+ const nsTArray<RefPtr<nsIWebTransportHash>>& aServerCertHashes) {
+ using namespace mozilla::pkix;
+ Input certDER;
+ mozilla::pkix::Result rv =
+ certDER.Init(peerCert.Elements(), peerCert.Length());
+ if (rv != Success) {
+ return rv;
+ }
+ BackCert cert(certDER, EndEntityOrCA::MustBeEndEntity, nullptr);
+ rv = cert.Init();
+ if (rv != Success) {
+ return rv;
+ }
+
+ Time notBefore(Time::uninitialized);
+ Time notAfter(Time::uninitialized);
+ rv = ParseValidity(cert.GetValidity(), &notBefore, &notAfter);
+ if (rv != Success) {
+ return rv;
+ }
+ // now we check that validity is not greater than 14 days
+ Duration certDuration(notBefore, notAfter);
+ if (certDuration > Duration(60 * 60 * 24 * 14)) {
+ return mozilla::pkix::Result::ERROR_VALIDITY_TOO_LONG;
+ }
+ Time now = Now();
+ // and if the certificate is actually valid?
+ rv = CheckValidity(now, notBefore, notAfter);
+ if (rv != Success) {
+ return rv;
+ }
+
+ mozilla::psm::ServerCertHashesTrustDomain trustDomain;
+ rv = CheckSubjectPublicKeyInfo(
+ cert.GetSubjectPublicKeyInfo(), trustDomain,
+ EndEntityOrCA::MustBeEndEntity /* should be ignored*/);
+ if (rv != Success) {
+ return rv;
+ }
+
+ // ok now the final check, calculate the hash and compare it:
+ // https://w3c.github.io/webtransport/#compute-a-certificate-hash
+ nsTArray<uint8_t> certHash;
+ if (NS_FAILED(Digest::DigestBuf(SEC_OID_SHA256, peerCert, certHash)) ||
+ certHash.Length() != SHA256_LENGTH) {
+ return mozilla::pkix::Result::ERROR_INVALID_ALGORITHM;
+ }
+
+ // https://w3c.github.io/webtransport/#verify-a-certificate-hash
+ for (const auto& hash : aServerCertHashes) {
+ nsCString algorithm;
+ if (NS_FAILED(hash->GetAlgorithm(algorithm)) || algorithm != "sha-256") {
+ continue;
+ }
+
+ nsTArray<uint8_t> value;
+ if (NS_FAILED(hash->GetValue(value))) {
+ continue;
+ }
+
+ if (certHash == value) {
+ return Success;
+ }
+ }
+ return mozilla::pkix::Result::ERROR_UNTRUSTED_CERT;
+}
+} // namespace mozilla::net