Replace NSS with Pale Moon's

This commit is contained in:
wuggy 2026-06-29 21:29:25 +01:00
commit 8c2e376f94
2870 changed files with 1762232 additions and 1374220 deletions

View file

@ -17,6 +17,7 @@
'include/pkix/Time.h',
'include/pkix/Result.h',
'include/pkix/pkix.h',
'include/pkix/pkixc.h',
'include/pkix/pkixnss.h',
'include/pkix/pkixtypes.h',
'include/pkix/pkixutil.h',
@ -44,4 +45,4 @@
'variables': {
'module': 'nss'
}
}
}

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This code is made available to you under your choice of the following sets
* of licensing terms:
*/
@ -41,7 +42,7 @@ TestKeyPair* CreateTestKeyPair(const TestPublicKeyAlgorithm publicKeyAlg,
const ScopedSECKEYPublicKey& publicKey,
const ScopedSECKEYPrivateKey& privateKey);
}
}
} // namespace mozilla::pkix::test
} // namespace pkix
} // namespace mozilla
#endif // mozilla_pkix_test_pkixtestnss_h

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This code is made available to you under your choice of the following sets
* of licensing terms:
*/
@ -302,10 +303,11 @@ TestKeyPair* GenerateDSSKeyPair();
inline void DeleteTestKeyPair(TestKeyPair* keyPair) { delete keyPair; }
typedef std::unique_ptr<TestKeyPair> ScopedTestKeyPair;
Result TestVerifyECDSASignedDigest(const SignedDigest& signedDigest,
Input subjectPublicKeyInfo);
Result TestVerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest,
Input subjectPublicKeyInfo);
Result TestVerifyECDSASignedData(Input data, DigestAlgorithm digestAlgorithm,
Input signature, Input subjectPublicKeyInfo);
Result TestVerifyRSAPKCS1SignedData(Input data, DigestAlgorithm digestAlgorithm,
Input signature,
Input subjectPublicKeyInfo);
Result TestDigestBuf(Input item, DigestAlgorithm digestAlg,
/*out*/ uint8_t* digestBuf, size_t digestBufLen);
@ -430,8 +432,8 @@ class OCSPResponseContext final {
};
ByteString CreateEncodedOCSPResponse(OCSPResponseContext& context);
}
}
} // namespace mozilla::pkix::test
} // namespace test
} // namespace pkix
} // namespace mozilla
#endif // mozilla_pkix_test_pkixtestutil_h

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This code is made available to you under your choice of the following sets
* of licensing terms:
*/
@ -303,7 +304,7 @@ inline bool InputContains(const Input& input, uint8_t toFind) {
}
}
}
}
} // namespace mozilla::pkix
} // namespace pkix
} // namespace mozilla
#endif // mozilla_pkix_Input_h

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This code is made available to you under your choice of the following sets
* of licensing terms:
*/
@ -187,7 +188,7 @@ static const unsigned int FATAL_ERROR_FLAG = 0x800;
SEC_ERROR_LIBRARY_FAILURE) \
MOZILLA_PKIX_MAP(FATAL_ERROR_NO_MEMORY, FATAL_ERROR_FLAG | 4, \
SEC_ERROR_NO_MEMORY) \
/* nothing here */
/* nothing here */
enum class Result {
#define MOZILLA_PKIX_MAP(name, value, nss_name) name = value,
@ -212,7 +213,7 @@ inline Result NotReached(const char* /*explanation*/, Result result) {
assert(false);
return result;
}
}
} // namespace mozilla::pkix
} // namespace pkix
} // namespace mozilla
#endif // mozilla_pkix_Result_h

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This code is made available to you under your choice of the following sets
* of licensing terms:
*/
@ -110,6 +111,9 @@ Time Now();
// Note the epoch is the unix epoch (ie 00:00:00 UTC, 1 January 1970)
Time TimeFromEpochInSeconds(uint64_t secondsSinceEpoch);
// Note the epoch is the unix epoch (ie 00:00:00 UTC, 1 January 1970)
Result SecondsSinceEpochFromTime(Time time, uint64_t* outSeconds);
class Duration final {
public:
Duration(Time timeA, Time timeB)
@ -128,9 +132,11 @@ class Duration final {
}
private:
friend Result SecondsSinceEpochFromTime(Time time, uint64_t* outSeconds);
uint64_t durationInSeconds;
};
}
} // namespace mozilla::pkix
} // namespace pkix
} // namespace mozilla
#endif // mozilla_pkix_Time_h

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This code is made available to you under your choice of the following sets
* of licensing terms:
*/
@ -116,6 +117,8 @@ Result BuildCertChain(TrustDomain& trustDomain, Input cert, Time time,
// - IP addresses are out of scope of RFC 6125, but this method accepts them for
// backward compatibility (see SearchNames in pkixnames.cpp)
// - A wildcard in a DNS-ID may only appear as the entirety of the first label.
// If the NameMatchingPolicy is omitted, a StrictNameMatchingPolicy is used.
Result CheckCertHostname(Input cert, Input hostname);
Result CheckCertHostname(Input cert, Input hostname,
NameMatchingPolicy& nameMatchingPolicy);
@ -153,7 +156,7 @@ Result VerifyEncodedOCSPResponse(
// requirement for another value. Empty extensions are also rejected.
Result CheckTLSFeaturesAreSatisfied(Input& cert,
const Input* stapledOCSPResponse);
}
} // namespace mozilla::pkix
} // namespace pkix
} // namespace mozilla
#endif // mozilla_pkix_pkix_h

View file

@ -0,0 +1,47 @@
/* 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/.
*/
#ifndef mozilla_pkix_pkixc_h
#define mozilla_pkix_pkixc_h
#include "prerror.h"
#include "stdint.h"
// VerifyCertificateChain will attempt to build a verified certificate chain
// starting from the 0th certificate in the given array to the indicated trust
// anchor. It returns true on success and false otherwise. No particular key
// usage is required, and no particular policy is required. The code signing
// extended key usage is required. No revocation checking is performed. RSA
// keys must be at least 2048 bits long, and EC keys must be from one of the
// curves secp256r1, secp384r1, or secp521r1. Only SHA256, SHA384, and SHA512
// are acceptable digest algorithms. When doing name checking, the subject
// common name field is ignored. At least one certificate must be provided.
// certificate is an array of pointers to certificates.
// certificateLengths is an array of the lengths of each certificate.
// numCertificates indicates how many certificates are in certificates.
// secondsSinceEpoch indicates the time at which the certificate chain must be
// valid, in seconds since the epoch.
// rootSHA256Hash identifies a trust anchor by the SHA256 hash of its contents.
// It must be an array of 32 bytes.
// hostname is a doman name for which the end-entity certificate must be valid.
// error will be set if and only if the return value is false. Its value may
// indicate why verification failed.
#ifdef __cplusplus
extern "C" {
#endif
bool VerifyCodeSigningCertificateChain(const uint8_t** certificates,
const uint16_t* certificateLengths,
size_t numCertificates,
uint64_t secondsSinceEpoch,
const uint8_t* rootSHA256Hash,
const uint8_t* hostname,
size_t hostnameLength,
/* out */ PRErrorCode* error);
#ifdef __cplusplus
}
#endif
#endif // mozilla_pkix_pkixc_h

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This code is made available to you under your choice of the following sets
* of licensing terms:
*/
@ -58,7 +59,7 @@ Result CheckValidity(Time time, Time notBefore, Time notAfter);
// Check that a subject has TLS Feature (rfc7633) requirements that match its
// potential issuer
Result CheckTLSFeatures(const BackCert& subject, BackCert& potentialIssuer);
}
} // namespace mozilla::pkix
} // namespace pkix
} // namespace mozilla
#endif // mozilla_pkix_pkixcheck_h

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This code is made available to you under your choice of the following sets
* of licensing terms:
*/
@ -487,7 +488,7 @@ inline Result OptionalExtensions(Reader& input, uint8_t tag,
Result DigestAlgorithmIdentifier(Reader& input,
/*out*/ DigestAlgorithm& algorithm);
enum class PublicKeyAlgorithm { RSA_PKCS1, ECDSA, Uninitialized };
enum class PublicKeyAlgorithm { RSA_PKCS1, RSA_PSS, ECDSA };
Result SignatureAlgorithmIdentifierValue(
Reader& input,
@ -523,8 +524,11 @@ struct SignedDataWithSignature final {
// certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
Result SignedData(Reader& input, /*out*/ Reader& tbs,
/*out*/ SignedDataWithSignature& signedDataWithSignature);
}
}
} // namespace mozilla::pkix::der
// Parses an ECDSASigValue (RFC 5480) into its components r and s.
Result ECDSASigValue(Input ecdsaSignature, /*out*/ Input& r, /*out*/ Input& s);
} // namespace der
} // namespace pkix
} // namespace mozilla
#endif // mozilla_pkix_pkixder_h

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This code is made available to you under your choice of the following sets
* of licensing terms:
*/
@ -33,15 +34,21 @@ namespace pkix {
// Verifies the PKCS#1.5 signature on the given data using the given RSA public
// key.
Result VerifyRSAPKCS1SignedDigestNSS(const SignedDigest& sd,
Input subjectPublicKeyInfo,
void* pkcs11PinArg);
Result VerifyRSAPKCS1SignedDataNSS(Input data, DigestAlgorithm digestAlgorithm,
Input signature, Input subjectPublicKeyInfo,
void* pkcs11PinArg);
// Verifies the RSA-PSS signature on the given data using the given RSA
// public key.
Result VerifyRSAPSSSignedDataNSS(Input data, DigestAlgorithm digestAlgorithm,
Input signature, Input subjectPublicKeyInfo,
void* pkcs11PinArg);
// Verifies the ECDSA signature on the given data using the given ECC public
// key.
Result VerifyECDSASignedDigestNSS(const SignedDigest& sd,
Input subjectPublicKeyInfo,
void* pkcs11PinArg);
Result VerifyECDSASignedDataNSS(Input data, DigestAlgorithm digestAlgorithm,
Input signature, Input subjectPublicKeyInfo,
void* pkcs11PinArg);
// Computes the digest of the given data using the given digest algorithm.
//
@ -99,7 +106,7 @@ inline SECItem UnsafeMapInputToSECItem(Input input) {
"input.GetLength() must fit in a SECItem");
return result;
}
}
} // namespace mozilla::pkix
} // namespace pkix
} // namespace mozilla
#endif // mozilla_pkix_pkixnss_h

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This code is made available to you under your choice of the following sets
* of licensing terms:
*/
@ -51,14 +52,6 @@ enum class NamedCurve {
secp256r1 = 3,
};
struct SignedDigest final {
Input digest;
DigestAlgorithm digestAlgorithm;
Input signature;
void operator=(const SignedDigest&) = delete;
};
enum class EndEntityOrCA { MustBeEndEntity = 0, MustBeCA = 1 };
enum class KeyUsage : uint8_t {
@ -277,10 +270,10 @@ class TrustDomain {
virtual Result CheckRevocation(EndEntityOrCA endEntityOrCA,
const CertID& certID, Time time,
Time validityBeginning,
Duration validityDuration,
/*optional*/ const Input* stapledOCSPresponse,
/*optional*/ const Input* aiaExtension) = 0;
/*optional*/ const Input* aiaExtension,
/*optional*/ const Input* sctExtension) = 0;
// Check that the given digest algorithm is acceptable for use in signatures.
//
@ -304,10 +297,22 @@ class TrustDomain {
//
// CheckRSAPublicKeyModulusSizeInBits will be called before calling this
// function, so it is not necessary to repeat those checks here. However,
// VerifyRSAPKCS1SignedDigest *is* responsible for doing the mathematical
// VerifyRSAPKCS1SignedData *is* responsible for doing the mathematical
// verification of the public key validity as specified in NIST SP 800-56A.
virtual Result VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest,
Input subjectPublicKeyInfo) = 0;
virtual Result VerifyRSAPKCS1SignedData(Input data,
DigestAlgorithm digestAlgorithm,
Input signature,
Input subjectPublicKeyInfo) = 0;
// Verify the given RSA-PSS signature on the given digest using the
// given RSA public key.
//
// CheckRSAPublicKeyModulusSizeInBits will be called before calling this
// function, so it is not necessary to repeat those checks here.
virtual Result VerifyRSAPSSSignedData(Input data,
DigestAlgorithm digestAlgorithm,
Input signature,
Input subjectPublicKeyInfo) = 0;
// Check that the given named ECC curve is acceptable for ECDSA signatures.
//
@ -322,10 +327,12 @@ class TrustDomain {
//
// CheckECDSACurveIsAcceptable will be called before calling this function,
// so it is not necessary to repeat that check here. However,
// VerifyECDSASignedDigest *is* responsible for doing the mathematical
// VerifyECDSASignedData *is* responsible for doing the mathematical
// verification of the public key validity as specified in NIST SP 800-56A.
virtual Result VerifyECDSASignedDigest(const SignedDigest& signedDigest,
Input subjectPublicKeyInfo) = 0;
virtual Result VerifyECDSASignedData(Input data,
DigestAlgorithm digestAlgorithm,
Input signature,
Input subjectPublicKeyInfo) = 0;
// Check that the validity duration is acceptable.
//
@ -394,7 +401,14 @@ class NameMatchingPolicy {
NameMatchingPolicy(const NameMatchingPolicy&) = delete;
void operator=(const NameMatchingPolicy&) = delete;
};
}
} // namespace mozilla::pkix
class StrictNameMatchingPolicy : public NameMatchingPolicy {
public:
virtual Result FallBackToCommonName(
Time notBefore,
/*out*/ FallBackToSearchWithinSubject& fallBacktoCommonName) override;
};
} // namespace pkix
} // namespace mozilla
#endif // mozilla_pkix_pkixtypes_h

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This code is made available to you under your choice of the following sets
* of licensing terms:
*/
@ -191,18 +192,6 @@ inline unsigned int DaysBeforeYear(unsigned int year) {
static const size_t MAX_DIGEST_SIZE_IN_BYTES = 512 / 8; // sha-512
Result DigestSignedData(TrustDomain& trustDomain,
const der::SignedDataWithSignature& signedData,
/*out*/ uint8_t (&digestBuf)[MAX_DIGEST_SIZE_IN_BYTES],
/*out*/ der::PublicKeyAlgorithm& publicKeyAlg,
/*out*/ SignedDigest& signedDigest);
Result VerifySignedDigest(TrustDomain& trustDomain,
der::PublicKeyAlgorithm publicKeyAlg,
const SignedDigest& signedDigest,
Input signerSubjectPublicKeyInfo);
// Combines DigestSignedData and VerifySignedDigest
Result VerifySignedData(TrustDomain& trustDomain,
const der::SignedDataWithSignature& signedData,
Input signerSubjectPublicKeyInfo);
@ -272,7 +261,7 @@ inline size_t DigestAlgorithmToSizeInBytes(DigestAlgorithm digestAlgorithm) {
MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
}
}
}
} // namespace mozilla::pkix
} // namespace pkix
} // namespace mozilla
#endif // mozilla_pkix_pkixutil_h

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This code is made available to you under your choice of the following sets
* of licensing terms:
*/
@ -60,7 +61,6 @@ public:
, stapledOCSPResponse(aStapledOCSPResponse)
, subCACount(aSubCACount)
, deferredSubjectError(aDeferredSubjectError)
, subjectSignaturePublicKeyAlg(der::PublicKeyAlgorithm::Uninitialized)
, result(Result::FATAL_ERROR_LIBRARY_FAILURE)
, resultWasSet(false)
, buildForwardCallBudget(aBuildForwardCallBudget)
@ -83,11 +83,6 @@ private:
const unsigned int subCACount;
const Result deferredSubjectError;
// Initialized lazily.
uint8_t subjectSignatureDigestBuf[MAX_DIGEST_SIZE_IN_BYTES];
der::PublicKeyAlgorithm subjectSignaturePublicKeyAlg;
SignedDigest subjectSignature;
Result RecordResult(Result currentResult, /*out*/ bool& keepGoing);
Result result;
bool resultWasSet;
@ -214,22 +209,8 @@ PathBuildingStep::Check(Input potentialIssuerDER,
return RecordResult(rv, keepGoing);
}
// Calculate the digest of the subject's signed data if we haven't already
// done so. We do this lazily to avoid doing it at all if we backtrack before
// getting to this point. We cache the result to avoid recalculating it if we
// backtrack after getting to this point.
if (subjectSignature.digest.GetLength() == 0) {
rv = DigestSignedData(trustDomain, subject.GetSignedData(),
subjectSignatureDigestBuf,
subjectSignaturePublicKeyAlg, subjectSignature);
if (rv != Success) {
return rv;
}
}
rv = VerifySignedDigest(trustDomain, subjectSignaturePublicKeyAlg,
subjectSignature,
potentialIssuer.GetSubjectPublicKeyInfo());
rv = VerifySignedData(trustDomain, subject.GetSignedData(),
potentialIssuer.GetSubjectPublicKeyInfo());
if (rv != Success) {
return RecordResult(rv, keepGoing);
}
@ -251,9 +232,9 @@ PathBuildingStep::Check(Input potentialIssuerDER,
}
Duration validityDuration(notAfter, notBefore);
rv = trustDomain.CheckRevocation(subject.endEntityOrCA, certID, time,
notBefore, validityDuration,
stapledOCSPResponse,
subject.GetAuthorityInfoAccess());
validityDuration, stapledOCSPResponse,
subject.GetAuthorityInfoAccess(),
subject.GetSignedCertificateTimestamps());
if (rv != Success) {
// Since this is actually a problem with the current subject certificate
// (rather than the issuer), it doesn't make sense to keep going; all

View file

@ -0,0 +1,228 @@
/* 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 "mozpkix/pkixc.h"
#include "mozpkix/pkix.h"
#include "mozpkix/pkixnss.h"
#include "mozpkix/pkixtypes.h"
#include "secerr.h"
using namespace mozilla::pkix;
const size_t SHA256_DIGEST_LENGTH = 256 / 8;
class CodeSigningTrustDomain final : public TrustDomain {
public:
explicit CodeSigningTrustDomain(const uint8_t** certificates,
const uint16_t* certificateLengths,
size_t numCertificates,
const uint8_t* rootSHA256Digest)
: mCertificates(certificates),
mCertificateLengths(certificateLengths),
mNumCertificates(numCertificates),
mRootSHA256Digest(rootSHA256Digest) {}
virtual Result GetCertTrust(EndEntityOrCA endEntityOrCA,
const CertPolicyId& policy,
Input candidateCertDER,
/*out*/ TrustLevel& trustLevel) override {
uint8_t digestBuf[SHA256_DIGEST_LENGTH] = {0};
Result rv = DigestBufNSS(candidateCertDER, DigestAlgorithm::sha256,
digestBuf, SHA256_DIGEST_LENGTH);
if (rv != Success) {
return rv;
}
Input candidateDigestInput;
rv = candidateDigestInput.Init(digestBuf, SHA256_DIGEST_LENGTH);
if (rv != Success) {
return rv;
}
Input rootDigestInput;
rv = rootDigestInput.Init(mRootSHA256Digest, SHA256_DIGEST_LENGTH);
if (rv != Success) {
return rv;
}
if (InputsAreEqual(candidateDigestInput, rootDigestInput)) {
trustLevel = TrustLevel::TrustAnchor;
} else {
trustLevel = TrustLevel::InheritsTrust;
}
return Success;
}
virtual Result FindIssuer(Input encodedIssuerName, IssuerChecker& checker,
Time time) override {
for (size_t i = 0; i < mNumCertificates; i++) {
Input certInput;
Result rv = certInput.Init(mCertificates[i], mCertificateLengths[i]);
if (rv != Success) {
return rv;
}
bool keepGoing;
rv = checker.Check(certInput, nullptr /*additionalNameConstraints*/,
keepGoing);
if (rv != Success) {
return rv;
}
if (!keepGoing) {
break;
}
}
return Success;
}
virtual Result CheckRevocation(
EndEntityOrCA endEntityOrCA, const CertID& certID, Time time,
Duration validityDuration,
/*optional*/ const Input* stapledOCSPresponse,
/*optional*/ const Input* aiaExtension,
/*optional*/ const Input* sctExtension) override {
return Success;
}
virtual Result IsChainValid(const DERArray& certChain, Time time,
const CertPolicyId& requiredPolicy) override {
return Success;
}
virtual Result CheckSignatureDigestAlgorithm(DigestAlgorithm digestAlg,
EndEntityOrCA endEntityOrCA,
Time notBefore) override {
switch (digestAlg) {
case DigestAlgorithm::sha256: // fall through
case DigestAlgorithm::sha384: // fall through
case DigestAlgorithm::sha512:
return Success;
default:
return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
}
}
virtual Result CheckRSAPublicKeyModulusSizeInBits(
EndEntityOrCA endEntityOrCA, unsigned int modulusSizeInBits) override {
if (modulusSizeInBits < 2048) {
return Result::ERROR_INADEQUATE_KEY_SIZE;
}
return Success;
}
virtual Result VerifyRSAPKCS1SignedData(
Input data, DigestAlgorithm digestAlgorithm, Input signature,
Input subjectPublicKeyInfo) override {
return VerifyRSAPKCS1SignedDataNSS(data, digestAlgorithm, signature,
subjectPublicKeyInfo, nullptr);
}
virtual Result VerifyRSAPSSSignedData(
Input data, DigestAlgorithm digestAlgorithm, Input signature,
Input subjectPublicKeyInfo) override {
return VerifyRSAPSSSignedDataNSS(data, digestAlgorithm, signature,
subjectPublicKeyInfo, nullptr);
}
virtual Result CheckECDSACurveIsAcceptable(EndEntityOrCA endEntityOrCA,
NamedCurve curve) override {
switch (curve) {
case NamedCurve::secp256r1: // fall through
case NamedCurve::secp384r1: // fall through
case NamedCurve::secp521r1:
return Success;
}
return Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE;
}
virtual Result VerifyECDSASignedData(
Input data, DigestAlgorithm digestAlgorithm, Input signature,
Input subjectPublicKeyInfo) override {
return VerifyECDSASignedDataNSS(data, digestAlgorithm, signature,
subjectPublicKeyInfo, nullptr);
}
virtual Result CheckValidityIsAcceptable(Time notBefore, Time notAfter,
EndEntityOrCA endEntityOrCA,
KeyPurposeId keyPurpose) override {
return Success;
}
virtual Result NetscapeStepUpMatchesServerAuth(
Time notBefore, /*out*/ bool& matches) override {
matches = false;
return Success;
}
virtual void NoteAuxiliaryExtension(AuxiliaryExtension extension,
Input extensionData) override {}
virtual Result DigestBuf(Input item, DigestAlgorithm digestAlg,
/*out*/ uint8_t* digestBuf,
size_t digestBufLen) override {
return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen);
}
private:
const uint8_t** mCertificates;
const uint16_t* mCertificateLengths;
size_t mNumCertificates;
const uint8_t* mRootSHA256Digest;
};
class CodeSigningNameMatchingPolicy : public NameMatchingPolicy {
public:
virtual Result FallBackToCommonName(
Time notBefore,
/*out*/ FallBackToSearchWithinSubject& fallBackToCommonName) override {
fallBackToCommonName = FallBackToSearchWithinSubject::No;
return Success;
}
};
bool VerifyCodeSigningCertificateChain(
const uint8_t** certificates, const uint16_t* certificateLengths,
size_t numCertificates, uint64_t secondsSinceEpoch,
const uint8_t* rootSHA256Digest, const uint8_t* hostname,
size_t hostnameLength, PRErrorCode* error) {
if (!error) {
return false;
}
if (!certificates || !certificateLengths || numCertificates < 1 ||
!rootSHA256Digest) {
*error = SEC_ERROR_INVALID_ARGS;
return false;
}
CodeSigningTrustDomain trustDomain(certificates, certificateLengths,
numCertificates, rootSHA256Digest);
Input certificate;
Result rv = certificate.Init(certificates[0], certificateLengths[0]);
if (rv != Success) {
*error = MapResultToPRErrorCode(rv);
return false;
}
Time time = TimeFromEpochInSeconds(secondsSinceEpoch);
rv = BuildCertChain(
trustDomain, certificate, time, EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired, KeyPurposeId::id_kp_codeSigning,
CertPolicyId::anyPolicy, nullptr);
if (rv != Success) {
*error = MapResultToPRErrorCode(rv);
return false;
}
Input hostnameInput;
rv = hostnameInput.Init(hostname, hostnameLength);
if (rv != Success) {
*error = MapResultToPRErrorCode(rv);
return false;
}
CodeSigningNameMatchingPolicy nameMatchingPolicy;
rv = CheckCertHostname(certificate, hostnameInput, nameMatchingPolicy);
if (rv != Success) {
*error = MapResultToPRErrorCode(rv);
return false;
}
return true;
}

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This code is made available to you under your choice of the following sets
* of licensing terms:
*/

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This code is made available to you under your choice of the following sets
* of licensing terms:
*/
@ -99,12 +100,13 @@ CheckSignatureAlgorithm(TrustDomain& trustDomain,
switch (publicKeyAlg) {
case der::PublicKeyAlgorithm::RSA_PKCS1:
case der::PublicKeyAlgorithm::RSA_PSS:
{
// The RSA computation may give a result that requires fewer bytes to
// encode than the public key (since it is modular arithmetic). However,
// the last step of generating a PKCS#1.5 signature is the I2OSP
// procedure, which pads any such shorter result with zeros so that it
// is exactly the same length as the public key.
// encode than the modulus (since it is modular arithmetic). However,
// the last step of generating a RSA-PKCS#1.5 or -PSS signature is the
// I2OSP procedure, which pads any such shorter result with zeros so that
// it is exactly the same length as the modulus.
unsigned int signatureSizeInBits = signedData.signature.GetLength() * 8u;
return trustDomain.CheckRSAPublicKeyModulusSizeInBits(
endEntityOrCA, signatureSizeInBits);
@ -116,11 +118,6 @@ CheckSignatureAlgorithm(TrustDomain& trustDomain,
// for any curve that we support, the chances of us encountering a curve
// during path building is too low to be worth bothering with.
break;
case der::PublicKeyAlgorithm::Uninitialized:
{
assert(false);
return Result::FATAL_ERROR_LIBRARY_FAILURE;
}
MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
}

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This code is made available to you under your choice of the following sets
* of licensing terms:
*/
@ -82,29 +83,6 @@ ReadTagAndGetValue(Reader& input, /*out*/ uint8_t& tag, /*out*/ Input& value)
return input.Skip(length, value);
}
static Result
OptionalNull(Reader& input)
{
if (input.Peek(NULLTag)) {
return Null(input);
}
return Success;
}
namespace {
Result
AlgorithmIdentifierValue(Reader& input, /*out*/ Reader& algorithmOIDValue)
{
Result rv = ExpectTagAndGetValue(input, der::OIDTag, algorithmOIDValue);
if (rv != Success) {
return rv;
}
return OptionalNull(input);
}
} // namespace
Result
SignatureAlgorithmIdentifierValue(Reader& input,
/*out*/ PublicKeyAlgorithm& publicKeyAlgorithm,
@ -115,13 +93,39 @@ SignatureAlgorithmIdentifierValue(Reader& input,
//
// RFC 4055 Section 5 and RFC 3279 Section 2.2.1 both say that parameters for
// RSA must be encoded as NULL; we relax that requirement by allowing the
// NULL to be omitted, to match all the other signature algorithms we support
// and for compatibility.
// NULL to be omitted.
//
// RFC 8017 Appendix A.2.3 specifies the format of the parameters for
// RSA-PSS. The CA/Browser Forum Baseline Requirements (as of 1.8.1)
// specify three specific parameters that may be used with RSA-PSS:
// * SHA-256, MGF-1 with SHA-256, and a salt length of 32 bytes
// * SHA-384, MGF-1 with SHA-384, and a salt length of 48 bytes
// * SHA-512, MGF-1 with SHA-512, and a salt length of 64 bytes
// as well as the corresponding specific encodings that must be used
// for the entire AlgorithmIdentifier:
// * 304106092a864886f70d01010a3034a00f300d0609608648016503040201
// 0500a11c301a06092a864886f70d010108300d0609608648016503040201
// 0500a203020120
// * 304106092a864886f70d01010a3034a00f300d0609608648016503040202
// 0500a11c301a06092a864886f70d010108300d0609608648016503040202
// 0500a203020130
// * 304106092a864886f70d01010a3034a00f300d0609608648016503040203
// 0500a11c301a06092a864886f70d010108300d0609608648016503040203
// 0500a203020140
// Note that these encodings include the outer SEQUENCE and length bytes;
// in this implementation, the caller has already validated those bytes.
// Currently these are the only sets of parameters mozilla::pkix supports.
Reader algorithmID;
Result rv = AlgorithmIdentifierValue(input, algorithmID);
Result rv = ExpectTagAndGetValue(input, der::OIDTag, algorithmID);
if (rv != Success) {
return rv;
}
Input algorithmParamsInput;
rv = input.SkipToEnd(algorithmParamsInput);
if (rv != Success) {
return rv;
}
Reader algorithmParams(algorithmParamsInput);
// RFC 5758 Section 3.2 (ecdsa-with-SHA224 is intentionally excluded)
// python DottedOIDToCode.py ecdsa-with-SHA256 1.2.840.10045.4.3.2
@ -172,6 +176,41 @@ SignatureAlgorithmIdentifierValue(Reader& input,
0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x01
};
// RFC 8017 Appendix A.2.3
// python DottedOIDToCode.py id-RSA-PSS 1.2.840.113549.1.1.10
static const uint8_t id_RSA_PSS[] = {
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0a
};
// CA/B Forum BR 1.8.1 Section 7.1.3.2.1
// Params for RSA-PSS with SHA-256, MGF-1 with SHA-256, and a salt length
// of 32 bytes:
static const uint8_t rsaPSSWithSHA256MGF1WithSHA256Salt32[] = {
0x30, 0x34, 0xa0, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0xa1, 0x1c, 0x30, 0x1a, 0x06,
0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x08, 0x30, 0x0d,
0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
0x00, 0xa2, 0x03, 0x02, 0x01, 0x20
};
// Params for RSA-PSS with SHA-384, MGF-1 with SHA-384, and a salt length
// of 48 bytes:
static const uint8_t rsaPSSWithSHA384MGF1WithSHA384Salt48[] = {
0x30, 0x34, 0xa0, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0xa1, 0x1c, 0x30, 0x1a, 0x06,
0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x08, 0x30, 0x0d,
0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05,
0x00, 0xa2, 0x03, 0x02, 0x01, 0x30
};
// Params for RSA-PSS with SHA-512, MGF-1 with SHA-512, and a salt length
// of 64 bytes:
static const uint8_t rsaPSSWithSHA512MGF1WithSHA512Salt64[] = {
0x30, 0x34, 0xa0, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0xa1, 0x1c, 0x30, 0x1a, 0x06,
0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x08, 0x30, 0x0d,
0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
0x00, 0xa2, 0x03, 0x02, 0x01, 0x40
};
// Matching is attempted based on a rough estimate of the commonality of the
// algorithm, to minimize the number of MatchRest calls.
if (algorithmID.MatchRest(sha256WithRSAEncryption)) {
@ -199,25 +238,62 @@ SignatureAlgorithmIdentifierValue(Reader& input,
publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1;
digestAlgorithm = DigestAlgorithm::sha512;
} else if (algorithmID.MatchRest(sha1WithRSASignature)) {
// XXX(bug 1042479): recognize this old OID for compatibility.
// This old OID is recognized for compatibility (see bug 1042479).
publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1;
digestAlgorithm = DigestAlgorithm::sha1;
} else if (algorithmID.MatchRest(id_RSA_PSS)) {
publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PSS;
if (algorithmParams.MatchRest(rsaPSSWithSHA256MGF1WithSHA256Salt32)) {
digestAlgorithm = DigestAlgorithm::sha256;
} else if (algorithmParams.MatchRest(rsaPSSWithSHA384MGF1WithSHA384Salt48)) {
digestAlgorithm = DigestAlgorithm::sha384;
} else if (algorithmParams.MatchRest(rsaPSSWithSHA512MGF1WithSHA512Salt64)) {
digestAlgorithm = DigestAlgorithm::sha512;
} else {
return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
}
} else {
return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
}
// Ensure that for non-RSA-PSS algorithms, the params are NULL or omitted.
if (publicKeyAlgorithm != PublicKeyAlgorithm::RSA_PSS) {
if (algorithmParams.Peek(NULLTag)) {
rv = Null(algorithmParams);
if (rv != Success) {
return rv;
}
}
if (!algorithmParams.AtEnd()) {
return Result::ERROR_BAD_DER;
}
}
return Success;
}
Result
DigestAlgorithmIdentifier(Reader& input, /*out*/ DigestAlgorithm& algorithm)
{
// AlgorithmIdentifier ::= SEQUENCE {
// algorithm OBJECT IDENTIFIER,
// parameters ANY DEFINED BY algorithm OPTIONAL }
return der::Nested(input, SEQUENCE, [&algorithm](Reader& r) -> Result {
Reader algorithmID;
Result rv = AlgorithmIdentifierValue(r, algorithmID);
Result rv = ExpectTagAndGetValue(r, der::OIDTag, algorithmID);
if (rv != Success) {
return rv;
}
// The parameters are expected to be NULL or omitted.
if (r.Peek(NULLTag)) {
rv = Null(r);
if (rv != Success) {
return rv;
}
}
if (!r.AtEnd()) {
return Result::ERROR_BAD_DER;
}
// RFC 4055 Section 2.1
// python DottedOIDToCode.py id-sha1 1.3.14.3.2.26
@ -607,4 +683,58 @@ OptionalVersion(Reader& input, /*out*/ Version& version)
});
}
// From RFC 5480 Appendix A:
// ECDSA-Sig-Value ::= SEQUENCE {
// r INTEGER,
// s INTEGER
// }
Result
ECDSASigValue(Input ecdsaSignature, /*out*/ Input& r, /*out*/ Input& s) {
Reader rAndS;
Result rv = ExpectTagAndGetValueAtEnd(ecdsaSignature, SEQUENCE, rAndS);
if (rv != Success) {
return rv;
}
Input rInput;
Input::size_type rSignificantBytes;
rv = PositiveInteger(rAndS, rInput, &rSignificantBytes);
if (rv != Success) {
return rv;
}
Reader rReader(rInput);
// Address potential leading 0 byte due to DER encoding.
if (rSignificantBytes + 1 == rInput.GetLength()) {
rv = rReader.Skip(1);
if (rv != Success) {
return rv;
}
}
rv = rReader.SkipToEnd(r);
if (rv != Success) {
return rv;
}
Input sInput;
Input::size_type sSignificantBytes;
rv = PositiveInteger(rAndS, sInput, &sSignificantBytes);
if (rv != Success) {
return rv;
}
Reader sReader(sInput);
// Address potential leading 0 byte due to DER encoding.
if (sSignificantBytes + 1 == sInput.GetLength()) {
rv = sReader.Skip(1);
if (rv != Success) {
return rv;
}
}
rv = sReader.SkipToEnd(s);
if (rv != Success) {
return rv;
}
return End(rAndS);
}
} } } // namespace mozilla::pkix::der

View file

@ -7,6 +7,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/* Copyright 2014 Mozilla Contributors
* Copyright 2026 Moonchild Productions
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -170,6 +171,12 @@ enum class IDRole
NameConstraint = 2,
};
enum class NameConstraintsSubtrees : uint8_t
{
permittedSubtrees = der::CONSTRUCTED | der::CONTEXT_SPECIFIC | 0,
excludedSubtrees = der::CONSTRUCTED | der::CONTEXT_SPECIFIC | 1
};
enum class AllowWildcards { No = 0, Yes = 1 };
// DNSName constraints implicitly allow subdomain matching when there is no
@ -182,16 +189,22 @@ enum class AllowDotlessSubdomainMatches { No = 0, Yes = 1 };
bool IsValidDNSID(Input hostname, IDRole idRole,
AllowWildcards allowWildcards);
// `subtreesType` is relevant only when `referenceDNSIDRole` is
// `IDRole::NameConstraint`.
Result MatchPresentedDNSIDWithReferenceDNSID(
Input presentedDNSID,
AllowWildcards allowWildcards,
AllowDotlessSubdomainMatches allowDotlessSubdomainMatches,
IDRole referenceDNSIDRole,
/*optional*/ const NameConstraintsSubtrees* subtreesType,
Input referenceDNSID,
/*out*/ bool& matches);
// `subtreesType` is relevant only when `referenceDNSIDRole` is
// `IDRole::NameConstraint`.
Result MatchPresentedRFC822NameWithReferenceRFC822Name(
Input presentedRFC822Name, IDRole referenceRFC822NameRole,
/*optional*/ const NameConstraintsSubtrees* subtreesType,
Input referenceRFC822Name, /*out*/ bool& matches);
} // namespace
@ -210,7 +223,7 @@ MatchPresentedDNSIDWithReferenceDNSID(Input presentedDNSID,
return MatchPresentedDNSIDWithReferenceDNSID(
presentedDNSID, AllowWildcards::Yes,
AllowDotlessSubdomainMatches::Yes, IDRole::ReferenceID,
referenceDNSID, matches);
nullptr, referenceDNSID, matches);
}
// Verify that the given end-entity cert, which is assumed to have been already
@ -279,6 +292,23 @@ CheckCertHostname(Input endEntityCertDER, Input hostname,
}
}
// A strict name matching policy for CheckCertHostname which never
// falls back to searching within the subject name.
Result StrictNameMatchingPolicy::FallBackToCommonName(
Time notBefore,
/*out*/ FallBackToSearchWithinSubject& fallBackToCommonName) {
fallBackToCommonName = FallBackToSearchWithinSubject::No;
return Success;
}
Result
CheckCertHostname(Input endEntityCertDER, Input hostname)
{
StrictNameMatchingPolicy policy{};
return CheckCertHostname(endEntityCertDER, hostname, policy);
}
// 4.2.1.10. Name Constraints
Result
CheckNameConstraints(Input encodedNameConstraints,
@ -694,7 +724,7 @@ MatchPresentedIDWithReferenceID(GeneralNameType presentedIDType,
rv = MatchPresentedDNSIDWithReferenceDNSID(
presentedID, AllowWildcards::Yes,
AllowDotlessSubdomainMatches::Yes, IDRole::ReferenceID,
referenceID, foundMatch);
nullptr, referenceID, foundMatch);
break;
case GeneralNameType::iPAddress:
@ -704,7 +734,7 @@ MatchPresentedIDWithReferenceID(GeneralNameType presentedIDType,
case GeneralNameType::rfc822Name:
rv = MatchPresentedRFC822NameWithReferenceRFC822Name(
presentedID, IDRole::ReferenceID, referenceID, foundMatch);
presentedID, IDRole::ReferenceID, nullptr, referenceID, foundMatch);
break;
case GeneralNameType::directoryName:
@ -730,12 +760,6 @@ MatchPresentedIDWithReferenceID(GeneralNameType presentedIDType,
return Success;
}
enum class NameConstraintsSubtrees : uint8_t
{
permittedSubtrees = der::CONSTRUCTED | der::CONTEXT_SPECIFIC | 0,
excludedSubtrees = der::CONSTRUCTED | der::CONTEXT_SPECIFIC | 1
};
Result CheckPresentedIDConformsToNameConstraintsSubtrees(
GeneralNameType presentedIDType,
Input presentedID,
@ -849,7 +873,7 @@ CheckPresentedIDConformsToNameConstraintsSubtrees(
rv = MatchPresentedDNSIDWithReferenceDNSID(
presentedID, AllowWildcards::Yes,
AllowDotlessSubdomainMatches::Yes, IDRole::NameConstraint,
base, matches);
&subtreesType, base, matches);
if (rv != Success) {
return rv;
}
@ -874,7 +898,7 @@ CheckPresentedIDConformsToNameConstraintsSubtrees(
case GeneralNameType::rfc822Name:
rv = MatchPresentedRFC822NameWithReferenceRFC822Name(
presentedID, IDRole::NameConstraint, base, matches);
presentedID, IDRole::NameConstraint, &subtreesType, base, matches);
if (rv != Success) {
return rv;
}
@ -1057,6 +1081,7 @@ MatchPresentedDNSIDWithReferenceDNSID(
AllowWildcards allowWildcards,
AllowDotlessSubdomainMatches allowDotlessSubdomainMatches,
IDRole referenceDNSIDRole,
/*optional*/ const NameConstraintsSubtrees* subtreesType,
Input referenceDNSID,
/*out*/ bool& matches)
{
@ -1147,18 +1172,28 @@ MatchPresentedDNSIDWithReferenceDNSID(
return NotReached("Skipping '*' failed",
Result::FATAL_ERROR_LIBRARY_FAILURE);
}
do {
// This will happen if reference is a single, relative label
if (reference.AtEnd()) {
matches = false;
return Success;
}
uint8_t referenceByte;
if (reference.Read(referenceByte) != Success) {
return NotReached("invalid reference ID",
Result::FATAL_ERROR_INVALID_ARGS);
}
} while (!reference.Peek('.'));
// For the permittedSubtrees of a name constraint, wildcard presented
// DNSIDs of the form `*.example.com` only match if the name constraint is
// of the form `.example.com` or `example.com`. To put it another way, a
// permittedSubtrees of `foo.example.com` does not match a wildcard
// presented DNSID of `*.example.com`, because in that case, the
// certificate could be valid for `bar.example.com`, which does not match
// the name constraint.
if (referenceDNSIDRole != IDRole::NameConstraint ||
(subtreesType && *subtreesType != NameConstraintsSubtrees::permittedSubtrees)) {
do {
// This will happen if reference is a single, relative label
if (reference.AtEnd()) {
matches = false;
return Success;
}
uint8_t referenceByte;
if (reference.Read(referenceByte) != Success) {
return NotReached("invalid reference ID",
Result::FATAL_ERROR_INVALID_ARGS);
}
} while (!reference.Peek('.'));
}
}
for (;;) {
@ -1515,9 +1550,12 @@ IsValidRFC822Name(Input input)
}
}
// `subtreesType` is relevant only when `referenceRFC822NameRole` is
// `IDRole::NameConstraint`.
Result
MatchPresentedRFC822NameWithReferenceRFC822Name(Input presentedRFC822Name,
IDRole referenceRFC822NameRole,
/*optional*/ const NameConstraintsSubtrees* subtreesType,
Input referenceRFC822Name,
/*out*/ bool& matches)
{
@ -1562,6 +1600,7 @@ MatchPresentedRFC822NameWithReferenceRFC822Name(Input presentedRFC822Name,
return MatchPresentedDNSIDWithReferenceDNSID(
presentedDNSID, AllowWildcards::No,
AllowDotlessSubdomainMatches::No, IDRole::NameConstraint,
subtreesType,
referenceRFC822Name, matches);
}
}

View file

@ -1,4 +1,5 @@
/*- *- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This code is made available to you under your choice of the following sets
* of licensing terms:
*/
@ -39,63 +40,233 @@ namespace mozilla { namespace pkix {
namespace {
Result
VerifySignedDigest(const SignedDigest& sd,
Input subjectPublicKeyInfo,
SECOidTag pubKeyAlg,
void* pkcs11PinArg)
SubjectPublicKeyInfoToSECKEYPublicKey(Input subjectPublicKeyInfo,
ScopedSECKEYPublicKey& publicKey)
{
SECOidTag digestAlg;
switch (sd.digestAlgorithm) {
case DigestAlgorithm::sha512: digestAlg = SEC_OID_SHA512; break;
case DigestAlgorithm::sha384: digestAlg = SEC_OID_SHA384; break;
case DigestAlgorithm::sha256: digestAlg = SEC_OID_SHA256; break;
case DigestAlgorithm::sha1: digestAlg = SEC_OID_SHA1; break;
MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
}
SECItem subjectPublicKeyInfoSECItem =
UnsafeMapInputToSECItem(subjectPublicKeyInfo);
ScopedCERTSubjectPublicKeyInfo
spki(SECKEY_DecodeDERSubjectPublicKeyInfo(&subjectPublicKeyInfoSECItem));
SECItem subjectPublicKeyInfoSECItem(
UnsafeMapInputToSECItem(subjectPublicKeyInfo));
ScopedCERTSubjectPublicKeyInfo spki(
SECKEY_DecodeDERSubjectPublicKeyInfo(&subjectPublicKeyInfoSECItem));
if (!spki) {
return MapPRErrorCodeToResult(PR_GetError());
}
ScopedSECKEYPublicKey
pubKey(SECKEY_ExtractPublicKey(spki.get()));
if (!pubKey) {
publicKey.reset(SECKEY_ExtractPublicKey(spki.get()));
if (!publicKey) {
return MapPRErrorCodeToResult(PR_GetError());
}
SECItem digestSECItem(UnsafeMapInputToSECItem(sd.digest));
SECItem signatureSECItem(UnsafeMapInputToSECItem(sd.signature));
SECStatus srv = VFY_VerifyDigestDirect(&digestSECItem, pubKey.get(),
&signatureSECItem, pubKeyAlg,
digestAlg, pkcs11PinArg);
if (srv != SECSuccess) {
return MapPRErrorCodeToResult(PR_GetError());
}
return Success;
}
template<size_t N>
Result
VerifySignedData(SECKEYPublicKey* publicKey, CK_MECHANISM_TYPE mechanism,
SECItem* params, SECItem* signature, SECItem* data,
SECOidTag (&policyTags)[N], void* pkcs11PinArg)
{
// Hash and signature algorithms can be disabled by policy in NSS. However,
// the policy engine in NSS is not currently sophisticated enough to, for
// example, infer that disabling SEC_OID_SHA1 (i.e. the hash algorithm SHA1)
// should also disable SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION. Thus, this
// implementation checks the signature algorithm, the hash algorithm, and the
// signature algorithm with the hash algorithm together.
for (size_t i = 0; i < sizeof(policyTags) / sizeof(policyTags[0]); i++) {
SECOidTag policyTag = policyTags[i];
uint32_t policyFlags;
if (NSS_GetAlgorithmPolicy(policyTag, &policyFlags) != SECSuccess) {
return MapPRErrorCodeToResult(PR_GetError());
}
if (!(policyFlags & NSS_USE_ALG_IN_ANY_SIGNATURE)) {
return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
}
}
SECStatus srv = PK11_VerifyWithMechanism(publicKey, mechanism, params,
signature, data, pkcs11PinArg);
if (srv != SECSuccess) {
return MapPRErrorCodeToResult(PR_GetError());
}
return Success;
}
} // namespace
Result
VerifyRSAPKCS1SignedDigestNSS(const SignedDigest& sd,
Input subjectPublicKeyInfo,
void* pkcs11PinArg)
VerifyRSAPKCS1SignedDataNSS(Input data, DigestAlgorithm digestAlgorithm,
Input signature, Input subjectPublicKeyInfo, void* pkcs11PinArg)
{
return VerifySignedDigest(sd, subjectPublicKeyInfo,
SEC_OID_PKCS1_RSA_ENCRYPTION, pkcs11PinArg);
ScopedSECKEYPublicKey publicKey;
Result rv = SubjectPublicKeyInfoToSECKEYPublicKey(subjectPublicKeyInfo,
publicKey);
if (rv != Success) {
return rv;
}
SECItem signatureItem(UnsafeMapInputToSECItem(signature));
SECItem dataItem(UnsafeMapInputToSECItem(data));
CK_MECHANISM_TYPE mechanism;
SECOidTag signaturePolicyTag = SEC_OID_PKCS1_RSA_ENCRYPTION;
SECOidTag hashPolicyTag;
SECOidTag combinedPolicyTag;
switch (digestAlgorithm) {
case DigestAlgorithm::sha512:
mechanism = CKM_SHA512_RSA_PKCS;
hashPolicyTag = SEC_OID_SHA512;
combinedPolicyTag = SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION;
break;
case DigestAlgorithm::sha384:
mechanism = CKM_SHA384_RSA_PKCS;
hashPolicyTag = SEC_OID_SHA384;
combinedPolicyTag = SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION;
break;
case DigestAlgorithm::sha256:
mechanism = CKM_SHA256_RSA_PKCS;
hashPolicyTag = SEC_OID_SHA256;
combinedPolicyTag = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION;
break;
case DigestAlgorithm::sha1:
mechanism = CKM_SHA1_RSA_PKCS;
hashPolicyTag = SEC_OID_SHA1;
combinedPolicyTag = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION;
break;
MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
}
SECOidTag policyTags[3] =
{ signaturePolicyTag, hashPolicyTag, combinedPolicyTag };
return VerifySignedData(publicKey.get(), mechanism, nullptr, &signatureItem,
&dataItem, policyTags, pkcs11PinArg);
}
Result
VerifyECDSASignedDigestNSS(const SignedDigest& sd,
Input subjectPublicKeyInfo,
void* pkcs11PinArg)
VerifyRSAPSSSignedDataNSS(Input data, DigestAlgorithm digestAlgorithm,
Input signature, Input subjectPublicKeyInfo, void* pkcs11PinArg)
{
return VerifySignedDigest(sd, subjectPublicKeyInfo,
SEC_OID_ANSIX962_EC_PUBLIC_KEY, pkcs11PinArg);
ScopedSECKEYPublicKey publicKey;
Result rv = SubjectPublicKeyInfoToSECKEYPublicKey(subjectPublicKeyInfo,
publicKey);
if (rv != Success) {
return rv;
}
SECItem signatureItem(UnsafeMapInputToSECItem(signature));
SECItem dataItem(UnsafeMapInputToSECItem(data));
CK_MECHANISM_TYPE mechanism;
SECOidTag signaturePolicyTag = SEC_OID_PKCS1_RSA_PSS_SIGNATURE;
SECOidTag hashPolicyTag;
CK_RSA_PKCS_PSS_PARAMS rsaPSSParams;
switch (digestAlgorithm) {
case DigestAlgorithm::sha512:
mechanism = CKM_SHA512_RSA_PKCS_PSS;
hashPolicyTag = SEC_OID_SHA512;
rsaPSSParams.hashAlg = CKM_SHA512;
rsaPSSParams.mgf = CKG_MGF1_SHA512;
rsaPSSParams.sLen = 64;
break;
case DigestAlgorithm::sha384:
mechanism = CKM_SHA384_RSA_PKCS_PSS;
hashPolicyTag = SEC_OID_SHA384;
rsaPSSParams.hashAlg = CKM_SHA384;
rsaPSSParams.mgf = CKG_MGF1_SHA384;
rsaPSSParams.sLen = 48;
break;
case DigestAlgorithm::sha256:
mechanism = CKM_SHA256_RSA_PKCS_PSS;
hashPolicyTag = SEC_OID_SHA256;
rsaPSSParams.hashAlg = CKM_SHA256;
rsaPSSParams.mgf = CKG_MGF1_SHA256;
rsaPSSParams.sLen = 32;
break;
case DigestAlgorithm::sha1:
return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
break;
MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
}
SECItem params;
params.data = reinterpret_cast<unsigned char*>(&rsaPSSParams);
params.len = sizeof(CK_RSA_PKCS_PSS_PARAMS);
SECOidTag policyTags[2] = { signaturePolicyTag, hashPolicyTag };
return VerifySignedData(publicKey.get(), mechanism, &params, &signatureItem,
&dataItem, policyTags, pkcs11PinArg);
}
Result
EncodedECDSASignatureToRawPoint(Input signature,
const ScopedSECKEYPublicKey& publicKey, ScopedSECItem& result) {
Input r;
Input s;
Result rv = der::ECDSASigValue(signature, r, s);
if (rv != Success) {
return Result::ERROR_BAD_SIGNATURE;
}
size_t signatureLength = SECKEY_SignatureLen(publicKey.get());
if (signatureLength == 0) {
return MapPRErrorCodeToResult(PR_GetError());
}
if (signatureLength % 2 != 0) {
return Result::FATAL_ERROR_LIBRARY_FAILURE;
}
size_t coordinateLength = signatureLength / 2;
if (r.GetLength() > coordinateLength || s.GetLength() > coordinateLength) {
return Result::ERROR_BAD_SIGNATURE;
}
ScopedSECItem signatureItem(
SECITEM_AllocItem(nullptr, nullptr, signatureLength));
if (!signatureItem) {
return Result::FATAL_ERROR_NO_MEMORY;
}
memset(signatureItem->data, 0, signatureLength);
memcpy(signatureItem->data + (coordinateLength - r.GetLength()),
r.UnsafeGetData(), r.GetLength());
memcpy(signatureItem->data + (2 * coordinateLength - s.GetLength()),
s.UnsafeGetData(), s.GetLength());
result.swap(signatureItem);
return Success;
}
Result
VerifyECDSASignedDataNSS(Input data, DigestAlgorithm digestAlgorithm,
Input signature, Input subjectPublicKeyInfo, void* pkcs11PinArg)
{
ScopedSECKEYPublicKey publicKey;
Result rv = SubjectPublicKeyInfoToSECKEYPublicKey(subjectPublicKeyInfo,
publicKey);
if (rv != Success) {
return rv;
}
ScopedSECItem signatureItem;
rv = EncodedECDSASignatureToRawPoint(signature, publicKey, signatureItem);
if (rv != Success) {
return rv;
}
SECItem dataItem(UnsafeMapInputToSECItem(data));
CK_MECHANISM_TYPE mechanism;
SECOidTag signaturePolicyTag = SEC_OID_ANSIX962_EC_PUBLIC_KEY;
SECOidTag hashPolicyTag;
SECOidTag combinedPolicyTag;
switch (digestAlgorithm) {
case DigestAlgorithm::sha512:
mechanism = CKM_ECDSA_SHA512;
hashPolicyTag = SEC_OID_SHA512;
combinedPolicyTag = SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE;
break;
case DigestAlgorithm::sha384:
mechanism = CKM_ECDSA_SHA384;
hashPolicyTag = SEC_OID_SHA384;
combinedPolicyTag = SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE;
break;
case DigestAlgorithm::sha256:
mechanism = CKM_ECDSA_SHA256;
hashPolicyTag = SEC_OID_SHA256;
combinedPolicyTag = SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE;
break;
case DigestAlgorithm::sha1:
mechanism = CKM_ECDSA_SHA1;
hashPolicyTag = SEC_OID_SHA1;
combinedPolicyTag = SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE;
break;
MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
}
SECOidTag policyTags[3] =
{ signaturePolicyTag, hashPolicyTag, combinedPolicyTag };
return VerifySignedData(publicKey.get(), mechanism, nullptr,
signatureItem.get(), &dataItem, policyTags, pkcs11PinArg);
}
Result

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This code is made available to you under your choice of the following sets
* of licensing terms:
*/

View file

@ -1,4 +1,5 @@
/*- *- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This code is made available to you under your choice of the following sets
* of licensing terms:
*/

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This code is made available to you under your choice of the following sets
* of licensing terms:
*/
@ -74,4 +75,18 @@ TimeFromEpochInSeconds(uint64_t secondsSinceEpoch)
return TimeFromElapsedSecondsAD(seconds);
}
Result
SecondsSinceEpochFromTime(Time time, uint64_t* outSeconds)
{
if (!outSeconds) {
return Result::FATAL_ERROR_INVALID_ARGS;
}
Time epoch = TimeFromEpochInSeconds(0);
if (time < epoch) {
return Result::FATAL_ERROR_INVALID_ARGS;
}
*outSeconds = Duration(time, epoch).durationInSeconds;
return Result::Success;
}
} } // namespace mozilla::pkix

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This code is made available to you under your choice of the following sets
* of licensing terms:
*/
@ -26,15 +27,15 @@
namespace mozilla { namespace pkix {
Result
DigestSignedData(TrustDomain& trustDomain,
VerifySignedData(TrustDomain& trustDomain,
const der::SignedDataWithSignature& signedData,
/*out*/ uint8_t(&digestBuf)[MAX_DIGEST_SIZE_IN_BYTES],
/*out*/ der::PublicKeyAlgorithm& publicKeyAlg,
/*out*/ SignedDigest& signedDigest)
Input signerSubjectPublicKeyInfo)
{
der::PublicKeyAlgorithm publicKeyAlg;
DigestAlgorithm digestAlgorithm;
Reader signatureAlg(signedData.algorithm);
Result rv = der::SignatureAlgorithmIdentifierValue(
signatureAlg, publicKeyAlg, signedDigest.digestAlgorithm);
signatureAlg, publicKeyAlg, digestAlgorithm);
if (rv != Success) {
return rv;
}
@ -42,57 +43,18 @@ DigestSignedData(TrustDomain& trustDomain,
return Result::ERROR_BAD_DER;
}
size_t digestLen = DigestAlgorithmToSizeInBytes(signedDigest.digestAlgorithm);
assert(digestLen <= sizeof(digestBuf));
rv = trustDomain.DigestBuf(signedData.data, signedDigest.digestAlgorithm,
digestBuf, digestLen);
if (rv != Success) {
return rv;
}
rv = signedDigest.digest.Init(digestBuf, digestLen);
if (rv != Success) {
return rv;
}
return signedDigest.signature.Init(signedData.signature);
}
Result
VerifySignedDigest(TrustDomain& trustDomain,
der::PublicKeyAlgorithm publicKeyAlg,
const SignedDigest& signedDigest,
Input signerSubjectPublicKeyInfo)
{
switch (publicKeyAlg) {
case der::PublicKeyAlgorithm::ECDSA:
return trustDomain.VerifyECDSASignedDigest(signedDigest,
signerSubjectPublicKeyInfo);
return trustDomain.VerifyECDSASignedData(signedData.data,
digestAlgorithm, signedData.signature, signerSubjectPublicKeyInfo);
case der::PublicKeyAlgorithm::RSA_PKCS1:
return trustDomain.VerifyRSAPKCS1SignedDigest(signedDigest,
signerSubjectPublicKeyInfo);
case der::PublicKeyAlgorithm::Uninitialized:
assert(false);
return Result::FATAL_ERROR_LIBRARY_FAILURE;
return trustDomain.VerifyRSAPKCS1SignedData(signedData.data,
digestAlgorithm, signedData.signature, signerSubjectPublicKeyInfo);
case der::PublicKeyAlgorithm::RSA_PSS:
return trustDomain.VerifyRSAPSSSignedData(signedData.data,
digestAlgorithm, signedData.signature, signerSubjectPublicKeyInfo);
MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
}
}
Result
VerifySignedData(TrustDomain& trustDomain,
const der::SignedDataWithSignature& signedData,
Input signerSubjectPublicKeyInfo)
{
uint8_t digestBuf[MAX_DIGEST_SIZE_IN_BYTES];
der::PublicKeyAlgorithm publicKeyAlg;
SignedDigest signedDigest;
Result rv = DigestSignedData(trustDomain, signedData, digestBuf,
publicKeyAlg, signedDigest);
if (rv != Success) {
return rv;
}
return VerifySignedDigest(trustDomain, publicKeyAlg, signedDigest,
signerSubjectPublicKeyInfo);
}
} } // namespace mozilla::pkix

View file

@ -12,6 +12,7 @@
'standalone_static_library': 1,
'sources': [
'lib/pkixbuild.cpp',
'lib/pkixc.cpp',
'lib/pkixcert.cpp',
'lib/pkixcheck.cpp',
'lib/pkixder.cpp',

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This code is made available to you under your choice of the following sets
* of licensing terms:
*/

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This code is made available to you under your choice of the following sets
* of licensing terms:
*/
@ -334,21 +335,30 @@ GenerateDSSKeyPair()
}
Result
TestVerifyECDSASignedDigest(const SignedDigest& signedDigest,
Input subjectPublicKeyInfo)
TestVerifyECDSASignedData(Input data, DigestAlgorithm digestAlgorithm,
Input signature, Input subjectPublicKeyInfo)
{
InitNSSIfNeeded();
return VerifyECDSASignedDigestNSS(signedDigest, subjectPublicKeyInfo,
nullptr);
return VerifyECDSASignedDataNSS(data, digestAlgorithm, signature,
subjectPublicKeyInfo, nullptr);
}
Result
TestVerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest,
Input subjectPublicKeyInfo)
TestVerifyRSAPKCS1SignedData(Input data, DigestAlgorithm digestAlgorithm,
Input signature, Input subjectPublicKeyInfo)
{
InitNSSIfNeeded();
return VerifyRSAPKCS1SignedDigestNSS(signedDigest, subjectPublicKeyInfo,
nullptr);
return VerifyRSAPKCS1SignedDataNSS(data, digestAlgorithm, signature,
subjectPublicKeyInfo, nullptr);
}
Result
TestVerifyRSAPSSSignedData(Input data, DigestAlgorithm digestAlgorithm,
Input signature, Input subjectPublicKeyInfo)
{
InitNSSIfNeeded();
return VerifyRSAPSSSignedDataNSS(data, digestAlgorithm, signature,
subjectPublicKeyInfo, nullptr);
}
Result

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This code is made available to you under your choice of the following sets
* of licensing terms:
*/

View file

@ -43,11 +43,11 @@ def base128(value):
# least significant byte has highest bit unset
result = [value % 0x80]
value /= 0x80
value = value // 0x80
while value != 0:
result = [0x80 | (value % 0x80)] + result
value /= 0x80
value = value // 0x80
return result