thunderbird/SOURCES/thunderbird-integrate-ml-dsa-signature-verification-for-pkix-certificate-chain-validation.patch
2025-11-20 13:50:25 +00:00

248 lines
12 KiB
Diff

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 <stdint.h>
+#include <optional>
#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<pkix::DigestAlgorithm> digestAlgorithm,
/*out*/ Maybe<nsTArray<uint8_t>>& 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<const uint8_t*>(&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<const uint8_t*>(&value),
+ sizeof(value)))) {
+ return;
+ }
}
nsTArray<uint8_t> 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> digestAlgorithm, Input signature,
Input subjectPublicKeyInfo, SignatureCache* signatureCache, void* pinArg) {
telemetryDenominator.Add(1);
Maybe<nsTArray<uint8_t>> 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<mozilla::pkix::DigestAlgorithm> 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<Span<const uint8_t>> 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,