diff --git a/netwerk/protocol/http/WebTransportCertificateVerifier.cpp b/netwerk/protocol/http/WebTransportCertificateVerifier.cpp index cc778640a1..298d6a61e8 100644 --- a/netwerk/protocol/http/WebTransportCertificateVerifier.cpp +++ b/netwerk/protocol/http/WebTransportCertificateVerifier.cpp @@ -53,6 +53,10 @@ class ServerCertHashesTrustDomain : public mozilla::pkix::TrustDomain { mozilla::pkix::Input signature, mozilla::pkix::Input subjectPublicKeyInfo) override; + virtual mozilla::pkix::Result VerifyMLDSASignedData( + mozilla::pkix::Input data, 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; @@ -151,6 +155,14 @@ mozilla::pkix::Result ServerCertHashesTrustDomain::VerifyECDSASignedData( return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; } +mozilla::pkix::Result ServerCertHashesTrustDomain::VerifyMLDSASignedData( + mozilla::pkix::Input data, 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) { diff --git a/security/certverifier/CertVerifier.cpp b/security/certverifier/CertVerifier.cpp index ca330770fb..1e8f1d4996 100644 --- a/security/certverifier/CertVerifier.cpp +++ b/security/certverifier/CertVerifier.cpp @@ -7,6 +7,7 @@ #include "CertVerifier.h" #include +#include #include "AppTrustDomain.h" #include "CTKnownLogs.h" @@ -1010,7 +1011,7 @@ Result CertVerifier::VerifySSLServerCert( void HashSignatureParams(pkix::Input data, pkix::Input signature, pkix::Input subjectPublicKeyInfo, pkix::der::PublicKeyAlgorithm publicKeyAlgorithm, - pkix::DigestAlgorithm digestAlgorithm, + std::optional digestAlgorithm, /*out*/ Maybe>& sha512Hash) { sha512Hash.reset(); Digest digest; @@ -1048,10 +1049,14 @@ void HashSignatureParams(pkix::Input data, pkix::Input signature, sizeof(publicKeyAlgorithm)))) { return; } - if (NS_FAILED( - digest.Update(reinterpret_cast(&digestAlgorithm), - sizeof(digestAlgorithm)))) { - return; + // There is no fallback digest algorithm when it's empty. + // Check that digestAlgorithm actually contains a value. + if (digestAlgorithm) { + pkix::DigestAlgorithm value = digestAlgorithm.value(); + if (NS_FAILED(digest.Update(reinterpret_cast(&value), + sizeof(value)))) { + return; + } } nsTArray result; if (NS_FAILED(digest.End(result))) { @@ -1064,10 +1069,17 @@ Result VerifySignedDataWithCache( der::PublicKeyAlgorithm publicKeyAlg, mozilla::glean::impl::DenominatorMetric telemetryDenominator, mozilla::glean::impl::NumeratorMetric telemetryNumerator, Input data, - DigestAlgorithm digestAlgorithm, Input signature, + std::optional digestAlgorithm, Input signature, Input subjectPublicKeyInfo, SignatureCache* signatureCache, void* pinArg) { telemetryDenominator.Add(1); Maybe> sha512Hash; + + // Currently, it is only acceptable for `digestAlgorithm` to be null when the + // public key algorithm is pure ML-DSA. Fail immediately otherwise. + if ((publicKeyAlg != der::PublicKeyAlgorithm::MLDSA) && !digestAlgorithm) { + return Result::ERROR_INVALID_ALGORITHM; + } + HashSignatureParams(data, signature, subjectPublicKeyInfo, publicKeyAlg, digestAlgorithm, sha512Hash); // If hashing the signature parameters succeeded, see if this signature is in @@ -1080,16 +1092,23 @@ Result VerifySignedDataWithCache( Result result; switch (publicKeyAlg) { case der::PublicKeyAlgorithm::ECDSA: - result = VerifyECDSASignedDataNSS(data, digestAlgorithm, signature, - subjectPublicKeyInfo, pinArg); + result = + VerifyECDSASignedDataNSS(data, digestAlgorithm.value(), signature, + subjectPublicKeyInfo, pinArg); break; case der::PublicKeyAlgorithm::RSA_PKCS1: - result = VerifyRSAPKCS1SignedDataNSS(data, digestAlgorithm, signature, - subjectPublicKeyInfo, pinArg); + result = + VerifyRSAPKCS1SignedDataNSS(data, digestAlgorithm.value(), signature, + subjectPublicKeyInfo, pinArg); break; case der::PublicKeyAlgorithm::RSA_PSS: - result = VerifyRSAPSSSignedDataNSS(data, digestAlgorithm, signature, - subjectPublicKeyInfo, pinArg); + result = + VerifyRSAPSSSignedDataNSS(data, digestAlgorithm.value(), signature, + subjectPublicKeyInfo, pinArg); + break; + case der::PublicKeyAlgorithm::MLDSA: + result = VerifyMLDSASignedDataNSS(data, signature, subjectPublicKeyInfo, + pinArg); break; default: MOZ_ASSERT_UNREACHABLE("unhandled public key algorithm"); diff --git a/security/certverifier/CertVerifier.h b/security/certverifier/CertVerifier.h index 6432547c8a..6e09e6fcdd 100644 --- a/security/certverifier/CertVerifier.h +++ b/security/certverifier/CertVerifier.h @@ -331,7 +331,8 @@ mozilla::pkix::Result VerifySignedDataWithCache( mozilla::pkix::der::PublicKeyAlgorithm publicKeyAlg, mozilla::glean::impl::DenominatorMetric telemetryDenominator, mozilla::glean::impl::NumeratorMetric telemetryNumerator, - mozilla::pkix::Input data, mozilla::pkix::DigestAlgorithm digestAlgorithm, + mozilla::pkix::Input data, + std::optional digestAlgorithm, mozilla::pkix::Input signature, mozilla::pkix::Input subjectPublicKeyInfo, SignatureCache* signatureCache, void* pinArg); diff --git a/security/certverifier/NSSCertDBTrustDomain.cpp b/security/certverifier/NSSCertDBTrustDomain.cpp index 70ba17d70f..a3ace3cee7 100644 --- a/security/certverifier/NSSCertDBTrustDomain.cpp +++ b/security/certverifier/NSSCertDBTrustDomain.cpp @@ -1541,6 +1541,15 @@ Result NSSCertDBTrustDomain::VerifyECDSASignedData( signature, subjectPublicKeyInfo, mSignatureCache, mPinArg); } +Result NSSCertDBTrustDomain::VerifyMLDSASignedData(Input data, Input signature, + Input subjectPublicKeyInfo) { + return VerifySignedDataWithCache( + der::PublicKeyAlgorithm::MLDSA, + mozilla::glean::cert_signature_cache::total, + mozilla::glean::cert_signature_cache::hits, data, std::nullopt, signature, + subjectPublicKeyInfo, mSignatureCache, mPinArg); +} + Result NSSCertDBTrustDomain::CheckValidityIsAcceptable( Time notBefore, Time notAfter, EndEntityOrCA endEntityOrCA, KeyPurposeId keyPurpose) { diff --git a/security/certverifier/NSSCertDBTrustDomain.h b/security/certverifier/NSSCertDBTrustDomain.h index fc210f3254..6178201758 100644 --- a/security/certverifier/NSSCertDBTrustDomain.h +++ b/security/certverifier/NSSCertDBTrustDomain.h @@ -197,6 +197,10 @@ class NSSCertDBTrustDomain : public mozilla::pkix::TrustDomain { mozilla::pkix::Input signature, mozilla::pkix::Input subjectPublicKeyInfo) override; + virtual Result VerifyMLDSASignedData( + mozilla::pkix::Input data, mozilla::pkix::Input signature, + mozilla::pkix::Input subjectPublicKeyInfo) override; + virtual Result DigestBuf(mozilla::pkix::Input item, mozilla::pkix::DigestAlgorithm digestAlg, /*out*/ uint8_t* digestBuf, diff --git a/security/ct/CTLogVerifier.cpp b/security/ct/CTLogVerifier.cpp index d5e665aaca..471213745d 100644 --- a/security/ct/CTLogVerifier.cpp +++ b/security/ct/CTLogVerifier.cpp @@ -99,6 +99,10 @@ class SignatureParamsTrustDomain final : public TrustDomain { return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; } + pkix::Result VerifyMLDSASignedData(Input, Input, Input) override { + return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; + } + pkix::Result CheckValidityIsAcceptable(Time, Time, EndEntityOrCA, KeyPurposeId) override { return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; diff --git a/security/ct/tests/gtest/CTTestUtils.cpp b/security/ct/tests/gtest/CTTestUtils.cpp index 6a25307ec3..dbec7adc91 100644 --- a/security/ct/tests/gtest/CTTestUtils.cpp +++ b/security/ct/tests/gtest/CTTestUtils.cpp @@ -807,6 +807,12 @@ class OCSPExtensionTrustDomain : public TrustDomain { subjectPublicKeyInfo, nullptr); } + pkix::Result VerifyMLDSASignedData(Input data, Input signature, + Input subjectPublicKeyInfo) override { + return VerifyMLDSASignedDataNSS(data, signature, subjectPublicKeyInfo, + nullptr); + } + pkix::Result CheckValidityIsAcceptable(Time, Time, EndEntityOrCA, KeyPurposeId) override { ADD_FAILURE(); diff --git a/security/manager/ssl/AppTrustDomain.cpp b/security/manager/ssl/AppTrustDomain.cpp index ab49d7eb1f..3963f90eb1 100644 --- a/security/manager/ssl/AppTrustDomain.cpp +++ b/security/manager/ssl/AppTrustDomain.cpp @@ -322,6 +322,12 @@ pkix::Result AppTrustDomain::VerifyECDSASignedData( subjectPublicKeyInfo, nullptr); } +pkix::Result AppTrustDomain::VerifyMLDSASignedData(Input data, Input signature, + Input subjectPublicKeyInfo) { + return VerifyMLDSASignedDataNSS(data, signature, subjectPublicKeyInfo, + nullptr); +} + pkix::Result AppTrustDomain::CheckValidityIsAcceptable( Time /*notBefore*/, Time /*notAfter*/, EndEntityOrCA /*endEntityOrCA*/, KeyPurposeId /*keyPurpose*/) { diff --git a/security/manager/ssl/AppTrustDomain.h b/security/manager/ssl/AppTrustDomain.h index 4b0212ede0..85fdff5f13 100644 --- a/security/manager/ssl/AppTrustDomain.h +++ b/security/manager/ssl/AppTrustDomain.h @@ -80,6 +80,9 @@ class AppTrustDomain final : public mozilla::pkix::TrustDomain { mozilla::pkix::DigestAlgorithm digestAlg, /*out*/ uint8_t* digestBuf, size_t digestBufLen) override; + virtual Result VerifyMLDSASignedData( + mozilla::pkix::Input data, mozilla::pkix::Input signature, + mozilla::pkix::Input subjectPublicKeyInfo) override; private: nsTArray> mTrustedRoots; diff --git a/security/manager/ssl/TLSClientAuthCertSelection.cpp b/security/manager/ssl/TLSClientAuthCertSelection.cpp index 3a84b15ee6..a3dc5a1af1 100644 --- a/security/manager/ssl/TLSClientAuthCertSelection.cpp +++ b/security/manager/ssl/TLSClientAuthCertSelection.cpp @@ -217,6 +217,11 @@ class ClientAuthCertNonverifyingTrustDomain final : public TrustDomain { pkix::Input subjectPublicKeyInfo) override { return pkix::Success; } + virtual mozilla::pkix::Result VerifyMLDSASignedData( + pkix::Input data, pkix::Input signature, + pkix::Input subjectPublicKeyInfo) override { + return pkix::Success; + } virtual mozilla::pkix::Result CheckValidityIsAcceptable( pkix::Time notBefore, pkix::Time notAfter, pkix::EndEntityOrCA endEntityOrCA,