author | Brian Smith <brian@briansmith.org> |
Fri, 18 Jul 2014 11:48:49 -0700 | |
changeset 220100 | 5f7dc391e8611d1f12f77d55f2c5a56ef8f6f29e |
parent 219966 | 1f216e2e339e2ec5db3c9df769de0bb1a8037c04 |
child 220101 | 68fe3e5c7181331a5bf13e865cb3c559bfe81400 |
push id | 583 |
push user | bhearsum@mozilla.com |
push date | Mon, 24 Nov 2014 19:04:58 +0000 |
treeherder | mozilla-release@c107e74250f4 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | keeler |
bugs | 1039064 |
milestone | 34.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/netwerk/base/public/nsINSSErrorsService.idl +++ b/netwerk/base/public/nsINSSErrorsService.idl @@ -1,17 +1,17 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * 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 "nsISupports.idl" -[scriptable, uuid(dc8013f2-4aed-47a8-bc4e-9fd7a6cc60fa)] +[scriptable, uuid(12f60021-e14b-4020-99d1-ed2c795be66a)] interface nsINSSErrorsService : nsISupports { /** * @param aNSPRCode An error code obtained using PR_GetError() * @return True if it is error code defined by the NSS library */ boolean isNSSErrorCode(in int32_t aNSPRCode); @@ -49,17 +49,20 @@ interface nsINSSErrorsService : nsISuppo const long NSS_SEC_ERROR_BASE = -(0x2000); const long NSS_SEC_ERROR_LIMIT = (NSS_SEC_ERROR_BASE + 1000); const long NSS_SSL_ERROR_BASE = -(0x3000); const long NSS_SSL_ERROR_LIMIT = (NSS_SSL_ERROR_BASE + 1000); /** * The error codes within each module must fit in 16 bits. We want these * errors to fit in the same module as the NSS errors but not overlap with - * any of them. Converting an NSS SEC, NSS SSL, or PSM error to an NS error - * involves negating the value of the error and then synthesizing an error - * in the NS_ERROR_MODULE_SECURITY module. Hence, PSM errors will start at - * a negative value that both doesn't overlap with the current value - * ranges for NSS errors and that will fit in 16 bits when negated. + * any of them. Converting an NSS SEC, NSS SSL, or mozilla::pkix error to + * an NS error involves negating the value of the error and then + * synthesizing an error in the NS_ERROR_MODULE_SECURITY module. Hence, + * mozilla::pkix errors will start at a negative value that both doesn't + * overlap with the current value ranges for NSS errors and that will fit + * in 16 bits when negated. + * + * Keep these in sync with pkixnss.h. */ - const long PSM_ERROR_BASE = -(0x4000); - const long PSM_ERROR_LIMIT = (PSM_ERROR_BASE + 1000); + const long MOZILLA_PKIX_ERROR_BASE = -(0x4000); + const long MOZILLA_PKIX_ERROR_LIMIT = (MOZILLA_PKIX_ERROR_BASE + 1000); };
--- a/security/apps/AppSignatureVerification.cpp +++ b/security/apps/AppSignatureVerification.cpp @@ -6,16 +6,17 @@ #ifdef MOZ_LOGGING #define FORCE_PR_LOG 1 #endif #include "nsNSSCertificateDB.h" #include "pkix/pkix.h" +#include "pkix/pkixnss.h" #include "mozilla/RefPtr.h" #include "CryptoTask.h" #include "AppTrustDomain.h" #include "nsComponentManagerUtils.h" #include "nsCOMPtr.h" #include "nsDataSignatureVerifier.h" #include "nsHashKeys.h" #include "nsIFile.h" @@ -536,23 +537,24 @@ VerifyCertificate(CERTCertificate* signe } const VerifyCertificateContext& context = *reinterpret_cast<const VerifyCertificateContext*>(voidContext); AppTrustDomain trustDomain(context.builtChain, pinArg); if (trustDomain.SetTrustedRoot(context.trustedRoot) != SECSuccess) { return MapSECStatus(SECFailure); } - if (BuildCertChain(trustDomain, signerCert->derCert, PR_Now(), - EndEntityOrCA::MustBeEndEntity, - KeyUsage::digitalSignature, - KeyPurposeId::id_kp_codeSigning, - CertPolicyId::anyPolicy, - nullptr/*stapledOCSPResponse*/) != SECSuccess) { - return MapSECStatus(SECFailure); + Result rv = BuildCertChain(trustDomain, signerCert->derCert, PR_Now(), + EndEntityOrCA::MustBeEndEntity, + KeyUsage::digitalSignature, + KeyPurposeId::id_kp_codeSigning, + CertPolicyId::anyPolicy, + nullptr/*stapledOCSPResponse*/); + if (rv != Success) { + return mozilla::psm::GetXPCOMFromNSSError(MapResultToPRErrorCode(rv)); } return NS_OK; } nsresult VerifySignature(AppTrustedRoot trustedRoot, const SECItem& buffer, const SECItem& detachedDigest,
--- a/security/apps/AppTrustDomain.cpp +++ b/security/apps/AppTrustDomain.cpp @@ -5,17 +5,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifdef MOZ_LOGGING #define FORCE_PR_LOG 1 #endif #include "AppTrustDomain.h" #include "certdb.h" -#include "pkix/pkix.h" +#include "pkix/pkixnss.h" #include "mozilla/ArrayUtils.h" #include "nsIX509CertDB.h" #include "nsNSSCertificate.h" #include "prerror.h" #include "secerr.h" // Generated in Makefile.in #include "marketplace-prod-public.inc" @@ -88,25 +88,24 @@ AppTrustDomain::SetTrustedRoot(AppTruste &trustedDER, nullptr, false, true); if (!mTrustedRoot) { return SECFailure; } return SECSuccess; } -SECStatus +Result AppTrustDomain::FindIssuer(const SECItem& encodedIssuerName, IssuerChecker& checker, PRTime time) { MOZ_ASSERT(mTrustedRoot); if (!mTrustedRoot) { - PR_SetError(PR_INVALID_STATE_ERROR, 0); - return SECFailure; + return Result::FATAL_ERROR_INVALID_STATE; } // TODO(bug 1035418): If/when mozilla::pkix relaxes the restriction that // FindIssuer must only pass certificates with a matching subject name to // checker.Check, we can stop using CERT_CreateSubjectCertList and instead // use logic like this: // // 1. First, try the trusted trust anchor. @@ -114,60 +113,58 @@ AppTrustDomain::FindIssuer(const SECItem // message, passing each one to checker.Check. ScopedCERTCertList candidates(CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(), &encodedIssuerName, time, true)); if (candidates) { for (CERTCertListNode* n = CERT_LIST_HEAD(candidates); !CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) { bool keepGoing; - SECStatus srv = checker.Check(n->cert->derCert, - nullptr/*additionalNameConstraints*/, - keepGoing); - if (srv != SECSuccess) { - return SECFailure; + Result rv = checker.Check(n->cert->derCert, + nullptr/*additionalNameConstraints*/, + keepGoing); + if (rv != Success) { + return rv; } if (!keepGoing) { break; } } } - return SECSuccess; + return Success; } -SECStatus +Result AppTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA, const CertPolicyId& policy, const SECItem& candidateCertDER, /*out*/ TrustLevel* trustLevel) { MOZ_ASSERT(policy.IsAnyPolicy()); MOZ_ASSERT(trustLevel); MOZ_ASSERT(mTrustedRoot); if (!trustLevel || !policy.IsAnyPolicy()) { - PR_SetError(SEC_ERROR_INVALID_ARGS, 0); - return SECFailure; + return Result::FATAL_ERROR_INVALID_ARGS; } if (!mTrustedRoot) { - PR_SetError(PR_INVALID_STATE_ERROR, 0); - return SECFailure; + return Result::FATAL_ERROR_INVALID_STATE; } // Handle active distrust of the certificate. // XXX: This would be cleaner and more efficient if we could get the trust // information without constructing a CERTCertificate here, but NSS doesn't // expose it in any other easy-to-use fashion. ScopedCERTCertificate candidateCert( CERT_NewTempCertificate(CERT_GetDefaultCertDB(), const_cast<SECItem*>(&candidateCertDER), nullptr, false, true)); if (!candidateCert) { - return SECFailure; + return MapPRErrorCodeToResult(PR_GetError()); } CERTCertTrust trust; if (CERT_GetCertTrust(candidateCert.get(), &trust) == SECSuccess) { PRUint32 flags = SEC_GET_TRUST_FLAGS(&trust, trustObjectSigning); // For DISTRUST, we use the CERTDB_TRUSTED or CERTDB_TRUSTED_CA bit, // because we can have active distrust for either type of cert. Note that @@ -175,60 +172,65 @@ AppTrustDomain::GetCertTrust(EndEntityOr // relevant trust bit isn't set then that means the cert must be considered // distrusted. PRUint32 relevantTrustBit = endEntityOrCA == EndEntityOrCA::MustBeCA ? CERTDB_TRUSTED_CA : CERTDB_TRUSTED; if (((flags & (relevantTrustBit | CERTDB_TERMINAL_RECORD))) == CERTDB_TERMINAL_RECORD) { *trustLevel = TrustLevel::ActivelyDistrusted; - return SECSuccess; + return Success; } } // mTrustedRoot is the only trust anchor for this validation. if (CERT_CompareCerts(mTrustedRoot.get(), candidateCert.get())) { *trustLevel = TrustLevel::TrustAnchor; - return SECSuccess; + return Success; } *trustLevel = TrustLevel::InheritsTrust; - return SECSuccess; + return Success; } -SECStatus +Result AppTrustDomain::VerifySignedData(const SignedDataWithSignature& signedData, const SECItem& subjectPublicKeyInfo) { return ::mozilla::pkix::VerifySignedData(signedData, subjectPublicKeyInfo, mPinArg); } -SECStatus +Result AppTrustDomain::DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf, size_t digestBufLen) { return ::mozilla::pkix::DigestBuf(item, digestBuf, digestBufLen); } -SECStatus +Result AppTrustDomain::CheckRevocation(EndEntityOrCA, const CertID&, PRTime time, /*optional*/ const SECItem*, /*optional*/ const SECItem*) { // We don't currently do revocation checking. If we need to distrust an Apps // certificate, we will use the active distrust mechanism. - return SECSuccess; + return Success; } -SECStatus +Result AppTrustDomain::IsChainValid(const DERArray& certChain) { - return ConstructCERTCertListFromReversedDERArray(certChain, mCertChain); + SECStatus srv = ConstructCERTCertListFromReversedDERArray(certChain, + mCertChain); + if (srv != SECSuccess) { + return MapPRErrorCodeToResult(PR_GetError()); + } + return Success; } -SECStatus +Result AppTrustDomain::CheckPublicKey(const SECItem& subjectPublicKeyInfo) { return ::mozilla::pkix::CheckPublicKey(subjectPublicKeyInfo); } } } // namespace mozilla::psm
--- a/security/apps/AppTrustDomain.h +++ b/security/apps/AppTrustDomain.h @@ -12,38 +12,43 @@ #include "nsIX509CertDB.h" #include "ScopedNSSTypes.h" namespace mozilla { namespace psm { class AppTrustDomain MOZ_FINAL : public mozilla::pkix::TrustDomain { public: + typedef mozilla::pkix::Result Result; + AppTrustDomain(ScopedCERTCertList&, void* pinArg); SECStatus SetTrustedRoot(AppTrustedRoot trustedRoot); - SECStatus GetCertTrust(mozilla::pkix::EndEntityOrCA endEntityOrCA, - const mozilla::pkix::CertPolicyId& policy, - const SECItem& candidateCertDER, - /*out*/ mozilla::pkix::TrustLevel* trustLevel) MOZ_OVERRIDE; - SECStatus FindIssuer(const SECItem& encodedIssuerName, - IssuerChecker& checker, PRTime time) MOZ_OVERRIDE; - SECStatus CheckRevocation(mozilla::pkix::EndEntityOrCA endEntityOrCA, - const mozilla::pkix::CertID& certID, PRTime time, - /*optional*/ const SECItem* stapledOCSPresponse, - /*optional*/ const SECItem* aiaExtension); - SECStatus IsChainValid(const mozilla::pkix::DERArray& certChain); - - SECStatus VerifySignedData( - const mozilla::pkix::SignedDataWithSignature& signedData, - const SECItem& subjectPublicKeyInfo) MOZ_OVERRIDE; - SECStatus DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf, - size_t digestBufLen) MOZ_OVERRIDE; - SECStatus CheckPublicKey(const SECItem& subjectPublicKeyInfo) MOZ_OVERRIDE; + virtual Result GetCertTrust(mozilla::pkix::EndEntityOrCA endEntityOrCA, + const mozilla::pkix::CertPolicyId& policy, + const SECItem& candidateCertDER, + /*out*/ mozilla::pkix::TrustLevel* trustLevel) + MOZ_OVERRIDE; + virtual Result FindIssuer(const SECItem& encodedIssuerName, + IssuerChecker& checker, + PRTime time) MOZ_OVERRIDE; + virtual Result CheckRevocation(mozilla::pkix::EndEntityOrCA endEntityOrCA, + const mozilla::pkix::CertID& certID, PRTime time, + /*optional*/ const SECItem* stapledOCSPresponse, + /*optional*/ const SECItem* aiaExtension); + virtual Result IsChainValid(const mozilla::pkix::DERArray& certChain) + MOZ_OVERRIDE; + virtual Result CheckPublicKey(const SECItem& subjectPublicKeyInfo) + MOZ_OVERRIDE; + virtual Result VerifySignedData( + const mozilla::pkix::SignedDataWithSignature& signedData, + const SECItem& subjectPublicKeyInfo) MOZ_OVERRIDE; + virtual Result DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf, + size_t digestBufLen) MOZ_OVERRIDE; private: /*out*/ ScopedCERTCertList& mCertChain; void* mPinArg; // non-owning! ScopedCERTCertificate mTrustedRoot; }; } } // namespace mozilla::psm
--- a/security/certverifier/CertVerifier.cpp +++ b/security/certverifier/CertVerifier.cpp @@ -10,16 +10,17 @@ #include "ExtendedValidation.h" #include "NSSCertDBTrustDomain.h" #include "NSSErrorsService.h" #include "PublicKeyPinningService.h" #include "cert.h" #include "pk11pub.h" #include "pkix/pkix.h" +#include "pkix/pkixnss.h" #include "prerror.h" #include "secerr.h" #include "sslerr.h" using namespace mozilla::pkix; using namespace mozilla::psm; #ifdef PR_LOGGING @@ -149,36 +150,36 @@ SECStatus chainValidationCallback(void* CertVerifier::pinningEnforceTestMode); *chainOK = PublicKeyPinningService:: ChainHasValidPins(certList, callbackState->hostname, callbackState->time, enforceTestMode); return SECSuccess; } -static SECStatus +static Result BuildCertChainForOneKeyUsage(TrustDomain& trustDomain, CERTCertificate* cert, PRTime time, KeyUsage ku1, KeyUsage ku2, KeyUsage ku3, KeyPurposeId eku, const CertPolicyId& requiredPolicy, const SECItem* stapledOCSPResponse) { - SECStatus rv = BuildCertChain(trustDomain, cert->derCert, time, - EndEntityOrCA::MustBeEndEntity, ku1, - eku, requiredPolicy, stapledOCSPResponse); - if (rv != SECSuccess && PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) { + Result rv = BuildCertChain(trustDomain, cert->derCert, time, + EndEntityOrCA::MustBeEndEntity, ku1, + eku, requiredPolicy, stapledOCSPResponse); + if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) { rv = BuildCertChain(trustDomain, cert->derCert, time, EndEntityOrCA::MustBeEndEntity, ku2, eku, requiredPolicy, stapledOCSPResponse); - if (rv != SECSuccess && PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) { + if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) { rv = BuildCertChain(trustDomain, cert->derCert, time, EndEntityOrCA::MustBeEndEntity, ku3, eku, requiredPolicy, stapledOCSPResponse); - if (rv != SECSuccess) { - PR_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE, 0); + if (rv != Success) { + rv = Result::ERROR_INADEQUATE_KEY_USAGE; } } } return rv; } SECStatus CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage, @@ -217,17 +218,17 @@ CertVerifier::VerifyCert(CERTCertificate = !mOCSPDownloadEnabled || (flags & FLAG_LOCAL_ONLY) ? NSSCertDBTrustDomain::NeverFetchOCSP : !mOCSPStrict ? NSSCertDBTrustDomain::FetchOCSPForDVSoftFail : NSSCertDBTrustDomain::FetchOCSPForDVHardFail; ocsp_get_config ocspGETConfig = mOCSPGETEnabled ? ocsp_get_enabled : ocsp_get_disabled; - SECStatus rv; + Result rv; switch (usage) { case certificateUsageSSLClient: { // XXX: We don't really have a trust bit for SSL client authentication so // just use trustEmail as it is the closest alternative. NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache, pinArg, ocspGETConfig, nullptr, builtChain); @@ -243,43 +244,42 @@ CertVerifier::VerifyCert(CERTCertificate // TODO: When verifying a certificate in an SSL handshake, we should // restrict the acceptable key usage based on the key exchange method // chosen by the server. #ifndef MOZ_NO_EV_CERTS // Try to validate for EV first. CertPolicyId evPolicy; SECOidTag evPolicyOidTag; - rv = GetFirstEVPolicy(cert, evPolicy, evPolicyOidTag); - if (rv == SECSuccess) { + SECStatus srv = GetFirstEVPolicy(cert, evPolicy, evPolicyOidTag); + if (srv == SECSuccess) { NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching == NSSCertDBTrustDomain::NeverFetchOCSP ? NSSCertDBTrustDomain::LocalOnlyOCSPForEV : NSSCertDBTrustDomain::FetchOCSPForEV, mOCSPCache, pinArg, ocspGETConfig, &callbackContainer, builtChain); rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time, KeyUsage::digitalSignature,// (EC)DHE KeyUsage::keyEncipherment, // RSA KeyUsage::keyAgreement, // (EC)DH KeyPurposeId::id_kp_serverAuth, evPolicy, stapledOCSPResponse); - if (rv == SECSuccess) { + if (rv == Success) { if (evOidPolicy) { *evOidPolicy = evPolicyOidTag; } break; } } #endif if (flags & FLAG_MUST_BE_EV) { - PR_SetError(SEC_ERROR_POLICY_VALIDATION_FAILED, 0); - rv = SECFailure; + rv = Result::ERROR_POLICY_VALIDATION_FAILED; break; } // Now try non-EV. NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache, pinArg, ocspGETConfig, &callbackContainer, builtChain); rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time, @@ -322,17 +322,17 @@ CertVerifier::VerifyCert(CERTCertificate NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache, pinArg, ocspGETConfig, nullptr, builtChain); rv = BuildCertChain(trustDomain, cert->derCert, time, EndEntityOrCA::MustBeEndEntity, KeyUsage::keyEncipherment, // RSA KeyPurposeId::id_kp_emailProtection, CertPolicyId::anyPolicy, stapledOCSPResponse); - if (rv != SECSuccess && PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) { + if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) { rv = BuildCertChain(trustDomain, cert->derCert, time, EndEntityOrCA::MustBeEndEntity, KeyUsage::keyAgreement, // ECDH/DH KeyPurposeId::id_kp_emailProtection, CertPolicyId::anyPolicy, stapledOCSPResponse); } break; } @@ -368,43 +368,47 @@ CertVerifier::VerifyCert(CERTCertificate eku = KeyPurposeId::id_kp_OCSPSigning; } NSSCertDBTrustDomain sslTrust(trustSSL, ocspFetching, mOCSPCache, pinArg, ocspGETConfig, nullptr, builtChain); rv = BuildCertChain(sslTrust, cert->derCert, time, endEntityOrCA, keyUsage, eku, CertPolicyId::anyPolicy, stapledOCSPResponse); - if (rv == SECFailure && PR_GetError() == SEC_ERROR_UNKNOWN_ISSUER) { + if (rv == Result::ERROR_UNKNOWN_ISSUER) { NSSCertDBTrustDomain emailTrust(trustEmail, ocspFetching, mOCSPCache, pinArg, ocspGETConfig, nullptr, builtChain); rv = BuildCertChain(emailTrust, cert->derCert, time, endEntityOrCA, keyUsage, eku, CertPolicyId::anyPolicy, stapledOCSPResponse); - if (rv == SECFailure && PR_GetError() == SEC_ERROR_UNKNOWN_ISSUER) { + if (rv == Result::ERROR_UNKNOWN_ISSUER) { NSSCertDBTrustDomain objectSigningTrust(trustObjectSigning, ocspFetching, mOCSPCache, pinArg, ocspGETConfig, nullptr, builtChain); rv = BuildCertChain(objectSigningTrust, cert->derCert, time, endEntityOrCA, keyUsage, eku, CertPolicyId::anyPolicy, stapledOCSPResponse); } } break; } default: - PR_SetError(SEC_ERROR_INVALID_ARGS, 0); - return SECFailure; + rv = Result::FATAL_ERROR_INVALID_ARGS; } - return rv; + if (rv != Success) { + PR_SetError(MapResultToPRErrorCode(rv), 0); + return SECFailure; + } + + return SECSuccess; } SECStatus CertVerifier::VerifySSLServerCert(CERTCertificate* peerCert, /*optional*/ const SECItem* stapledOCSPResponse, PRTime time, /*optional*/ void* pinarg, const char* hostname,
--- a/security/certverifier/NSSCertDBTrustDomain.cpp +++ b/security/certverifier/NSSCertDBTrustDomain.cpp @@ -12,16 +12,17 @@ #include "nsNSSCertificate.h" #include "NSSErrorsService.h" #include "OCSPRequestor.h" #include "certdb.h" #include "mozilla/Telemetry.h" #include "nss.h" #include "pk11pub.h" #include "pkix/pkix.h" +#include "pkix/pkixnss.h" #include "pkix/ScopedPtr.h" #include "prerror.h" #include "prmem.h" #include "prprf.h" #include "ScopedNSSTypes.h" #include "secerr.h" #include "secmod.h" @@ -101,17 +102,17 @@ static const uint8_t PERMIT_FRANCE_GOV_N "\x30\x05\x82\x03" ".tf"; static const SECItem PERMIT_FRANCE_GOV_NAME_CONSTRAINTS = { siBuffer, const_cast<uint8_t*>(PERMIT_FRANCE_GOV_NAME_CONSTRAINTS_DATA), sizeof(PERMIT_FRANCE_GOV_NAME_CONSTRAINTS_DATA) - 1 }; -SECStatus +Result NSSCertDBTrustDomain::FindIssuer(const SECItem& encodedIssuerName, IssuerChecker& checker, PRTime time) { // TODO: NSS seems to be ambiguous between "no potential issuers found" and // "there was an error trying to retrieve the potential issuers." ScopedCERTCertList candidates(CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(), &encodedIssuerName, time, true)); @@ -119,62 +120,59 @@ NSSCertDBTrustDomain::FindIssuer(const S for (CERTCertListNode* n = CERT_LIST_HEAD(candidates); !CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) { const SECItem* additionalNameConstraints = nullptr; // TODO: Use CERT_CompareName or equivalent if (SECITEM_ItemsAreEqual(&encodedIssuerName, &ANSSI_SUBJECT)) { additionalNameConstraints = &PERMIT_FRANCE_GOV_NAME_CONSTRAINTS; } bool keepGoing; - SECStatus srv = checker.Check(n->cert->derCert, - additionalNameConstraints, keepGoing); - if (srv != SECSuccess) { - return SECFailure; + Result rv = checker.Check(n->cert->derCert, + additionalNameConstraints, keepGoing); + if (rv != Success) { + return rv; } if (!keepGoing) { break; } } } - return SECSuccess; + return Success; } -SECStatus +Result NSSCertDBTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA, const CertPolicyId& policy, const SECItem& candidateCertDER, /*out*/ TrustLevel* trustLevel) { PR_ASSERT(trustLevel); - if (!trustLevel) { - PR_SetError(SEC_ERROR_INVALID_ARGS, 0); - return SECFailure; + return Result::FATAL_ERROR_INVALID_ARGS; } #ifdef MOZ_NO_EV_CERTS if (!policy.IsAnyPolicy()) { - PR_SetError(SEC_ERROR_POLICY_VALIDATION_FAILED, 0); - return SECFailure; + return Result::ERROR_POLICY_VALIDATION_FAILED; } #endif // XXX: This would be cleaner and more efficient if we could get the trust // information without constructing a CERTCertificate here, but NSS doesn't // expose it in any other easy-to-use fashion. The use of // CERT_NewTempCertificate to get a CERTCertificate shouldn't be a // performance problem because NSS will just find the existing // CERTCertificate in its in-memory cache and return it. ScopedCERTCertificate candidateCert( CERT_NewTempCertificate(CERT_GetDefaultCertDB(), const_cast<SECItem*>(&candidateCertDER), nullptr, false, true)); if (!candidateCert) { - return SECFailure; + return MapPRErrorCodeToResult(PR_GetError()); } // XXX: CERT_GetCertTrust seems to be abusing SECStatus as a boolean, where // SECSuccess means that there is a trust record and SECFailure means there // is not a trust record. I looked at NSS's internal uses of // CERT_GetCertTrust, and all that code uses the result as a boolean meaning // "We have a trust record." CERTCertTrust trust; @@ -187,49 +185,49 @@ NSSCertDBTrustDomain::GetCertTrust(EndEn // relevant trust bit isn't set then that means the cert must be considered // distrusted. PRUint32 relevantTrustBit = endEntityOrCA == EndEntityOrCA::MustBeCA ? CERTDB_TRUSTED_CA : CERTDB_TRUSTED; if (((flags & (relevantTrustBit|CERTDB_TERMINAL_RECORD))) == CERTDB_TERMINAL_RECORD) { *trustLevel = TrustLevel::ActivelyDistrusted; - return SECSuccess; + return Success; } // For TRUST, we only use the CERTDB_TRUSTED_CA bit, because Gecko hasn't // needed to consider end-entity certs to be their own trust anchors since // Gecko implemented nsICertOverrideService. if (flags & CERTDB_TRUSTED_CA) { if (policy.IsAnyPolicy()) { *trustLevel = TrustLevel::TrustAnchor; - return SECSuccess; + return Success; } #ifndef MOZ_NO_EV_CERTS if (CertIsAuthoritativeForEVPolicy(candidateCert.get(), policy)) { *trustLevel = TrustLevel::TrustAnchor; - return SECSuccess; + return Success; } #endif } } *trustLevel = TrustLevel::InheritsTrust; - return SECSuccess; + return Success; } -SECStatus +Result NSSCertDBTrustDomain::VerifySignedData(const SignedDataWithSignature& signedData, const SECItem& subjectPublicKeyInfo) { return ::mozilla::pkix::VerifySignedData(signedData, subjectPublicKeyInfo, mPinArg); } -SECStatus +Result NSSCertDBTrustDomain::DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf, size_t digestBufLen) { return ::mozilla::pkix::DigestBuf(item, digestBuf, digestBufLen); } static PRIntervalTime @@ -252,68 +250,66 @@ OCSPFetchingTypeToTimeoutTime(NSSCertDBT return PR_SecondsToInterval(2); } // Copied and modified from CERT_GetOCSPAuthorityInfoAccessLocation and // CERT_GetGeneralNameByType. Returns SECFailure on error, SECSuccess // with url == nullptr when an OCSP URI was not found, and SECSuccess with // url != nullptr when an OCSP URI was found. The output url will be owned // by the arena. -static SECStatus +static Result GetOCSPAuthorityInfoAccessLocation(PLArenaPool* arena, const SECItem& aiaExtension, /*out*/ char const*& url) { url = nullptr; // TODO(bug 1028380): Remove the const_cast. CERTAuthInfoAccess** aia = CERT_DecodeAuthInfoAccessExtension( arena, const_cast<SECItem*>(&aiaExtension)); if (!aia) { - PR_SetError(SEC_ERROR_CERT_BAD_ACCESS_LOCATION, 0); - return SECFailure; + return Result::ERROR_CERT_BAD_ACCESS_LOCATION; } for (size_t i = 0; aia[i]; ++i) { if (SECOID_FindOIDTag(&aia[i]->method) == SEC_OID_PKIX_OCSP) { // NSS chooses the **last** OCSP URL; we choose the **first** CERTGeneralName* current = aia[i]->location; if (!current) { continue; } do { if (current->type == certURI) { const SECItem& location = current->name.other; // (location.len + 1) must be small enough to fit into a uint32_t, // but we limit it to a smaller bound to reduce OOM risk. if (location.len > 1024 || memchr(location.data, 0, location.len)) { // Reject embedded nulls. (NSS doesn't do this) - PR_SetError(SEC_ERROR_CERT_BAD_ACCESS_LOCATION, 0); - return SECFailure; + return Result::ERROR_CERT_BAD_ACCESS_LOCATION; } // Copy the non-null-terminated SECItem into a null-terminated string. char* nullTerminatedURL(static_cast<char*>( PORT_ArenaAlloc(arena, location.len + 1))); if (!nullTerminatedURL) { - return SECFailure; + return Result::FATAL_ERROR_NO_MEMORY; } memcpy(nullTerminatedURL, location.data, location.len); nullTerminatedURL[location.len] = 0; url = nullTerminatedURL; - return SECSuccess; + return Success; } current = CERT_GetNextGeneralName(current); } while (current != aia[i]->location); } } - return SECSuccess; + return Success; } -SECStatus +Result NSSCertDBTrustDomain::CheckRevocation(EndEntityOrCA endEntityOrCA, const CertID& certID, PRTime time, /*optional*/ const SECItem* stapledOCSPResponse, /*optional*/ const SECItem* aiaExtension) { // Actively distrusted certificates will have already been blocked by // GetCertTrust. @@ -333,363 +329,345 @@ NSSCertDBTrustDomain::CheckRevocation(En } // If we have a stapled OCSP response then the verification of that response // determines the result unless the OCSP response is expired. We make an // exception for expired responses because some servers, nginx in particular, // are known to serve expired responses due to bugs. // We keep track of the result of verifying the stapled response but don't // immediately return failure if the response has expired. - PRErrorCode stapledOCSPResponseErrorCode = 0; + Result stapledOCSPResponseResult = Success; if (stapledOCSPResponse) { PR_ASSERT(endEntityOrCA == EndEntityOrCA::MustBeEndEntity); bool expired; - SECStatus rv = VerifyAndMaybeCacheEncodedOCSPResponse(certID, time, - maxOCSPLifetimeInDays, - *stapledOCSPResponse, - ResponseWasStapled, - expired); - if (rv == SECSuccess) { + stapledOCSPResponseResult = + VerifyAndMaybeCacheEncodedOCSPResponse(certID, time, + maxOCSPLifetimeInDays, + *stapledOCSPResponse, + ResponseWasStapled, expired); + if (stapledOCSPResponseResult == Success) { // stapled OCSP response present and good Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 1); PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("NSSCertDBTrustDomain: stapled OCSP response: good")); - return rv; + return Success; } - stapledOCSPResponseErrorCode = PR_GetError(); - if (stapledOCSPResponseErrorCode == SEC_ERROR_OCSP_OLD_RESPONSE || + if (stapledOCSPResponseResult == Result::ERROR_OCSP_OLD_RESPONSE || expired) { // stapled OCSP response present but expired Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 3); PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("NSSCertDBTrustDomain: expired stapled OCSP response")); } else { // stapled OCSP response present but invalid for some reason Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 4); PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("NSSCertDBTrustDomain: stapled OCSP response: failure")); - return rv; + return stapledOCSPResponseResult; } } else { // no stapled OCSP response Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 2); PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("NSSCertDBTrustDomain: no stapled OCSP response")); } - PRErrorCode cachedResponseErrorCode = 0; + Result cachedResponseResult = Success; PRTime cachedResponseValidThrough = 0; bool cachedResponsePresent = mOCSPCache.Get(certID, - cachedResponseErrorCode, + cachedResponseResult, cachedResponseValidThrough); if (cachedResponsePresent) { - if (cachedResponseErrorCode == 0 && cachedResponseValidThrough >= time) { + if (cachedResponseResult == Success && cachedResponseValidThrough >= time) { PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("NSSCertDBTrustDomain: cached OCSP response: good")); - return SECSuccess; + return Success; } // If we have a cached revoked response, use it. - if (cachedResponseErrorCode == SEC_ERROR_REVOKED_CERTIFICATE) { + if (cachedResponseResult == Result::ERROR_REVOKED_CERTIFICATE) { PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("NSSCertDBTrustDomain: cached OCSP response: revoked")); - PR_SetError(SEC_ERROR_REVOKED_CERTIFICATE, 0); - return SECFailure; + return Result::ERROR_REVOKED_CERTIFICATE; } // The cached response may indicate an unknown certificate or it may be // expired. Don't return with either of these statuses yet - we may be // able to fetch a more recent one. PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("NSSCertDBTrustDomain: cached OCSP response: error %ld valid " - "until %lld", cachedResponseErrorCode, cachedResponseValidThrough)); + "until %lld", cachedResponseResult, cachedResponseValidThrough)); // When a good cached response has expired, it is more convenient // to convert that to an error code and just deal with - // cachedResponseErrorCode from here on out. - if (cachedResponseErrorCode == 0 && cachedResponseValidThrough < time) { - cachedResponseErrorCode = SEC_ERROR_OCSP_OLD_RESPONSE; + // cachedResponseResult from here on out. + if (cachedResponseResult == Success && cachedResponseValidThrough < time) { + cachedResponseResult = Result::ERROR_OCSP_OLD_RESPONSE; } // We may have a cached indication of server failure. Ignore it if // it has expired. - if (cachedResponseErrorCode != 0 && - cachedResponseErrorCode != SEC_ERROR_OCSP_UNKNOWN_CERT && - cachedResponseErrorCode != SEC_ERROR_OCSP_OLD_RESPONSE && + if (cachedResponseResult != Success && + cachedResponseResult != Result::ERROR_OCSP_UNKNOWN_CERT && + cachedResponseResult != Result::ERROR_OCSP_OLD_RESPONSE && cachedResponseValidThrough < time) { - cachedResponseErrorCode = 0; + cachedResponseResult = Success; cachedResponsePresent = false; } } else { PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("NSSCertDBTrustDomain: no cached OCSP response")); } - // At this point, if and only if cachedErrorResponseCode is 0, there was no + // At this point, if and only if cachedErrorResult is Success, there was no // cached response. - PR_ASSERT((!cachedResponsePresent && cachedResponseErrorCode == 0) || - (cachedResponsePresent && cachedResponseErrorCode != 0)); + PR_ASSERT((!cachedResponsePresent && cachedResponseResult == Success) || + (cachedResponsePresent && cachedResponseResult != Success)); // TODO: We still need to handle the fallback for expired responses. But, // if/when we disable OCSP fetching by default, it would be ambiguous whether // security.OCSP.enable==0 means "I want the default" or "I really never want // you to ever fetch OCSP." if ((mOCSPFetching == NeverFetchOCSP) || (endEntityOrCA == EndEntityOrCA::MustBeCA && (mOCSPFetching == FetchOCSPForDVHardFail || mOCSPFetching == FetchOCSPForDVSoftFail))) { // We're not going to be doing any fetching, so if there was a cached // "unknown" response, say so. - if (cachedResponseErrorCode == SEC_ERROR_OCSP_UNKNOWN_CERT) { - PR_SetError(SEC_ERROR_OCSP_UNKNOWN_CERT, 0); - return SECFailure; + if (cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) { + return Result::ERROR_OCSP_UNKNOWN_CERT; } // If we're doing hard-fail, we want to know if we have a cached response // that has expired. if (mOCSPFetching == FetchOCSPForDVHardFail && - cachedResponseErrorCode == SEC_ERROR_OCSP_OLD_RESPONSE) { - PR_SetError(SEC_ERROR_OCSP_OLD_RESPONSE, 0); - return SECFailure; + cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) { + return Result::ERROR_OCSP_OLD_RESPONSE; } - return SECSuccess; + return Success; } if (mOCSPFetching == LocalOnlyOCSPForEV) { - PR_SetError(cachedResponseErrorCode != 0 ? cachedResponseErrorCode - : SEC_ERROR_OCSP_UNKNOWN_CERT, 0); - return SECFailure; + if (cachedResponseResult != Success) { + return cachedResponseResult; + } + return Result::ERROR_OCSP_UNKNOWN_CERT; } ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); if (!arena) { - return SECFailure; + return Result::FATAL_ERROR_NO_MEMORY; } + Result rv; const char* url = nullptr; // owned by the arena if (aiaExtension) { - if (GetOCSPAuthorityInfoAccessLocation(arena.get(), *aiaExtension, url) - != SECSuccess) { - return SECFailure; + rv = GetOCSPAuthorityInfoAccessLocation(arena.get(), *aiaExtension, url); + if (rv != Success) { + return rv; } } if (!url) { if (mOCSPFetching == FetchOCSPForEV || - cachedResponseErrorCode == SEC_ERROR_OCSP_UNKNOWN_CERT) { - PR_SetError(SEC_ERROR_OCSP_UNKNOWN_CERT, 0); - return SECFailure; + cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) { + return Result::ERROR_OCSP_UNKNOWN_CERT; } - if (cachedResponseErrorCode == SEC_ERROR_OCSP_OLD_RESPONSE) { - PR_SetError(SEC_ERROR_OCSP_OLD_RESPONSE, 0); - return SECFailure; + if (cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) { + return Result::ERROR_OCSP_OLD_RESPONSE; } - if (stapledOCSPResponseErrorCode != 0) { - PR_SetError(stapledOCSPResponseErrorCode, 0); - return SECFailure; + if (stapledOCSPResponseResult != Success) { + return stapledOCSPResponseResult; } // Nothing to do if we don't have an OCSP responder URI for the cert; just // assume it is good. Note that this is the confusing, but intended, // interpretation of "strict" revocation checking in the face of a // certificate that lacks an OCSP responder URI. - return SECSuccess; + return Success; } // Only request a response if we didn't have a cached indication of failure // (don't keep requesting responses from a failing server). const SECItem* response; bool attemptedRequest; - // Initialize error here to a value we check that it isn't later, because - // the compiler on windows isn't smart enough to figure out that it's - // always initialized before it gets used. - PRErrorCode error = 0; - if (cachedResponseErrorCode == 0 || - cachedResponseErrorCode == SEC_ERROR_OCSP_UNKNOWN_CERT || - cachedResponseErrorCode == SEC_ERROR_OCSP_OLD_RESPONSE) { - const SECItem* request(CreateEncodedOCSPRequest(*this, arena.get(), - certID)); - if (!request) { - return SECFailure; + if (cachedResponseResult == Success || + cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT || + cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) { + uint8_t ocspRequest[OCSP_REQUEST_MAX_LENGTH]; + size_t ocspRequestLength; + rv = CreateEncodedOCSPRequest(*this, certID, ocspRequest, + ocspRequestLength); + if (rv != Success) { + return rv; } - - response = DoOCSPRequest(arena.get(), url, request, + SECItem ocspRequestItem = { + siBuffer, + ocspRequest, + static_cast<unsigned int>(ocspRequestLength) + }; + response = DoOCSPRequest(arena.get(), url, &ocspRequestItem, OCSPFetchingTypeToTimeoutTime(mOCSPFetching), mOCSPGetConfig == CertVerifier::ocsp_get_enabled); if (!response) { - error = PR_GetError(); + rv = MapPRErrorCodeToResult(PR_GetError()); } attemptedRequest = true; } else { - error = cachedResponseErrorCode; + rv = cachedResponseResult; response = nullptr; attemptedRequest = false; } // If we don't have a response, either something went wrong when fetching it // or we didn't attempt to fetch a response because of a failing responder. if (!response) { - MOZ_ASSERT(error != 0); - // If we haven't actually attempted to fetch a response, we have nothing - // new to tell the cache. Otherwise, we do. + Result error = rv; if (attemptedRequest) { PRTime timeout = time + ServerFailureDelay; - SECStatus rv = mOCSPCache.Put(certID, error, time, timeout); - if (rv != SECSuccess) { - return SECFailure; + rv = mOCSPCache.Put(certID, error, time, timeout); + if (rv != Success) { + return rv; } } - PR_SetError(error, 0); if (mOCSPFetching != FetchOCSPForDVSoftFail) { PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("NSSCertDBTrustDomain: returning SECFailure after " "OCSP request failure")); - return SECFailure; + return error; } - if (cachedResponseErrorCode == SEC_ERROR_OCSP_UNKNOWN_CERT) { + if (cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) { PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("NSSCertDBTrustDomain: returning SECFailure from cached " "response after OCSP request failure")); - PR_SetError(cachedResponseErrorCode, 0); - return SECFailure; + return cachedResponseResult; } - if (stapledOCSPResponseErrorCode != 0) { + if (stapledOCSPResponseResult != Success) { PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("NSSCertDBTrustDomain: returning SECFailure from expired " "stapled response after OCSP request failure")); - PR_SetError(stapledOCSPResponseErrorCode, 0); - return SECFailure; + return stapledOCSPResponseResult; } PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("NSSCertDBTrustDomain: returning SECSuccess after " "OCSP request failure")); - return SECSuccess; // Soft fail -> success :( + return Success; // Soft fail -> success :( } // If the response from the network has expired but indicates a revoked // or unknown certificate, PR_GetError() will return the appropriate error. // We actually ignore expired here. bool expired; - SECStatus rv = VerifyAndMaybeCacheEncodedOCSPResponse(certID, time, - maxOCSPLifetimeInDays, - *response, - ResponseIsFromNetwork, - expired); - if (rv == SECSuccess || mOCSPFetching != FetchOCSPForDVSoftFail) { + rv = VerifyAndMaybeCacheEncodedOCSPResponse(certID, time, + maxOCSPLifetimeInDays, + *response, ResponseIsFromNetwork, + expired); + if (rv == Success || mOCSPFetching != FetchOCSPForDVSoftFail) { PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("NSSCertDBTrustDomain: returning after VerifyEncodedOCSPResponse")); return rv; } - error = PR_GetError(); - if (error == SEC_ERROR_OCSP_UNKNOWN_CERT || - error == SEC_ERROR_REVOKED_CERTIFICATE) { + if (rv == Result::ERROR_OCSP_UNKNOWN_CERT || + rv == Result::ERROR_REVOKED_CERTIFICATE) { return rv; } - - if (stapledOCSPResponseErrorCode != 0) { + if (stapledOCSPResponseResult != Success) { PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("NSSCertDBTrustDomain: returning SECFailure from expired stapled " "response after OCSP request verification failure")); - PR_SetError(stapledOCSPResponseErrorCode, 0); - return SECFailure; + return stapledOCSPResponseResult; } PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("NSSCertDBTrustDomain: end of CheckRevocation")); - return SECSuccess; // Soft fail -> success :( + return Success; // Soft fail -> success :( } -SECStatus +Result NSSCertDBTrustDomain::VerifyAndMaybeCacheEncodedOCSPResponse( const CertID& certID, PRTime time, uint16_t maxLifetimeInDays, const SECItem& encodedResponse, EncodedResponseSource responseSource, /*out*/ bool& expired) { PRTime thisUpdate = 0; PRTime validThrough = 0; - SECStatus rv = VerifyEncodedOCSPResponse(*this, certID, time, - maxLifetimeInDays, encodedResponse, - expired, &thisUpdate, &validThrough); - PRErrorCode error = (rv == SECSuccess ? 0 : PR_GetError()); + Result rv = VerifyEncodedOCSPResponse(*this, certID, time, + maxLifetimeInDays, encodedResponse, + expired, &thisUpdate, &validThrough); // If a response was stapled and expired, we don't want to cache it. Return // early to simplify the logic here. if (responseSource == ResponseWasStapled && expired) { - PR_ASSERT(rv != SECSuccess); + PR_ASSERT(rv != Success); return rv; } // validThrough is only trustworthy if the response successfully verifies // or it indicates a revoked or unknown certificate. // If this isn't the case, store an indication of failure (to prevent // repeatedly requesting a response from a failing server). - if (rv != SECSuccess && error != SEC_ERROR_REVOKED_CERTIFICATE && - error != SEC_ERROR_OCSP_UNKNOWN_CERT) { + if (rv != Success && rv != Result::ERROR_REVOKED_CERTIFICATE && + rv != Result::ERROR_OCSP_UNKNOWN_CERT) { validThrough = time + ServerFailureDelay; } if (responseSource == ResponseIsFromNetwork || - rv == SECSuccess || - error == SEC_ERROR_REVOKED_CERTIFICATE || - error == SEC_ERROR_OCSP_UNKNOWN_CERT) { + rv == Success || + rv == Result::ERROR_REVOKED_CERTIFICATE || + rv == Result::ERROR_OCSP_UNKNOWN_CERT) { PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("NSSCertDBTrustDomain: caching OCSP response")); - if (mOCSPCache.Put(certID, error, thisUpdate, validThrough) != SECSuccess) { - return SECFailure; + Result putRV = mOCSPCache.Put(certID, rv, thisUpdate, validThrough); + if (putRV != Success) { + return putRV; } } - // If the verification failed, re-set to that original error - // (the call to Put may have un-set it). - if (rv != SECSuccess) { - PR_SetError(error, 0); - } return rv; } -SECStatus +Result NSSCertDBTrustDomain::IsChainValid(const DERArray& certArray) { PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("NSSCertDBTrustDomain: Top of IsChainValid mCheckChainCallback=%p", mCheckChainCallback)); if (!mBuiltChain && !mCheckChainCallback) { // No need to create a CERTCertList, and nothing else to do. - return SECSuccess; + return Success; } ScopedCERTCertList certList; - SECStatus rv = ConstructCERTCertListFromReversedDERArray(certArray, certList); - if (rv != SECSuccess) { - return rv; + SECStatus srv = ConstructCERTCertListFromReversedDERArray(certArray, + certList); + if (srv != SECSuccess) { + return MapPRErrorCodeToResult(PR_GetError()); } if (mCheckChainCallback) { if (!mCheckChainCallback->isChainValid) { - PR_SetError(SEC_ERROR_INVALID_ARGS, 0); - return SECFailure; + return Result::FATAL_ERROR_INVALID_ARGS; } PRBool chainOK; - rv = (mCheckChainCallback->isChainValid)( + srv = (mCheckChainCallback->isChainValid)( mCheckChainCallback->isChainValidArg, certList.get(), &chainOK); - if (rv != SECSuccess) { - return rv; + if (srv != SECSuccess) { + return MapPRErrorCodeToResult(PR_GetError()); } if (!chainOK) { - PR_SetError(PSM_ERROR_KEY_PINNING_FAILURE, 0); - return SECFailure; + return Result::ERROR_KEY_PINNING_FAILURE; } } if (mBuiltChain) { *mBuiltChain = certList.forget(); } - return SECSuccess; + return Success; } -SECStatus +Result NSSCertDBTrustDomain::CheckPublicKey(const SECItem& subjectPublicKeyInfo) { return ::mozilla::pkix::CheckPublicKey(subjectPublicKeyInfo); } namespace { static char*
--- a/security/certverifier/NSSCertDBTrustDomain.h +++ b/security/certverifier/NSSCertDBTrustDomain.h @@ -36,61 +36,67 @@ void UnloadLoadableRoots(const char* mod char* DefaultServerNicknameForCert(CERTCertificate* cert); void SaveIntermediateCerts(const ScopedCERTCertList& certList); class NSSCertDBTrustDomain : public mozilla::pkix::TrustDomain { public: + typedef mozilla::pkix::Result Result; + enum OCSPFetching { NeverFetchOCSP = 0, FetchOCSPForDVSoftFail = 1, FetchOCSPForDVHardFail = 2, FetchOCSPForEV = 3, LocalOnlyOCSPForEV = 4, }; NSSCertDBTrustDomain(SECTrustType certDBTrustType, OCSPFetching ocspFetching, OCSPCache& ocspCache, void* pinArg, CertVerifier::ocsp_get_config ocspGETConfig, /*optional*/ CERTChainVerifyCallback* checkChainCallback = nullptr, /*optional*/ ScopedCERTCertList* builtChain = nullptr); - virtual SECStatus FindIssuer(const SECItem& encodedIssuerName, - IssuerChecker& checker, PRTime time); + virtual Result FindIssuer(const SECItem& encodedIssuerName, + IssuerChecker& checker, + PRTime time) MOZ_OVERRIDE; - virtual SECStatus GetCertTrust(mozilla::pkix::EndEntityOrCA endEntityOrCA, - const mozilla::pkix::CertPolicyId& policy, - const SECItem& candidateCertDER, - /*out*/ mozilla::pkix::TrustLevel* trustLevel); + virtual Result GetCertTrust(mozilla::pkix::EndEntityOrCA endEntityOrCA, + const mozilla::pkix::CertPolicyId& policy, + const SECItem& candidateCertDER, + /*out*/ mozilla::pkix::TrustLevel* trustLevel) + MOZ_OVERRIDE; - virtual SECStatus VerifySignedData( - const mozilla::pkix::SignedDataWithSignature& signedData, - const SECItem& subjectPublicKeyInfo); + virtual Result CheckPublicKey(const SECItem& subjectPublicKeyInfo) + MOZ_OVERRIDE; - virtual SECStatus DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf, - size_t digestBufLen); + virtual Result VerifySignedData( + const mozilla::pkix::SignedDataWithSignature& signedData, + const SECItem& subjectPublicKeyInfo) MOZ_OVERRIDE; + + virtual Result DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf, + size_t digestBufLen) MOZ_OVERRIDE; - virtual SECStatus CheckRevocation(mozilla::pkix::EndEntityOrCA endEntityOrCA, - const mozilla::pkix::CertID& certID, - PRTime time, - /*optional*/ const SECItem* stapledOCSPResponse, - /*optional*/ const SECItem* aiaExtension); + virtual Result CheckRevocation(mozilla::pkix::EndEntityOrCA endEntityOrCA, + const mozilla::pkix::CertID& certID, + PRTime time, + /*optional*/ const SECItem* stapledOCSPResponse, + /*optional*/ const SECItem* aiaExtension) MOZ_OVERRIDE; - virtual SECStatus IsChainValid(const mozilla::pkix::DERArray& certChain); - - virtual SECStatus CheckPublicKey(const SECItem& subjectPublicKeyInfo); + virtual Result IsChainValid(const mozilla::pkix::DERArray& certChain) + MOZ_OVERRIDE; private: enum EncodedResponseSource { ResponseIsFromNetwork = 1, ResponseWasStapled = 2 }; static const PRTime ServerFailureDelay = 5 * 60 * PR_USEC_PER_SEC; - SECStatus VerifyAndMaybeCacheEncodedOCSPResponse( + Result VerifyAndMaybeCacheEncodedOCSPResponse( const mozilla::pkix::CertID& certID, PRTime time, uint16_t maxLifetimeInDays, const SECItem& encodedResponse, EncodedResponseSource responseSource, /*out*/ bool& expired); const SECTrustType mCertDBTrustType; const OCSPFetching mOCSPFetching; OCSPCache& mOCSPCache; // non-owning! void* mPinArg; // non-owning!
--- a/security/certverifier/OCSPCache.cpp +++ b/security/certverifier/OCSPCache.cpp @@ -23,17 +23,17 @@ */ #include "OCSPCache.h" #include <limits> #include "NSSCertDBTrustDomain.h" #include "pk11pub.h" -#include "pkix/pkixtypes.h" +#include "pkix/pkixnss.h" #include "ScopedNSSTypes.h" #include "secerr.h" #ifdef PR_LOGGING extern PRLogModuleInfo* gCertVerifierLog; #endif using namespace mozilla::pkix; @@ -79,24 +79,28 @@ CertIDHash(SHA384Buffer& buf, const Cert uint32_t outLen = 0; rv = PK11_DigestFinal(context.get(), buf, &outLen, SHA384_LENGTH); if (outLen != SHA384_LENGTH) { return SECFailure; } return rv; } -SECStatus -OCSPCache::Entry::Init(const CertID& aCertID, PRErrorCode aErrorCode, +Result +OCSPCache::Entry::Init(const CertID& aCertID, Result aResult, PRTime aThisUpdate, PRTime aValidThrough) { - mErrorCode = aErrorCode; + mResult = aResult; mThisUpdate = aThisUpdate; mValidThrough = aValidThrough; - return CertIDHash(mIDHash, aCertID); + SECStatus srv = CertIDHash(mIDHash, aCertID); + if (srv != SECSuccess) { + return MapPRErrorCodeToResult(PR_GetError()); + } + return Success; } OCSPCache::OCSPCache() : mMutex("OCSPCache-mutex") { } OCSPCache::~OCSPCache() @@ -145,123 +149,117 @@ OCSPCache::MakeMostRecentlyUsed(size_t a Entry* entry = mEntries[aIndex]; // Since mEntries is sorted with the most-recently-used entry at the end, // aIndex is likely to be near the end, so this is likely to be fast. mEntries.erase(mEntries.begin() + aIndex); mEntries.append(entry); } bool -OCSPCache::Get(const CertID& aCertID, PRErrorCode& aErrorCode, - PRTime& aValidThrough) +OCSPCache::Get(const CertID& aCertID, Result& aResult, PRTime& aValidThrough) { MutexAutoLock lock(mMutex); size_t index; if (!FindInternal(aCertID, index, lock)) { LogWithCertID("OCSPCache::Get(%p) not in cache", aCertID); return false; } LogWithCertID("OCSPCache::Get(%p) in cache", aCertID); - aErrorCode = mEntries[index]->mErrorCode; + aResult = mEntries[index]->mResult; aValidThrough = mEntries[index]->mValidThrough; MakeMostRecentlyUsed(index, lock); return true; } -SECStatus -OCSPCache::Put(const CertID& aCertID, PRErrorCode aErrorCode, +Result +OCSPCache::Put(const CertID& aCertID, Result aResult, PRTime aThisUpdate, PRTime aValidThrough) { MutexAutoLock lock(mMutex); size_t index; if (FindInternal(aCertID, index, lock)) { // Never replace an entry indicating a revoked certificate. - if (mEntries[index]->mErrorCode == SEC_ERROR_REVOKED_CERTIFICATE) { + if (mEntries[index]->mResult == Result::ERROR_REVOKED_CERTIFICATE) { LogWithCertID("OCSPCache::Put(%p) already in cache as revoked - " "not replacing", aCertID); MakeMostRecentlyUsed(index, lock); - return SECSuccess; + return Success; } // Never replace a newer entry with an older one unless the older entry // indicates a revoked certificate, which we want to remember. if (mEntries[index]->mThisUpdate > aThisUpdate && - aErrorCode != SEC_ERROR_REVOKED_CERTIFICATE) { + aResult != Result::ERROR_REVOKED_CERTIFICATE) { LogWithCertID("OCSPCache::Put(%p) already in cache with more recent " "validity - not replacing", aCertID); MakeMostRecentlyUsed(index, lock); - return SECSuccess; + return Success; } // Only known good responses or responses indicating an unknown // or revoked certificate should replace previously known responses. - if (aErrorCode != 0 && aErrorCode != SEC_ERROR_OCSP_UNKNOWN_CERT && - aErrorCode != SEC_ERROR_REVOKED_CERTIFICATE) { + if (aResult != Success && + aResult != Result::ERROR_OCSP_UNKNOWN_CERT && + aResult != Result::ERROR_REVOKED_CERTIFICATE) { LogWithCertID("OCSPCache::Put(%p) already in cache - not replacing " "with less important status", aCertID); MakeMostRecentlyUsed(index, lock); - return SECSuccess; + return Success; } LogWithCertID("OCSPCache::Put(%p) already in cache - replacing", aCertID); - mEntries[index]->mErrorCode = aErrorCode; + mEntries[index]->mResult = aResult; mEntries[index]->mThisUpdate = aThisUpdate; mEntries[index]->mValidThrough = aValidThrough; MakeMostRecentlyUsed(index, lock); - return SECSuccess; + return Success; } if (mEntries.length() == MaxEntries) { LogWithCertID("OCSPCache::Put(%p) too full - evicting an entry", aCertID); for (Entry** toEvict = mEntries.begin(); toEvict != mEntries.end(); toEvict++) { // Never evict an entry that indicates a revoked or unknokwn certificate, // because revoked responses are more security-critical to remember. - if ((*toEvict)->mErrorCode != SEC_ERROR_REVOKED_CERTIFICATE && - (*toEvict)->mErrorCode != SEC_ERROR_OCSP_UNKNOWN_CERT) { + if ((*toEvict)->mResult != Result::ERROR_REVOKED_CERTIFICATE && + (*toEvict)->mResult != Result::ERROR_OCSP_UNKNOWN_CERT) { delete *toEvict; mEntries.erase(toEvict); break; } } // Well, we tried, but apparently everything is revoked or unknown. // We don't want to remove a cached revoked or unknown response. If we're // trying to insert a good response, we can just return "successfully" // without doing so. This means we'll lose some speed, but it's not a // security issue. If we're trying to insert a revoked or unknown response, // we can't. We should return with an error that causes the current // verification to fail. if (mEntries.length() == MaxEntries) { - if (aErrorCode != 0) { - PR_SetError(aErrorCode, 0); - return SECFailure; - } - return SECSuccess; + return aResult; } } - Entry* newEntry = new Entry(); + Entry* newEntry = new (std::nothrow) Entry(); // Normally we don't have to do this in Gecko, because OOM is fatal. // However, if we want to embed this in another project, OOM might not // be fatal, so handle this case. if (!newEntry) { - PR_SetError(SEC_ERROR_NO_MEMORY, 0); - return SECFailure; + return Result::FATAL_ERROR_NO_MEMORY; } - SECStatus rv = newEntry->Init(aCertID, aErrorCode, aThisUpdate, - aValidThrough); - if (rv != SECSuccess) { + Result rv = newEntry->Init(aCertID, aResult, aThisUpdate, aValidThrough); + if (rv != Success) { delete newEntry; return rv; } mEntries.append(newEntry); LogWithCertID("OCSPCache::Put(%p) added to cache", aCertID); - return SECSuccess; + return Success; } void OCSPCache::Clear() { MutexAutoLock lock(mMutex); PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("OCSPCache::Clear: clearing cache")); // First go through and delete the memory being pointed to by the pointers
--- a/security/certverifier/OCSPCache.h +++ b/security/certverifier/OCSPCache.h @@ -23,16 +23,17 @@ */ #ifndef mozilla_psm_OCSPCache_h #define mozilla_psm_OCSPCache_h #include "hasht.h" #include "mozilla/Mutex.h" #include "mozilla/Vector.h" +#include "pkix/Result.h" #include "prerror.h" #include "prtime.h" #include "seccomon.h" namespace mozilla { namespace pkix { struct CertID; } } // namespace mozilla::pkix @@ -53,41 +54,44 @@ public: OCSPCache(); ~OCSPCache(); // Returns true if the status of the given certificate (issued by the given // issuer) is in the cache, and false otherwise. // If it is in the cache, returns by reference the error code of the cached // status and the time through which the status is considered trustworthy. bool Get(const mozilla::pkix::CertID& aCertID, - /* out */ PRErrorCode& aErrorCode, /* out */ PRTime& aValidThrough); + /*out*/ mozilla::pkix::Result& aResult, + /*out*/ PRTime& aValidThrough); // Caches the status of the given certificate (issued by the given issuer). // The status is considered trustworthy through the given time. // A status with an error code of SEC_ERROR_REVOKED_CERTIFICATE will not // be replaced or evicted. // A status with an error code of SEC_ERROR_OCSP_UNKNOWN_CERT will not // be evicted when the cache is full. // A status with a more recent thisUpdate will not be replaced with a // status with a less recent thisUpdate unless the less recent status // indicates the certificate is revoked. - SECStatus Put(const mozilla::pkix::CertID& aCertID, PRErrorCode aErrorCode, - PRTime aThisUpdate, PRTime aValidThrough); + mozilla::pkix::Result Put(const mozilla::pkix::CertID& aCertID, + mozilla::pkix::Result aResult, PRTime aThisUpdate, + PRTime aValidThrough); // Removes everything from the cache. void Clear(); private: class Entry { public: - SECStatus Init(const mozilla::pkix::CertID& aCertID, PRErrorCode aErrorCode, - PRTime aThisUpdate, PRTime aValidThrough); + mozilla::pkix::Result Init(const mozilla::pkix::CertID& aCertID, + mozilla::pkix::Result aResult, + PRTime aThisUpdate, PRTime aValidThrough); - PRErrorCode mErrorCode; + mozilla::pkix::Result mResult; PRTime mThisUpdate; PRTime mValidThrough; // The SHA-384 hash of the concatenation of the DER encodings of the // issuer name and issuer key, followed by the serial number. // See the documentation for CertIDHash in OCSPCache.cpp. SHA384Buffer mIDHash; };
--- a/security/certverifier/moz.build +++ b/security/certverifier/moz.build @@ -27,16 +27,17 @@ DIRS += [ ] CXXFLAGS += ['-Wall'] if CONFIG['_MSC_VER']: # -Wall with Visual C++ enables too many problematic warnings CXXFLAGS += [ '-wd4480', # nonstandard extension used: specifying underlying type for # enum 'enum' + '-wd4481', # nonstandard extension used: override specifier 'keyword' '-wd4510', # default constructor could not be generated '-wd4512', # assignment operator could not be generated '-wd4514', # 'function': unreferenced inline function has been removed '-wd4610', # struct 'symbol' can never be instantiated - user defined # constructor required '-wd4619', # pragma warning: there is no warning 'warning' '-wd4625', # copy constructor could not be generated because a base # class copy constructor is inaccessible or deleted
--- a/security/manager/ssl/src/NSSErrorsService.cpp +++ b/security/manager/ssl/src/NSSErrorsService.cpp @@ -1,50 +1,40 @@ /* 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 "NSSErrorsService.h" #include "nsNSSComponent.h" #include "nsServiceManagerUtils.h" +#include "pkix/pkixnss.h" #include "secerr.h" #include "sslerr.h" #define PIPNSS_STRBUNDLE_URL "chrome://pipnss/locale/pipnss.properties" #define NSSERR_STRBUNDLE_URL "chrome://pipnss/locale/nsserrors.properties" namespace mozilla { namespace psm { -static const struct PRErrorMessage PSMErrorTableText[] = { - { "PSM_ERROR_KEY_PINNING_FAILURE", - "The server uses key pinning (HPKP) but no trusted certificate chain " - "could be constructed that matches the pinset. Key pinning violations " - "cannot be overridden." } -}; - -static const struct PRErrorTable PSMErrorTable = { - PSMErrorTableText, - "psmerrors", - nsINSSErrorsService::PSM_ERROR_BASE, - PR_ARRAY_SIZE(PSMErrorTableText) -}; - -void -RegisterPSMErrorTable() -{ - PR_ErrorInstallTable(&PSMErrorTable); -} +static_assert(mozilla::pkix::ERROR_BASE == + nsINSSErrorsService::MOZILLA_PKIX_ERROR_BASE, + "MOZILLA_PKIX_ERROR_BASE and " + "nsINSSErrorsService::MOZILLA_PKIX_ERROR_BASE do not match."); +static_assert(mozilla::pkix::ERROR_LIMIT == + nsINSSErrorsService::MOZILLA_PKIX_ERROR_LIMIT, + "MOZILLA_PKIX_ERROR_LIMIT and " + "nsINSSErrorsService::MOZILLA_PKIX_ERROR_LIMIT do not match."); static bool IsPSMError(PRErrorCode error) { - return (error >= nsINSSErrorsService::PSM_ERROR_BASE && - error < nsINSSErrorsService::PSM_ERROR_LIMIT); + return (error >= mozilla::pkix::ERROR_BASE && + error < mozilla::pkix::ERROR_LIMIT); } NS_IMPL_ISUPPORTS(NSSErrorsService, nsINSSErrorsService) NSSErrorsService::~NSSErrorsService() { } nsresult NSSErrorsService::Init()
--- a/security/manager/ssl/src/NSSErrorsService.h +++ b/security/manager/ssl/src/NSSErrorsService.h @@ -1,31 +1,24 @@ /* 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 NSSErrorsService_h #define NSSErrorsService_h #include "nsINSSErrorsService.h" - #include "mozilla/Attributes.h" #include "nsCOMPtr.h" #include "nsIStringBundle.h" #include "prerror.h" namespace mozilla { namespace psm { -enum PSMErrorCodes { - PSM_ERROR_KEY_PINNING_FAILURE = (nsINSSErrorsService::PSM_ERROR_BASE + 0) -}; - -void RegisterPSMErrorTable(); - class NSSErrorsService MOZ_FINAL : public nsINSSErrorsService { NS_DECL_ISUPPORTS NS_DECL_NSINSSERRORSSERVICE public: nsresult Init();
--- a/security/manager/ssl/src/nsNSSComponent.cpp +++ b/security/manager/ssl/src/nsNSSComponent.cpp @@ -46,16 +46,17 @@ #include "nsIBufEntropyCollector.h" #include "nsITokenPasswordDialogs.h" #include "nsServiceManagerUtils.h" #include "nsNSSShutDown.h" #include "SharedSSLState.h" #include "NSSErrorsService.h" #include "nss.h" +#include "pkix/pkixnss.h" #include "ssl.h" #include "sslproto.h" #include "secmod.h" #include "secerr.h" #include "sslerr.h" #include "nsXULAppAPI.h" @@ -1204,17 +1205,17 @@ nsNSSComponent::InitializeNSS() setValidationOptions(true, lock); mHttpForNSS.initTable(); #ifndef MOZ_DISABLE_CRYPTOLEGACY LaunchSmartCardThreads(); #endif - RegisterPSMErrorTable(); + mozilla::pkix::RegisterErrorTable(); PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("NSS Initialization done\n")); return NS_OK; } void nsNSSComponent::ShutdownNSS() {
--- a/security/manager/ssl/tests/gtest/OCSPCacheTest.cpp +++ b/security/manager/ssl/tests/gtest/OCSPCacheTest.cpp @@ -26,30 +26,31 @@ class OCSPCacheTest : public ::testing:: NSS_NoDB_Init(nullptr); mozilla::psm::InitCertVerifierLog(); } mozilla::psm::OCSPCache cache; }; static void -PutAndGet(OCSPCache& cache, const CertID& certID, PRErrorCode error, +PutAndGet(OCSPCache& cache, const CertID& certID, Result result, PRTime time) { // The first time is thisUpdate. The second is validUntil. // The caller is expecting the validUntil returned with Get // to be equal to the passed-in time. Since these values will // be different in practice, make thisUpdate less than validUntil. ASSERT_TRUE(time >= 10); - SECStatus rv = cache.Put(certID, error, time - 10, time); - ASSERT_TRUE(rv == SECSuccess); - PRErrorCode errorOut; + Result rv = cache.Put(certID, result, time - 10, time); + ASSERT_TRUE(rv == Success); + Result resultOut; PRTime timeOut; - ASSERT_TRUE(cache.Get(certID, errorOut, timeOut)); - ASSERT_TRUE(error == errorOut && time == timeOut); + ASSERT_TRUE(cache.Get(certID, resultOut, timeOut)); + ASSERT_EQ(result, resultOut); + ASSERT_EQ(time, timeOut); } static const SECItem fakeIssuer1 = { siBuffer, const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>("CN=issuer1")), 10 }; static const SECItem fakeKey000 = { @@ -77,69 +78,75 @@ TEST_F(OCSPCacheTest, TestPutAndGet) }; static const SECItem fakeSerial001 = { siBuffer, const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>("001")), 3 }; SCOPED_TRACE(""); - PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial001), 0, PR_Now()); - PRErrorCode errorOut; + PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial001), + Success, PR_Now()); + Result resultOut; PRTime timeOut; ASSERT_FALSE(cache.Get(CertID(fakeIssuer1, fakeKey001, fakeSerial000), - errorOut, timeOut)); + resultOut, timeOut)); } TEST_F(OCSPCacheTest, TestVariousGets) { SCOPED_TRACE(""); PRTime timeIn = PR_Now(); for (int i = 0; i < MaxCacheEntries; i++) { char serialBuf[8]; PR_snprintf(serialBuf, sizeof(serialBuf), "%04d", i); const SECItem fakeSerial = { siBuffer, const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(serialBuf)), 4 }; - PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial), 0, timeIn + i); + PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial), + Success, timeIn + i); } - PRErrorCode errorOut; + Result resultOut; PRTime timeOut; // This will be at the end of the list in the cache CertID cert0000(fakeIssuer1, fakeKey000, fakeSerial0000); - ASSERT_TRUE(cache.Get(cert0000, errorOut, timeOut)); - ASSERT_TRUE(errorOut == 0 && timeOut == timeIn); + ASSERT_TRUE(cache.Get(cert0000, resultOut, timeOut)); + ASSERT_EQ(Success, resultOut); + ASSERT_EQ(timeIn, timeOut); // Once we access it, it goes to the front - ASSERT_TRUE(cache.Get(cert0000, errorOut, timeOut)); - ASSERT_TRUE(errorOut == 0 && timeOut == timeIn); + ASSERT_TRUE(cache.Get(cert0000, resultOut, timeOut)); + ASSERT_EQ(Success, resultOut); + ASSERT_EQ(timeIn, timeOut); // This will be in the middle static const SECItem fakeSerial0512 = { siBuffer, const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>("0512")), 4 }; CertID cert0512(fakeIssuer1, fakeKey000, fakeSerial0512); - ASSERT_TRUE(cache.Get(cert0512, errorOut, timeOut)); - ASSERT_TRUE(errorOut == 0 && timeOut == timeIn + 512); - ASSERT_TRUE(cache.Get(cert0512, errorOut, timeOut)); - ASSERT_TRUE(errorOut == 0 && timeOut == timeIn + 512); + ASSERT_TRUE(cache.Get(cert0512, resultOut, timeOut)); + ASSERT_EQ(Success, resultOut); + ASSERT_EQ(timeIn + 512, timeOut); + ASSERT_TRUE(cache.Get(cert0512, resultOut, timeOut)); + ASSERT_EQ(Success, resultOut); + ASSERT_EQ(timeIn + 512, timeOut); // We've never seen this certificate static const SECItem fakeSerial1111 = { siBuffer, const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>("1111")), 4 }; ASSERT_FALSE(cache.Get(CertID(fakeIssuer1, fakeKey000, fakeSerial1111), - errorOut, timeOut)); + resultOut, timeOut)); } TEST_F(OCSPCacheTest, TestEviction) { SCOPED_TRACE(""); PRTime timeIn = PR_Now(); // By putting more distinct entries in the cache than it can hold, @@ -147,99 +154,100 @@ TEST_F(OCSPCacheTest, TestEviction) for (int i = 0; i < MaxCacheEntries + 1; i++) { char serialBuf[8]; PR_snprintf(serialBuf, sizeof(serialBuf), "%04d", i); const SECItem fakeSerial = { siBuffer, const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(serialBuf)), 4 }; - PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial), 0, timeIn + i); + PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial), + Success, timeIn + i); } - PRErrorCode errorOut; + Result resultOut; PRTime timeOut; ASSERT_FALSE(cache.Get(CertID(fakeIssuer1, fakeKey001, fakeSerial0000), - errorOut, timeOut)); + resultOut, timeOut)); } TEST_F(OCSPCacheTest, TestNoEvictionForRevokedResponses) { SCOPED_TRACE(""); PRTime timeIn = PR_Now(); CertID notEvicted(fakeIssuer1, fakeKey000, fakeSerial0000); - PutAndGet(cache, notEvicted, SEC_ERROR_REVOKED_CERTIFICATE, timeIn); + PutAndGet(cache, notEvicted, Result::ERROR_REVOKED_CERTIFICATE, timeIn); // By putting more distinct entries in the cache than it can hold, // we cause the least recently used entry that isn't revoked to be evicted. for (int i = 1; i < MaxCacheEntries + 1; i++) { char serialBuf[8]; PR_snprintf(serialBuf, sizeof(serialBuf), "%04d", i); const SECItem fakeSerial = { siBuffer, reinterpret_cast<uint8_t*>(serialBuf), 4 }; - PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial), 0, timeIn + i); + PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial), + Success, timeIn + i); } - PRErrorCode errorOut; + Result resultOut; PRTime timeOut; - ASSERT_TRUE(cache.Get(notEvicted, errorOut, timeOut)); - ASSERT_TRUE(errorOut == SEC_ERROR_REVOKED_CERTIFICATE && timeOut == timeIn); + ASSERT_TRUE(cache.Get(notEvicted, resultOut, timeOut)); + ASSERT_EQ(Result::ERROR_REVOKED_CERTIFICATE, resultOut); + ASSERT_EQ(timeIn, timeOut); const SECItem fakeSerial0001 = { siBuffer, const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>("0001")), 4 }; CertID evicted(fakeIssuer1, fakeKey000, fakeSerial0001); - ASSERT_FALSE(cache.Get(evicted, errorOut, timeOut)); + ASSERT_FALSE(cache.Get(evicted, resultOut, timeOut)); } TEST_F(OCSPCacheTest, TestEverythingIsRevoked) { SCOPED_TRACE(""); PRTime timeIn = PR_Now(); // Fill up the cache with revoked responses. for (int i = 0; i < MaxCacheEntries; i++) { char serialBuf[8]; PR_snprintf(serialBuf, sizeof(serialBuf), "%04d", i); const SECItem fakeSerial = { siBuffer, reinterpret_cast<uint8_t*>(serialBuf), 4 }; PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial), - SEC_ERROR_REVOKED_CERTIFICATE, timeIn + i); + Result::ERROR_REVOKED_CERTIFICATE, timeIn + i); } const SECItem fakeSerial1025 = { siBuffer, const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>("1025")), 4 }; CertID good(fakeIssuer1, fakeKey000, fakeSerial1025); // This will "succeed", allowing verification to continue. However, // nothing was actually put in the cache. - SECStatus result = cache.Put(good, 0, timeIn + 1025 - 50, timeIn + 1025); - ASSERT_TRUE(result == SECSuccess); - PRErrorCode errorOut; + Result result = cache.Put(good, Success, timeIn + 1025 - 50, timeIn + 1025); + ASSERT_EQ(Success, result); + Result resultOut; PRTime timeOut; - ASSERT_FALSE(cache.Get(good, errorOut, timeOut)); + ASSERT_FALSE(cache.Get(good, resultOut, timeOut)); const SECItem fakeSerial1026 = { siBuffer, const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>("1026")), 4 }; CertID revoked(fakeIssuer1, fakeKey000, fakeSerial1026); // This will fail, causing verification to fail. - result = cache.Put(revoked, SEC_ERROR_REVOKED_CERTIFICATE, + result = cache.Put(revoked, Result::ERROR_REVOKED_CERTIFICATE, timeIn + 1026 - 50, timeIn + 1026); - PRErrorCode error = PR_GetError(); - ASSERT_TRUE(result == SECFailure); - ASSERT_TRUE(error == SEC_ERROR_REVOKED_CERTIFICATE); + ASSERT_EQ(Result::ERROR_REVOKED_CERTIFICATE, result); } TEST_F(OCSPCacheTest, VariousIssuers) { SCOPED_TRACE(""); static const SECItem fakeIssuer2 = { siBuffer, const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>("CN=issuer2")), @@ -248,68 +256,73 @@ TEST_F(OCSPCacheTest, VariousIssuers) static const SECItem fakeSerial001 = { siBuffer, const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>("001")), 3 }; PRTime timeIn = PR_Now(); CertID subject(fakeIssuer1, fakeKey000, fakeSerial001); - PutAndGet(cache, subject, 0, timeIn); - PRErrorCode errorOut; + PutAndGet(cache, subject, Success, timeIn); + Result resultOut; PRTime timeOut; - ASSERT_TRUE(cache.Get(subject, errorOut, timeOut)); - ASSERT_TRUE(errorOut == 0 && timeOut == timeIn); + ASSERT_TRUE(cache.Get(subject, resultOut, timeOut)); + ASSERT_EQ(Success, resultOut); + ASSERT_EQ(timeIn, timeOut); // Test that we don't match a different issuer DN ASSERT_FALSE(cache.Get(CertID(fakeIssuer2, fakeKey000, fakeSerial001), - errorOut, timeOut)); + resultOut, timeOut)); // Test that we don't match a different issuer key ASSERT_FALSE(cache.Get(CertID(fakeIssuer1, fakeKey001, fakeSerial001), - errorOut, timeOut)); + resultOut, timeOut)); } TEST_F(OCSPCacheTest, Times) { SCOPED_TRACE(""); CertID certID(fakeIssuer1, fakeKey000, fakeSerial0000); - PutAndGet(cache, certID, SEC_ERROR_OCSP_UNKNOWN_CERT, 100); - PutAndGet(cache, certID, 0, 200); + PutAndGet(cache, certID, Result::ERROR_OCSP_UNKNOWN_CERT, 100); + PutAndGet(cache, certID, Success, 200); // This should not override the more recent entry. - ASSERT_EQ(SECSuccess, cache.Put(certID, SEC_ERROR_OCSP_UNKNOWN_CERT, 100, - 100)); - PRErrorCode errorOut; + ASSERT_EQ(Success, + cache.Put(certID, Result::ERROR_OCSP_UNKNOWN_CERT, 100, 100)); + Result resultOut; PRTime timeOut; - ASSERT_TRUE(cache.Get(certID, errorOut, timeOut)); + ASSERT_TRUE(cache.Get(certID, resultOut, timeOut)); // Here we see the more recent time. - ASSERT_TRUE(errorOut == 0 && timeOut == 200); + ASSERT_EQ(Success, resultOut); + ASSERT_EQ(200, timeOut); - // SEC_ERROR_REVOKED_CERTIFICATE overrides everything - PutAndGet(cache, certID, SEC_ERROR_REVOKED_CERTIFICATE, 50); + // Result::ERROR_REVOKED_CERTIFICATE overrides everything + PutAndGet(cache, certID, Result::ERROR_REVOKED_CERTIFICATE, 50); } TEST_F(OCSPCacheTest, NetworkFailure) { SCOPED_TRACE(""); CertID certID(fakeIssuer1, fakeKey000, fakeSerial0000); - PutAndGet(cache, certID, PR_CONNECT_REFUSED_ERROR, 100); - PutAndGet(cache, certID, 0, 200); + PutAndGet(cache, certID, Result::ERROR_CONNECT_REFUSED, 100); + PutAndGet(cache, certID, Success, 200); // This should not override the already present entry. - SECStatus rv = cache.Put(certID, PR_CONNECT_REFUSED_ERROR, 300, 350); - ASSERT_TRUE(rv == SECSuccess); - PRErrorCode errorOut; + ASSERT_EQ(Success, + cache.Put(certID, Result::ERROR_CONNECT_REFUSED, 300, 350)); + Result resultOut; PRTime timeOut; - ASSERT_TRUE(cache.Get(certID, errorOut, timeOut)); - ASSERT_TRUE(errorOut == 0 && timeOut == 200); + ASSERT_TRUE(cache.Get(certID, resultOut, timeOut)); + ASSERT_EQ(Success, resultOut); + ASSERT_EQ(200, timeOut); - PutAndGet(cache, certID, SEC_ERROR_OCSP_UNKNOWN_CERT, 400); + PutAndGet(cache, certID, Result::ERROR_OCSP_UNKNOWN_CERT, 400); // This should not override the already present entry. - rv = cache.Put(certID, PR_CONNECT_REFUSED_ERROR, 500, 550); - ASSERT_TRUE(rv == SECSuccess); - ASSERT_TRUE(cache.Get(certID, errorOut, timeOut)); - ASSERT_TRUE(errorOut == SEC_ERROR_OCSP_UNKNOWN_CERT && timeOut == 400); + ASSERT_EQ(Success, + cache.Put(certID, Result::ERROR_CONNECT_REFUSED, 500, 550)); + ASSERT_TRUE(cache.Get(certID, resultOut, timeOut)); + ASSERT_EQ(Result::ERROR_OCSP_UNKNOWN_CERT, resultOut); + ASSERT_EQ(400, timeOut); - PutAndGet(cache, certID, SEC_ERROR_REVOKED_CERTIFICATE, 600); + PutAndGet(cache, certID, Result::ERROR_REVOKED_CERTIFICATE, 600); // This should not override the already present entry. - rv = cache.Put(certID, PR_CONNECT_REFUSED_ERROR, 700, 750); - ASSERT_TRUE(rv == SECSuccess); - ASSERT_TRUE(cache.Get(certID, errorOut, timeOut)); - ASSERT_TRUE(errorOut == SEC_ERROR_REVOKED_CERTIFICATE && timeOut == 600); + ASSERT_EQ(Success, + cache.Put(certID, Result::ERROR_CONNECT_REFUSED, 700, 750)); + ASSERT_TRUE(cache.Get(certID, resultOut, timeOut)); + ASSERT_EQ(Result::ERROR_REVOKED_CERTIFICATE, resultOut); + ASSERT_EQ(600, timeOut); }
--- a/security/manager/ssl/tests/unit/head_psm.js +++ b/security/manager/ssl/tests/unit/head_psm.js @@ -15,17 +15,17 @@ let { ctypes } = Cu.import("resource://g let gIsWindows = ("@mozilla.org/windows-registry-key;1" in Cc); const isDebugBuild = Cc["@mozilla.org/xpcom/debug;1"] .getService(Ci.nsIDebug2).isDebugBuild; const SEC_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SEC_ERROR_BASE; const SSL_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SSL_ERROR_BASE; -const PSM_ERROR_BASE = Ci.nsINSSErrorsService.PSM_ERROR_BASE; +const MOZILLA_PKIX_ERROR_BASE = Ci.nsINSSErrorsService.MOZILLA_PKIX_ERROR_BASE; // Sort in numerical order const SEC_ERROR_INVALID_ARGS = SEC_ERROR_BASE + 5; // -8187 const SEC_ERROR_BAD_DER = SEC_ERROR_BASE + 9; const SEC_ERROR_EXPIRED_CERTIFICATE = SEC_ERROR_BASE + 11; const SEC_ERROR_REVOKED_CERTIFICATE = SEC_ERROR_BASE + 12; // -8180 const SEC_ERROR_UNKNOWN_ISSUER = SEC_ERROR_BASE + 13; const SEC_ERROR_BAD_DATABASE = SEC_ERROR_BASE + 18; @@ -53,17 +53,17 @@ const SEC_ERROR_OCSP_OLD_RESPONSE const SEC_ERROR_OCSP_INVALID_SIGNING_CERT = SEC_ERROR_BASE + 144; const SEC_ERROR_POLICY_VALIDATION_FAILED = SEC_ERROR_BASE + 160; // -8032 const SEC_ERROR_OCSP_BAD_SIGNATURE = SEC_ERROR_BASE + 157; const SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED = SEC_ERROR_BASE + 176; const SEC_ERROR_APPLICATION_CALLBACK_ERROR = SEC_ERROR_BASE + 178; const SSL_ERROR_BAD_CERT_DOMAIN = SSL_ERROR_BASE + 12; -const PSM_ERROR_KEY_PINNING_FAILURE = PSM_ERROR_BASE + 0; +const MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE = MOZILLA_PKIX_ERROR_BASE + 0; // Supported Certificate Usages const certificateUsageSSLClient = 0x0001; const certificateUsageSSLServer = 0x0002; const certificateUsageSSLCA = 0x0008; const certificateUsageEmailSigner = 0x0010; const certificateUsageEmailRecipient = 0x0020; const certificateUsageObjectSigner = 0x0040;
--- a/security/manager/ssl/tests/unit/test_pinning.js +++ b/security/manager/ssl/tests/unit/test_pinning.js @@ -34,17 +34,17 @@ function test_strict() { // test mode. add_test(function() { Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 2); run_next_test(); }); // Issued by otherCA, which is not in the pinset for pinning.example.com. add_connection_test("bad.include-subdomains.pinning.example.com", - getXPCOMStatusFromNSS(PSM_ERROR_KEY_PINNING_FAILURE)); + getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE)); // These domains serve certs that match the pinset. add_connection_test("include-subdomains.pinning.example.com", Cr.NS_OK); add_connection_test("good.include-subdomains.pinning.example.com", Cr.NS_OK); add_connection_test("exclude-subdomains.pinning.example.com", Cr.NS_OK); // This domain serves a cert that doesn't match the pinset, but subdomains // are excluded. @@ -96,33 +96,33 @@ function test_enforce_test_mode() { // In enforce test mode, we always enforce all pins, even test pins. add_test(function() { Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 3); run_next_test(); }); // Issued by otherCA, which is not in the pinset for pinning.example.com. add_connection_test("bad.include-subdomains.pinning.example.com", - getXPCOMStatusFromNSS(PSM_ERROR_KEY_PINNING_FAILURE)); + getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE)); // These domains serve certs that match the pinset. add_connection_test("include-subdomains.pinning.example.com", Cr.NS_OK); add_connection_test("good.include-subdomains.pinning.example.com", Cr.NS_OK); add_connection_test("exclude-subdomains.pinning.example.com", Cr.NS_OK); // This domain serves a cert that doesn't match the pinset, but subdomains // are excluded. add_connection_test("sub.exclude-subdomains.pinning.example.com", Cr.NS_OK); // This domain's pinset is exactly the same as // include-subdomains.pinning.example.com, serves the same cert as // bad.include-subdomains.pinning.example.com, is in test-mode, but we are // enforcing test mode pins. add_connection_test("test-mode.pinning.example.com", - getXPCOMStatusFromNSS(PSM_ERROR_KEY_PINNING_FAILURE)); + getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE)); } function check_pinning_telemetry() { let service = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry); let prod_histogram = service.getHistogramById("CERT_PINNING_RESULTS") .snapshot(); let test_histogram = service.getHistogramById("CERT_PINNING_TEST_RESULTS") .snapshot();
--- a/security/pkix/include/pkix/Input.h +++ b/security/pkix/include/pkix/Input.h @@ -22,16 +22,17 @@ * limitations under the License. */ #ifndef mozilla_pkix__Input_h #define mozilla_pkix__Input_h #include "pkix/nullptr.h" #include "pkix/Result.h" +#include "seccomon.h" #include "stdint.h" namespace mozilla { namespace pkix { // Expect* functions advance the input mark and return Success if the input // matches the given criteria; they fail with the input mark in an undefined // state if the input does not match the criteria. // @@ -50,21 +51,21 @@ public: , end(nullptr) { } Result Init(const uint8_t* data, size_t len) { if (input) { // already initialized - return Fail(SEC_ERROR_INVALID_ARGS); + return Result::FATAL_ERROR_INVALID_ARGS; } if (!data || len > 0xffffu) { // input too large - return Fail(SEC_ERROR_BAD_DER); + return Result::ERROR_BAD_DER; } // XXX: this->input = input bug was not caught by tests! Why not? // this->end = end bug was not caught by tests! Why not? this->input = data; this->end = data + len; return Success; @@ -72,17 +73,17 @@ public: Result Expect(const uint8_t* expected, uint16_t expectedLen) { Result rv = EnsureLength(expectedLen); if (rv != Success) { return rv; } if (memcmp(input, expected, expectedLen)) { - return Fail(SEC_ERROR_BAD_DER); + return Result::ERROR_BAD_DER; } input += expectedLen; return Success; } bool Peek(uint8_t expectedByte) const { return input < end && *input == expectedByte; @@ -191,17 +192,17 @@ public: void SkipToEnd() { input = end; } Result EnsureLength(uint16_t len) { if (static_cast<size_t>(end - input) < len) { - return Fail(SEC_ERROR_BAD_DER); + return Result::ERROR_BAD_DER; } return Success; } bool AtEnd() const { return input == end; } class Mark { @@ -214,17 +215,17 @@ public: }; Mark GetMark() const { return Mark(*this, input); } Result GetSECItem(SECItemType type, const Mark& mark, /*out*/ SECItem& item) { if (&mark.input != this || mark.mark > input) { PR_NOT_REACHED("invalid mark"); - return Fail(SEC_ERROR_INVALID_ARGS); + return Result::FATAL_ERROR_INVALID_ARGS; } item.type = type; item.data = const_cast<uint8_t*>(mark.mark); item.len = static_cast<decltype(item.len)>(input - mark.mark); return Success; } private:
--- a/security/pkix/include/pkix/Result.h +++ b/security/pkix/include/pkix/Result.h @@ -20,70 +20,89 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef mozilla_pkix__Result_h #define mozilla_pkix__Result_h -#include "prerror.h" -#include "seccomon.h" -#include "secerr.h" +#include "pkix/enumclass.h" namespace mozilla { namespace pkix { -enum Result +static const unsigned int FATAL_ERROR_FLAG = 0x800; + +MOZILLA_PKIX_ENUM_CLASS Result { Success = 0, - FatalError = -1, // An error was encountered that caused path building - // to stop immediately. example: out-of-memory. - RecoverableError = -2 // an error that will cause path building to continue - // searching for alternative paths. example: expired - // certificate. + + ERROR_BAD_DER = 1, + ERROR_CA_CERT_INVALID = 2, + ERROR_BAD_SIGNATURE = 3, + ERROR_CERT_BAD_ACCESS_LOCATION = 4, + ERROR_CERT_NOT_IN_NAME_SPACE = 5, + ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED = 6, + ERROR_CONNECT_REFUSED = 7, + ERROR_EXPIRED_CERTIFICATE = 8, + ERROR_EXTENSION_VALUE_INVALID = 9, + ERROR_INADEQUATE_CERT_TYPE = 10, + ERROR_INADEQUATE_KEY_USAGE = 11, + ERROR_INVALID_ALGORITHM = 12, + ERROR_INVALID_TIME = 13, + ERROR_KEY_PINNING_FAILURE = 14, + ERROR_PATH_LEN_CONSTRAINT_INVALID = 15, + ERROR_POLICY_VALIDATION_FAILED = 16, + ERROR_REVOKED_CERTIFICATE = 17, + ERROR_UNKNOWN_CRITICAL_EXTENSION = 18, + ERROR_UNKNOWN_ISSUER = 19, + ERROR_UNTRUSTED_CERT = 20, + ERROR_UNTRUSTED_ISSUER = 21, + + ERROR_OCSP_BAD_SIGNATURE = 22, + ERROR_OCSP_INVALID_SIGNING_CERT = 23, + ERROR_OCSP_MALFORMED_REQUEST = 24, + ERROR_OCSP_MALFORMED_RESPONSE = 25, + ERROR_OCSP_OLD_RESPONSE = 26, + ERROR_OCSP_REQUEST_NEEDS_SIG = 27, + ERROR_OCSP_RESPONDER_CERT_INVALID = 28, + ERROR_OCSP_SERVER_ERROR = 29, + ERROR_OCSP_TRY_SERVER_LATER = 30, + ERROR_OCSP_UNAUTHORIZED_REQUEST = 31, + ERROR_OCSP_UNKNOWN_RESPONSE_STATUS = 32, + ERROR_OCSP_UNKNOWN_CERT = 33, + ERROR_OCSP_FUTURE_RESPONSE = 34, + + ERROR_UNKNOWN_ERROR = 35, + + ERROR_INVALID_KEY = 36, + ERROR_UNSUPPORTED_KEYALG = 37, + + // Keep this in sync with MAP_LIST in pkixnss.cpp + + FATAL_ERROR_INVALID_ARGS = FATAL_ERROR_FLAG | 1, + FATAL_ERROR_INVALID_STATE = FATAL_ERROR_FLAG | 2, + FATAL_ERROR_LIBRARY_FAILURE = FATAL_ERROR_FLAG | 3, + FATAL_ERROR_NO_MEMORY = FATAL_ERROR_FLAG | 4, + + // Keep this in sync with MAP_LIST in pkixnss.cpp }; -// When returning errors, use this function instead of calling PR_SetError -// directly. This helps ensure that we always call PR_SetError when we return -// an error code. This is a useful place to set a breakpoint when a debugging -// a certificate verification failure. -inline Result -Fail(Result result, PRErrorCode errorCode) -{ - PR_ASSERT(result != Success); - PR_SetError(errorCode, 0); - return result; -} - -inline Result -MapSECStatus(SECStatus srv) -{ - if (srv == SECSuccess) { - return Success; - } +// We write many comparisons as (x != Success), and this shortened name makes +// those comparisons clearer, especially because the shortened name often +// results in less line wrapping. +// +// Visual Studio before VS2013 does not support "enum class," so +// Result::Success will already be visible in this scope, and compilation will +// fail if we try to define a variable with that name here. +#if !defined(_MSC_VER) || (_MSC_VER >= 1700) +static const Result Success = Result::Success; +#endif - PRErrorCode error = PORT_GetError(); - switch (error) { - case SEC_ERROR_EXTENSION_NOT_FOUND: - return RecoverableError; - - case PR_INVALID_STATE_ERROR: - case SEC_ERROR_INVALID_ARGS: - case SEC_ERROR_LIBRARY_FAILURE: - case SEC_ERROR_NO_MEMORY: - return FatalError; - } - - // TODO: PORT_Assert(false); // we haven't classified the error yet - return RecoverableError; -} - -inline Result -Fail(PRErrorCode errorCode) +inline bool +IsFatalError(Result rv) { - PR_ASSERT(errorCode != 0); - PR_SetError(errorCode, 0); - return mozilla::pkix::MapSECStatus(SECFailure); + return static_cast<unsigned int>(rv) & FATAL_ERROR_FLAG; } } } // namespace mozilla::pkix #endif // mozilla_pkix__Result_h
--- a/security/pkix/include/pkix/pkix.h +++ b/security/pkix/include/pkix/pkix.h @@ -20,19 +20,17 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef mozilla_pkix__pkix_h #define mozilla_pkix__pkix_h -#include "pkix/nullptr.h" #include "pkixtypes.h" -#include "prtime.h" namespace mozilla { namespace pkix { // ---------------------------------------------------------------------------- // LIMITED SUPPORT FOR CERTIFICATE POLICIES // // If SEC_OID_X509_ANY_POLICY is passed as the value of the requiredPolicy // parameter then all policy validation will be skipped. Otherwise, path @@ -55,93 +53,74 @@ namespace mozilla { namespace pkix { // // BuildCertChain prioritizes certain checks ahead of others so that when a // certificate chain has multiple errors, the "most serious" error is // returned. In practice, this ranking of seriousness is tied directly to how // Firefox's certificate error override mechanism. // // The ranking is: // -// 1. Active distrust (SEC_ERROR_UNTRUSTED_CERT). +// 1. Active distrust (Result::ERROR_UNTRUSTED_CERT). // 2. Problems with issuer-independent properties for CA certificates. -// 3. Unknown issuer (SEC_ERROR_UNKNOWN_ISSUER). +// 3. Unknown issuer (Result::ERROR_UNKNOWN_ISSUER). // 4. Problems with issuer-independent properties for EE certificates. // 5. Revocation. // -// In particular, if BuildCertChain returns SEC_ERROR_UNKNOWN_ISSUER then the -// caller can call CERT_CheckCertValidTimes to determine if the certificate is -// ALSO expired. +// In particular, if BuildCertChain returns Result::ERROR_UNKNOWN_ISSUER then +// the caller can call CERT_CheckCertValidTimes to determine if the certificate +// is ALSO expired. // // It would be better if revocation were prioritized above expiration and // unknown issuer. However, it is impossible to do revocation checking without // knowing the issuer, since the issuer information is needed to validate the // revocation information. Also, generally revocation checking only works // during the validity period of the certificate. // // In general, when path building fails, BuildCertChain will return -// SEC_ERROR_UNKNOWN_ISSUER. However, if all attempted paths resulted in the -// same error (which is trivially true when there is only one potential path), -// more specific errors will be returned. +// Result::ERROR_UNKNOWN_ISSUER. However, if all attempted paths resulted in +// the same error (which is trivially true when there is only one potential +// path), more specific errors will be returned. // // ---------------------------------------------------------------------------- // Meaning of specific error codes // -// SEC_ERROR_UNTRUSTED_CERT means that the end-entity certificate was actively -// distrusted. -// SEC_ERROR_UNTRUSTED_ISSUER means that path building failed because of active -// distrust. +// Result::ERROR_UNTRUSTED_CERT means that the end-entity certificate was +// actively distrusted. +// Result::SEC_ERROR_UNTRUSTED_ISSUER means that path building failed because +// of active distrust. // TODO(bug 968451): Document more of these. -SECStatus BuildCertChain(TrustDomain& trustDomain, const SECItem& cert, - PRTime time, EndEntityOrCA endEntityOrCA, - KeyUsage requiredKeyUsageIfPresent, - KeyPurposeId requiredEKUIfPresent, - const CertPolicyId& requiredPolicy, - /*optional*/ const SECItem* stapledOCSPResponse); +Result BuildCertChain(TrustDomain& trustDomain, const SECItem& cert, + PRTime time, EndEntityOrCA endEntityOrCA, + KeyUsage requiredKeyUsageIfPresent, + KeyPurposeId requiredEKUIfPresent, + const CertPolicyId& requiredPolicy, + /*optional*/ const SECItem* stapledOCSPResponse); -// Verify the given signed data using the given public key. -SECStatus VerifySignedData(const SignedDataWithSignature& sd, - const SECItem& subjectPublicKeyInfo, - void* pkcs11PinArg); - -// The return value, if non-null, is owned by the arena and MUST NOT be freed. -SECItem* CreateEncodedOCSPRequest(TrustDomain& trustDomain, PLArenaPool* arena, - const CertID& certID); +static const size_t OCSP_REQUEST_MAX_LENGTH = 127; +Result CreateEncodedOCSPRequest(TrustDomain& trustDomain, + const struct CertID& certID, + /*out*/ uint8_t (&out)[OCSP_REQUEST_MAX_LENGTH], + /*out*/ size_t& outLen); // The out parameter expired will be true if the response has expired. If the // response also indicates a revoked or unknown certificate, that error -// will be returned by PR_GetError(). Otherwise, SEC_ERROR_OCSP_OLD_RESPONSE -// will be returned by PR_GetError() for an expired response. +// will be returned. Otherwise, REsult::ERROR_OCSP_OLD_RESPONSE will be +// returned for an expired response. +// // The optional parameter thisUpdate will be the thisUpdate value of // the encoded response if it is considered trustworthy. Only // good, unknown, or revoked responses that verify correctly are considered // trustworthy. If the response is not trustworthy, thisUpdate will be 0. // Similarly, the optional parameter validThrough will be the time through // which the encoded response is considered trustworthy (that is, if a response had a // thisUpdate time of validThrough, it would be considered trustworthy). -SECStatus VerifyEncodedOCSPResponse(TrustDomain& trustDomain, - const CertID& certID, PRTime time, - uint16_t maxLifetimeInDays, - const SECItem& encodedResponse, - /* out */ bool& expired, - /* optional out */ PRTime* thisUpdate = nullptr, - /* optional out */ PRTime* validThrough = nullptr); - -// Computes the SHA-1 hash of the data in the current item. -// -// item contains the data to hash. -// digestBuf must point to a buffer to where the SHA-1 hash will be written. -// digestBufLen must be 20 (the length of a SHA-1 hash, -// TrustDomain::DIGEST_LENGTH). -// -// TODO(bug 966856): Add SHA-2 support -// TODO: Taking the output buffer as (uint8_t*, size_t) is counter to our -// other, extensive, memory safety efforts in mozilla::pkix, and we should find -// a way to provide a more-obviously-safe interface. -SECStatus DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf, - size_t digestBufLen); - -// Checks, for RSA keys and DSA keys, that the modulus is at least 1024 bits. -SECStatus CheckPublicKey(const SECItem& subjectPublicKeyInfo); +Result VerifyEncodedOCSPResponse(TrustDomain& trustDomain, + const CertID& certID, PRTime time, + uint16_t maxLifetimeInDays, + const SECItem& encodedResponse, + /* out */ bool& expired, + /* optional out */ PRTime* thisUpdate = nullptr, + /* optional out */ PRTime* validThrough = nullptr); } } // namespace mozilla::pkix #endif // mozilla_pkix__pkix_h
new file mode 100644 --- /dev/null +++ b/security/pkix/include/pkix/pkixnss.h @@ -0,0 +1,81 @@ +/* -*- 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: + */ +/* 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/. + */ +/* Copyright 2013 Mozilla Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef mozilla_pkix__pkixnss_h +#define mozilla_pkix__pkixnss_h + +#include "pkixtypes.h" +#include "prerror.h" +#include "seccomon.h" + +namespace mozilla { namespace pkix { + +// Verify the given signed data using the given public key. +Result VerifySignedData(const SignedDataWithSignature& sd, + const SECItem& subjectPublicKeyInfo, + void* pkcs11PinArg); + +// Computes the SHA-1 hash of the data in the current item. +// +// item contains the data to hash. +// digestBuf must point to a buffer to where the SHA-1 hash will be written. +// digestBufLen must be 20 (the length of a SHA-1 hash, +// TrustDomain::DIGEST_LENGTH). +// +// TODO(bug 966856): Add SHA-2 support +// TODO: Taking the output buffer as (uint8_t*, size_t) is counter to our +// other, extensive, memory safety efforts in mozilla::pkix, and we should find +// a way to provide a more-obviously-safe interface. +Result DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf, + size_t digestBufLen); + +// Checks, for RSA keys and DSA keys, that the modulus is at least 1024 bits. +Result CheckPublicKey(const SECItem& subjectPublicKeyInfo); + +Result MapPRErrorCodeToResult(PRErrorCode errorCode); +PRErrorCode MapResultToPRErrorCode(Result result); + +// Returns the stringified name of the given result, e.g. "Result::Success", +// or nullptr if result is unknown (invalid). +const char* MapResultToName(Result result); + +// The error codes within each module must fit in 16 bits. We want these +// errors to fit in the same module as the NSS errors but not overlap with +// any of them. Converting an NSS SEC, NSS SSL, or PSM error to an NS error +// involves negating the value of the error and then synthesizing an error +// in the NS_ERROR_MODULE_SECURITY module. Hence, PSM errors will start at +// a negative value that both doesn't overlap with the current value +// ranges for NSS errors and that will fit in 16 bits when negated. +static const PRErrorCode ERROR_BASE = -0x4000; +static const PRErrorCode ERROR_LIMIT = ERROR_BASE + 1000; + +enum ErrorCode { + MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE = ERROR_BASE + 0 +}; + +void RegisterErrorTable(); + +} } // namespace mozilla::pkix + +#endif // mozilla_pkix__pkixnss_h
--- a/security/pkix/include/pkix/pkixtypes.h +++ b/security/pkix/include/pkix/pkixtypes.h @@ -20,17 +20,17 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef mozilla_pkix__pkixtypes_h #define mozilla_pkix__pkixtypes_h -#include "pkix/enumclass.h" +#include "pkix/Result.h" #include "pkix/nullptr.h" #include "prtime.h" #include "seccomon.h" #include "stdint.h" namespace mozilla { namespace pkix { MOZILLA_PKIX_ENUM_CLASS DigestAlgorithm @@ -193,34 +193,34 @@ public: // When policy.IsAnyPolicy(), then no policy-related checking should be done. // When !policy.IsAnyPolicy(), then GetCertTrust MUST NOT return with // *trustLevel == TrustAnchor unless the given cert is considered a trust // anchor *for that policy*. In particular, if the user has marked an // intermediate certificate as trusted, but that intermediate isn't in the // list of EV roots, then GetCertTrust must result in // *trustLevel == InheritsTrust instead of *trustLevel == TrustAnchor // (assuming the candidate cert is not actively distrusted). - virtual SECStatus GetCertTrust(EndEntityOrCA endEntityOrCA, - const CertPolicyId& policy, - const SECItem& candidateCertDER, - /*out*/ TrustLevel* trustLevel) = 0; + virtual Result GetCertTrust(EndEntityOrCA endEntityOrCA, + const CertPolicyId& policy, + const SECItem& candidateCertDER, + /*out*/ TrustLevel* trustLevel) = 0; class IssuerChecker { public: // potentialIssuerDER is the complete DER encoding of the certificate to // be checked as a potential issuer. // // If additionalNameConstraints is not nullptr then it must point to an // encoded NameConstraints extension value; in that case, those name // constraints will be checked in addition to any any name constraints // contained in potentialIssuerDER. - virtual SECStatus Check(const SECItem& potentialIssuerDER, - /*optional*/ const SECItem* additionalNameConstraints, - /*out*/ bool& keepGoing) = 0; + virtual Result Check(const SECItem& potentialIssuerDER, + /*optional*/ const SECItem* additionalNameConstraints, + /*out*/ bool& keepGoing) = 0; protected: IssuerChecker(); virtual ~IssuerChecker(); private: IssuerChecker(const IssuerChecker&) /*= delete*/; void operator=(const IssuerChecker&) /*= delete*/; }; @@ -260,18 +260,18 @@ public: // [...] // TrustDomain::FindIssuer // [...] // IssuerChecker::Check // [...] // // checker.Check is responsible for limiting the recursion to a reasonable // limit. - virtual SECStatus FindIssuer(const SECItem& encodedIssuerName, - IssuerChecker& checker, PRTime time) = 0; + virtual Result FindIssuer(const SECItem& encodedIssuerName, + IssuerChecker& checker, PRTime time) = 0; // Called as soon as we think we have a valid chain but before revocation // checks are done. This function can be used to compute additional checks, // especilaly checks that require the entire certificate chain. This callback // can also be used to save a copy of the built certificate chain for later // use. // // This function may be called multiple times, regardless of whether it @@ -284,57 +284,56 @@ public: // Keep in mind, in particular, that if the application saves a copy of the // certificate chain the last invocation of IsChainValid during a validation, // it is still possible for BuildCertChain to fail (return SECFailure), in // which case the application must not assume anything about the validity of // the last certificate chain passed to IsChainValid; especially, it would be // very wrong to assume that the certificate chain is valid. // // certChain.GetDER(0) is the trust anchor. - virtual SECStatus IsChainValid(const DERArray& certChain) = 0; + virtual Result IsChainValid(const DERArray& certChain) = 0; // issuerCertToDup is only non-const so CERT_DupCertificate can be called on // it. - virtual SECStatus CheckRevocation(EndEntityOrCA endEntityOrCA, - const CertID& certID, PRTime time, - /*optional*/ const SECItem* stapledOCSPresponse, - /*optional*/ const SECItem* aiaExtension) = 0; + virtual Result CheckRevocation(EndEntityOrCA endEntityOrCA, + const CertID& certID, PRTime time, + /*optional*/ const SECItem* stapledOCSPresponse, + /*optional*/ const SECItem* aiaExtension) = 0; + + // Check that the key size, algorithm, and parameters of the given public key + // are acceptable. + // + // VerifySignedData() should do the same checks that this function does, but + // mainly for efficiency, some keys are not passed to VerifySignedData(). + // This function is called instead for those keys. + virtual Result CheckPublicKey(const SECItem& subjectPublicKeyInfo) = 0; // Verify the given signature using the given public key. // // Most implementations of this function should probably forward the call // directly to mozilla::pkix::VerifySignedData. // // In any case, the implementation must perform checks on the public key // similar to how mozilla::pkix::CheckPublicKey() does. - virtual SECStatus VerifySignedData(const SignedDataWithSignature& signedData, - const SECItem& subjectPublicKeyInfo) = 0; + virtual Result VerifySignedData(const SignedDataWithSignature& signedData, + const SECItem& subjectPublicKeyInfo) = 0; // Compute the SHA-1 hash of the data in the current item. // // item contains the data to hash. // digestBuf must point to a buffer to where the SHA-1 hash will be written. // digestBufLen must be DIGEST_LENGTH (20, the length of a SHA-1 hash). // // TODO(bug 966856): Add SHA-2 support // TODO: Taking the output buffer as (uint8_t*, size_t) is counter to our // other, extensive, memory safety efforts in mozilla::pkix, and we should // find a way to provide a more-obviously-safe interface. static const size_t DIGEST_LENGTH = 20; // length of SHA-1 digest - virtual SECStatus DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf, - size_t digestBufLen) = 0; - - // Check that the key size, algorithm, and parameters of the given public key - // are acceptable. - // - // VerifySignedData() should do the same checks that this function does, but - // mainly for efficiency, some keys are not passed to VerifySignedData(). - // This function is called instead for those keys. - virtual SECStatus CheckPublicKey(const SECItem& subjectPublicKeyInfo) = 0; - + virtual Result DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf, + size_t digestBufLen) = 0; protected: TrustDomain() { } private: TrustDomain(const TrustDomain&) /* = delete */; void operator=(const TrustDomain&) /* = delete */; };
--- a/security/pkix/lib/pkixbuild.cpp +++ b/security/pkix/lib/pkixbuild.cpp @@ -54,158 +54,152 @@ public: unsigned int subCACount) : trustDomain(trustDomain) , subject(subject) , time(time) , requiredEKUIfPresent(requiredEKUIfPresent) , requiredPolicy(requiredPolicy) , stapledOCSPResponse(stapledOCSPResponse) , subCACount(subCACount) - , result(SEC_ERROR_LIBRARY_FAILURE) + , result(Result::FATAL_ERROR_LIBRARY_FAILURE) , resultWasSet(false) { } - SECStatus Check(const SECItem& potentialIssuerDER, - /*optional*/ const SECItem* additionalNameConstraints, - /*out*/ bool& keepGoing); + Result Check(const SECItem& potentialIssuerDER, + /*optional*/ const SECItem* additionalNameConstraints, + /*out*/ bool& keepGoing); Result CheckResult() const; private: TrustDomain& trustDomain; const BackCert& subject; const PRTime time; const KeyPurposeId requiredEKUIfPresent; const CertPolicyId& requiredPolicy; /*optional*/ SECItem const* const stapledOCSPResponse; const unsigned int subCACount; - SECStatus RecordResult(PRErrorCode currentResult, /*out*/ bool& keepGoing); - PRErrorCode result; + Result RecordResult(Result currentResult, /*out*/ bool& keepGoing); + Result result; bool resultWasSet; PathBuildingStep(const PathBuildingStep&) /*= delete*/; void operator=(const PathBuildingStep&) /*= delete*/; }; -SECStatus -PathBuildingStep::RecordResult(PRErrorCode newResult, - /*out*/ bool& keepGoing) +Result +PathBuildingStep::RecordResult(Result newResult, /*out*/ bool& keepGoing) { - if (newResult == SEC_ERROR_UNTRUSTED_CERT) { - newResult = SEC_ERROR_UNTRUSTED_ISSUER; + if (newResult == Result::ERROR_UNTRUSTED_CERT) { + newResult = Result::ERROR_UNTRUSTED_ISSUER; } + if (resultWasSet) { - if (result == 0) { + if (result == Success) { PR_NOT_REACHED("RecordResult called after finding a chain"); - PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0); - return SECFailure; + return Result::FATAL_ERROR_INVALID_STATE; } // If every potential issuer has the same problem (e.g. expired) and/or if // there is only one bad potential issuer, then return a more specific // error. Otherwise, punt on trying to decide which error should be - // returned by returning the generic SEC_ERROR_UNKNOWN_ISSUER error. - if (newResult != 0 && newResult != result) { - newResult = SEC_ERROR_UNKNOWN_ISSUER; + // returned by returning the generic Result::ERROR_UNKNOWN_ISSUER error. + if (newResult != Success && newResult != result) { + newResult = Result::ERROR_UNKNOWN_ISSUER; } } result = newResult; resultWasSet = true; - keepGoing = result != 0; - return SECSuccess; + keepGoing = result != Success; + return Success; } Result PathBuildingStep::CheckResult() const { if (!resultWasSet) { - return Fail(RecoverableError, SEC_ERROR_UNKNOWN_ISSUER); + return Result::ERROR_UNKNOWN_ISSUER; } - if (result == 0) { - return Success; - } - PR_SetError(result, 0); - return MapSECStatus(SECFailure); + return result; } // The code that executes in the inner loop of BuildForward -SECStatus +Result PathBuildingStep::Check(const SECItem& potentialIssuerDER, /*optional*/ const SECItem* additionalNameConstraints, /*out*/ bool& keepGoing) { BackCert potentialIssuer(potentialIssuerDER, EndEntityOrCA::MustBeCA, &subject); Result rv = potentialIssuer.Init(); if (rv != Success) { - return RecordResult(PR_GetError(), keepGoing); + return RecordResult(rv, keepGoing); } // RFC5280 4.2.1.1. Authority Key Identifier // RFC5280 4.2.1.2. Subject Key Identifier // Loop prevention, done as recommended by RFC4158 Section 5.2 // TODO: this doesn't account for subjectAltNames! // TODO(perf): This probably can and should be optimized in some way. bool loopDetected = false; for (const BackCert* prev = potentialIssuer.childCert; !loopDetected && prev != nullptr; prev = prev->childCert) { if (SECITEM_ItemsAreEqual(&potentialIssuer.GetSubjectPublicKeyInfo(), &prev->GetSubjectPublicKeyInfo()) && SECITEM_ItemsAreEqual(&potentialIssuer.GetSubject(), &prev->GetSubject())) { // XXX: error code - return RecordResult(SEC_ERROR_UNKNOWN_ISSUER, keepGoing); + return RecordResult(Result::ERROR_UNKNOWN_ISSUER, keepGoing); } } if (potentialIssuer.GetNameConstraints()) { rv = CheckNameConstraints(*potentialIssuer.GetNameConstraints(), subject, requiredEKUIfPresent); if (rv != Success) { - return RecordResult(PR_GetError(), keepGoing); + return RecordResult(rv, keepGoing); } } if (additionalNameConstraints) { rv = CheckNameConstraints(*additionalNameConstraints, subject, requiredEKUIfPresent); if (rv != Success) { - return RecordResult(PR_GetError(), keepGoing); + return RecordResult(rv, keepGoing); } } // RFC 5280, Section 4.2.1.3: "If the keyUsage extension is present, then the // subject public key MUST NOT be used to verify signatures on certificates // or CRLs unless the corresponding keyCertSign or cRLSign bit is set." rv = BuildForward(trustDomain, potentialIssuer, time, KeyUsage::keyCertSign, requiredEKUIfPresent, requiredPolicy, nullptr, subCACount); if (rv != Success) { - return RecordResult(PR_GetError(), keepGoing); + return RecordResult(rv, keepGoing); } - SECStatus srv = trustDomain.VerifySignedData( - subject.GetSignedData(), - potentialIssuer.GetSubjectPublicKeyInfo()); - if (srv != SECSuccess) { - return RecordResult(PR_GetError(), keepGoing); + rv = trustDomain.VerifySignedData(subject.GetSignedData(), + potentialIssuer.GetSubjectPublicKeyInfo()); + if (rv != Success) { + return RecordResult(rv, keepGoing); } CertID certID(subject.GetIssuer(), potentialIssuer.GetSubjectPublicKeyInfo(), subject.GetSerialNumber()); - srv = trustDomain.CheckRevocation(subject.endEntityOrCA, certID, time, - stapledOCSPResponse, - subject.GetAuthorityInfoAccess()); - if (srv != SECSuccess) { - return RecordResult(PR_GetError(), keepGoing); + rv = trustDomain.CheckRevocation(subject.endEntityOrCA, certID, time, + stapledOCSPResponse, + subject.GetAuthorityInfoAccess()); + if (rv != Success) { + return RecordResult(rv, keepGoing); } - return RecordResult(0/*PRErrorCode::success*/, keepGoing); + return RecordResult(Success, keepGoing); } // Recursively build the path from the given subject certificate to the root. // // Be very careful about changing the order of checks. The order is significant // because it affects which error we return when a certificate or certificate // chain has multiple problems. See the error ranking documentation in // pkix/pkix.h. @@ -224,116 +218,107 @@ BuildForward(TrustDomain& trustDomain, TrustLevel trustLevel; // If this is an end-entity and not a trust anchor, we defer reporting // any error found here until after attempting to find a valid chain. // See the explanation of error prioritization in pkix.h. rv = CheckIssuerIndependentProperties(trustDomain, subject, time, requiredKeyUsageIfPresent, requiredEKUIfPresent, requiredPolicy, subCACount, &trustLevel); - PRErrorCode deferredEndEntityError = 0; + Result deferredEndEntityError = Success; if (rv != Success) { if (subject.endEntityOrCA == EndEntityOrCA::MustBeEndEntity && trustLevel != TrustLevel::TrustAnchor) { - deferredEndEntityError = PR_GetError(); + deferredEndEntityError = rv; } else { return rv; } } if (trustLevel == TrustLevel::TrustAnchor) { // End of the recursion. NonOwningDERArray chain; for (const BackCert* cert = &subject; cert; cert = cert->childCert) { - Result rv = chain.Append(cert->GetDER()); + rv = chain.Append(cert->GetDER()); if (rv != Success) { PR_NOT_REACHED("NonOwningDERArray::SetItem failed."); return rv; } } // This must be done here, after the chain is built but before any // revocation checks have been done. - SECStatus srv = trustDomain.IsChainValid(chain); - if (srv != SECSuccess) { - return MapSECStatus(srv); - } - - return Success; + return trustDomain.IsChainValid(chain); } if (subject.endEntityOrCA == EndEntityOrCA::MustBeCA) { // Avoid stack overflows and poor performance by limiting cert chain // length. static const unsigned int MAX_SUBCA_COUNT = 6; static_assert(1/*end-entity*/ + MAX_SUBCA_COUNT + 1/*root*/ == NonOwningDERArray::MAX_LENGTH, "MAX_SUBCA_COUNT and NonOwningDERArray::MAX_LENGTH mismatch."); if (subCACount >= MAX_SUBCA_COUNT) { - return Fail(RecoverableError, SEC_ERROR_UNKNOWN_ISSUER); + return Result::ERROR_UNKNOWN_ISSUER; } ++subCACount; } else { PR_ASSERT(subCACount == 0); } // Find a trusted issuer. PathBuildingStep pathBuilder(trustDomain, subject, time, requiredEKUIfPresent, requiredPolicy, stapledOCSPResponse, subCACount); // TODO(bug 965136): Add SKI/AKI matching optimizations - if (trustDomain.FindIssuer(subject.GetIssuer(), pathBuilder, time) - != SECSuccess) { - return MapSECStatus(SECFailure); + rv = trustDomain.FindIssuer(subject.GetIssuer(), pathBuilder, time); + if (rv != Success) { + return rv; } rv = pathBuilder.CheckResult(); if (rv != Success) { return rv; } // If we found a valid chain but deferred reporting an error with the // end-entity certificate, report it now. - if (deferredEndEntityError != 0) { - return Fail(RecoverableError, deferredEndEntityError); + if (deferredEndEntityError != Success) { + return deferredEndEntityError; } // We've built a valid chain from the subject cert up to a trusted root. return Success; } -SECStatus +Result BuildCertChain(TrustDomain& trustDomain, const SECItem& certDER, PRTime time, EndEntityOrCA endEntityOrCA, KeyUsage requiredKeyUsageIfPresent, KeyPurposeId requiredEKUIfPresent, const CertPolicyId& requiredPolicy, /*optional*/ const SECItem* stapledOCSPResponse) { // XXX: Support the legacy use of the subject CN field for indicating the // domain name the certificate is valid for. BackCert cert(certDER, endEntityOrCA, nullptr); Result rv = cert.Init(); if (rv != Success) { - return SECFailure; + return rv; } // See documentation for CheckPublicKey() in pkixtypes.h for why the public // key also needs to be checked here when trustDomain.VerifySignedData() // should already be doing it. - if (trustDomain.CheckPublicKey(cert.GetSubjectPublicKeyInfo()) != SECSuccess) { - return SECFailure; + rv = trustDomain.CheckPublicKey(cert.GetSubjectPublicKeyInfo()); + if (rv != Success) { + return rv; } - rv = BuildForward(trustDomain, cert, time, requiredKeyUsageIfPresent, - requiredEKUIfPresent, requiredPolicy, stapledOCSPResponse, - 0/*subCACount*/); - if (rv != Success) { - return SECFailure; - } - - return SECSuccess; + return BuildForward(trustDomain, cert, time, requiredKeyUsageIfPresent, + requiredEKUIfPresent, requiredPolicy, stapledOCSPResponse, + 0/*subCACount*/); } } } // namespace mozilla::pkix
--- a/security/pkix/lib/pkixcert.cpp +++ b/security/pkix/lib/pkixcert.cpp @@ -238,21 +238,21 @@ BackCert::RememberExtension(Input& extnI } else if (extnID.MatchRest(id_pe_authorityInfoAccess)) { out = &authorityInfoAccess; } if (out) { // Don't allow an empty value for any extension we understand. This way, we // can test out->len to check for duplicates. if (extnValue.len == 0) { - return Fail(SEC_ERROR_EXTENSION_VALUE_INVALID); + return Result::ERROR_EXTENSION_VALUE_INVALID; } if (out->len != 0) { // Duplicate extension - return Fail(SEC_ERROR_EXTENSION_VALUE_INVALID); + return Result::ERROR_EXTENSION_VALUE_INVALID; } *out = extnValue; understood = true; } return Success; }
--- a/security/pkix/lib/pkixcheck.cpp +++ b/security/pkix/lib/pkixcheck.cpp @@ -23,41 +23,42 @@ */ #include "cert.h" #include "pkix/bind.h" #include "pkix/pkix.h" #include "pkix/ScopedPtr.h" #include "pkixcheck.h" #include "pkixder.h" +#include "pkix/pkixnss.h" #include "pkixutil.h" namespace mozilla { namespace pkix { Result CheckValidity(const SECItem& encodedValidity, PRTime time) { Input validity; if (validity.Init(encodedValidity.data, encodedValidity.len) != Success) { - return Fail(RecoverableError, SEC_ERROR_EXPIRED_CERTIFICATE); + return Result::ERROR_EXPIRED_CERTIFICATE; } PRTime notBefore; if (der::TimeChoice(validity, notBefore) != Success) { - return Fail(RecoverableError, SEC_ERROR_EXPIRED_CERTIFICATE); + return Result::ERROR_EXPIRED_CERTIFICATE; } if (time < notBefore) { - return Fail(RecoverableError, SEC_ERROR_EXPIRED_CERTIFICATE); + return Result::ERROR_EXPIRED_CERTIFICATE; } PRTime notAfter; if (der::TimeChoice(validity, notAfter) != Success) { - return Fail(RecoverableError, SEC_ERROR_EXPIRED_CERTIFICATE); + return Result::ERROR_EXPIRED_CERTIFICATE; } if (time > notAfter) { - return Fail(RecoverableError, SEC_ERROR_EXPIRED_CERTIFICATE); + return Result::ERROR_EXPIRED_CERTIFICATE; } return der::End(validity); } // 4.2.1.3. Key Usage (id-ce-keyUsage) // As explained in the comment in CheckKeyUsage, bit 0 is the most significant @@ -73,46 +74,46 @@ CheckKeyUsage(EndEntityOrCA endEntityOrC KeyUsage requiredKeyUsageIfPresent) { if (!encodedKeyUsage) { // TODO(bug 970196): Reject certificates that are being used to verify // certificate signatures unless the certificate is a trust anchor, to // reduce the chances of an end-entity certificate being abused as a CA // certificate. // if (endEntityOrCA == EndEntityOrCA::MustBeCA && !isTrustAnchor) { - // return Fail(RecoverableError, SEC_ERROR_INADEQUATE_KEY_USAGE); + // return Result::ERROR_INADEQUATE_KEY_USAGE; // } // // TODO: Users may configure arbitrary certificates as trust anchors, not // just roots. We should only allow a certificate without a key usage to be // used as a CA when it is self-issued and self-signed. return Success; } Input input; if (input.Init(encodedKeyUsage->data, encodedKeyUsage->len) != Success) { - return Fail(RecoverableError, SEC_ERROR_INADEQUATE_KEY_USAGE); + return Result::ERROR_INADEQUATE_KEY_USAGE; } Input value; if (der::ExpectTagAndGetValue(input, der::BIT_STRING, value) != Success) { - return Fail(RecoverableError, SEC_ERROR_INADEQUATE_KEY_USAGE); + return Result::ERROR_INADEQUATE_KEY_USAGE; } uint8_t numberOfPaddingBits; if (value.Read(numberOfPaddingBits) != Success) { - return Fail(RecoverableError, SEC_ERROR_INADEQUATE_KEY_USAGE); + return Result::ERROR_INADEQUATE_KEY_USAGE; } if (numberOfPaddingBits > 7) { - return Fail(RecoverableError, SEC_ERROR_INADEQUATE_KEY_USAGE); + return Result::ERROR_INADEQUATE_KEY_USAGE; } uint8_t bits; if (value.Read(bits) != Success) { // Reject empty bit masks. - return Fail(RecoverableError, SEC_ERROR_INADEQUATE_KEY_USAGE); + return Result::ERROR_INADEQUATE_KEY_USAGE; } // The most significant bit is numbered 0 (digitalSignature) and the least // significant bit is numbered 7 (encipherOnly), and the padding is in the // least significant bits of the last byte. The numbering of bits in a byte // is backwards from how we usually interpret them. // // For example, let's say bits is encoded in one byte with of value 0xB0 and @@ -143,41 +144,41 @@ CheckKeyUsage(EndEntityOrCA endEntityOrC // // Note that since the KeyUsage enumeration is limited to values 0-7, we // only ever need to examine the first byte test for // requiredKeyUsageIfPresent. if (requiredKeyUsageIfPresent != KeyUsage::noParticularKeyUsageRequired) { // Check that the required key usage bit is set. if ((bits & KeyUsageToBitMask(requiredKeyUsageIfPresent)) == 0) { - return Fail(RecoverableError, SEC_ERROR_INADEQUATE_KEY_USAGE); + return Result::ERROR_INADEQUATE_KEY_USAGE; } } if (endEntityOrCA != EndEntityOrCA::MustBeCA) { // RFC 5280 says "The keyCertSign bit is asserted when the subject public // key is used for verifying signatures on public key certificates. If the // keyCertSign bit is asserted, then the cA bit in the basic constraints // extension (Section 4.2.1.9) MUST also be asserted." if ((bits & KeyUsageToBitMask(KeyUsage::keyCertSign)) != 0) { - return Fail(RecoverableError, SEC_ERROR_INADEQUATE_KEY_USAGE); + return Result::ERROR_INADEQUATE_KEY_USAGE; } } // The padding applies to the last byte, so skip to the last byte. while (!value.AtEnd()) { if (value.Read(bits) != Success) { - return Fail(RecoverableError, SEC_ERROR_INADEQUATE_KEY_USAGE); + return Result::ERROR_INADEQUATE_KEY_USAGE; } } // All of the padding bits must be zero, according to DER rules. uint8_t paddingMask = static_cast<uint8_t>((1 << numberOfPaddingBits) - 1); if ((bits & paddingMask) != 0) { - return Fail(RecoverableError, SEC_ERROR_INADEQUATE_KEY_USAGE); + return Result::ERROR_INADEQUATE_KEY_USAGE; } return Success; } // RFC5820 4.2.1.4. Certificate Policies // "The user-initial-policy-set contains the special value any-policy if the @@ -234,63 +235,63 @@ Result CheckCertificatePolicies(EndEntityOrCA endEntityOrCA, const SECItem* encodedCertificatePolicies, const SECItem* encodedInhibitAnyPolicy, TrustLevel trustLevel, const CertPolicyId& requiredPolicy) { if (requiredPolicy.numBytes == 0 || requiredPolicy.numBytes > sizeof requiredPolicy.bytes) { - return Fail(FatalError, SEC_ERROR_INVALID_ARGS); + return Result::FATAL_ERROR_INVALID_ARGS; } // Ignore all policy information if the caller indicates any policy is // acceptable. See TrustDomain::GetCertTrust and the policy part of // BuildCertChain's documentation. if (requiredPolicy.IsAnyPolicy()) { return Success; } // Bug 989051. Until we handle inhibitAnyPolicy we will fail close when // inhibitAnyPolicy extension is present and we need to evaluate certificate // policies. if (encodedInhibitAnyPolicy) { - return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED); + return Result::ERROR_POLICY_VALIDATION_FAILED; } // The root CA certificate may omit the policies that it has been // trusted for, so we cannot require the policies to be present in those // certificates. Instead, the determination of which roots are trusted for // which policies is made by the TrustDomain's GetCertTrust method. if (trustLevel == TrustLevel::TrustAnchor && endEntityOrCA == EndEntityOrCA::MustBeCA) { return Success; } if (!encodedCertificatePolicies) { - return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED); + return Result::ERROR_POLICY_VALIDATION_FAILED; } bool found = false; Input input; if (input.Init(encodedCertificatePolicies->data, encodedCertificatePolicies->len) != Success) { - return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED); + return Result::ERROR_POLICY_VALIDATION_FAILED; } if (der::NestedOf(input, der::SEQUENCE, der::SEQUENCE, der::EmptyAllowed::No, bind(CheckPolicyInformation, _1, endEntityOrCA, requiredPolicy, ref(found))) != Success) { - return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED); + return Result::ERROR_POLICY_VALIDATION_FAILED; } if (der::End(input) != Success) { - return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED); + return Result::ERROR_POLICY_VALIDATION_FAILED; } if (!found) { - return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED); + return Result::ERROR_POLICY_VALIDATION_FAILED; } return Success; } static const long UNLIMITED_PATH_LEN = -1; // must be less than zero // BasicConstraints ::= SEQUENCE { @@ -302,25 +303,25 @@ DecodeBasicConstraints(Input& input, /*o { // TODO(bug 989518): cA is by default false. According to DER, default // values must not be explicitly encoded in a SEQUENCE. So, if this // value is present and false, it is an encoding error. However, Go Daddy // has issued many certificates with this improper encoding, so we can't // enforce this yet (hence passing true for allowInvalidExplicitEncoding // to der::OptionalBoolean). if (der::OptionalBoolean(input, true, isCA) != Success) { - return Fail(SEC_ERROR_EXTENSION_VALUE_INVALID); + return Result::ERROR_EXTENSION_VALUE_INVALID; } // TODO(bug 985025): If isCA is false, pathLenConstraint MUST NOT // be included (as per RFC 5280 section 4.2.1.9), but for compatibility // reasons, we don't check this for now. if (der::OptionalInteger(input, UNLIMITED_PATH_LEN, pathLenConstraint) != Success) { - return Fail(SEC_ERROR_EXTENSION_VALUE_INVALID); + return Result::ERROR_EXTENSION_VALUE_INVALID; } return Success; } // RFC5280 4.2.1.9. Basic Constraints (id-ce-basicConstraints) Result CheckBasicConstraints(EndEntityOrCA endEntityOrCA, @@ -330,25 +331,25 @@ CheckBasicConstraints(EndEntityOrCA endE { bool isCA = false; long pathLenConstraint = UNLIMITED_PATH_LEN; if (encodedBasicConstraints) { Input input; if (input.Init(encodedBasicConstraints->data, encodedBasicConstraints->len) != Success) { - return Fail(RecoverableError, SEC_ERROR_EXTENSION_VALUE_INVALID); + return Result::ERROR_EXTENSION_VALUE_INVALID; } if (der::Nested(input, der::SEQUENCE, bind(DecodeBasicConstraints, _1, ref(isCA), ref(pathLenConstraint))) != Success) { - return Fail(RecoverableError, SEC_ERROR_EXTENSION_VALUE_INVALID); + return Result::ERROR_EXTENSION_VALUE_INVALID; } if (der::End(input) != Success) { - return Fail(RecoverableError, SEC_ERROR_EXTENSION_VALUE_INVALID); + return Result::ERROR_EXTENSION_VALUE_INVALID; } } else { // "If the basic constraints extension is not present in a version 3 // certificate, or the extension is present but the cA boolean is not // asserted, then the certified public key MUST NOT be used to verify // certificate signatures." // // For compatibility, we must accept v1 trust anchors without basic @@ -360,102 +361,103 @@ CheckBasicConstraints(EndEntityOrCA endE isCA = true; } } if (endEntityOrCA == EndEntityOrCA::MustBeEndEntity) { // CA certificates are not trusted as EE certs. if (isCA) { - // XXX: We use SEC_ERROR_CA_CERT_INVALID here so we can distinguish - // this error from other errors, given that NSS does not have a "CA cert - // used as end-entity" error code since it doesn't have such a + // TODO(bug 1040446): We use Result::ERROR_CA_CERT_INVALID here so we can + // distinguish this error from other errors, given that NSS does not have + // a "CA cert used as end-entity" error code since it doesn't have such a // prohibition. We should add such an error code and stop abusing - // SEC_ERROR_CA_CERT_INVALID this way. + // Result::ERROR_CA_CERT_INVALID this way. // // Note, in particular, that this check prevents a delegated OCSP // response signing certificate with the CA bit from successfully // validating when we check it from pkixocsp.cpp, which is a good thing. // - return Fail(RecoverableError, SEC_ERROR_CA_CERT_INVALID); + return Result::ERROR_CA_CERT_INVALID; } return Success; } PORT_Assert(endEntityOrCA == EndEntityOrCA::MustBeCA); // End-entity certificates are not allowed to act as CA certs. if (!isCA) { - return Fail(RecoverableError, SEC_ERROR_CA_CERT_INVALID); + return Result::ERROR_CA_CERT_INVALID; } if (pathLenConstraint >= 0 && static_cast<long>(subCACount) > pathLenConstraint) { - return Fail(RecoverableError, SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID); + return Result::ERROR_PATH_LEN_CONSTRAINT_INVALID; } return Success; } // 4.2.1.10. Name Constraints inline void PORT_FreeArena_false(PLArenaPool* arena) { // PL_FreeArenaPool can't be used because it doesn't actually free the // memory, which doesn't work well with memory analysis tools return PORT_FreeArena(arena, PR_FALSE); } +// TODO: remove #include "pkix/pkixnss.h" when this is rewritten to be +// independent of NSS. Result CheckNameConstraints(const SECItem& encodedNameConstraints, const BackCert& firstChild, KeyPurposeId requiredEKUIfPresent) { ScopedPtr<PLArenaPool, PORT_FreeArena_false> arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); if (!arena) { - return MapSECStatus(SECFailure); + return Result::FATAL_ERROR_NO_MEMORY; } // Owned by arena const CERTNameConstraints* constraints = CERT_DecodeNameConstraintsExtension(arena.get(), &encodedNameConstraints); if (!constraints) { - return MapSECStatus(SECFailure); + return MapPRErrorCodeToResult(PR_GetError()); } for (const BackCert* child = &firstChild; child; child = child->childCert) { ScopedPtr<CERTCertificate, CERT_DestroyCertificate> nssCert(CERT_NewTempCertificate(CERT_GetDefaultCertDB(), const_cast<SECItem*>(&child->GetDER()), nullptr, false, true)); if (!nssCert) { - return MapSECStatus(SECFailure); + return MapPRErrorCodeToResult(PR_GetError()); } bool includeCN = child->endEntityOrCA == EndEntityOrCA::MustBeEndEntity && requiredEKUIfPresent == KeyPurposeId::id_kp_serverAuth; // owned by arena const CERTGeneralName* names(CERT_GetConstrainedCertificateNames(nssCert.get(), arena.get(), includeCN)); if (!names) { - return MapSECStatus(SECFailure); + return MapPRErrorCodeToResult(PR_GetError()); } CERTGeneralName* currentName = const_cast<CERTGeneralName*>(names); do { if (CERT_CheckNameSpace(arena.get(), constraints, currentName) != SECSuccess) { // XXX: It seems like CERT_CheckNameSpace doesn't always call - // PR_SetError when it fails. We set the error code here, though this - // may be papering over some fatal errors. NSS's - // cert_VerifyCertChainOld does something similar. - return Fail(RecoverableError, SEC_ERROR_CERT_NOT_IN_NAME_SPACE); + // PR_SetError when it fails, so we ignore what PR_GetError would + // return. NSS's cert_VerifyCertChainOld does something similar. + return Result::ERROR_CERT_NOT_IN_NAME_SPACE; } currentName = CERT_GetNextGeneralName(currentName); } while (currentName != names); } return Success; } @@ -517,21 +519,21 @@ MatchEKU(Input& value, KeyPurposeId requ break; case KeyPurposeId::id_kp_OCSPSigning: match = value.MatchRest(ocsp); break; case KeyPurposeId::anyExtendedKeyUsage: PR_NOT_REACHED("anyExtendedKeyUsage should start with found==true"); - return Fail(SEC_ERROR_LIBRARY_FAILURE); + return Result::FATAL_ERROR_LIBRARY_FAILURE; default: PR_NOT_REACHED("unrecognized EKU"); - return Fail(SEC_ERROR_LIBRARY_FAILURE); + return Result::FATAL_ERROR_LIBRARY_FAILURE; } } if (match) { found = true; if (requiredEKU == KeyPurposeId::id_kp_OCSPSigning) { foundOCSPSigning = true; } @@ -544,45 +546,45 @@ MatchEKU(Input& value, KeyPurposeId requ return Success; } Result CheckExtendedKeyUsage(EndEntityOrCA endEntityOrCA, const SECItem* encodedExtendedKeyUsage, KeyPurposeId requiredEKU) { - // XXX: We're using SEC_ERROR_INADEQUATE_CERT_TYPE here so that callers can - // distinguish EKU mismatch from KU mismatch from basic constraints mismatch. - // We should probably add a new error code that is more clear for this type - // of problem. + // XXX: We're using Result::ERROR_INADEQUATE_CERT_TYPE here so that callers + // can distinguish EKU mismatch from KU mismatch from basic constraints + // mismatch. We should probably add a new error code that is more clear for + // this type of problem. bool foundOCSPSigning = false; if (encodedExtendedKeyUsage) { bool found = requiredEKU == KeyPurposeId::anyExtendedKeyUsage; Input input; if (input.Init(encodedExtendedKeyUsage->data, encodedExtendedKeyUsage->len) != Success) { - return Fail(RecoverableError, SEC_ERROR_INADEQUATE_CERT_TYPE); + return Result::ERROR_INADEQUATE_CERT_TYPE; } if (der::NestedOf(input, der::SEQUENCE, der::OIDTag, der::EmptyAllowed::No, bind(MatchEKU, _1, requiredEKU, endEntityOrCA, ref(found), ref(foundOCSPSigning))) != Success) { - return Fail(RecoverableError, SEC_ERROR_INADEQUATE_CERT_TYPE); + return Result::ERROR_INADEQUATE_CERT_TYPE; } if (der::End(input) != Success) { - return Fail(RecoverableError, SEC_ERROR_INADEQUATE_CERT_TYPE); + return Result::ERROR_INADEQUATE_CERT_TYPE; } // If the EKU extension was included, then the required EKU must be in the // list. if (!found) { - return Fail(RecoverableError, SEC_ERROR_INADEQUATE_CERT_TYPE); + return Result::ERROR_INADEQUATE_CERT_TYPE; } } // pkixocsp.cpp depends on the following additional checks. if (endEntityOrCA == EndEntityOrCA::MustBeEndEntity) { // When validating anything other than an delegated OCSP signing cert, // reject any cert that also claims to be an OCSP responder, because such @@ -592,29 +594,29 @@ CheckExtendedKeyUsage(EndEntityOrCA endE // That said, we accept CA certificates with id-kp-OCSPSigning because // some CAs in Mozilla's CA program have issued such intermediate // certificates, and because some CAs have reported some Microsoft server // software wrongly requires CA certificates to have id-kp-OCSPSigning. // Allowing this exception does not cause any security issues because we // require delegated OCSP response signing certificates to be end-entity // certificates. if (foundOCSPSigning && requiredEKU != KeyPurposeId::id_kp_OCSPSigning) { - return Fail(RecoverableError, SEC_ERROR_INADEQUATE_CERT_TYPE); + return Result::ERROR_INADEQUATE_CERT_TYPE; } // http://tools.ietf.org/html/rfc6960#section-4.2.2.2: // "OCSP signing delegation SHALL be designated by the inclusion of // id-kp-OCSPSigning in an extended key usage certificate extension // included in the OCSP response signer's certificate." // // id-kp-OCSPSigning is the only EKU that isn't implicitly assumed when the // EKU extension is missing from an end-entity certificate. However, any CA // certificate can issue a delegated OCSP response signing certificate, so // we can't require the EKU be explicitly included for CA certificates. if (!foundOCSPSigning && requiredEKU == KeyPurposeId::id_kp_OCSPSigning) { - return Fail(RecoverableError, SEC_ERROR_INADEQUATE_CERT_TYPE); + return Result::ERROR_INADEQUATE_CERT_TYPE; } } return Success; } Result CheckIssuerIndependentProperties(TrustDomain& trustDomain, @@ -626,29 +628,28 @@ CheckIssuerIndependentProperties(TrustDo unsigned int subCACount, /*optional out*/ TrustLevel* trustLevelOut) { Result rv; const EndEntityOrCA endEntityOrCA = cert.endEntityOrCA; TrustLevel trustLevel; - rv = MapSECStatus(trustDomain.GetCertTrust(endEntityOrCA, requiredPolicy, - cert.GetDER(), &trustLevel)); + rv = trustDomain.GetCertTrust(endEntityOrCA, requiredPolicy, cert.GetDER(), + &trustLevel); if (rv != Success) { return rv; } if (trustLevel == TrustLevel::ActivelyDistrusted) { - return Fail(RecoverableError, SEC_ERROR_UNTRUSTED_CERT); + return Result::ERROR_UNTRUSTED_CERT; } if (trustLevel != TrustLevel::TrustAnchor && trustLevel != TrustLevel::InheritsTrust) { // The TrustDomain returned a trust level that we weren't expecting. - PORT_SetError(PR_INVALID_STATE_ERROR); - return FatalError; + return Result::FATAL_ERROR_INVALID_STATE; } if (trustLevelOut) { *trustLevelOut = trustLevel; } // 4.2.1.1. Authority Key Identifier is ignored (see bug 965136). // 4.2.1.2. Subject Key Identifier is ignored (see bug 965136).
--- a/security/pkix/lib/pkixder.cpp +++ b/security/pkix/lib/pkixder.cpp @@ -39,17 +39,17 @@ ExpectTagAndGetLength(Input& input, uint uint8_t tag; Result rv; rv = input.Read(tag); if (rv != Success) { return rv; } if (tag != expectedTag) { - return Fail(SEC_ERROR_BAD_DER); + return Result::ERROR_BAD_DER; } // The short form of length is a single byte with the high order bit set // to zero. The long form of length is one byte with the high order bit // set, followed by N bytes, where N is encoded in the lowest 7 bits of // the first byte. uint8_t length1; rv = input.Read(length1); @@ -61,31 +61,31 @@ ExpectTagAndGetLength(Input& input, uint } else if (length1 == 0x81) { uint8_t length2; rv = input.Read(length2); if (rv != Success) { return rv; } if (length2 < 128) { // Not shortest possible encoding - return Fail(SEC_ERROR_BAD_DER); + return Result::ERROR_BAD_DER; } length = length2; } else if (length1 == 0x82) { rv = input.Read(length); if (rv != Success) { return rv; } if (length < 256) { // Not shortest possible encoding - return Fail(SEC_ERROR_BAD_DER); + return Result::ERROR_BAD_DER; } } else { // We don't support lengths larger than 2^16 - 1. - return Fail(SEC_ERROR_BAD_DER); + return Result::ERROR_BAD_DER; } // Ensure the input is long enough for the length it says it has. return input.EnsureLength(length); } } // namespace internal @@ -127,17 +127,17 @@ DigestAlgorithmOIDValue(Input& algorithm algorithm = DigestAlgorithm::sha1; } else if (algorithmID.MatchRest(id_sha256)) { algorithm = DigestAlgorithm::sha256; } else if (algorithmID.MatchRest(id_sha384)) { algorithm = DigestAlgorithm::sha384; } else if (algorithmID.MatchRest(id_sha512)) { algorithm = DigestAlgorithm::sha512; } else { - return Fail(SEC_ERROR_INVALID_ALGORITHM); + return Result::ERROR_INVALID_ALGORITHM; } return Success; } Result SignatureAlgorithmOIDValue(Input& algorithmID, /*out*/ SignatureAlgorithm& algorithm) @@ -222,17 +222,17 @@ SignatureAlgorithmOIDValue(Input& algori } else if (algorithmID.MatchRest(sha512WithRSAEncryption)) { algorithm = SignatureAlgorithm::rsa_pkcs1_with_sha512; } else if (algorithmID.MatchRest(id_dsa_with_sha1)) { algorithm = SignatureAlgorithm::dsa_with_sha1; } else if (algorithmID.MatchRest(id_dsa_with_sha256)) { algorithm = SignatureAlgorithm::dsa_with_sha256; } else { // Any MD5-based signature algorithm, or any unknown signature algorithm. - return Fail(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED); + return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED; } return Success; } template <typename OidValueParser, typename Algorithm> Result AlgorithmIdentifier(OidValueParser oidValueParser, Input& input, @@ -299,43 +299,43 @@ SignedData(Input& input, /*out*/ Input& return rv; } rv = ExpectTagAndGetValue(input, BIT_STRING, signedData.signature); if (rv != Success) { return rv; } if (signedData.signature.len == 0) { - return Fail(SEC_ERROR_BAD_SIGNATURE); + return Result::ERROR_BAD_SIGNATURE; } unsigned int unusedBitsAtEnd = signedData.signature.data[0]; // XXX: Really the constraint should be that unusedBitsAtEnd must be less // than 7. But, we suspect there are no real-world OCSP responses or X.509 // certificates with non-zero unused bits. It seems like NSS assumes this in // various places, so we enforce it too in order to simplify this code. If we // find compatibility issues, we'll know we're wrong and we'll have to figure // out how to shift the bits around. if (unusedBitsAtEnd != 0) { - return Fail(SEC_ERROR_BAD_SIGNATURE); + return Result::ERROR_BAD_SIGNATURE; } ++signedData.signature.data; --signedData.signature.len; return Success; } static inline Result ReadDigit(Input& input, /*out*/ int& value) { uint8_t b; if (input.Read(b) != Success) { - return Fail(SEC_ERROR_INVALID_TIME); + return Result::ERROR_INVALID_TIME; } if (b < '0' || b > '9') { - return Fail(SEC_ERROR_INVALID_TIME); + return Result::ERROR_INVALID_TIME; } value = b - '0'; return Success; } static inline Result ReadTwoDigits(Input& input, int minValue, int maxValue, /*out*/ int& value) { @@ -346,17 +346,17 @@ ReadTwoDigits(Input& input, int minValue } int lo; rv = ReadDigit(input, lo); if (rv != Success) { return rv; } value = (hi * 10) + lo; if (value < minValue || value > maxValue) { - return Fail(SEC_ERROR_INVALID_TIME); + return Result::ERROR_INVALID_TIME; } return Success; } inline int daysBeforeYear(int year) { return (365 * (year - 1)) @@ -397,22 +397,22 @@ TimeChoice(Input& tagged, uint8_t expect } else if (expectedTag == UTCTime) { rv = ReadTwoDigits(input, 0, 99, yearLo); if (rv != Success) { return rv; } yearHi = yearLo >= 50 ? 19 : 20; } else { PR_NOT_REACHED("invalid tag given to TimeChoice"); - return Fail(SEC_ERROR_INVALID_TIME); + return Result::ERROR_INVALID_TIME; } int year = (yearHi * 100) + yearLo; if (year < 1970) { // We don't support dates before January 1, 1970 because that is the epoch. - return Fail(SEC_ERROR_INVALID_TIME); // TODO: better error code + return Result::ERROR_INVALID_TIME; } if (year > 1970) { // This is NOT equivalent to daysBeforeYear(year - 1970) because the // leap year calculations in daysBeforeYear only works on absolute years. days = daysBeforeYear(year) - daysBeforeYear(1970); // We subtract 1 because we're interested in knowing how many days there // were *before* the given year, relative to 1970. } else { @@ -461,17 +461,17 @@ TimeChoice(Input& tagged, uint8_t expect case 11: daysInMonth = nov; days += jan + feb + mar + apr + may + jun + jul + aug + sep + oct; break; case 12: daysInMonth = dec; days += jan + feb + mar + apr + may + jun + jul + aug + sep + oct + nov; break; default: PR_NOT_REACHED("month already bounds-checked by ReadTwoDigits"); - return Fail(PR_INVALID_STATE_ERROR); + return Result::FATAL_ERROR_INVALID_STATE; } int dayOfMonth; rv = ReadTwoDigits(input, 1, daysInMonth, dayOfMonth); if (rv != Success) { return rv; } days += dayOfMonth - 1; @@ -489,23 +489,23 @@ TimeChoice(Input& tagged, uint8_t expect int seconds; rv = ReadTwoDigits(input, 0, 59, seconds); if (rv != Success) { return rv; } uint8_t b; if (input.Read(b) != Success) { - return Fail(SEC_ERROR_INVALID_TIME); + return Result::ERROR_INVALID_TIME; } if (b != 'Z') { - return Fail(SEC_ERROR_INVALID_TIME); // TODO: better error code? + return Result::ERROR_INVALID_TIME; } if (End(input) != Success) { - return Fail(SEC_ERROR_INVALID_TIME); + return Result::ERROR_INVALID_TIME; } int64_t totalSeconds = (static_cast<int64_t>(days) * 24 * 60 * 60) + (static_cast<int64_t>(hours) * 60 * 60) + (static_cast<int64_t>(minutes) * 60) + seconds; time = totalSeconds * PR_USEC_PER_SEC;
--- a/security/pkix/lib/pkixder.h +++ b/security/pkix/lib/pkixder.h @@ -87,17 +87,17 @@ ExpectTagAndLength(Input& input, uint8_t if (rv != Success) { return rv; } uint16_t expectedTagAndLength = static_cast<uint16_t>(expectedTag << 8); expectedTagAndLength |= expectedLength; if (tagAndLength != expectedTagAndLength) { - return Fail(SEC_ERROR_BAD_DER); + return Result::ERROR_BAD_DER; } return Success; } namespace internal { Result @@ -162,17 +162,17 @@ ExpectTagAndGetTLV(Input& input, uint8_t } return input.GetSECItem(siBuffer, mark, tlv); } inline Result End(Input& input) { if (!input.AtEnd()) { - return Fail(SEC_ERROR_BAD_DER); + return Result::ERROR_BAD_DER; } return Success; } template <typename Decoder> inline Result Nested(Input& input, uint8_t tag, Decoder decoder) @@ -233,17 +233,17 @@ NestedOf(Input& input, uint8_t outerTag, Input inner; Result rv = ExpectTagAndGetValue(input, outerTag, inner); if (rv != Success) { return rv; } if (inner.AtEnd()) { if (mayBeEmpty != EmptyAllowed::Yes) { - return Fail(SEC_ERROR_BAD_DER); + return Result::ERROR_BAD_DER; } return Success; } do { rv = Nested(inner, innerTag, decoder); if (rv != Success) { return rv; @@ -270,17 +270,17 @@ IntegralValue(Input& input, uint8_t tag, return rv; } uint8_t valueByte; rv = input.Read(valueByte); if (rv != Success) { return rv; } if (valueByte & 0x80) { // negative - return Fail(SEC_ERROR_BAD_DER); + return Result::ERROR_BAD_DER; } value = valueByte; return Success; } } // namespace internal inline Result @@ -295,17 +295,17 @@ Boolean(Input& input, /*out*/ bool& valu rv = input.Read(intValue); if (rv != Success) { return rv; } switch (intValue) { case 0: value = false; return Success; case 0xFF: value = true; return Success; default: - return Fail(SEC_ERROR_BAD_DER); + return Result::ERROR_BAD_DER; } } // This is for any BOOLEAN DEFAULT FALSE. // (If it is present and false, this is a bad encoding.) // TODO(bug 989518): For compatibility reasons, in some places we allow // invalid encodings with the explicit default value. inline Result @@ -314,17 +314,17 @@ OptionalBoolean(Input& input, bool allow { value = false; if (input.Peek(BOOLEAN)) { Result rv = Boolean(input, value); if (rv != Success) { return rv; } if (!allowInvalidExplicitEncoding && !value) { - return Fail(SEC_ERROR_BAD_DER); + return Result::ERROR_BAD_DER; } } return Success; } // This parser will only parse values between 0..127. If this range is // increased then callers will need to be changed. inline Result @@ -377,17 +377,17 @@ Integer(Input& input, /*out*/ uint8_t& v // -1; defaultValue is only a parameter to make it clear in the calling code // what the default value is. inline Result OptionalInteger(Input& input, long defaultValue, /*out*/ long& value) { // If we need to support a different default value in the future, we need to // test that parsedValue != defaultValue. if (defaultValue != -1) { - return Fail(SEC_ERROR_INVALID_ARGS); + return Result::FATAL_ERROR_INVALID_ARGS; } if (!input.Peek(INTEGER)) { value = defaultValue; return Success; } uint8_t parsedValue; @@ -434,28 +434,28 @@ CertificateSerialNumber(Input& input, /* // gracefully handle such certificates." Result rv = ExpectTagAndGetValue(input, INTEGER, value); if (rv != Success) { return rv; } if (value.len == 0) { - return Fail(SEC_ERROR_BAD_DER); + return Result::ERROR_BAD_DER; } // Check for overly-long encodings. If the first byte is 0x00 then the high // bit on the second byte must be 1; otherwise the same *positive* value // could be encoded without the leading 0x00 byte. If the first byte is 0xFF // then the second byte must NOT have its high bit set; otherwise the same // *negative* value could be encoded without the leading 0xFF byte. if (value.len > 1) { if ((value.data[0] == 0x00 && (value.data[1] & 0x80) == 0) || (value.data[0] == 0xff && (value.data[1] & 0x80) != 0)) { - return Fail(SEC_ERROR_BAD_DER); + return Result::ERROR_BAD_DER; } } return Success; } // x.509 and OCSP both use this same version numbering scheme, though OCSP // only supports v1. @@ -488,17 +488,17 @@ OptionalVersion(Input& input, /*out*/ Ve } switch (integerValue) { case static_cast<uint8_t>(Version::v3): version = Version::v3; break; case static_cast<uint8_t>(Version::v2): version = Version::v2; break; // XXX(bug 1031093): We shouldn't accept an explicit encoding of v1, but we // do here for compatibility reasons. case static_cast<uint8_t>(Version::v1): version = Version::v1; break; default: - return Fail(SEC_ERROR_BAD_DER); + return Result::ERROR_BAD_DER; } return Success; } template <typename ExtensionHandler> inline Result OptionalExtensions(Input& input, uint8_t tag, ExtensionHandler extensionHandler) { @@ -563,17 +563,17 @@ OptionalExtensions(Input& input, uint8_t } bool understood = false; rv = extensionHandler(extnID, extnValue, understood); if (rv != Success) { return rv; } if (critical && !understood) { - return Fail(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION); + return Result::ERROR_UNKNOWN_CRITICAL_EXTENSION; } } return Success; } Result DigestAlgorithmIdentifier(Input& input, /*out*/ DigestAlgorithm& algorithm);
deleted file mode 100644 --- a/security/pkix/lib/pkixkey.cpp +++ /dev/null @@ -1,188 +0,0 @@ -/* -*- 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: - */ -/* 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/. - */ -/* Copyright 2013 Mozilla Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <limits> -#include <stdint.h> - -#include "cert.h" -#include "cryptohi.h" -#include "keyhi.h" -#include "pk11pub.h" -#include "pkix/pkix.h" -#include "pkix/ScopedPtr.h" -#include "prerror.h" -#include "secerr.h" - -namespace mozilla { namespace pkix { - -typedef ScopedPtr<SECKEYPublicKey, SECKEY_DestroyPublicKey> ScopedSECKeyPublicKey; - -SECStatus -CheckPublicKeySize(const SECItem& subjectPublicKeyInfo, - /*out*/ ScopedSECKeyPublicKey& publicKey) -{ - ScopedPtr<CERTSubjectPublicKeyInfo, SECKEY_DestroySubjectPublicKeyInfo> - spki(SECKEY_DecodeDERSubjectPublicKeyInfo(&subjectPublicKeyInfo)); - if (!spki) { - return SECFailure; - } - publicKey = SECKEY_ExtractPublicKey(spki.get()); - if (!publicKey) { - return SECFailure; - } - - static const unsigned int MINIMUM_NON_ECC_BITS = 1024; - - switch (publicKey.get()->keyType) { - case ecKey: - // TODO(bug 622859): We should check which curve. - return SECSuccess; - case dsaKey: // fall through - case rsaKey: - // TODO(bug 622859): Enforce a minimum of 2048 bits for EV certs. - if (SECKEY_PublicKeyStrengthInBits(publicKey.get()) < MINIMUM_NON_ECC_BITS) { - // TODO(bug 1031946): Create a new error code. - PR_SetError(SEC_ERROR_INVALID_KEY, 0); - return SECFailure; - } - break; - case nullKey: - case fortezzaKey: - case dhKey: - case keaKey: - case rsaPssKey: - case rsaOaepKey: - default: - PR_SetError(SEC_ERROR_UNSUPPORTED_KEYALG, 0); - return SECFailure; - } - - return SECSuccess; -} - -SECStatus -CheckPublicKey(const SECItem& subjectPublicKeyInfo) -{ - ScopedSECKeyPublicKey unused; - return CheckPublicKeySize(subjectPublicKeyInfo, unused); -} - -SECStatus -VerifySignedData(const SignedDataWithSignature& sd, - const SECItem& subjectPublicKeyInfo, void* pkcs11PinArg) -{ - if (!sd.data.data || !sd.signature.data) { - PR_NOT_REACHED("invalid args to VerifySignedData"); - PR_SetError(SEC_ERROR_INVALID_ARGS, 0); - return SECFailure; - } - - // See bug 921585. - if (sd.data.len > static_cast<unsigned int>(std::numeric_limits<int>::max())) { - PR_SetError(SEC_ERROR_INVALID_ARGS, 0); - return SECFailure; - } - - SECOidTag pubKeyAlg; - SECOidTag digestAlg; - switch (sd.algorithm) { - case SignatureAlgorithm::ecdsa_with_sha512: - pubKeyAlg = SEC_OID_ANSIX962_EC_PUBLIC_KEY; - digestAlg = SEC_OID_SHA512; - break; - case SignatureAlgorithm::ecdsa_with_sha384: - pubKeyAlg = SEC_OID_ANSIX962_EC_PUBLIC_KEY; - digestAlg = SEC_OID_SHA384; - break; - case SignatureAlgorithm::ecdsa_with_sha256: - pubKeyAlg = SEC_OID_ANSIX962_EC_PUBLIC_KEY; - digestAlg = SEC_OID_SHA256; - break; - case SignatureAlgorithm::ecdsa_with_sha1: - pubKeyAlg = SEC_OID_ANSIX962_EC_PUBLIC_KEY; - digestAlg = SEC_OID_SHA1; - break; - case SignatureAlgorithm::rsa_pkcs1_with_sha512: - pubKeyAlg = SEC_OID_PKCS1_RSA_ENCRYPTION; - digestAlg = SEC_OID_SHA512; - break; - case SignatureAlgorithm::rsa_pkcs1_with_sha384: - pubKeyAlg = SEC_OID_PKCS1_RSA_ENCRYPTION; - digestAlg = SEC_OID_SHA384; - break; - case SignatureAlgorithm::rsa_pkcs1_with_sha256: - pubKeyAlg = SEC_OID_PKCS1_RSA_ENCRYPTION; - digestAlg = SEC_OID_SHA256; - break; - case SignatureAlgorithm::rsa_pkcs1_with_sha1: - pubKeyAlg = SEC_OID_PKCS1_RSA_ENCRYPTION; - digestAlg = SEC_OID_SHA1; - break; - case SignatureAlgorithm::dsa_with_sha256: - pubKeyAlg = SEC_OID_ANSIX9_DSA_SIGNATURE; - digestAlg = SEC_OID_SHA256; - break; - case SignatureAlgorithm::dsa_with_sha1: - pubKeyAlg = SEC_OID_ANSIX9_DSA_SIGNATURE; - digestAlg = SEC_OID_SHA1; - break; - default: - PR_NOT_REACHED("unknown signature algorithm"); - PR_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED, 0); - return SECFailure; - } - - ScopedSECKeyPublicKey pubKey; - if (CheckPublicKeySize(subjectPublicKeyInfo, pubKey) != SECSuccess) { - return SECFailure; - } - - // The static_cast is safe according to the check above that references - // bug 921585. - return VFY_VerifyDataDirect(sd.data.data, static_cast<int>(sd.data.len), - pubKey.get(), &sd.signature, pubKeyAlg, - digestAlg, nullptr, pkcs11PinArg); -} - -SECStatus -DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf, size_t digestBufLen) -{ - static_assert(TrustDomain::DIGEST_LENGTH == SHA1_LENGTH, - "TrustDomain::DIGEST_LENGTH must be 20 (SHA-1 digest length)"); - if (digestBufLen != TrustDomain::DIGEST_LENGTH) { - PR_NOT_REACHED("invalid hash length"); - PR_SetError(SEC_ERROR_INVALID_ARGS, 0); - return SECFailure; - } - if (item.len > - static_cast<decltype(item.len)>(std::numeric_limits<int32_t>::max())) { - PR_NOT_REACHED("large OCSP responses should have already been rejected"); - PR_SetError(SEC_ERROR_INVALID_ARGS, 0); - return SECFailure; - } - return PK11_HashBuf(SEC_OID_SHA1, digestBuf, item.data, - static_cast<int32_t>(item.len)); -} - -} } // namespace mozilla::pkix
new file mode 100644 --- /dev/null +++ b/security/pkix/lib/pkixnss.cpp @@ -0,0 +1,312 @@ +/*- *- 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: + */ +/* 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/. + */ +/* Copyright 2013 Mozilla Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "pkix/pkixnss.h" + +#include <limits> +#include <stdint.h> + +#include "cert.h" +#include "cryptohi.h" +#include "keyhi.h" +#include "pk11pub.h" +#include "pkix/pkix.h" +#include "pkix/ScopedPtr.h" +#include "secerr.h" + +namespace mozilla { namespace pkix { + +typedef ScopedPtr<SECKEYPublicKey, SECKEY_DestroyPublicKey> ScopedSECKeyPublicKey; + +Result +CheckPublicKeySize(const SECItem& subjectPublicKeyInfo, + /*out*/ ScopedSECKeyPublicKey& publicKey) +{ + ScopedPtr<CERTSubjectPublicKeyInfo, SECKEY_DestroySubjectPublicKeyInfo> + spki(SECKEY_DecodeDERSubjectPublicKeyInfo(&subjectPublicKeyInfo)); + if (!spki) { + return MapPRErrorCodeToResult(PR_GetError()); + } + publicKey = SECKEY_ExtractPublicKey(spki.get()); + if (!publicKey) { + return MapPRErrorCodeToResult(PR_GetError()); + } + + static const unsigned int MINIMUM_NON_ECC_BITS = 1024; + + switch (publicKey.get()->keyType) { + case ecKey: + // TODO(bug 622859): We should check which curve. + return Success; + case dsaKey: // fall through + case rsaKey: + // TODO(bug 622859): Enforce a minimum of 2048 bits for EV certs. + if (SECKEY_PublicKeyStrengthInBits(publicKey.get()) < MINIMUM_NON_ECC_BITS) { + // TODO(bug 1031946): Create a new error code. + return Result::ERROR_INVALID_KEY; + } + break; + case nullKey: + case fortezzaKey: + case dhKey: + case keaKey: + case rsaPssKey: + case rsaOaepKey: + default: + return Result::ERROR_UNSUPPORTED_KEYALG; + } + + return Success; +} + +Result +CheckPublicKey(const SECItem& subjectPublicKeyInfo) +{ + ScopedSECKeyPublicKey unused; + return CheckPublicKeySize(subjectPublicKeyInfo, unused); +} + +Result +VerifySignedData(const SignedDataWithSignature& sd, + const SECItem& subjectPublicKeyInfo, void* pkcs11PinArg) +{ + if (!sd.data.data || !sd.signature.data) { + PR_NOT_REACHED("invalid args to VerifySignedData"); + return Result::FATAL_ERROR_INVALID_ARGS; + } + + // See bug 921585. + if (sd.data.len > static_cast<unsigned int>(std::numeric_limits<int>::max())) { + return Result::FATAL_ERROR_INVALID_ARGS; + } + + SECOidTag pubKeyAlg; + SECOidTag digestAlg; + switch (sd.algorithm) { + case SignatureAlgorithm::ecdsa_with_sha512: + pubKeyAlg = SEC_OID_ANSIX962_EC_PUBLIC_KEY; + digestAlg = SEC_OID_SHA512; + break; + case SignatureAlgorithm::ecdsa_with_sha384: + pubKeyAlg = SEC_OID_ANSIX962_EC_PUBLIC_KEY; + digestAlg = SEC_OID_SHA384; + break; + case SignatureAlgorithm::ecdsa_with_sha256: + pubKeyAlg = SEC_OID_ANSIX962_EC_PUBLIC_KEY; + digestAlg = SEC_OID_SHA256; + break; + case SignatureAlgorithm::ecdsa_with_sha1: + pubKeyAlg = SEC_OID_ANSIX962_EC_PUBLIC_KEY; + digestAlg = SEC_OID_SHA1; + break; + case SignatureAlgorithm::rsa_pkcs1_with_sha512: + pubKeyAlg = SEC_OID_PKCS1_RSA_ENCRYPTION; + digestAlg = SEC_OID_SHA512; + break; + case SignatureAlgorithm::rsa_pkcs1_with_sha384: + pubKeyAlg = SEC_OID_PKCS1_RSA_ENCRYPTION; + digestAlg = SEC_OID_SHA384; + break; + case SignatureAlgorithm::rsa_pkcs1_with_sha256: + pubKeyAlg = SEC_OID_PKCS1_RSA_ENCRYPTION; + digestAlg = SEC_OID_SHA256; + break; + case SignatureAlgorithm::rsa_pkcs1_with_sha1: + pubKeyAlg = SEC_OID_PKCS1_RSA_ENCRYPTION; + digestAlg = SEC_OID_SHA1; + break; + case SignatureAlgorithm::dsa_with_sha256: + pubKeyAlg = SEC_OID_ANSIX9_DSA_SIGNATURE; + digestAlg = SEC_OID_SHA256; + break; + case SignatureAlgorithm::dsa_with_sha1: + pubKeyAlg = SEC_OID_ANSIX9_DSA_SIGNATURE; + digestAlg = SEC_OID_SHA1; + break; + default: + PR_NOT_REACHED("unknown signature algorithm"); + return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED; + } + + Result rv; + ScopedSECKeyPublicKey pubKey; + rv = CheckPublicKeySize(subjectPublicKeyInfo, pubKey); + if (rv != Success) { + return rv; + } + + // The static_cast is safe according to the check above that references + // bug 921585. + SECStatus srv = VFY_VerifyDataDirect(sd.data.data, + static_cast<int>(sd.data.len), + pubKey.get(), &sd.signature, pubKeyAlg, + digestAlg, nullptr, pkcs11PinArg); + if (srv != SECSuccess) { + return MapPRErrorCodeToResult(PR_GetError()); + } + + return Success; +} + +Result +DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf, size_t digestBufLen) +{ + static_assert(TrustDomain::DIGEST_LENGTH == SHA1_LENGTH, + "TrustDomain::DIGEST_LENGTH must be 20 (SHA-1 digest length)"); + if (digestBufLen != TrustDomain::DIGEST_LENGTH) { + PR_NOT_REACHED("invalid hash length"); + return Result::FATAL_ERROR_INVALID_ARGS; + } + if (item.len > + static_cast<decltype(item.len)>(std::numeric_limits<int32_t>::max())) { + PR_NOT_REACHED("large OCSP responses should have already been rejected"); + return Result::FATAL_ERROR_INVALID_ARGS; + } + SECStatus srv = PK11_HashBuf(SEC_OID_SHA1, digestBuf, item.data, + static_cast<int32_t>(item.len)); + if (srv != SECSuccess) { + return MapPRErrorCodeToResult(PR_GetError()); + } + return Success; +} + +#define MAP_LIST \ + MAP(Result::Success, 0) \ + MAP(Result::ERROR_BAD_DER, SEC_ERROR_BAD_DER) \ + MAP(Result::ERROR_CA_CERT_INVALID, SEC_ERROR_CA_CERT_INVALID) \ + MAP(Result::ERROR_BAD_SIGNATURE, SEC_ERROR_BAD_SIGNATURE) \ + MAP(Result::ERROR_CERT_BAD_ACCESS_LOCATION, SEC_ERROR_CERT_BAD_ACCESS_LOCATION) \ + MAP(Result::ERROR_CERT_NOT_IN_NAME_SPACE, SEC_ERROR_CERT_NOT_IN_NAME_SPACE) \ + MAP(Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED, SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED) \ + MAP(Result::ERROR_CONNECT_REFUSED, PR_CONNECT_REFUSED_ERROR) \ + MAP(Result::ERROR_EXPIRED_CERTIFICATE, SEC_ERROR_EXPIRED_CERTIFICATE) \ + MAP(Result::ERROR_EXTENSION_VALUE_INVALID, SEC_ERROR_EXTENSION_VALUE_INVALID) \ + MAP(Result::ERROR_INADEQUATE_CERT_TYPE, SEC_ERROR_INADEQUATE_CERT_TYPE) \ + MAP(Result::ERROR_INADEQUATE_KEY_USAGE, SEC_ERROR_INADEQUATE_KEY_USAGE) \ + MAP(Result::ERROR_INVALID_ALGORITHM, SEC_ERROR_INVALID_ALGORITHM) \ + MAP(Result::ERROR_INVALID_TIME, SEC_ERROR_INVALID_TIME) \ + MAP(Result::ERROR_KEY_PINNING_FAILURE, MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE) \ + MAP(Result::ERROR_PATH_LEN_CONSTRAINT_INVALID, SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID) \ + MAP(Result::ERROR_POLICY_VALIDATION_FAILED, SEC_ERROR_POLICY_VALIDATION_FAILED) \ + MAP(Result::ERROR_REVOKED_CERTIFICATE, SEC_ERROR_REVOKED_CERTIFICATE) \ + MAP(Result::ERROR_UNKNOWN_CRITICAL_EXTENSION, SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION) \ + MAP(Result::ERROR_UNKNOWN_ERROR, PR_UNKNOWN_ERROR) \ + MAP(Result::ERROR_UNKNOWN_ISSUER, SEC_ERROR_UNKNOWN_ISSUER) \ + MAP(Result::ERROR_UNTRUSTED_CERT, SEC_ERROR_UNTRUSTED_CERT) \ + MAP(Result::ERROR_UNTRUSTED_ISSUER, SEC_ERROR_UNTRUSTED_ISSUER) \ + MAP(Result::ERROR_OCSP_BAD_SIGNATURE, SEC_ERROR_OCSP_BAD_SIGNATURE) \ + MAP(Result::ERROR_OCSP_INVALID_SIGNING_CERT, SEC_ERROR_OCSP_INVALID_SIGNING_CERT) \ + MAP(Result::ERROR_OCSP_MALFORMED_REQUEST, SEC_ERROR_OCSP_MALFORMED_REQUEST) \ + MAP(Result::ERROR_OCSP_MALFORMED_RESPONSE, SEC_ERROR_OCSP_MALFORMED_RESPONSE) \ + MAP(Result::ERROR_OCSP_OLD_RESPONSE, SEC_ERROR_OCSP_OLD_RESPONSE) \ + MAP(Result::ERROR_OCSP_REQUEST_NEEDS_SIG, SEC_ERROR_OCSP_REQUEST_NEEDS_SIG) \ + MAP(Result::ERROR_OCSP_RESPONDER_CERT_INVALID, SEC_ERROR_OCSP_RESPONDER_CERT_INVALID) \ + MAP(Result::ERROR_OCSP_SERVER_ERROR, SEC_ERROR_OCSP_SERVER_ERROR) \ + MAP(Result::ERROR_OCSP_TRY_SERVER_LATER, SEC_ERROR_OCSP_TRY_SERVER_LATER) \ + MAP(Result::ERROR_OCSP_UNAUTHORIZED_REQUEST, SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST) \ + MAP(Result::ERROR_OCSP_UNKNOWN_RESPONSE_STATUS, SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS) \ + MAP(Result::ERROR_OCSP_UNKNOWN_CERT, SEC_ERROR_OCSP_UNKNOWN_CERT) \ + MAP(Result::ERROR_OCSP_FUTURE_RESPONSE, SEC_ERROR_OCSP_FUTURE_RESPONSE) \ + MAP(Result::ERROR_INVALID_KEY, SEC_ERROR_INVALID_KEY) \ + MAP(Result::ERROR_UNSUPPORTED_KEYALG, SEC_ERROR_UNSUPPORTED_KEYALG) \ + MAP(Result::FATAL_ERROR_INVALID_ARGS, SEC_ERROR_INVALID_ARGS) \ + MAP(Result::FATAL_ERROR_INVALID_STATE, PR_INVALID_STATE_ERROR) \ + MAP(Result::FATAL_ERROR_LIBRARY_FAILURE, SEC_ERROR_LIBRARY_FAILURE) \ + MAP(Result::FATAL_ERROR_NO_MEMORY, SEC_ERROR_NO_MEMORY) \ + /* nothing here */ + +Result +MapPRErrorCodeToResult(PRErrorCode error) +{ + switch (error) + { +#define MAP(mozilla_pkix_result, nss_result) \ + case nss_result: return mozilla_pkix_result; + + MAP_LIST + +#undef MAP + + default: + return Result::ERROR_UNKNOWN_ERROR; + } +} + +PRErrorCode +MapResultToPRErrorCode(Result result) +{ + switch (result) + { +#define MAP(mozilla_pkix_result, nss_result) \ + case mozilla_pkix_result: return nss_result; + + MAP_LIST + +#undef MAP + + default: + PR_NOT_REACHED("Unknown error code in MapResultToPRErrorCode"); + return SEC_ERROR_LIBRARY_FAILURE; + } +} + +const char* +MapResultToName(Result result) +{ + switch (result) + { +#define MAP(mozilla_pkix_result, nss_result) \ + case mozilla_pkix_result: return #mozilla_pkix_result; + + MAP_LIST + +#undef MAP + + default: + PR_NOT_REACHED("Unknown error code in MapResultToName"); + return nullptr; + } +} + +void +RegisterErrorTable() +{ + static const struct PRErrorMessage ErrorTableText[] = { + { "MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE", + "The server uses key pinning (HPKP) but no trusted certificate chain " + "could be constructed that matches the pinset. Key pinning violations " + "cannot be overridden." } + }; + + static const struct PRErrorTable ErrorTable = { + ErrorTableText, + "pkixerrors", + ERROR_BASE, + PR_ARRAY_SIZE(ErrorTableText) + }; + + (void) PR_ErrorInstallTable(&ErrorTable); +} + +} } // namespace mozilla::pkix
--- a/security/pkix/lib/pkixocsp.cpp +++ b/security/pkix/lib/pkixocsp.cpp @@ -24,19 +24,16 @@ #include <limits> #include "pkix/bind.h" #include "pkix/pkix.h" #include "pkixcheck.h" #include "pkixder.h" -// TODO: use typed/qualified typedefs everywhere? -// TODO: When should we return SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE? - namespace mozilla { namespace pkix { static const PRTime ONE_DAY = INT64_C(24) * INT64_C(60) * INT64_C(60) * PR_USEC_PER_SEC; static const PRTime SLOP = ONE_DAY; // These values correspond to the tag values in the ASN.1 CertStatus MOZILLA_PKIX_ENUM_CLASS CertStatus : uint8_t { @@ -125,31 +122,28 @@ CheckOCSPResponseSignerCert(TrustDomain& } // It is possible that there exists a certificate with the same key as the // issuer but with a different name, so we need to compare names // XXX(bug 926270) XXX(bug 1008133) XXX(bug 980163): Improve name // comparison. // TODO: needs test if (!SECITEM_ItemsAreEqual(&potentialSigner.GetIssuer(), &issuerSubject)) { - return Fail(RecoverableError, SEC_ERROR_OCSP_RESPONDER_CERT_INVALID); + return Result::ERROR_OCSP_RESPONDER_CERT_INVALID; } // TODO(bug 926260): check name constraints - SECStatus srv = trustDomain.VerifySignedData(potentialSigner.GetSignedData(), - issuerSubjectPublicKeyInfo); - if (srv != SECSuccess) { - return MapSECStatus(srv); - } + rv = trustDomain.VerifySignedData(potentialSigner.GetSignedData(), + issuerSubjectPublicKeyInfo); // TODO: check for revocation of the OCSP responder certificate unless no-check // or the caller forcing no-check. To properly support the no-check policy, we'd // need to enforce policy constraints from the issuerChain. - return Success; + return rv; } MOZILLA_PKIX_ENUM_CLASS ResponderIDType : uint8_t { byName = der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1, byKey = der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 2 }; @@ -205,32 +199,30 @@ MatchResponderID(TrustDomain& trustDomai if (rv != Success) { return rv; } return MatchKeyHash(trustDomain, keyHash, potentialSignerSubjectPublicKeyInfo, match); } default: - return Fail(RecoverableError, SEC_ERROR_OCSP_MALFORMED_RESPONSE); + return Result::ERROR_OCSP_MALFORMED_RESPONSE; } } static Result VerifyOCSPSignedData(TrustDomain& trustDomain, const SignedDataWithSignature& signedResponseData, const SECItem& spki) { - SECStatus srv = trustDomain.VerifySignedData(signedResponseData, spki); - if (srv != SECSuccess) { - if (PR_GetError() == SEC_ERROR_BAD_SIGNATURE) { - PR_SetError(SEC_ERROR_OCSP_BAD_SIGNATURE, 0); - } + Result rv = trustDomain.VerifySignedData(signedResponseData, spki); + if (rv == Result::ERROR_BAD_SIGNATURE) { + rv = Result::ERROR_OCSP_BAD_SIGNATURE; } - return MapSECStatus(srv); + return rv; } // RFC 6960 section 4.2.2.2: The OCSP responder must either be the issuer of // the cert or it must be a delegated OCSP response signing cert directly // issued by the issuer. If the OCSP responder is a delegated OCSP response // signer, then its certificate is (probably) embedded within the OCSP // response and we'll need to verify that it is a valid certificate that chains // *directly* to issuerCert. @@ -257,101 +249,98 @@ VerifySignature(Context& context, Respon BackCert cert(certs[i], EndEntityOrCA::MustBeEndEntity, nullptr); rv = cert.Init(); if (rv != Success) { return rv; } rv = MatchResponderID(context.trustDomain, responderIDType, responderID, cert.GetSubject(), cert.GetSubjectPublicKeyInfo(), match); - if (rv == FatalError) { - return rv; - } - if (rv == RecoverableError) { + if (rv != Success) { + if (IsFatalError(rv)) { + return rv; + } continue; } if (match) { rv = CheckOCSPResponseSignerCert(context.trustDomain, cert, context.certID.issuer, context.certID.issuerSubjectPublicKeyInfo, context.time); - if (rv == FatalError) { - return rv; - } - if (rv == RecoverableError) { + if (rv != Success) { + if (IsFatalError(rv)) { + return rv; + } continue; } return VerifyOCSPSignedData(context.trustDomain, signedResponseData, cert.GetSubjectPublicKeyInfo()); } } - return Fail(RecoverableError, SEC_ERROR_OCSP_INVALID_SIGNING_CERT); + return Result::ERROR_OCSP_INVALID_SIGNING_CERT; } -static inline void -SetErrorToMalformedResponseOnBadDERError() +static inline Result +MapBadDERToMalformedOCSPResponse(Result rv) { - if (PR_GetError() == SEC_ERROR_BAD_DER) { - PR_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE, 0); + if (rv == Result::ERROR_BAD_DER) { + return Result::ERROR_OCSP_MALFORMED_RESPONSE; } + return rv; } -SECStatus +Result VerifyEncodedOCSPResponse(TrustDomain& trustDomain, const struct CertID& certID, PRTime time, uint16_t maxOCSPLifetimeInDays, const SECItem& encodedResponse, /*out*/ bool& expired, /*optional out*/ PRTime* thisUpdate, /*optional out*/ PRTime* validThrough) { // Always initialize this to something reasonable. expired = false; Input input; - if (input.Init(encodedResponse.data, encodedResponse.len) != Success) { - SetErrorToMalformedResponseOnBadDERError(); - return SECFailure; + Result rv = input.Init(encodedResponse.data, encodedResponse.len); + if (rv != Success) { + return MapBadDERToMalformedOCSPResponse(rv); } + Context context(trustDomain, certID, time, maxOCSPLifetimeInDays, thisUpdate, validThrough); - if (der::Nested(input, der::SEQUENCE, - bind(OCSPResponse, _1, ref(context))) != Success) { - SetErrorToMalformedResponseOnBadDERError(); - return SECFailure; + rv = der::Nested(input, der::SEQUENCE, bind(OCSPResponse, _1, ref(context))); + if (rv != Success) { + return MapBadDERToMalformedOCSPResponse(rv); } - if (der::End(input) != Success) { - SetErrorToMalformedResponseOnBadDERError(); - return SECFailure; + rv = der::End(input); + if (rv != Success) { + return MapBadDERToMalformedOCSPResponse(rv); } expired = context.expired; switch (context.certStatus) { case CertStatus::Good: if (expired) { - PR_SetError(SEC_ERROR_OCSP_OLD_RESPONSE, 0); - return SECFailure; + return Result::ERROR_OCSP_OLD_RESPONSE; } - return SECSuccess; + return Success; case CertStatus::Revoked: - PR_SetError(SEC_ERROR_REVOKED_CERTIFICATE, 0); - return SECFailure; + return Result::ERROR_REVOKED_CERTIFICATE; case CertStatus::Unknown: - PR_SetError(SEC_ERROR_OCSP_UNKNOWN_CERT, 0); - return SECFailure; + return Result::ERROR_OCSP_UNKNOWN_CERT; } PR_NOT_REACHED("unknown CertStatus"); - PR_SetError(SEC_ERROR_OCSP_UNKNOWN_CERT, 0); - return SECFailure; + return Result::ERROR_OCSP_UNKNOWN_CERT; } // OCSPResponse ::= SEQUENCE { // responseStatus OCSPResponseStatus, // responseBytes [0] EXPLICIT ResponseBytes OPTIONAL } // static inline Result OCSPResponse(Input& input, Context& context) @@ -368,22 +357,22 @@ OCSPResponse(Input& input, Context& cont uint8_t responseStatus; Result rv = der::Enumerated(input, responseStatus); if (rv != Success) { return rv; } switch (responseStatus) { case 0: break; // successful - case 1: return Fail(SEC_ERROR_OCSP_MALFORMED_REQUEST); - case 2: return Fail(SEC_ERROR_OCSP_SERVER_ERROR); - case 3: return Fail(SEC_ERROR_OCSP_TRY_SERVER_LATER); - case 5: return Fail(SEC_ERROR_OCSP_REQUEST_NEEDS_SIG); - case 6: return Fail(SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST); - default: return Fail(SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS); + case 1: return Result::ERROR_OCSP_MALFORMED_REQUEST; + case 2: return Result::ERROR_OCSP_SERVER_ERROR; + case 3: return Result::ERROR_OCSP_TRY_SERVER_LATER; + case 5: return Result::ERROR_OCSP_REQUEST_NEEDS_SIG; + case 6: return Result::ERROR_OCSP_UNAUTHORIZED_REQUEST; + default: return Result::ERROR_OCSP_UNKNOWN_RESPONSE_STATUS; } return der::Nested(input, der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 0, der::SEQUENCE, bind(ResponseBytes, _1, ref(context))); } // ResponseBytes ::= SEQUENCE { // responseType OBJECT IDENTIFIER, @@ -411,18 +400,18 @@ ResponseBytes(Input& input, Context& con // certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } Result BasicResponse(Input& input, Context& context) { Input tbsResponseData; SignedDataWithSignature signedData; Result rv = der::SignedData(input, tbsResponseData, signedData); if (rv != Success) { - if (PR_GetError() == SEC_ERROR_BAD_SIGNATURE) { - return Fail(RecoverableError, SEC_ERROR_OCSP_BAD_SIGNATURE); + if (rv == Result::ERROR_BAD_SIGNATURE) { + return Result::ERROR_OCSP_BAD_SIGNATURE; } return rv; } // Parse certificates, if any SECItem certs[8]; size_t numCerts = 0; @@ -443,17 +432,17 @@ BasicResponse(Input& input, Context& con rv = der::ExpectTagAndSkipLength(input, der::SEQUENCE); if (rv != Success) { return rv; } // sequence of certificates while (!input.AtEnd()) { if (numCerts == PR_ARRAY_SIZE(certs)) { - return Fail(SEC_ERROR_BAD_DER); + return Result::ERROR_BAD_DER; } rv = der::ExpectTagAndGetTLV(input, der::SEQUENCE, certs[numCerts]); if (rv != Success) { return rv; } ++numCerts; } @@ -475,17 +464,17 @@ ResponseData(Input& input, Context& cont { der::Version version; Result rv = der::OptionalVersion(input, version); if (rv != Success) { return rv; } if (version != der::Version::v1) { // TODO: more specific error code for bad version? - return Fail(SEC_ERROR_BAD_DER); + return Result::ERROR_BAD_DER; } // ResponderID ::= CHOICE { // byName [1] Name, // byKey [2] KeyHash } SECItem responderID; ResponderIDType responderIDType = input.Peek(static_cast<uint8_t>(ResponderIDType::byName)) @@ -606,46 +595,46 @@ SingleResponse(Input& input, Context& co PRTime thisUpdate; rv = der::GeneralizedTime(input, thisUpdate); if (rv != Success) { return rv; } if (thisUpdate > context.time + SLOP) { - return Fail(SEC_ERROR_OCSP_FUTURE_RESPONSE); + return Result::ERROR_OCSP_FUTURE_RESPONSE; } PRTime notAfter; static const uint8_t NEXT_UPDATE_TAG = der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 0; if (input.Peek(NEXT_UPDATE_TAG)) { PRTime nextUpdate; rv = der::Nested(input, NEXT_UPDATE_TAG, bind(der::GeneralizedTime, _1, ref(nextUpdate))); if (rv != Success) { return rv; } if (nextUpdate < thisUpdate) { - return Fail(SEC_ERROR_OCSP_MALFORMED_RESPONSE); + return Result::ERROR_OCSP_MALFORMED_RESPONSE; } if (nextUpdate - thisUpdate <= maxLifetime) { notAfter = nextUpdate; } else { notAfter = thisUpdate + maxLifetime; } } else { // NSS requires all OCSP responses without a nextUpdate to be recent. // Match that stricter behavior. notAfter = thisUpdate + ONE_DAY; } if (context.time < SLOP) { // prevent underflow - return Fail(SEC_ERROR_INVALID_ARGS); + return Result::FATAL_ERROR_INVALID_ARGS; } if (context.time - SLOP > notAfter) { context.expired = true; } rv = der::OptionalExtensions(input, der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1, @@ -672,17 +661,17 @@ SingleResponse(Input& input, Context& co static inline Result CertID(Input& input, const Context& context, /*out*/ bool& match) { match = false; DigestAlgorithm hashAlgorithm; Result rv = der::DigestAlgorithmIdentifier(input, hashAlgorithm); if (rv != Success) { - if (PR_GetError() == SEC_ERROR_INVALID_ALGORITHM) { + if (rv == Result::ERROR_INVALID_ALGORITHM) { // Skip entries that are hashed with algorithms we don't support. input.SkipToEnd(); return Success; } return rv; } SECItem issuerNameHash; @@ -715,26 +704,27 @@ CertID(Input& input, const Context& cont if (hashAlgorithm != DigestAlgorithm::sha1) { // Again, not interested in this response. Consume input, return success. input.SkipToEnd(); return Success; } if (issuerNameHash.len != TrustDomain::DIGEST_LENGTH) { - return Fail(SEC_ERROR_OCSP_MALFORMED_RESPONSE); + return Result::ERROR_OCSP_MALFORMED_RESPONSE; } // From http://tools.ietf.org/html/rfc6960#section-4.1.1: // "The hash shall be calculated over the DER encoding of the // issuer's name field in the certificate being checked." uint8_t hashBuf[TrustDomain::DIGEST_LENGTH]; - if (context.trustDomain.DigestBuf(context.certID.issuer, hashBuf, - sizeof(hashBuf)) != SECSuccess) { - return MapSECStatus(SECFailure); + rv = context.trustDomain.DigestBuf(context.certID.issuer, hashBuf, + sizeof(hashBuf)); + if (rv != Success) { + return rv; } if (memcmp(hashBuf, issuerNameHash.data, issuerNameHash.len)) { // Again, not interested in this response. Consume input, return success. input.SkipToEnd(); return Success; } return MatchKeyHash(context.trustDomain, issuerKeyHash, @@ -751,17 +741,17 @@ CertID(Input& input, const Context& cont // -- BIT STRING subjectPublicKey [excluding // -- the tag, length, and number of unused // -- bits] in the responder's certificate) static Result MatchKeyHash(TrustDomain& trustDomain, const SECItem& keyHash, const SECItem& subjectPublicKeyInfo, /*out*/ bool& match) { if (keyHash.len != TrustDomain::DIGEST_LENGTH) { - return Fail(RecoverableError, SEC_ERROR_OCSP_MALFORMED_RESPONSE); + return Result::ERROR_OCSP_MALFORMED_RESPONSE; } static uint8_t hashBuf[TrustDomain::DIGEST_LENGTH]; Result rv = KeyHash(trustDomain, subjectPublicKeyInfo, hashBuf, sizeof hashBuf); if (rv != Success) { return rv; } match = !memcmp(hashBuf, keyHash.data, keyHash.len); @@ -769,71 +759,71 @@ MatchKeyHash(TrustDomain& trustDomain, c } // TODO(bug 966856): support SHA-2 hashes Result KeyHash(TrustDomain& trustDomain, const SECItem& subjectPublicKeyInfo, /*out*/ uint8_t* hashBuf, size_t hashBufSize) { if (!hashBuf || hashBufSize != TrustDomain::DIGEST_LENGTH) { - return Fail(FatalError, SEC_ERROR_LIBRARY_FAILURE); + return Result::FATAL_ERROR_LIBRARY_FAILURE; } // RFC 5280 Section 4.1 // // SubjectPublicKeyInfo ::= SEQUENCE { // algorithm AlgorithmIdentifier, // subjectPublicKey BIT STRING } Input spki; + Result rv; { // The scope of input is limited to reduce the possibility of confusing it // with spki in places we need to be using spki below. Input input; - if (input.Init(subjectPublicKeyInfo.data, subjectPublicKeyInfo.len) - != Success) { - return MapSECStatus(SECFailure); + rv = input.Init(subjectPublicKeyInfo.data, subjectPublicKeyInfo.len); + if (rv != Success) { + return rv; } - - if (der::ExpectTagAndGetValue(input, der::SEQUENCE, spki) != Success) { - return MapSECStatus(SECFailure); + rv = der::ExpectTagAndGetValue(input, der::SEQUENCE, spki); + if (rv != Success) { + return rv; } - if (der::End(input) != Success) { - return MapSECStatus(SECFailure); + rv = der::End(input); + if (rv != Success) { + return rv; } } // Skip AlgorithmIdentifier - if (der::ExpectTagAndSkipValue(spki, der::SEQUENCE) != Success) { - return MapSECStatus(SECFailure); + rv = der::ExpectTagAndSkipValue(spki, der::SEQUENCE); + if (rv != Success) { + return rv; } SECItem subjectPublicKey; - if (der::ExpectTagAndGetValue(spki, der::BIT_STRING, subjectPublicKey) - != Success) { - return MapSECStatus(SECFailure); + rv = der::ExpectTagAndGetValue(spki, der::BIT_STRING, subjectPublicKey); + if (rv != Success) { + return rv; } - if (der::End(spki) != Success) { - return MapSECStatus(SECFailure); + rv = der::End(spki); + if (rv != Success) { + return rv; } // Assume/require that the number of unused bits in the public key is zero. if (subjectPublicKey.len == 0 || subjectPublicKey.data[0] != 0) { - return Fail(RecoverableError, SEC_ERROR_BAD_DER); + return Result::ERROR_BAD_DER; } ++subjectPublicKey.data; --subjectPublicKey.len; - if (trustDomain.DigestBuf(subjectPublicKey, hashBuf, hashBufSize) - != SECSuccess) { - return MapSECStatus(SECFailure); - } - return Success; + return trustDomain.DigestBuf(subjectPublicKey, hashBuf, hashBufSize); } Result ExtensionNotUnderstood(Input& /*extnID*/, const SECItem& /*extnValue*/, /*out*/ bool& understood) { understood = false; return Success; @@ -857,25 +847,21 @@ ExtensionNotUnderstood(Input& /*extnID*/ // Responses whose thisUpdate time is later than the local system time // SHOULD be considered unreliable. // // If nextUpdate is not set, the responder is indicating that newer // revocation information is available all the time. // // http://tools.ietf.org/html/rfc5019#section-4 -SECItem* -CreateEncodedOCSPRequest(TrustDomain& trustDomain, PLArenaPool* arena, - const struct CertID& certID) +Result +CreateEncodedOCSPRequest(TrustDomain& trustDomain, const struct CertID& certID, + /*out*/ uint8_t (&out)[OCSP_REQUEST_MAX_LENGTH], + /*out*/ size_t& outLen) { - if (!arena) { - PR_SetError(SEC_ERROR_INVALID_ARGS, 0); - return nullptr; - } - // We do not add any extensions to the request. // RFC 6960 says "An OCSP client MAY wish to specify the kinds of response // types it understands. To do so, it SHOULD use an extension with the OID // id-pkix-ocsp-response." This use of MAY and SHOULD is unclear. MSIE11 // on Windows 8.1 does not include any extensions, whereas NSS has always // included the id-pkix-ocsp-response extension. Avoiding the sending the // extension is better for OCSP GET because it makes the request smaller, @@ -906,63 +892,62 @@ CreateEncodedOCSPRequest(TrustDomain& tr + 2; // serialNumber (header) // The only way we could have a request this large is if the serialNumber was // ridiculously and unreasonably large. RFC 5280 says "Conforming CAs MUST // NOT use serialNumber values longer than 20 octets." With this restriction, // we allow for some amount of non-conformance with that requirement while // still ensuring we can encode the length values in the ASN.1 TLV structures // in a single byte. - if (certID.serialNumber.len > 127u - totalLenWithoutSerialNumberData) { - PR_SetError(SEC_ERROR_BAD_DATA, 0); - return nullptr; + static_assert(totalLenWithoutSerialNumberData < OCSP_REQUEST_MAX_LENGTH, + "totalLenWithoutSerialNumberData too big"); + if (certID.serialNumber.len > + OCSP_REQUEST_MAX_LENGTH - totalLenWithoutSerialNumberData) { + return Result::ERROR_BAD_DER; } - uint8_t totalLen = static_cast<uint8_t>(totalLenWithoutSerialNumberData + - certID.serialNumber.len); + outLen = totalLenWithoutSerialNumberData + certID.serialNumber.len; - SECItem* encodedRequest = SECITEM_AllocItem(arena, nullptr, totalLen); - if (!encodedRequest) { - return nullptr; - } + uint8_t totalLen = static_cast<uint8_t>(outLen); - uint8_t* d = encodedRequest->data; + uint8_t* d = out; *d++ = 0x30; *d++ = totalLen - 2u; // OCSPRequest (SEQUENCE) *d++ = 0x30; *d++ = totalLen - 4u; // tbsRequest (SEQUENCE) *d++ = 0x30; *d++ = totalLen - 6u; // requestList (SEQUENCE OF) *d++ = 0x30; *d++ = totalLen - 8u; // Request (SEQUENCE) *d++ = 0x30; *d++ = totalLen - 10u; // reqCert (CertID SEQUENCE) // reqCert.hashAlgorithm for (size_t i = 0; i < PR_ARRAY_SIZE(hashAlgorithm); ++i) { *d++ = hashAlgorithm[i]; } // reqCert.issuerNameHash (OCTET STRING) *d++ = 0x04; *d++ = hashLen; - if (trustDomain.DigestBuf(certID.issuer, d, hashLen) != SECSuccess) { - return nullptr; + Result rv = trustDomain.DigestBuf(certID.issuer, d, hashLen); + if (rv != Success) { + return rv; } d += hashLen; // reqCert.issuerKeyHash (OCTET STRING) *d++ = 0x04; *d++ = hashLen; - if (KeyHash(trustDomain, certID.issuerSubjectPublicKeyInfo, d, hashLen) - != Success) { - return nullptr; + rv = KeyHash(trustDomain, certID.issuerSubjectPublicKeyInfo, d, hashLen); + if (rv != Success) { + return rv; } d += hashLen; // reqCert.serialNumber (INTEGER) *d++ = 0x02; // INTEGER *d++ = static_cast<uint8_t>(certID.serialNumber.len); for (size_t i = 0; i < certID.serialNumber.len; ++i) { *d++ = certID.serialNumber.data[i]; } - PR_ASSERT(d == encodedRequest->data + totalLen); + PR_ASSERT(d == out + totalLen); - return encodedRequest; + return Success; } } } // namespace mozilla::pkix
--- a/security/pkix/lib/pkixutil.h +++ b/security/pkix/lib/pkixutil.h @@ -21,19 +21,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef mozilla_pkix__pkixutil_h #define mozilla_pkix__pkixutil_h #include "pkixder.h" -#include "prerror.h" -#include "seccomon.h" -#include "secerr.h" namespace mozilla { namespace pkix { // During path building and verification, we build a linked list of BackCerts // from the current cert toward the end-entity certificate. The linked list // is used to verify properties that aren't local to the current certificate // and/or the direct link between the current certificate and its issuer, // such as name constraints. @@ -165,17 +162,17 @@ public: virtual const SECItem* GetDER(size_t i) const { return i < numItems ? items[i] : nullptr; } Result Append(const SECItem& der) { if (numItems >= MAX_LENGTH) { - return Fail(SEC_ERROR_INVALID_ARGS); + return Result::FATAL_ERROR_INVALID_ARGS; } items[numItems] = &der; ++numItems; return Success; } // Public so we can static_assert on this. Keep in sync with MAX_SUBCA_COUNT. static const size_t MAX_LENGTH = 8;
--- a/security/pkix/moz.build +++ b/security/pkix/moz.build @@ -5,17 +5,17 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. SOURCES += [ 'lib/pkixbind.cpp', 'lib/pkixbuild.cpp', 'lib/pkixcert.cpp', 'lib/pkixcheck.cpp', 'lib/pkixder.cpp', - 'lib/pkixkey.cpp', + 'lib/pkixnss.cpp', 'lib/pkixocsp.cpp', ] LOCAL_INCLUDES += [ 'include', ] TEST_DIRS += [
--- a/security/pkix/test/gtest/moz.build +++ b/security/pkix/test/gtest/moz.build @@ -11,17 +11,16 @@ SOURCES += [ 'pkixcheck_CheckKeyUsage_tests.cpp', 'pkixcheck_CheckValidity_tests.cpp', # The naming conventions are described in ./README.txt. 'pkixder_input_tests.cpp', 'pkixder_pki_types_tests.cpp', 'pkixder_universal_types_tests.cpp', - 'pkixgtest.cpp', 'pkixocsp_CreateEncodedOCSPRequest_tests.cpp', 'pkixocsp_VerifyEncodedOCSPResponse.cpp', ] LOCAL_INCLUDES += [ '../../include', '../../lib', '../lib',
--- a/security/pkix/test/gtest/pkixbuild_tests.cpp +++ b/security/pkix/test/gtest/pkixbuild_tests.cpp @@ -19,16 +19,19 @@ * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "nssgtest.h" #include "pkix/pkix.h" +#include "pkix/pkixnss.h" +#include "pkixgtest.h" +#include "pkixtestutil.h" #include "prinit.h" #include "secerr.h" using namespace mozilla::pkix; using namespace mozilla::pkix::test; static bool CreateCert(PLArenaPool* arena, const char* issuerStr, @@ -107,82 +110,80 @@ public: return false; } } return true; } private: - SECStatus GetCertTrust(EndEntityOrCA, - const CertPolicyId&, - const SECItem& candidateCert, - /*out*/ TrustLevel* trustLevel) + virtual Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, + const SECItem& candidateCert, + /*out*/ TrustLevel* trustLevel) { if (SECITEM_ItemsAreEqual(&candidateCert, &certChainTail[0]->derCert)) { *trustLevel = TrustLevel::TrustAnchor; } else { *trustLevel = TrustLevel::InheritsTrust; } - return SECSuccess; + return Success; } - SECStatus FindIssuer(const SECItem& encodedIssuerName, - IssuerChecker& checker, PRTime time) + virtual Result FindIssuer(const SECItem& encodedIssuerName, + IssuerChecker& checker, PRTime time) { ScopedCERTCertList candidates(CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(), &encodedIssuerName, time, true)); if (candidates) { for (CERTCertListNode* n = CERT_LIST_HEAD(candidates); !CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) { bool keepGoing; - SECStatus srv = checker.Check(n->cert->derCert, - nullptr/*additionalNameConstraints*/, - keepGoing); - if (srv != SECSuccess) { - return SECFailure; + Result rv = checker.Check(n->cert->derCert, + nullptr/*additionalNameConstraints*/, + keepGoing); + if (rv != Success) { + return rv; } if (!keepGoing) { break; } } } - return SECSuccess; + return Success; } - SECStatus CheckRevocation(EndEntityOrCA, const CertID&, PRTime, - /*optional*/ const SECItem*, - /*optional*/ const SECItem*) + virtual Result CheckRevocation(EndEntityOrCA, const CertID&, PRTime, + /*optional*/ const SECItem*, + /*optional*/ const SECItem*) { - return SECSuccess; + return Success; } - virtual SECStatus IsChainValid(const DERArray&) + virtual Result IsChainValid(const DERArray&) { - return SECSuccess; + return Success; } - SECStatus VerifySignedData(const SignedDataWithSignature& signedData, - const SECItem& subjectPublicKeyInfo) + virtual Result VerifySignedData(const SignedDataWithSignature& signedData, + const SECItem& subjectPublicKeyInfo) { return ::mozilla::pkix::VerifySignedData(signedData, subjectPublicKeyInfo, nullptr); } - virtual SECStatus DigestBuf(const SECItem& item, /*out*/ uint8_t *digestBuf, - size_t digestBufLen) + virtual Result DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf, + size_t digestBufLen) { ADD_FAILURE(); - PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0); - return SECFailure; + return Result::FATAL_ERROR_LIBRARY_FAILURE; } - SECStatus CheckPublicKey(const SECItem& subjectPublicKeyInfo) + virtual Result CheckPublicKey(const SECItem& subjectPublicKeyInfo) { return ::mozilla::pkix::CheckPublicKey(subjectPublicKeyInfo); } // We hold references to CERTCertificates in the cert chain tail so that we // CERT_CreateSubjectCertList can find them. ScopedCERTCertificate certChainTail[7]; @@ -210,64 +211,63 @@ public: protected: static TestTrustDomain trustDomain; }; /*static*/ TestTrustDomain pkixbuild::trustDomain; TEST_F(pkixbuild, MaxAcceptableCertChainLength) { - ASSERT_SECSuccess(BuildCertChain(trustDomain, - trustDomain.GetLeafCACert()->derCert, - now, EndEntityOrCA::MustBeCA, - KeyUsage::noParticularKeyUsageRequired, - KeyPurposeId::id_kp_serverAuth, - CertPolicyId::anyPolicy, - nullptr/*stapledOCSPResponse*/)); + ASSERT_EQ(Success, + BuildCertChain(trustDomain, trustDomain.GetLeafCACert()->derCert, + now, EndEntityOrCA::MustBeCA, + KeyUsage::noParticularKeyUsageRequired, + KeyPurposeId::id_kp_serverAuth, + CertPolicyId::anyPolicy, + nullptr/*stapledOCSPResponse*/)); ScopedSECKEYPrivateKey privateKey; ScopedCERTCertificate cert; ASSERT_TRUE(CreateCert(arena.get(), trustDomain.GetLeafCACert()->subjectName, "CN=Direct End-Entity", EndEntityOrCA::MustBeEndEntity, trustDomain.leafCAKey.get(), privateKey, cert)); - ASSERT_SECSuccess(BuildCertChain(trustDomain, cert->derCert, now, - EndEntityOrCA::MustBeEndEntity, - KeyUsage::noParticularKeyUsageRequired, - KeyPurposeId::id_kp_serverAuth, - CertPolicyId::anyPolicy, - nullptr/*stapledOCSPResponse*/)); + ASSERT_EQ(Success, + BuildCertChain(trustDomain, cert->derCert, now, + EndEntityOrCA::MustBeEndEntity, + KeyUsage::noParticularKeyUsageRequired, + KeyPurposeId::id_kp_serverAuth, + CertPolicyId::anyPolicy, + nullptr/*stapledOCSPResponse*/)); } TEST_F(pkixbuild, BeyondMaxAcceptableCertChainLength) { ScopedSECKEYPrivateKey caPrivateKey; ScopedCERTCertificate caCert; ASSERT_TRUE(CreateCert(arena.get(), trustDomain.GetLeafCACert()->subjectName, "CN=CA Too Far", EndEntityOrCA::MustBeCA, trustDomain.leafCAKey.get(), caPrivateKey, caCert)); - PR_SetError(0, 0); - ASSERT_SECFailure(SEC_ERROR_UNKNOWN_ISSUER, - BuildCertChain(trustDomain, caCert->derCert, now, - EndEntityOrCA::MustBeCA, - KeyUsage::noParticularKeyUsageRequired, - KeyPurposeId::id_kp_serverAuth, - CertPolicyId::anyPolicy, - nullptr/*stapledOCSPResponse*/)); + ASSERT_EQ(Result::ERROR_UNKNOWN_ISSUER, + BuildCertChain(trustDomain, caCert->derCert, now, + EndEntityOrCA::MustBeCA, + KeyUsage::noParticularKeyUsageRequired, + KeyPurposeId::id_kp_serverAuth, + CertPolicyId::anyPolicy, + nullptr/*stapledOCSPResponse*/)); ScopedSECKEYPrivateKey privateKey; ScopedCERTCertificate cert; ASSERT_TRUE(CreateCert(arena.get(), caCert->subjectName, "CN=End-Entity Too Far", EndEntityOrCA::MustBeEndEntity, caPrivateKey.get(), privateKey, cert)); - PR_SetError(0, 0); - ASSERT_SECFailure(SEC_ERROR_UNKNOWN_ISSUER, - BuildCertChain(trustDomain, cert->derCert, now, - EndEntityOrCA::MustBeEndEntity, - KeyUsage::noParticularKeyUsageRequired, - KeyPurposeId::id_kp_serverAuth, - CertPolicyId::anyPolicy, - nullptr/*stapledOCSPResponse*/)); + ASSERT_EQ(Result::ERROR_UNKNOWN_ISSUER, + BuildCertChain(trustDomain, cert->derCert, now, + EndEntityOrCA::MustBeEndEntity, + KeyUsage::noParticularKeyUsageRequired, + KeyPurposeId::id_kp_serverAuth, + CertPolicyId::anyPolicy, + nullptr/*stapledOCSPResponse*/)); }
--- a/security/pkix/test/gtest/pkixcert_extension_tests.cpp +++ b/security/pkix/test/gtest/pkixcert_extension_tests.cpp @@ -19,16 +19,18 @@ * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "nssgtest.h" #include "pkix/pkix.h" +#include "pkix/pkixnss.h" +#include "pkixtestutil.h" #include "secerr.h" using namespace mozilla::pkix; using namespace mozilla::pkix::test; // Creates a self-signed certificate with the given extension. static const SECItem* CreateCert(PLArenaPool* arena, const char* subjectStr, @@ -68,60 +70,57 @@ CreateCert(PLArenaPool* arena, const cha { const SECItem * extensions[] = { extension, nullptr }; return CreateCert(arena, subjectStr, extensions, subjectKey); } class TrustEverythingTrustDomain : public TrustDomain { private: - SECStatus GetCertTrust(EndEntityOrCA, - const CertPolicyId&, - const SECItem& candidateCert, - /*out*/ TrustLevel* trustLevel) + virtual Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, + const SECItem& candidateCert, + /*out*/ TrustLevel* trustLevel) { *trustLevel = TrustLevel::TrustAnchor; - return SECSuccess; + return Success; } - SECStatus FindIssuer(const SECItem& /*encodedIssuerName*/, - IssuerChecker& /*checker*/, PRTime /*time*/) + virtual Result FindIssuer(const SECItem& /*encodedIssuerName*/, + IssuerChecker& /*checker*/, PRTime /*time*/) { ADD_FAILURE(); - PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0); - return SECFailure; + return Result::FATAL_ERROR_LIBRARY_FAILURE; } - SECStatus CheckRevocation(EndEntityOrCA, const CertID&, PRTime, - /*optional*/ const SECItem*, - /*optional*/ const SECItem*) + virtual Result CheckRevocation(EndEntityOrCA, const CertID&, PRTime, + /*optional*/ const SECItem*, + /*optional*/ const SECItem*) { - return SECSuccess; + return Success; } - virtual SECStatus IsChainValid(const DERArray&) + virtual Result IsChainValid(const DERArray&) { - return SECSuccess; + return Success; } - SECStatus VerifySignedData(const SignedDataWithSignature& signedData, - const SECItem& subjectPublicKeyInfo) + virtual Result VerifySignedData(const SignedDataWithSignature& signedData, + const SECItem& subjectPublicKeyInfo) { return ::mozilla::pkix::VerifySignedData(signedData, subjectPublicKeyInfo, nullptr); } - SECStatus DigestBuf(const SECItem&, /*out*/ uint8_t *, size_t) + virtual Result DigestBuf(const SECItem&, /*out*/ uint8_t*, size_t) { ADD_FAILURE(); - PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0); - return SECFailure; + return Result::FATAL_ERROR_LIBRARY_FAILURE; } - SECStatus CheckPublicKey(const SECItem& subjectPublicKeyInfo) + virtual Result CheckPublicKey(const SECItem& subjectPublicKeyInfo) { return ::mozilla::pkix::CheckPublicKey(subjectPublicKeyInfo); } }; class pkixcert_extension: public NSSTest { public: @@ -156,23 +155,23 @@ TEST_F(pkixcert_extension, UnknownCritic sizeof(unknownCriticalExtensionBytes) }; const char* certCN = "CN=Cert With Unknown Critical Extension"; ScopedSECKEYPrivateKey key; // cert is owned by the arena const SECItem* cert(CreateCert(arena.get(), certCN, &unknownCriticalExtension, key)); ASSERT_TRUE(cert); - ASSERT_SECFailure(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION, - BuildCertChain(trustDomain, *cert, now, - EndEntityOrCA::MustBeEndEntity, - KeyUsage::noParticularKeyUsageRequired, - KeyPurposeId::anyExtendedKeyUsage, - CertPolicyId::anyPolicy, - nullptr/*stapledOCSPResponse*/)); + ASSERT_EQ(Result::ERROR_UNKNOWN_CRITICAL_EXTENSION, + BuildCertChain(trustDomain, *cert, now, + EndEntityOrCA::MustBeEndEntity, + KeyUsage::noParticularKeyUsageRequired, + KeyPurposeId::anyExtendedKeyUsage, + CertPolicyId::anyPolicy, + nullptr/*stapledOCSPResponse*/)); } // Tests that a non-critical extension not in the id-ce or id-pe arcs (which is // thus unknown to us) verifies successfully. TEST_F(pkixcert_extension, UnknownNonCriticalExtension) { static const uint8_t unknownNonCriticalExtensionBytes[] = { 0x30, 0x16, // SEQUENCE (length = 22) @@ -188,22 +187,23 @@ TEST_F(pkixcert_extension, UnknownNonCri sizeof(unknownNonCriticalExtensionBytes) }; const char* certCN = "CN=Cert With Unknown NonCritical Extension"; ScopedSECKEYPrivateKey key; // cert is owned by the arena const SECItem* cert(CreateCert(arena.get(), certCN, &unknownNonCriticalExtension, key)); ASSERT_TRUE(cert); - ASSERT_SECSuccess(BuildCertChain(trustDomain, *cert, now, - EndEntityOrCA::MustBeEndEntity, - KeyUsage::noParticularKeyUsageRequired, - KeyPurposeId::anyExtendedKeyUsage, - CertPolicyId::anyPolicy, - nullptr/*stapledOCSPResponse*/)); + ASSERT_EQ(Success, + BuildCertChain(trustDomain, *cert, now, + EndEntityOrCA::MustBeEndEntity, + KeyUsage::noParticularKeyUsageRequired, + KeyPurposeId::anyExtendedKeyUsage, + CertPolicyId::anyPolicy, + nullptr/*stapledOCSPResponse*/)); } // Tests that an incorrect OID for id-pe-authorityInformationAccess // (when marked critical) is detected and that verification fails. // (Until bug 1020993 was fixed, the code checked for this OID.) TEST_F(pkixcert_extension, WrongOIDCriticalExtension) { static const uint8_t wrongOIDCriticalExtensionBytes[] = { @@ -220,23 +220,23 @@ TEST_F(pkixcert_extension, WrongOIDCriti sizeof(wrongOIDCriticalExtensionBytes) }; const char* certCN = "CN=Cert With Critical Wrong OID Extension"; ScopedSECKEYPrivateKey key; // cert is owned by the arena const SECItem* cert(CreateCert(arena.get(), certCN, &wrongOIDCriticalExtension, key)); ASSERT_TRUE(cert); - ASSERT_SECFailure(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION, - BuildCertChain(trustDomain, *cert, now, - EndEntityOrCA::MustBeEndEntity, - KeyUsage::noParticularKeyUsageRequired, - KeyPurposeId::anyExtendedKeyUsage, - CertPolicyId::anyPolicy, - nullptr/*stapledOCSPResponse*/)); + ASSERT_EQ(Result::ERROR_UNKNOWN_CRITICAL_EXTENSION, + BuildCertChain(trustDomain, *cert, now, + EndEntityOrCA::MustBeEndEntity, + KeyUsage::noParticularKeyUsageRequired, + KeyPurposeId::anyExtendedKeyUsage, + CertPolicyId::anyPolicy, + nullptr/*stapledOCSPResponse*/)); } // Tests that a id-pe-authorityInformationAccess critical extension // is detected and that verification succeeds. TEST_F(pkixcert_extension, CriticalAIAExtension) { // XXX: According to RFC 5280 an AIA that consists of an empty sequence is // not legal, but we accept it and that is not what we're testing here. @@ -255,22 +255,23 @@ TEST_F(pkixcert_extension, CriticalAIAEx sizeof(criticalAIAExtensionBytes) }; const char* certCN = "CN=Cert With Critical AIA Extension"; ScopedSECKEYPrivateKey key; // cert is owned by the arena const SECItem* cert(CreateCert(arena.get(), certCN, &criticalAIAExtension, key)); ASSERT_TRUE(cert); - ASSERT_SECSuccess(BuildCertChain(trustDomain, *cert, now, - EndEntityOrCA::MustBeEndEntity, - KeyUsage::noParticularKeyUsageRequired, - KeyPurposeId::anyExtendedKeyUsage, - CertPolicyId::anyPolicy, - nullptr/*stapledOCSPResponse*/)); + ASSERT_EQ(Success, + BuildCertChain(trustDomain, *cert, now, + EndEntityOrCA::MustBeEndEntity, + KeyUsage::noParticularKeyUsageRequired, + KeyPurposeId::anyExtendedKeyUsage, + CertPolicyId::anyPolicy, + nullptr/*stapledOCSPResponse*/)); } // We know about some id-ce extensions (OID arc 2.5.29), but not all of them. // Tests that an unknown id-ce extension is detected and that verification // fails. TEST_F(pkixcert_extension, UnknownCriticalCEExtension) { static const uint8_t unknownCriticalCEExtensionBytes[] = { @@ -286,23 +287,23 @@ TEST_F(pkixcert_extension, UnknownCritic sizeof(unknownCriticalCEExtensionBytes) }; const char* certCN = "CN=Cert With Unknown Critical id-ce Extension"; ScopedSECKEYPrivateKey key; // cert is owned by the arena const SECItem* cert(CreateCert(arena.get(), certCN, &unknownCriticalCEExtension, key)); ASSERT_TRUE(cert); - ASSERT_SECFailure(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION, - BuildCertChain(trustDomain, *cert, now, - EndEntityOrCA::MustBeEndEntity, - KeyUsage::noParticularKeyUsageRequired, - KeyPurposeId::anyExtendedKeyUsage, - CertPolicyId::anyPolicy, - nullptr/*stapledOCSPResponse*/)); + ASSERT_EQ(Result::ERROR_UNKNOWN_CRITICAL_EXTENSION, + BuildCertChain(trustDomain, *cert, now, + EndEntityOrCA::MustBeEndEntity, + KeyUsage::noParticularKeyUsageRequired, + KeyPurposeId::anyExtendedKeyUsage, + CertPolicyId::anyPolicy, + nullptr/*stapledOCSPResponse*/)); } // Tests that a certificate with a known critical id-ce extension (in this case, // OID 2.5.29.54, which is id-ce-inhibitAnyPolicy), verifies successfully. TEST_F(pkixcert_extension, KnownCriticalCEExtension) { static const uint8_t criticalCEExtensionBytes[] = { 0x30, 0x0d, // SEQUENCE (length = 13) @@ -318,22 +319,23 @@ TEST_F(pkixcert_extension, KnownCritical sizeof(criticalCEExtensionBytes) }; const char* certCN = "CN=Cert With Known Critical id-ce Extension"; ScopedSECKEYPrivateKey key; // cert is owned by the arena const SECItem* cert(CreateCert(arena.get(), certCN, &criticalCEExtension, key)); ASSERT_TRUE(cert); - ASSERT_SECSuccess(BuildCertChain(trustDomain, *cert, - now, EndEntityOrCA::MustBeEndEntity, - KeyUsage::noParticularKeyUsageRequired, - KeyPurposeId::anyExtendedKeyUsage, - CertPolicyId::anyPolicy, - nullptr/*stapledOCSPResponse*/)); + ASSERT_EQ(Success, + BuildCertChain(trustDomain, *cert, now, + EndEntityOrCA::MustBeEndEntity, + KeyUsage::noParticularKeyUsageRequired, + KeyPurposeId::anyExtendedKeyUsage, + CertPolicyId::anyPolicy, + nullptr/*stapledOCSPResponse*/)); } // Two subjectAltNames must result in an error. TEST_F(pkixcert_extension, DuplicateSubjectAltName) { static const uint8_t DER_BYTES[] = { 0x30, 22, // SEQUENCE (length = 22) 0x06, 3, // OID (length = 3) @@ -349,16 +351,16 @@ TEST_F(pkixcert_extension, DuplicateSubj sizeof(DER_BYTES) }; static SECItem const* const extensions[] = { &DER, &DER, nullptr }; static const char* certCN = "CN=Cert With Duplicate subjectAltName"; ScopedSECKEYPrivateKey key; // cert is owned by the arena const SECItem* cert(CreateCert(arena.get(), certCN, extensions, key)); ASSERT_TRUE(cert); - ASSERT_SECFailure(SEC_ERROR_EXTENSION_VALUE_INVALID, - BuildCertChain(trustDomain, *cert, - now, EndEntityOrCA::MustBeEndEntity, - KeyUsage::noParticularKeyUsageRequired, - KeyPurposeId::anyExtendedKeyUsage, - CertPolicyId::anyPolicy, - nullptr/*stapledOCSPResponse*/)); + ASSERT_EQ(Result::ERROR_EXTENSION_VALUE_INVALID, + BuildCertChain(trustDomain, *cert, now, + EndEntityOrCA::MustBeEndEntity, + KeyUsage::noParticularKeyUsageRequired, + KeyPurposeId::anyExtendedKeyUsage, + CertPolicyId::anyPolicy, + nullptr/*stapledOCSPResponse*/)); }
--- a/security/pkix/test/gtest/pkixcheck_CheckKeyUsage_tests.cpp +++ b/security/pkix/test/gtest/pkixcheck_CheckKeyUsage_tests.cpp @@ -17,40 +17,32 @@ * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ -#include "pkixgtest.h" +#include "gtest/gtest.h" +#include "pkix/pkixtypes.h" using namespace mozilla::pkix; -using namespace mozilla::pkix::test; namespace mozilla { namespace pkix { extern Result CheckKeyUsage(EndEntityOrCA endEntityOrCA, const SECItem* encodedKeyUsage, KeyUsage requiredKeyUsageIfPresent); } } // namespace mozilla::pkix -class pkixcheck_CheckKeyUsage : public ::testing::Test -{ -public: - pkixcheck_CheckKeyUsage() - { - PR_SetError(0, 0); - } -}; +class pkixcheck_CheckKeyUsage : public ::testing::Test { }; -#define ASSERT_BAD(x) \ - ASSERT_RecoverableError(SEC_ERROR_INADEQUATE_KEY_USAGE, x) +#define ASSERT_BAD(x) ASSERT_EQ(Result::ERROR_INADEQUATE_KEY_USAGE, x) // Make it easy to define test data for the common, simplest cases. #define NAMED_SIMPLE_KU(name, unusedBits, bits) \ const uint8_t name##_bytes[4] = { \ 0x03/*BIT STRING*/, 0x02/*LENGTH=2*/, unusedBits, bits \ }; \ const SECItem name = { \ siBuffer, \ @@ -66,61 +58,61 @@ static const SECItem empty_nonnull = { s // certificates since we don't support cRLSign. TEST_F(pkixcheck_CheckKeyUsage, EE_none) { // The input SECItem is nullptr. This means the cert had no keyUsage // extension. This is always valid because no key usage in an end-entity // means that there are no key usage restrictions. - ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr, - KeyUsage::noParticularKeyUsageRequired)); - ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr, - KeyUsage::digitalSignature)); - ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr, - KeyUsage::nonRepudiation)); - ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr, - KeyUsage::keyEncipherment)); - ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr, - KeyUsage::dataEncipherment)); - ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr, - KeyUsage::keyAgreement)); + ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr, + KeyUsage::noParticularKeyUsageRequired)); + ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr, + KeyUsage::digitalSignature)); + ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr, + KeyUsage::nonRepudiation)); + ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr, + KeyUsage::keyEncipherment)); + ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr, + KeyUsage::dataEncipherment)); + ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr, + KeyUsage::keyAgreement)); } TEST_F(pkixcheck_CheckKeyUsage, EE_empty) { // The input SECItem is empty. The cert had an empty keyUsage extension, // which is syntactically invalid. ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &empty_null, KeyUsage::digitalSignature)); ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &empty_nonnull, KeyUsage::digitalSignature)); } TEST_F(pkixcheck_CheckKeyUsage, CA_none) { // A CA certificate does not have a KU extension. - ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeCA, nullptr, - KeyUsage::keyCertSign)); + ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeCA, nullptr, + KeyUsage::keyCertSign)); } TEST_F(pkixcheck_CheckKeyUsage, CA_empty) { // A CA certificate has an empty KU extension. ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &empty_null, KeyUsage::keyCertSign)); ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &empty_nonnull, KeyUsage::keyCertSign)); } TEST_F(pkixcheck_CheckKeyUsage, maxUnusedBits) { NAMED_SIMPLE_KU(encoded, 7, 0x80); - ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &encoded, - KeyUsage::digitalSignature)); + ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &encoded, + KeyUsage::digitalSignature)); } TEST_F(pkixcheck_CheckKeyUsage, tooManyUnusedBits) { static uint8_t oneValueByteData[] = { 0x03/*BIT STRING*/, 0x02/*LENGTH=2*/, 8/*unused bits*/, 0x80 }; const SECItem oneValueByte = { @@ -177,18 +169,19 @@ TEST_F(pkixcheck_CheckKeyUsage, NoValueB KeyUsage::keyCertSign)); } void ASSERT_SimpleCase(uint8_t unusedBits, uint8_t bits, KeyUsage usage) { // Test that only the right bit is accepted for the usage for both EE and CA // certs. NAMED_SIMPLE_KU(good, unusedBits, bits); - ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &good, usage)); - ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeCA, &good, usage)); + ASSERT_EQ(Success, + CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &good, usage)); + ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeCA, &good, usage)); // We use (~bits >> unusedBits) << unusedBits) instead of using the same // calculation that is in CheckKeyUsage to validate that the calculation in // CheckKeyUsage is correct. // Test that none of the other non-padding bits are mistaken for the given // key usage in the single-byte value case. NAMED_SIMPLE_KU(notGood, unusedBits, @@ -221,18 +214,18 @@ TEST_F(pkixcheck_CheckKeyUsage, simpleCa } // Only CAs are allowed to assert keyCertSign TEST_F(pkixcheck_CheckKeyUsage, keyCertSign) { NAMED_SIMPLE_KU(good, 2, 0x04); ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &good, KeyUsage::keyCertSign)); - ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeCA, &good, - KeyUsage::keyCertSign)); + ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeCA, &good, + KeyUsage::keyCertSign)); // Test that none of the other non-padding bits are mistaken for the given // key usage in the one-byte value case. NAMED_SIMPLE_KU(notGood, 2, 0xFB); ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, ¬Good, KeyUsage::keyCertSign)); ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, ¬Good, KeyUsage::keyCertSign)); @@ -257,21 +250,22 @@ TEST_F(pkixcheck_CheckKeyUsage, unusedBi static uint8_t controlOneValueByteData[] = { 0x03/*BIT STRING*/, 0x02/*LENGTH=2*/, 7/*unused bits*/, 0x80 }; const SECItem controlOneValueByte = { siBuffer, controlOneValueByteData, sizeof(controlOneValueByteData) }; - ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, - &controlOneValueByte, - KeyUsage::digitalSignature)); - ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeCA, &controlOneValueByte, - KeyUsage::digitalSignature)); + ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, + &controlOneValueByte, + KeyUsage::digitalSignature)); + ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeCA, + &controlOneValueByte, + KeyUsage::digitalSignature)); // single-byte test case static uint8_t oneValueByteData[] = { 0x03/*BIT STRING*/, 0x02/*LENGTH=2*/, 7/*unused bits*/, 0x80 | 0x01 }; const SECItem oneValueByte = { siBuffer, oneValueByteData, @@ -287,21 +281,22 @@ TEST_F(pkixcheck_CheckKeyUsage, unusedBi 0x03/*BIT STRING*/, 0x03/*LENGTH=3*/, 7/*unused bits*/, 0x80 | 0x01, 0x80 }; const SECItem controlTwoValueBytes = { siBuffer, controlTwoValueBytesData, sizeof(controlTwoValueBytesData) }; - ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, - &controlTwoValueBytes, - KeyUsage::digitalSignature)); - ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeCA, &controlTwoValueBytes, - KeyUsage::digitalSignature)); + ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, + &controlTwoValueBytes, + KeyUsage::digitalSignature)); + ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeCA, + &controlTwoValueBytes, + KeyUsage::digitalSignature)); // two-byte test case static uint8_t twoValueBytesData[] = { 0x03/*BIT STRING*/, 0x03/*LENGTH=3*/, 7/*unused bits*/, 0x80 | 0x01, 0x80 | 0x01 }; const SECItem twoValueBytes = { siBuffer,
--- a/security/pkix/test/gtest/pkixcheck_CheckValidity_tests.cpp +++ b/security/pkix/test/gtest/pkixcheck_CheckValidity_tests.cpp @@ -17,17 +17,18 @@ * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ -#include "pkixgtest.h" +#include "gtest/gtest.h" +#include "pkix/pkixtypes.h" #include "pkixtestutil.h" using namespace mozilla::pkix; using namespace mozilla::pkix::test; namespace mozilla { namespace pkix { Result CheckValidity(const SECItem& encodedValidity, PRTime time); @@ -55,147 +56,134 @@ static const PRTime NOW(YMDHMS(2016, 12, #define NEWER_UTCTIME \ 0x17, 13, /* tag, length */ \ '2', '1', '0', '1', '0', '1', /* 2021-01-01 */ \ '0', '0', '0', '0', '0', '0', 'Z' /* 00:00:00Z */ static const PRTime FUTURE_TIME(YMDHMS(2025, 12, 31, 12, 23, 56)); -class pkixcheck_CheckValidity : public ::testing::Test -{ -public: - virtual void SetUp() - { - PR_SetError(0, 0); - } -}; +class pkixcheck_CheckValidity : public ::testing::Test { }; TEST_F(pkixcheck_CheckValidity, BothEmptyNull) { static const uint8_t DER[] = { 0x17/*UTCTime*/, 0/*length*/, 0x17/*UTCTime*/, 0/*length*/, }; static const SECItem validity = { siBuffer, const_cast<uint8_t*>(DER), sizeof(DER) }; - ASSERT_RecoverableError(SEC_ERROR_EXPIRED_CERTIFICATE, - CheckValidity(validity, NOW)); + ASSERT_EQ(Result::ERROR_EXPIRED_CERTIFICATE, CheckValidity(validity, NOW)); } TEST_F(pkixcheck_CheckValidity, NotBeforeEmptyNull) { static const uint8_t DER[] = { 0x17/*UTCTime*/, 0x00/*length*/, NEWER_UTCTIME }; static const SECItem validity = { siBuffer, const_cast<uint8_t*>(DER), sizeof(DER) }; - ASSERT_RecoverableError(SEC_ERROR_EXPIRED_CERTIFICATE, - CheckValidity(validity, NOW)); + ASSERT_EQ(Result::ERROR_EXPIRED_CERTIFICATE, CheckValidity(validity, NOW)); } TEST_F(pkixcheck_CheckValidity, NotAfterEmptyNull) { static const uint8_t DER[] = { NEWER_UTCTIME, 0x17/*UTCTime*/, 0x00/*length*/, }; static const SECItem validity = { siBuffer, const_cast<uint8_t*>(DER), sizeof(DER) }; - ASSERT_RecoverableError(SEC_ERROR_EXPIRED_CERTIFICATE, - CheckValidity(validity, NOW)); + ASSERT_EQ(Result::ERROR_EXPIRED_CERTIFICATE, CheckValidity(validity, NOW)); } static const uint8_t OLDER_UTCTIME_NEWER_UTCTIME_DATA[] = { OLDER_UTCTIME, NEWER_UTCTIME, }; static const SECItem OLDER_UTCTIME_NEWER_UTCTIME = { siBuffer, const_cast<uint8_t*>(OLDER_UTCTIME_NEWER_UTCTIME_DATA), sizeof(OLDER_UTCTIME_NEWER_UTCTIME_DATA) }; TEST_F(pkixcheck_CheckValidity, Valid_UTCTIME_UTCTIME) { - ASSERT_Success(CheckValidity(OLDER_UTCTIME_NEWER_UTCTIME, NOW)); + ASSERT_EQ(Success, CheckValidity(OLDER_UTCTIME_NEWER_UTCTIME, NOW)); } TEST_F(pkixcheck_CheckValidity, Valid_GENERALIZEDTIME_GENERALIZEDTIME) { static const uint8_t DER[] = { OLDER_GENERALIZEDTIME, NEWER_GENERALIZEDTIME, }; static const SECItem validity = { siBuffer, const_cast<uint8_t*>(DER), sizeof(DER) }; - ASSERT_Success(CheckValidity(validity, NOW)); + ASSERT_EQ(Success, CheckValidity(validity, NOW)); } TEST_F(pkixcheck_CheckValidity, Valid_GENERALIZEDTIME_UTCTIME) { static const uint8_t DER[] = { OLDER_GENERALIZEDTIME, NEWER_UTCTIME, }; static const SECItem validity = { siBuffer, const_cast<uint8_t*>(DER), sizeof(DER) }; - ASSERT_Success(CheckValidity(validity, NOW)); + ASSERT_EQ(Success, CheckValidity(validity, NOW)); } TEST_F(pkixcheck_CheckValidity, Valid_UTCTIME_GENERALIZEDTIME) { static const uint8_t DER[] = { OLDER_UTCTIME, NEWER_GENERALIZEDTIME, }; static const SECItem validity = { siBuffer, const_cast<uint8_t*>(DER), sizeof(DER) }; - ASSERT_Success(CheckValidity(validity, NOW)); + ASSERT_EQ(Success, CheckValidity(validity, NOW)); } TEST_F(pkixcheck_CheckValidity, InvalidBeforeNotBefore) { - ASSERT_RecoverableError(SEC_ERROR_EXPIRED_CERTIFICATE, - CheckValidity(OLDER_UTCTIME_NEWER_UTCTIME, - PAST_TIME)); + ASSERT_EQ(Result::ERROR_EXPIRED_CERTIFICATE, + CheckValidity(OLDER_UTCTIME_NEWER_UTCTIME, PAST_TIME)); } TEST_F(pkixcheck_CheckValidity, InvalidAfterNotAfter) { - ASSERT_RecoverableError(SEC_ERROR_EXPIRED_CERTIFICATE, - CheckValidity(OLDER_UTCTIME_NEWER_UTCTIME, - FUTURE_TIME)); + ASSERT_EQ(Result::ERROR_EXPIRED_CERTIFICATE, + CheckValidity(OLDER_UTCTIME_NEWER_UTCTIME, FUTURE_TIME)); } TEST_F(pkixcheck_CheckValidity, InvalidNotAfterBeforeNotBefore) { static const uint8_t DER[] = { NEWER_UTCTIME, OLDER_UTCTIME, }; static const SECItem validity = { siBuffer, const_cast<uint8_t*>(DER), sizeof(DER) }; - ASSERT_RecoverableError(SEC_ERROR_EXPIRED_CERTIFICATE, - CheckValidity(validity, NOW)); + ASSERT_EQ(Result::ERROR_EXPIRED_CERTIFICATE, CheckValidity(validity, NOW)); }
--- a/security/pkix/test/gtest/pkixder_input_tests.cpp +++ b/security/pkix/test/gtest/pkixder_input_tests.cpp @@ -21,34 +21,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ #include <functional> #include <vector> #include <gtest/gtest.h> -#include "pkixgtest.h" #include "pkix/bind.h" #include "pkixder.h" using namespace mozilla::pkix; using namespace mozilla::pkix::der; -using namespace mozilla::pkix::test; namespace { -class pkixder_input_tests : public ::testing::Test -{ -protected: - virtual void SetUp() - { - PR_SetError(0, 0); - } -}; +class pkixder_input_tests : public ::testing::Test { }; static const uint8_t DER_SEQUENCE_EMPTY[] = { 0x30, // SEQUENCE 0x00, // length }; static const uint8_t DER_SEQUENCE_NOT_EMPTY[] = { 0x30, // SEQUENCE @@ -90,65 +81,57 @@ const uint8_t DER_OVERRUN_SEQUENCE_OF_IN }; const uint8_t DER_INT16[] = { 0x02, // INTEGER 0x02, // length 0x12, 0x34 // 0x1234 }; -TEST_F(pkixder_input_tests, FailWithError) -{ - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Fail(SEC_ERROR_BAD_DER)); - ASSERT_FatalError(SEC_ERROR_INVALID_ARGS, Fail(SEC_ERROR_INVALID_ARGS)); -} - TEST_F(pkixder_input_tests, InputInit) { Input input; ASSERT_EQ(Success, input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8)); } TEST_F(pkixder_input_tests, InputInitWithNullPointerOrZeroLength) { Input input; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, input.Init(nullptr, 0)); + ASSERT_EQ(Result::ERROR_BAD_DER, input.Init(nullptr, 0)); - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, input.Init(nullptr, 100)); + ASSERT_EQ(Result::ERROR_BAD_DER, input.Init(nullptr, 100)); // Though it seems odd to initialize with zero-length and non-null ptr, this // is working as intended. The Input class was intended to protect against // buffer overflows, and there's no risk with the current behavior. See bug // 1000354. ASSERT_EQ(Success, input.Init((const uint8_t*) "hello", 0)); ASSERT_TRUE(input.AtEnd()); } TEST_F(pkixder_input_tests, InputInitWithLargeData) { Input input; // Data argument length does not matter, it is not touched, just // needs to be non-null - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, - input.Init((const uint8_t*) "", 0xffff+1)); + ASSERT_EQ(Result::ERROR_BAD_DER, input.Init((const uint8_t*) "", 0xffff+1)); ASSERT_EQ(Success, input.Init((const uint8_t*) "", 0xffff)); } TEST_F(pkixder_input_tests, InputInitMultipleTimes) { Input input; ASSERT_EQ(Success, input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8)); - ASSERT_FatalError(SEC_ERROR_INVALID_ARGS, - input.Init(DER_SEQUENCE_OF_INT8, - sizeof DER_SEQUENCE_OF_INT8)); + ASSERT_EQ(Result::FATAL_ERROR_INVALID_ARGS, + input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8)); } TEST_F(pkixder_input_tests, ExpectSuccess) { Input input; ASSERT_EQ(Success, input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8)); @@ -160,30 +143,28 @@ TEST_F(pkixder_input_tests, ExpectSucces TEST_F(pkixder_input_tests, ExpectMismatch) { Input input; ASSERT_EQ(Success, input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8)); const uint8_t expected[] = { 0x11, 0x22 }; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, - input.Expect(expected, sizeof expected)); + ASSERT_EQ(Result::ERROR_BAD_DER, input.Expect(expected, sizeof expected)); } TEST_F(pkixder_input_tests, ExpectTooMuch) { Input input; const uint8_t der[] = { 0x11, 0x22 }; ASSERT_EQ(Success, input.Init(der, sizeof der)); const uint8_t expected[] = { 0x11, 0x22, 0x33 }; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, - input.Expect(expected, sizeof expected)); + ASSERT_EQ(Result::ERROR_BAD_DER, input.Expect(expected, sizeof expected)); } TEST_F(pkixder_input_tests, PeekWithinBounds) { Input input; const uint8_t der[] = { 0x11, 0x11 }; ASSERT_EQ(Success, input.Init(der, sizeof der)); ASSERT_TRUE(input.Peek(0x11)); @@ -224,34 +205,34 @@ TEST_F(pkixder_input_tests, ReadBytePast // Initialize with too-short length ASSERT_EQ(Success, input.Init(der, 1)); uint8_t readByte1 = 0; ASSERT_EQ(Success, input.Read(readByte1)); ASSERT_EQ(0x11, readByte1); uint8_t readByte2 = 0; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, input.Read(readByte2)); + ASSERT_EQ(Result::ERROR_BAD_DER, input.Read(readByte2)); ASSERT_NE(0x22, readByte2); } TEST_F(pkixder_input_tests, ReadByteWrapAroundPointer) { // The original implementation of our buffer read overflow checks was // susceptible to integer overflows which could make the checks ineffective. // This attempts to verify that we've fixed that. Unfortunately, decrementing // a null pointer is undefined behavior according to the C++ language spec., // but this should catch the problem on at least some compilers, if not all of // them. const uint8_t* der = nullptr; --der; Input input; ASSERT_EQ(Success, input.Init(der, 0)); uint8_t b; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, input.Read(b)); + ASSERT_EQ(Result::ERROR_BAD_DER, input.Read(b)); } TEST_F(pkixder_input_tests, ReadWord) { Input input; const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 }; ASSERT_EQ(Success, input.Init(der, sizeof der)); @@ -271,45 +252,45 @@ TEST_F(pkixder_input_tests, ReadWordPast // Initialize with too-short length ASSERT_EQ(Success, input.Init(der, 2)); uint16_t readWord1 = 0; ASSERT_EQ(Success, input.Read(readWord1)); ASSERT_EQ(0x1122, readWord1); uint16_t readWord2 = 0; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, input.Read(readWord2)); + ASSERT_EQ(Result::ERROR_BAD_DER, input.Read(readWord2)); ASSERT_NE(0x3344, readWord2); } TEST_F(pkixder_input_tests, ReadWordWithInsufficentData) { Input input; const uint8_t der[] = { 0x11, 0x22 }; ASSERT_EQ(Success, input.Init(der, 1)); uint16_t readWord1 = 0; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, input.Read(readWord1)); + ASSERT_EQ(Result::ERROR_BAD_DER, input.Read(readWord1)); ASSERT_NE(0x1122, readWord1); } TEST_F(pkixder_input_tests, ReadWordWrapAroundPointer) { // The original implementation of our buffer read overflow checks was // susceptible to integer overflows which could make the checks ineffective. // This attempts to verify that we've fixed that. Unfortunately, decrementing // a null pointer is undefined behavior according to the C++ language spec., // but this should catch the problem on at least some compilers, if not all of // them. const uint8_t* der = nullptr; --der; Input input; ASSERT_EQ(Success, input.Init(der, 0)); uint16_t b; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, input.Read(b)); + ASSERT_EQ(Result::ERROR_BAD_DER, input.Read(b)); } TEST_F(pkixder_input_tests, InputSkip) { Input input; const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 }; ASSERT_EQ(Success, input.Init(der, sizeof der)); @@ -336,17 +317,17 @@ TEST_F(pkixder_input_tests, InputSkipToE } TEST_F(pkixder_input_tests, InputSkipPastEnd) { Input input; const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 }; ASSERT_EQ(Success, input.Init(der, sizeof der)); - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, input.Skip(sizeof der + 1)); + ASSERT_EQ(Result::ERROR_BAD_DER, input.Skip(sizeof der + 1)); } TEST_F(pkixder_input_tests, InputSkipToNewInput) { Input input; const uint8_t der[] = { 0x01, 0x02, 0x03, 0x04 }; ASSERT_EQ(Success, input.Init(der, sizeof der)); @@ -373,18 +354,17 @@ TEST_F(pkixder_input_tests, InputSkipToN TEST_F(pkixder_input_tests, InputSkipToNewInputPastEnd) { Input input; const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 }; ASSERT_EQ(Success, input.Init(der, sizeof der)); Input skippedInput; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, - input.Skip(sizeof der * 2, skippedInput)); + ASSERT_EQ(Result::ERROR_BAD_DER, input.Skip(sizeof der * 2, skippedInput)); } TEST_F(pkixder_input_tests, InputSkipToSECItem) { Input input; const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 }; ASSERT_EQ(Success, input.Init(der, sizeof der)); @@ -405,28 +385,27 @@ TEST_F(pkixder_input_tests, SkipWrapArou // This attempts to verify that we've fixed that. Unfortunately, decrementing // a null pointer is undefined behavior according to the C++ language spec., // but this should catch the problem on at least some compilers, if not all of // them. const uint8_t* der = nullptr; --der; Input input; ASSERT_EQ(Success, input.Init(der, 0)); - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, input.Skip(1)); + ASSERT_EQ(Result::ERROR_BAD_DER, input.Skip(1)); } TEST_F(pkixder_input_tests, SkipToSECItemPastEnd) { Input input; const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 }; ASSERT_EQ(Success, input.Init(der, sizeof der)); SECItem skippedSECItem; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, - input.Skip(sizeof der + 1, skippedSECItem)); + ASSERT_EQ(Result::ERROR_BAD_DER, input.Skip(sizeof der + 1, skippedSECItem)); } TEST_F(pkixder_input_tests, ExpectTagAndSkipValue) { Input input; ASSERT_EQ(Success, input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8)); @@ -435,27 +414,26 @@ TEST_F(pkixder_input_tests, ExpectTagAnd } TEST_F(pkixder_input_tests, ExpectTagAndSkipValueWithTruncatedData) { Input input; ASSERT_EQ(Success, input.Init(DER_TRUNCATED_SEQUENCE_OF_INT8, sizeof DER_TRUNCATED_SEQUENCE_OF_INT8)); - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, - ExpectTagAndSkipValue(input, SEQUENCE)); + ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndSkipValue(input, SEQUENCE)); } TEST_F(pkixder_input_tests, ExpectTagAndSkipValueWithOverrunData) { Input input; ASSERT_EQ(Success, input.Init(DER_OVERRUN_SEQUENCE_OF_INT8, sizeof DER_OVERRUN_SEQUENCE_OF_INT8)); ASSERT_EQ(Success, ExpectTagAndSkipValue(input, SEQUENCE)); - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, End(input)); + ASSERT_EQ(Result::ERROR_BAD_DER, End(input)); } TEST_F(pkixder_input_tests, AtEndOnUnInitializedInput) { Input input; ASSERT_TRUE(input.AtEnd()); } @@ -507,18 +485,18 @@ TEST_F(pkixder_input_tests, MarkAndGetSE ASSERT_EQ(Success, input.Init(der, sizeof der)); Input another; Input::Mark mark = another.GetMark(); ASSERT_EQ(Success, input.Skip(3)); SECItem item; - ASSERT_FatalError(SEC_ERROR_INVALID_ARGS, - input.GetSECItem(siBuffer, mark, item)); + ASSERT_EQ(Result::FATAL_ERROR_INVALID_ARGS, + input.GetSECItem(siBuffer, mark, item)); } #endif TEST_F(pkixder_input_tests, ExpectTagAndLength) { Input input; ASSERT_EQ(Success, input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8)); @@ -528,28 +506,26 @@ TEST_F(pkixder_input_tests, ExpectTagAnd } TEST_F(pkixder_input_tests, ExpectTagAndLengthWithWrongLength) { Input input; ASSERT_EQ(Success, input.Init(DER_INT16, sizeof DER_INT16)); // Wrong length - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, - ExpectTagAndLength(input, INTEGER, 4)); + ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndLength(input, INTEGER, 4)); } TEST_F(pkixder_input_tests, ExpectTagAndLengthWithWrongTag) { Input input; ASSERT_EQ(Success, input.Init(DER_INT16, sizeof DER_INT16)); // Wrong type - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, - ExpectTagAndLength(input, OCTET_STRING, 2)); + ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndLength(input, OCTET_STRING, 2)); } TEST_F(pkixder_input_tests, ExpectTagAndGetLength) { Input input; ASSERT_EQ(Success, input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8)); @@ -563,31 +539,29 @@ TEST_F(pkixder_input_tests, ExpectTagAnd TEST_F(pkixder_input_tests, ExpectTagAndGetLengthWithWrongTag) { Input input; ASSERT_EQ(Success, input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8)); uint16_t length = 0; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, - der::internal::ExpectTagAndGetLength(input, INTEGER, - length)); + ASSERT_EQ(Result::ERROR_BAD_DER, + der::internal::ExpectTagAndGetLength(input, INTEGER, length)); } TEST_F(pkixder_input_tests, ExpectTagAndGetLengthWithWrongLength) { Input input; ASSERT_EQ(Success, input.Init(DER_TRUNCATED_SEQUENCE_OF_INT8, sizeof DER_TRUNCATED_SEQUENCE_OF_INT8)); uint16_t length = 0; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, - der::internal::ExpectTagAndGetLength(input, SEQUENCE, - length)); + ASSERT_EQ(Result::ERROR_BAD_DER, + der::internal::ExpectTagAndGetLength(input, SEQUENCE, length)); } TEST_F(pkixder_input_tests, ExpectTagAndGetValue_Input_ValidEmpty) { Input input; ASSERT_EQ(Success, input.Init(DER_SEQUENCE_EMPTY, sizeof DER_SEQUENCE_EMPTY)); Input value; @@ -610,38 +584,38 @@ TEST_F(pkixder_input_tests, ExpectTagAnd TEST_F(pkixder_input_tests, ExpectTagAndGetValue_Input_InvalidNotEmptyValueTruncated) { Input input; ASSERT_EQ(Success, input.Init(DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED, sizeof DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED)); Input value; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, - ExpectTagAndGetValue(input, SEQUENCE, value)); + ASSERT_EQ(Result::ERROR_BAD_DER, + ExpectTagAndGetValue(input, SEQUENCE, value)); } TEST_F(pkixder_input_tests, ExpectTagAndGetValue_Input_InvalidWrongLength) { Input input; ASSERT_EQ(Success, input.Init(DER_TRUNCATED_SEQUENCE_OF_INT8, sizeof DER_TRUNCATED_SEQUENCE_OF_INT8)); Input value; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, - ExpectTagAndGetValue(input, SEQUENCE, value)); + ASSERT_EQ(Result::ERROR_BAD_DER, + ExpectTagAndGetValue(input, SEQUENCE, value)); } TEST_F(pkixder_input_tests, ExpectTagAndGetLength_Input_InvalidWrongTag) { Input input; ASSERT_EQ(Success, input.Init(DER_SEQUENCE_NOT_EMPTY, sizeof DER_SEQUENCE_NOT_EMPTY)); Input value; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, - ExpectTagAndGetValue(input, INTEGER, value)); + ASSERT_EQ(Result::ERROR_BAD_DER, + ExpectTagAndGetValue(input, INTEGER, value)); } TEST_F(pkixder_input_tests, ExpectTagAndGetValue_SECItem_ValidEmpty) { Input input; ASSERT_EQ(Success, input.Init(DER_SEQUENCE_EMPTY, sizeof DER_SEQUENCE_EMPTY)); SECItem value = { siBuffer, nullptr, 5 }; @@ -667,38 +641,38 @@ TEST_F(pkixder_input_tests, ExpectTagAnd TEST_F(pkixder_input_tests, ExpectTagAndGetValue_SECItem_InvalidNotEmptyValueTruncated) { Input input; ASSERT_EQ(Success, input.Init(DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED, sizeof DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED)); SECItem value; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, - ExpectTagAndGetValue(input, SEQUENCE, value)); + ASSERT_EQ(Result::ERROR_BAD_DER, + ExpectTagAndGetValue(input, SEQUENCE, value)); } TEST_F(pkixder_input_tests, ExpectTagAndGetValue_SECItem_InvalidWrongLength) { Input input; ASSERT_EQ(Success, input.Init(DER_TRUNCATED_SEQUENCE_OF_INT8, sizeof DER_TRUNCATED_SEQUENCE_OF_INT8)); SECItem value; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, - ExpectTagAndGetValue(input, SEQUENCE, value)); + ASSERT_EQ(Result::ERROR_BAD_DER, + ExpectTagAndGetValue(input, SEQUENCE, value)); } TEST_F(pkixder_input_tests, ExpectTagAndGetLength_SECItem_InvalidWrongTag) { Input input; ASSERT_EQ(Success, input.Init(DER_SEQUENCE_NOT_EMPTY, sizeof DER_SEQUENCE_NOT_EMPTY)); SECItem value; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, - ExpectTagAndGetValue(input, INTEGER, value)); + ASSERT_EQ(Result::ERROR_BAD_DER, + ExpectTagAndGetValue(input, INTEGER, value)); } TEST_F(pkixder_input_tests, ExpectTagAndGetTLV_SECItem_ValidEmpty) { Input input; ASSERT_EQ(Success, input.Init(DER_SEQUENCE_EMPTY, sizeof DER_SEQUENCE_EMPTY)); SECItem tlv = { siBuffer, nullptr, 5 }; @@ -727,77 +701,74 @@ TEST_F(pkixder_input_tests, ExpectTagAnd TEST_F(pkixder_input_tests, ExpectTagAndGetTLV_SECItem_InvalidNotEmptyValueTruncated) { Input input; ASSERT_EQ(Success, input.Init(DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED, sizeof DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED)); SECItem tlv; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, - ExpectTagAndGetTLV(input, SEQUENCE, tlv)); + ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndGetTLV(input, SEQUENCE, tlv)); } TEST_F(pkixder_input_tests, ExpectTagAndGetTLV_SECItem_InvalidWrongLength) { Input input; ASSERT_EQ(Success, input.Init(DER_TRUNCATED_SEQUENCE_OF_INT8, sizeof DER_TRUNCATED_SEQUENCE_OF_INT8)); SECItem tlv; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, - ExpectTagAndGetTLV(input, SEQUENCE, tlv)); + ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndGetTLV(input, SEQUENCE, tlv)); } TEST_F(pkixder_input_tests, ExpectTagAndGetTLV_SECItem_InvalidWrongTag) { Input input; ASSERT_EQ(Success, input.Init(DER_SEQUENCE_NOT_EMPTY, sizeof DER_SEQUENCE_NOT_EMPTY)); SECItem tlv; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, - ExpectTagAndGetTLV(input, INTEGER, tlv)); + ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndGetTLV(input, INTEGER, tlv)); } TEST_F(pkixder_input_tests, ExpectTagAndSkipLength) { Input input; ASSERT_EQ(Success, input.Init(DER_INT16, sizeof DER_INT16)); ASSERT_EQ(Success, ExpectTagAndSkipLength(input, INTEGER)); } TEST_F(pkixder_input_tests, ExpectTagAndSkipLengthWithWrongTag) { Input input; ASSERT_EQ(Success, input.Init(DER_INT16, sizeof DER_INT16)); - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, - ExpectTagAndSkipLength(input, OCTET_STRING)); + ASSERT_EQ(Result::ERROR_BAD_DER, + ExpectTagAndSkipLength(input, OCTET_STRING)); } TEST_F(pkixder_input_tests, EndAtEnd) { Input input; ASSERT_EQ(Success, input.Init(DER_INT16, sizeof DER_INT16)); ASSERT_EQ(Success, input.Skip(4)); ASSERT_EQ(Success, End(input)); } TEST_F(pkixder_input_tests, EndBeforeEnd) { Input input; ASSERT_EQ(Success, input.Init(DER_INT16, sizeof DER_INT16)); ASSERT_EQ(Success, input.Skip(2)); - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, End(input)); + ASSERT_EQ(Result::ERROR_BAD_DER, End(input)); } TEST_F(pkixder_input_tests, EndAtBeginning) { Input input; ASSERT_EQ(Success, input.Init(DER_INT16, sizeof DER_INT16)); - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, End(input)); + ASSERT_EQ(Result::ERROR_BAD_DER, End(input)); } // TODO: Need tests for Nested too? Result NestedOfHelper(Input& input, std::vector<uint8_t>& readValues) { uint8_t value = 0; Result rv = input.Read(value); @@ -829,21 +800,20 @@ TEST_F(pkixder_input_tests, NestedOf) TEST_F(pkixder_input_tests, NestedOfWithTruncatedData) { Input input; ASSERT_EQ(Success, input.Init(DER_TRUNCATED_SEQUENCE_OF_INT8, sizeof DER_TRUNCATED_SEQUENCE_OF_INT8)); std::vector<uint8_t> readValues; - ASSERT_RecoverableError( - SEC_ERROR_BAD_DER, - NestedOf(input, SEQUENCE, INTEGER, EmptyAllowed::No, - mozilla::pkix::bind(NestedOfHelper, mozilla::pkix::_1, - mozilla::pkix::ref(readValues)))); + ASSERT_EQ(Result::ERROR_BAD_DER, + NestedOf(input, SEQUENCE, INTEGER, EmptyAllowed::No, + mozilla::pkix::bind(NestedOfHelper, mozilla::pkix::_1, + mozilla::pkix::ref(readValues)))); ASSERT_EQ((size_t) 0, readValues.size()); } TEST_F(pkixder_input_tests, MatchRestAtEnd) { Input input; static const uint8_t der[1] = { }; ASSERT_EQ(Success, input.Init(der, 0));
--- a/security/pkix/test/gtest/pkixder_pki_types_tests.cpp +++ b/security/pkix/test/gtest/pkixder_pki_types_tests.cpp @@ -23,32 +23,24 @@ */ #include <functional> #include <vector> #include "nssgtest.h" #include "pkix/pkixtypes.h" #include "pkixder.h" -#include "pkixgtest.h" using namespace mozilla::pkix; using namespace mozilla::pkix::der; using namespace mozilla::pkix::test; namespace { -class pkixder_pki_types_tests : public ::testing::Test -{ -protected: - virtual void SetUp() - { - PR_SetError(0, 0); - } -}; +class pkixder_pki_types_tests : public ::testing::Test { }; TEST_F(pkixder_pki_types_tests, CertificateSerialNumber) { const uint8_t DER_CERT_SERIAL[] = { 0x02, // INTEGER 8, // length 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }; @@ -108,36 +100,34 @@ TEST_F(pkixder_pki_types_tests, Certific 0x00 // length }; Input input; ASSERT_EQ(Success, input.Init(DER_CERT_SERIAL_ZERO_LENGTH, sizeof DER_CERT_SERIAL_ZERO_LENGTH)); SECItem item; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, - CertificateSerialNumber(input, item)); + ASSERT_EQ(Result::ERROR_BAD_DER, CertificateSerialNumber(input, item)); } TEST_F(pkixder_pki_types_tests, OptionalVersionV1ExplicitEncodingAllowed) { const uint8_t DER_OPTIONAL_VERSION_V1[] = { 0xa0, 0x03, // context specific 0 0x02, 0x01, 0x00 // INTEGER(0) }; Input input; ASSERT_EQ(Success, input.Init(DER_OPTIONAL_VERSION_V1, sizeof DER_OPTIONAL_VERSION_V1)); // XXX(bug 1031093): We shouldn't accept an explicit encoding of v1, but we // do here for compatibility reasons. // Version version; - // ASSERT_RecoverableError(SEC_ERROR_BAD_DER, - // OptionalVersion(input, version)); + // ASSERT_EQ(Result::ERROR_BAD_DER, OptionalVersion(input, version)); der::Version version = der::Version::v3; ASSERT_EQ(Success, OptionalVersion(input, version)); ASSERT_EQ(der::Version::v1, version); } TEST_F(pkixder_pki_types_tests, OptionalVersionV2) { const uint8_t DER_OPTIONAL_VERSION_V2[] = { @@ -177,32 +167,32 @@ TEST_F(pkixder_pki_types_tests, Optional 0x02, 0x01, 0x42 // INTEGER(0x42) }; Input input; ASSERT_EQ(Success, input.Init(DER_OPTIONAL_VERSION_INVALID, sizeof DER_OPTIONAL_VERSION_INVALID)); der::Version version = der::Version::v1; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, OptionalVersion(input, version)); + ASSERT_EQ(Result::ERROR_BAD_DER, OptionalVersion(input, version)); } TEST_F(pkixder_pki_types_tests, OptionalVersionInvalidTooLong) { const uint8_t DER_OPTIONAL_VERSION_INVALID_TOO_LONG[] = { 0xa0, 0x03, // context specific 0 0x02, 0x02, 0x12, 0x34 // INTEGER(0x1234) }; Input input; ASSERT_EQ(Success, input.Init(DER_OPTIONAL_VERSION_INVALID_TOO_LONG, sizeof DER_OPTIONAL_VERSION_INVALID_TOO_LONG)); der::Version version; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, OptionalVersion(input, version)); + ASSERT_EQ(Result::ERROR_BAD_DER, OptionalVersion(input, version)); } TEST_F(pkixder_pki_types_tests, OptionalVersionMissing) { const uint8_t DER_OPTIONAL_VERSION_MISSING[] = { 0x02, 0x11, 0x22 // INTEGER }; @@ -296,34 +286,34 @@ TEST_F(pkixder_DigestAlgorithmIdentifier static const uint8_t DER[] = { 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05 }; Input input; ASSERT_EQ(Success, input.Init(DER, sizeof(DER))); DigestAlgorithm alg; - ASSERT_RecoverableError(SEC_ERROR_INVALID_ALGORITHM, - DigestAlgorithmIdentifier(input, alg)); + ASSERT_EQ(Result::ERROR_INVALID_ALGORITHM, + DigestAlgorithmIdentifier(input, alg)); } TEST_F(pkixder_DigestAlgorithmIdentifier, Invalid_Digest_ECDSA_WITH_SHA256) { // The OID identifies ecdsa-with-SHA256 (1.2.840.10045.4.3.2). It is invalid // because ECDSA-with-SHA256 is not a hash algorithm. static const uint8_t DER[] = { 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, // }; Input input; ASSERT_EQ(Success, input.Init(DER, sizeof(DER))); DigestAlgorithm alg; - ASSERT_RecoverableError(SEC_ERROR_INVALID_ALGORITHM, - DigestAlgorithmIdentifier(input, alg)); + ASSERT_EQ(Result::ERROR_INVALID_ALGORITHM, + DigestAlgorithmIdentifier(input, alg)); } static const AlgorithmIdentifierTestInfo<SignatureAlgorithm> SIGNATURE_ALGORITHM_TEST_INFO[] = { { SignatureAlgorithm::ecdsa_with_sha512, { 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04 }, @@ -427,29 +417,29 @@ TEST_F(pkixder_SignatureAlgorithmIdentif static const uint8_t DER[] = { 0x30, 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x04 }; Input input; ASSERT_EQ(Success, input.Init(DER, sizeof(DER))); SignatureAlgorithm alg; - ASSERT_RecoverableError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED, - SignatureAlgorithmIdentifier(input, alg)); + ASSERT_EQ(Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED, + SignatureAlgorithmIdentifier(input, alg)); } TEST_F(pkixder_SignatureAlgorithmIdentifier, Invalid_SignatureAlgorithm_SHA256) { // The OID identifies id-sha256 (2.16.840.1.101.3.4.2.1). It is invalid // because SHA-256 is not a signature algorithm. static const uint8_t DER[] = { 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 }; Input input; ASSERT_EQ(Success, input.Init(DER, sizeof(DER))); SignatureAlgorithm alg; - ASSERT_RecoverableError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED, - SignatureAlgorithmIdentifier(input, alg)); + ASSERT_EQ(Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED, + SignatureAlgorithmIdentifier(input, alg)); } } // unnamed namespace
--- a/security/pkix/test/gtest/pkixder_universal_types_tests.cpp +++ b/security/pkix/test/gtest/pkixder_universal_types_tests.cpp @@ -23,66 +23,58 @@ */ #include <limits> #include <vector> #include <gtest/gtest.h> #include "pkix/bind.h" #include "pkixder.h" -#include "pkixgtest.h" #include "pkixtestutil.h" #include "stdint.h" using namespace mozilla::pkix; using namespace mozilla::pkix::der; using namespace mozilla::pkix::test; using namespace std; namespace { -class pkixder_universal_types_tests : public ::testing::Test -{ -protected: - virtual void SetUp() - { - PR_SetError(0, 0); - } -}; +class pkixder_universal_types_tests : public ::testing::Test { }; TEST_F(pkixder_universal_types_tests, BooleanTrue01) { const uint8_t DER_BOOLEAN_TRUE_01[] = { 0x01, // BOOLEAN 0x01, // length 0x01 // invalid }; Input input; ASSERT_EQ(Success, input.Init(DER_BOOLEAN_TRUE_01, sizeof DER_BOOLEAN_TRUE_01)); bool value = false; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Boolean(input, value)); + ASSERT_EQ(Result::ERROR_BAD_DER, Boolean(input, value)); } TEST_F(pkixder_universal_types_tests, BooleanTrue42) { const uint8_t DER_BOOLEAN_TRUE_42[] = { 0x01, // BOOLEAN 0x01, // length 0x42 // invalid }; Input input; ASSERT_EQ(Success, input.Init(DER_BOOLEAN_TRUE_42, sizeof DER_BOOLEAN_TRUE_42)); bool value = false; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Boolean(input, value)); + ASSERT_EQ(Result::ERROR_BAD_DER, Boolean(input, value)); } static const uint8_t DER_BOOLEAN_TRUE[] = { 0x01, // BOOLEAN 0x01, // length 0xff // true }; @@ -121,32 +113,32 @@ TEST_F(pkixder_universal_types_tests, Bo 0x42, 0x42 // invalid }; Input input; ASSERT_EQ(Success, input.Init(DER_BOOLEAN_INVALID_LENGTH, sizeof DER_BOOLEAN_INVALID_LENGTH)); bool value = true; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Boolean(input, value)); + ASSERT_EQ(Result::ERROR_BAD_DER, Boolean(input, value)); } TEST_F(pkixder_universal_types_tests, BooleanInvalidZeroLength) { const uint8_t DER_BOOLEAN_INVALID_ZERO_LENGTH[] = { 0x01, // BOOLEAN 0x00 // length }; Input input; ASSERT_EQ(Success, input.Init(DER_BOOLEAN_INVALID_ZERO_LENGTH, sizeof DER_BOOLEAN_INVALID_ZERO_LENGTH)); bool value = true; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Boolean(input, value)); + ASSERT_EQ(Result::ERROR_BAD_DER, Boolean(input, value)); } // OptionalBoolean implements decoding of OPTIONAL BOOLEAN DEFAULT FALSE. // If the field is present, it must be a valid encoding of a BOOLEAN with // value TRUE. If the field is not present, it defaults to FALSE. For // compatibility reasons, OptionalBoolean can be told to accept an encoding // where the field is present with value FALSE (this is technically not a // valid DER encoding). @@ -201,19 +193,18 @@ TEST_F(pkixder_universal_types_tests, Op Input input1; ASSERT_EQ(Success, input1.Init(DER_OPTIONAL_BOOLEAN_PRESENT_FALSE, sizeof DER_OPTIONAL_BOOLEAN_PRESENT_FALSE)); bool value; // If the second parameter to OptionalBoolean is false, invalid encodings // that include the field even when it is the DEFAULT FALSE are rejected. bool allowInvalidEncodings = false; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, - OptionalBoolean(input1, allowInvalidEncodings, - value)) << + ASSERT_EQ(Result::ERROR_BAD_DER, + OptionalBoolean(input1, allowInvalidEncodings, value)) << "Should reject an invalid encoding of present OPTIONAL BOOLEAN"; Input input2; ASSERT_EQ(Success, input2.Init(DER_OPTIONAL_BOOLEAN_PRESENT_FALSE, sizeof DER_OPTIONAL_BOOLEAN_PRESENT_FALSE)); value = true; // If the second parameter to OptionalBoolean is true, invalid encodings // that include the field even when it is the DEFAULT FALSE are accepted. @@ -229,19 +220,18 @@ TEST_F(pkixder_universal_types_tests, Op 0x42 // (invalid value for a BOOLEAN) }; Input input3; ASSERT_EQ(Success, input3.Init(DER_OPTIONAL_BOOLEAN_PRESENT_42, sizeof DER_OPTIONAL_BOOLEAN_PRESENT_42)); // Even with the second parameter to OptionalBoolean as true, encodings // of BOOLEAN that are invalid altogether are rejected. - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, - OptionalBoolean(input3, allowInvalidEncodings, - value)) << + ASSERT_EQ(Result::ERROR_BAD_DER, + OptionalBoolean(input3, allowInvalidEncodings, value)) << "Should reject another invalid encoding of present OPTIONAL BOOLEAN"; } TEST_F(pkixder_universal_types_tests, Enumerated) { const uint8_t DER_ENUMERATED[] = { 0x0a, // ENUMERATED 0x01, // length @@ -262,17 +252,17 @@ TEST_F(pkixder_universal_types_tests, En 0x0a, // ENUMERATED 0x02, // length 0x00, 0x01 // value }; Input input; ASSERT_EQ(Success, input.Init(DER_ENUMERATED, sizeof DER_ENUMERATED)); uint8_t value = 0; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Enumerated(input, value)); + ASSERT_EQ(Result::ERROR_BAD_DER, Enumerated(input, value)); } TEST_F(pkixder_universal_types_tests, EnumeratedOutOfAcceptedRange) { // Although this is a valid ENUMERATED value according to ASN.1, we // intentionally don't support these large values because there are no // ENUMERATED values in X.509 certs or OCSP this large, and we're trying to // keep the parser simple and fast. @@ -282,32 +272,32 @@ TEST_F(pkixder_universal_types_tests, En 0x12, 0x34 // value }; Input input; ASSERT_EQ(Success, input.Init(DER_ENUMERATED_INVALID_LENGTH, sizeof DER_ENUMERATED_INVALID_LENGTH)); uint8_t value = 0; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Enumerated(input, value)); + ASSERT_EQ(Result::ERROR_BAD_DER, Enumerated(input, value)); } TEST_F(pkixder_universal_types_tests, EnumeratedInvalidZeroLength) { const uint8_t DER_ENUMERATED_INVALID_ZERO_LENGTH[] = { 0x0a, // ENUMERATED 0x00 // length }; Input input; ASSERT_EQ(Success, input.Init(DER_ENUMERATED_INVALID_ZERO_LENGTH, sizeof DER_ENUMERATED_INVALID_ZERO_LENGTH)); uint8_t value = 0; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Enumerated(input, value)); + ASSERT_EQ(Result::ERROR_BAD_DER, Enumerated(input, value)); } //////////////////////////////////////// // GeneralizedTime and TimeChoice // // From RFC 5280 section 4.1.2.5.2 // // For the purposes of this profile, GeneralizedTime values MUST be @@ -391,34 +381,32 @@ template <uint16_t LENGTH> void ExpectBadTime(const uint8_t (&generalizedTimeDER)[LENGTH]) { // GeneralizedTime { Input input; ASSERT_EQ(Success, input.Init(generalizedTimeDER, LENGTH)); PRTime value; - ASSERT_RecoverableError(SEC_ERROR_INVALID_TIME, - GeneralizedTime(input, value)); + ASSERT_EQ(Result::ERROR_INVALID_TIME, GeneralizedTime(input, value)); } // TimeChoice: GeneralizedTime { Input input; ASSERT_EQ(Success, input.Init(generalizedTimeDER, LENGTH)); PRTime value; - ASSERT_RecoverableError(SEC_ERROR_INVALID_TIME, TimeChoice(input, value)); + ASSERT_EQ(Result::ERROR_INVALID_TIME, TimeChoice(input, value)); } // TimeChoice: UTCTime { PRTime value; - ASSERT_RecoverableError( - SEC_ERROR_INVALID_TIME, - TimeChoiceForEquivalentUTCTime(generalizedTimeDER, value)); + ASSERT_EQ(Result::ERROR_INVALID_TIME, + TimeChoiceForEquivalentUTCTime(generalizedTimeDER, value)); } } // Control value: a valid time TEST_F(pkixder_universal_types_tests, ValidControl) { const uint8_t GT_DER[] = { 0x18, // Generalized Time @@ -448,34 +436,34 @@ TEST_F(pkixder_universal_types_tests, Ti PRTime value; // GeneralizedTime Input gt; ASSERT_EQ(Success, gt.Init(DER_GENERALIZED_TIME_INVALID_ZERO_LENGTH, sizeof DER_GENERALIZED_TIME_INVALID_ZERO_LENGTH)); - ASSERT_RecoverableError(SEC_ERROR_INVALID_TIME, GeneralizedTime(gt, value)); + ASSERT_EQ(Result::ERROR_INVALID_TIME, GeneralizedTime(gt, value)); // TimeChoice: GeneralizedTime Input tc_gt; ASSERT_EQ(Success, tc_gt.Init(DER_GENERALIZED_TIME_INVALID_ZERO_LENGTH, sizeof DER_GENERALIZED_TIME_INVALID_ZERO_LENGTH)); - ASSERT_RecoverableError(SEC_ERROR_INVALID_TIME, TimeChoice(tc_gt, value)); + ASSERT_EQ(Result::ERROR_INVALID_TIME, TimeChoice(tc_gt, value)); // TimeChoice: UTCTime const uint8_t DER_UTCTIME_INVALID_ZERO_LENGTH[] = { 0x17, // UTCTime 0x00 // Length = 0 }; Input tc_utc; ASSERT_EQ(Success, tc_utc.Init(DER_UTCTIME_INVALID_ZERO_LENGTH, sizeof DER_UTCTIME_INVALID_ZERO_LENGTH)); - ASSERT_RecoverableError(SEC_ERROR_INVALID_TIME, TimeChoice(tc_utc, value)); + ASSERT_EQ(Result::ERROR_INVALID_TIME, TimeChoice(tc_utc, value)); } // A non zulu time should fail TEST_F(pkixder_universal_types_tests, TimeInvalidLocal) { const uint8_t DER_GENERALIZED_TIME_INVALID_LOCAL[] = { 0x18, // Generalized Time 14, // Length = 14 @@ -745,26 +733,25 @@ TEST_F(pkixder_universal_types_tests, Ti // We don't use ExpectBadTime here because UTCTime can't represent 2100. // GeneralizedTime { Input input; ASSERT_EQ(Success, input.Init(DER, sizeof(DER))); PRTime value; - ASSERT_RecoverableError(SEC_ERROR_INVALID_TIME, - GeneralizedTime(input, value)); + ASSERT_EQ(Result::ERROR_INVALID_TIME, GeneralizedTime(input, value)); } // TimeChoice: GeneralizedTime { Input input; ASSERT_EQ(Success, input.Init(DER, sizeof(DER))); PRTime value; - ASSERT_RecoverableError(SEC_ERROR_INVALID_TIME, TimeChoice(input, value)); + ASSERT_EQ(Result::ERROR_INVALID_TIME, TimeChoice(input, value)); } } TEST_F(pkixder_universal_types_tests, TimeHoursValidRange) { for (uint8_t i = 0; i <= 23; ++i) { const uint8_t DER[] = { 0x18, // Generalized Time @@ -886,28 +873,27 @@ TEST_F(pkixder_universal_types_tests, Ti // GeneralizedTime { Input input; ASSERT_EQ(Success, input.Init(DER_GENERALIZED_TIME_INVALID_CENTURY_CHAR, sizeof DER_GENERALIZED_TIME_INVALID_CENTURY_CHAR)); PRTime value = 0; - ASSERT_RecoverableError(SEC_ERROR_INVALID_TIME, - GeneralizedTime(input, value)); + ASSERT_EQ(Result::ERROR_INVALID_TIME, GeneralizedTime(input, value)); } // TimeChoice: GeneralizedTime { Input input; ASSERT_EQ(Success, input.Init(DER_GENERALIZED_TIME_INVALID_CENTURY_CHAR, sizeof DER_GENERALIZED_TIME_INVALID_CENTURY_CHAR)); PRTime value = 0; - ASSERT_RecoverableError(SEC_ERROR_INVALID_TIME, TimeChoice(input, value)); + ASSERT_EQ(Result::ERROR_INVALID_TIME, TimeChoice(input, value)); } // This test is not applicable to TimeChoice: UTCTime } TEST_F(pkixder_universal_types_tests, TimeInvalidYearChar) { const uint8_t DER_GENERALIZED_TIME_INVALID_YEAR_CHAR[] = { @@ -980,17 +966,17 @@ TEST_F(pkixder_universal_types_tests, In 0x01, // length 0xff, // -1 (two's complement) }; Input input; ASSERT_EQ(Success, input.Init(DER, sizeof DER)); uint8_t value; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Integer(input, value)); + ASSERT_EQ(Result::ERROR_BAD_DER, Integer(input, value)); } TEST_F(pkixder_universal_types_tests, Integer_Negative128) { // This is a valid integer value but our integer parser cannot parse // negative values. static const uint8_t DER[] = { @@ -998,17 +984,17 @@ TEST_F(pkixder_universal_types_tests, In 0x01, // length 0x80, // -128 (two's complement) }; Input input; ASSERT_EQ(Success, input.Init(DER, sizeof DER)); uint8_t value; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Integer(input, value)); + ASSERT_EQ(Result::ERROR_BAD_DER, Integer(input, value)); } TEST_F(pkixder_universal_types_tests, Integer_128) { // This is a valid integer value but our integer parser cannot parse // values that require more than one byte to encode. static const uint8_t DER[] = { @@ -1016,17 +1002,17 @@ TEST_F(pkixder_universal_types_tests, In 0x02, // length 0x00, 0x80 // 128 }; Input input; ASSERT_EQ(Success, input.Init(DER, sizeof DER)); uint8_t value; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Integer(input, value)); + ASSERT_EQ(Result::ERROR_BAD_DER, Integer(input, value)); } TEST_F(pkixder_universal_types_tests, Integer11223344) { // This is a valid integer value but our integer parser cannot parse // values that require more than one byte to be encoded. static const uint8_t DER[] = { @@ -1034,94 +1020,94 @@ TEST_F(pkixder_universal_types_tests, In 0x04, // length 0x11, 0x22, 0x33, 0x44 // 0x11223344 }; Input input; ASSERT_EQ(Success, input.Init(DER, sizeof DER)); uint8_t value; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Integer(input, value)); + ASSERT_EQ(Result::ERROR_BAD_DER, Integer(input, value)); } TEST_F(pkixder_universal_types_tests, IntegerTruncatedOneByte) { const uint8_t DER_INTEGER_TRUNCATED[] = { 0x02, // INTEGER 0x01, // length // MISSING DATA HERE }; Input input; ASSERT_EQ(Success, input.Init(DER_INTEGER_TRUNCATED, sizeof DER_INTEGER_TRUNCATED)); uint8_t value; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Integer(input, value)); + ASSERT_EQ(Result::ERROR_BAD_DER, Integer(input, value)); } TEST_F(pkixder_universal_types_tests, IntegerTruncatedLarge) { const uint8_t DER_INTEGER_TRUNCATED[] = { 0x02, // INTEGER 0x04, // length 0x11, 0x22 // 0x1122 // MISSING DATA HERE }; Input input; ASSERT_EQ(Success, input.Init(DER_INTEGER_TRUNCATED, sizeof DER_INTEGER_TRUNCATED)); uint8_t value; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Integer(input, value)); + ASSERT_EQ(Result::ERROR_BAD_DER, Integer(input, value)); } TEST_F(pkixder_universal_types_tests, IntegerZeroLength) { const uint8_t DER_INTEGER_ZERO_LENGTH[] = { 0x02, // INTEGER 0x00 // length }; Input input; ASSERT_EQ(Success, input.Init(DER_INTEGER_ZERO_LENGTH, sizeof DER_INTEGER_ZERO_LENGTH)); uint8_t value; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Integer(input, value)); + ASSERT_EQ(Result::ERROR_BAD_DER, Integer(input, value)); } TEST_F(pkixder_universal_types_tests, IntegerOverlyLong1) { const uint8_t DER_INTEGER_OVERLY_LONG1[] = { 0x02, // INTEGER 0x02, // length 0x00, 0x01 // }; Input input; ASSERT_EQ(Success, input.Init(DER_INTEGER_OVERLY_LONG1, sizeof DER_INTEGER_OVERLY_LONG1)); uint8_t value; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Integer(input, value)); + ASSERT_EQ(Result::ERROR_BAD_DER, Integer(input, value)); } TEST_F(pkixder_universal_types_tests, IntegerOverlyLong2) { const uint8_t DER_INTEGER_OVERLY_LONG2[] = { 0x02, // INTEGER 0x02, // length 0xff, 0x80 // }; Input input; ASSERT_EQ(Success, input.Init(DER_INTEGER_OVERLY_LONG2, sizeof DER_INTEGER_OVERLY_LONG2)); uint8_t value; - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Integer(input, value)); + ASSERT_EQ(Result::ERROR_BAD_DER, Integer(input, value)); } TEST_F(pkixder_universal_types_tests, OptionalIntegerSupportedDefault) { // The input is a BOOLEAN and not INTEGER for the input so we'll not parse // anything and instead use the default value. Input input; ASSERT_EQ(Success, input.Init(DER_BOOLEAN_TRUE, sizeof DER_BOOLEAN_TRUE)); @@ -1134,17 +1120,17 @@ TEST_F(pkixder_universal_types_tests, Op TEST_F(pkixder_universal_types_tests, OptionalIntegerUnsupportedDefault) { // The same as the previous test, except with an unsupported default value // passed in. Input input; ASSERT_EQ(Success, input.Init(DER_BOOLEAN_TRUE, sizeof DER_BOOLEAN_TRUE)); long value; - ASSERT_FatalError(SEC_ERROR_INVALID_ARGS, OptionalInteger(input, 0, value)); + ASSERT_EQ(Result::FATAL_ERROR_INVALID_ARGS, OptionalInteger(input, 0, value)); } TEST_F(pkixder_universal_types_tests, OptionalIntegerSupportedDefaultAtEnd) { static const uint8_t dummy = 1; Input input; ASSERT_EQ(Success, input.Init(&dummy, 0)); @@ -1188,17 +1174,17 @@ TEST_F(pkixder_universal_types_tests, Nu 0x01, 0x00 }; Input input; ASSERT_EQ(Success, input.Init(DER_NULL_BAD_LENGTH, sizeof DER_NULL_BAD_LENGTH)); - ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Null(input)); + ASSERT_EQ(Result::ERROR_BAD_DER, Null(input)); } TEST_F(pkixder_universal_types_tests, OID) { const uint8_t DER_VALID_OID[] = { 0x06, 0x09, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x01
deleted file mode 100644 --- a/security/pkix/test/gtest/pkixgtest.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* -*- 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: - */ -/* 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/. - */ -/* Copyright 2014 Mozilla Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "pkixgtest.h" - -namespace mozilla { namespace pkix { namespace test { - -std::ostream& -operator<<(std::ostream& os, const ResultWithPRErrorCode& value) -{ - switch (value.mRv) - { - case Success: - os << "Success"; - break; - case RecoverableError: - os << "RecoverableError"; - break; - case SECFailure: - os << "FatalError"; - break; - default: - os << "[Invalid Result: " << static_cast<int64_t>(value.mRv) << ']'; - break; - } - - if (value.mRv != Success) { - os << '('; - const char* name = PR_ErrorToName(value.mErrorCode); - if (name) { - os << name; - } else { - os << value.mErrorCode; - } - os << ')'; - } - - return os; -} - -} } } // namespace mozilla::pkix::test
--- a/security/pkix/test/gtest/pkixgtest.h +++ b/security/pkix/test/gtest/pkixgtest.h @@ -22,76 +22,27 @@ * limitations under the License. */ #ifndef mozilla_pkix__pkixgtest_h #define mozilla_pkix__pkixgtest_h #include <ostream> #include "gtest/gtest.h" -#include "pkixutil.h" -#include "prerror.h" -#include "stdint.h" - -namespace mozilla { namespace pkix { namespace test { - -class ResultWithPRErrorCode -{ -public: - ResultWithPRErrorCode(Result rv, PRErrorCode errorCode) - : mRv(rv) - , mErrorCode(errorCode) - { - } +#include "pkix/pkixnss.h" - explicit ResultWithPRErrorCode(Result rv) - : mRv(rv) - , mErrorCode(rv == Success ? 0 : PR_GetError()) - { - } - - bool operator==(const ResultWithPRErrorCode& other) const - { - return mRv == other.mRv && mErrorCode == other.mErrorCode; - } - -private: - const Result mRv; - const PRErrorCode mErrorCode; - - friend std::ostream& operator<<(std::ostream& os, - const ResultWithPRErrorCode & value); +// PrintTo must be in the same namespace as the type we're overloading it for. +namespace mozilla { namespace pkix { - void operator=(const ResultWithPRErrorCode&) /*= delete*/; -}; - -::std::ostream& operator<<(::std::ostream&, const ResultWithPRErrorCode &); - -#define ASSERT_Success(rv) \ - ASSERT_EQ(::mozilla::pkix::test::ResultWithPRErrorCode( \ - ::mozilla::pkix::Success, 0), \ - ::mozilla::pkix::test::ResultWithPRErrorCode(rv)) -#define EXPECT_Success(rv) \ - EXPECT_EQ(::mozilla::pkix::test::ResultWithPRErrorCode( \ - ::mozilla::pkix::Success, 0), \ - ::mozilla::pkix::test::ResultWithPRErrorCode(rv)) +inline void +PrintTo(const Result& result, ::std::ostream* os) +{ + const char* stringified = MapResultToName(result); + if (stringified) { + *os << stringified; + } else { + *os << "mozilla::pkix::Result(" << static_cast<unsigned int>(result) << ")"; + } +} -#define ASSERT_RecoverableError(expectedError, rv) \ - ASSERT_EQ(::mozilla::pkix::test::ResultWithPRErrorCode( \ - ::mozilla::pkix::RecoverableError, expectedError), \ - ::mozilla::pkix::test::ResultWithPRErrorCode(rv)) -#define EXPECT_RecoverableError(expectedError, rv) \ - EXPECT_EQ(::mozilla::pkix::test::ResultWithPRErrorCode( \ - ::mozilla::pkix::RecoverableError, expectedError), \ - ::mozilla::pkix::test::ResultWithPRErrorCode(rv)) - -#define ASSERT_FatalError(expectedError, rv) \ - ASSERT_EQ(::mozilla::pkix::test::ResultWithPRErrorCode( \ - ::mozilla::pkix::FatalError, expectedError), \ - ::mozilla::pkix::test::ResultWithPRErrorCode(rv)) -#define EXPECT_FatalError(expectedError, rv) \ - EXPECT_EQ(::mozilla::pkix::test::ResultWithPRErrorCode( \ - ::mozilla::pkix::FatalError, expectedError), \ - ::mozilla::pkix::test::ResultWithPRErrorCode(rv)) - -} } } // namespace mozilla::pkix::test +} } // namespace mozilla::pkix #endif // mozilla_pkix__pkixgtest_h
--- a/security/pkix/test/gtest/pkixocsp_CreateEncodedOCSPRequest_tests.cpp +++ b/security/pkix/test/gtest/pkixocsp_CreateEncodedOCSPRequest_tests.cpp @@ -19,71 +19,67 @@ * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "nssgtest.h" #include "pkix/pkix.h" +#include "pkix/pkixnss.h" #include "pkixder.h" -#include "prerror.h" +#include "pkixtestutil.h" #include "secerr.h" using namespace mozilla::pkix; using namespace mozilla::pkix::test; class CreateEncodedOCSPRequestTrustDomain : public TrustDomain { private: - virtual SECStatus GetCertTrust(EndEntityOrCA, const CertPolicyId&, - const SECItem&, /*out*/ TrustLevel*) + virtual Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, + const SECItem&, /*out*/ TrustLevel*) { ADD_FAILURE(); - PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0); - return SECFailure; + return Result::FATAL_ERROR_LIBRARY_FAILURE; } - virtual SECStatus FindIssuer(const SECItem&, IssuerChecker&, PRTime) + virtual Result FindIssuer(const SECItem&, IssuerChecker&, PRTime) { ADD_FAILURE(); - PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0); - return SECFailure; + return Result::FATAL_ERROR_LIBRARY_FAILURE; } - virtual SECStatus CheckRevocation(EndEntityOrCA, const CertID&, PRTime, - const SECItem*, const SECItem*) + virtual Result CheckRevocation(EndEntityOrCA, const CertID&, PRTime, + const SECItem*, const SECItem*) { ADD_FAILURE(); - PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0); - return SECFailure; + return Result::FATAL_ERROR_LIBRARY_FAILURE; } - virtual SECStatus IsChainValid(const DERArray&) + virtual Result IsChainValid(const DERArray&) { ADD_FAILURE(); - PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0); - return SECFailure; + return Result::FATAL_ERROR_LIBRARY_FAILURE; } - virtual SECStatus VerifySignedData(const SignedDataWithSignature&, - const SECItem&) + virtual Result VerifySignedData(const SignedDataWithSignature&, + const SECItem&) { ADD_FAILURE(); - PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0); - return SECFailure; + return Result::FATAL_ERROR_LIBRARY_FAILURE; } - virtual SECStatus DigestBuf(const SECItem& item, /*out*/ uint8_t *digestBuf, - size_t digestBufLen) + virtual Result DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf, + size_t digestBufLen) { return ::mozilla::pkix::DigestBuf(item, digestBuf, digestBufLen); } - virtual SECStatus CheckPublicKey(const SECItem& subjectPublicKeyInfo) + virtual Result CheckPublicKey(const SECItem& subjectPublicKeyInfo) { return ::mozilla::pkix::CheckPublicKey(subjectPublicKeyInfo); } }; class pkixocsp_CreateEncodedOCSPRequest : public NSSTest { protected: @@ -145,26 +141,33 @@ protected: // Test that the large length of the child serial number causes // CreateEncodedOCSPRequest to fail. TEST_F(pkixocsp_CreateEncodedOCSPRequest, ChildCertLongSerialNumberTest) { const SECItem* issuerDER; ScopedSECItem issuerSPKI; ASSERT_EQ(SECSuccess, MakeIssuerCertIDComponents("CN=CA", issuerDER, issuerSPKI)); - ASSERT_FALSE(CreateEncodedOCSPRequest(trustDomain, arena.get(), - CertID(*issuerDER, *issuerSPKI, - *unsupportedLongSerialNumber))); - ASSERT_EQ(SEC_ERROR_BAD_DATA, PR_GetError()); + uint8_t ocspRequest[OCSP_REQUEST_MAX_LENGTH]; + size_t ocspRequestLength; + ASSERT_EQ(Result::ERROR_BAD_DER, + CreateEncodedOCSPRequest(trustDomain, + CertID(*issuerDER, *issuerSPKI, + *unsupportedLongSerialNumber), + ocspRequest, ocspRequestLength)); } // Test that CreateEncodedOCSPRequest handles the longest serial number that // it's required to support (i.e. 20 octets). TEST_F(pkixocsp_CreateEncodedOCSPRequest, LongestSupportedSerialNumberTest) { const SECItem* issuerDER; ScopedSECItem issuerSPKI; ASSERT_EQ(SECSuccess, MakeIssuerCertIDComponents("CN=CA", issuerDER, issuerSPKI)); - ASSERT_TRUE(CreateEncodedOCSPRequest(trustDomain, arena.get(), - CertID(*issuerDER, *issuerSPKI, - *longestRequiredSerialNumber))); + uint8_t ocspRequest[OCSP_REQUEST_MAX_LENGTH]; + size_t ocspRequestLength; + ASSERT_EQ(Success, + CreateEncodedOCSPRequest(trustDomain, + CertID(*issuerDER, *issuerSPKI, + *longestRequiredSerialNumber), + ocspRequest, ocspRequestLength)); }
--- a/security/pkix/test/gtest/pkixocsp_VerifyEncodedOCSPResponse.cpp +++ b/security/pkix/test/gtest/pkixocsp_VerifyEncodedOCSPResponse.cpp @@ -20,84 +20,81 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "nss.h" #include "nssgtest.h" #include "pkix/pkix.h" +#include "pkix/pkixnss.h" +#include "pkixgtest.h" #include "pkixtestutil.h" #include "prinit.h" #include "secerr.h" using namespace mozilla::pkix; using namespace mozilla::pkix::test; const uint16_t END_ENTITY_MAX_LIFETIME_IN_DAYS = 10; class OCSPTestTrustDomain : public TrustDomain { public: OCSPTestTrustDomain() { } - virtual SECStatus GetCertTrust(EndEntityOrCA endEntityOrCA, - const CertPolicyId&, - const SECItem& candidateCert, - /*out*/ TrustLevel* trustLevel) + virtual Result GetCertTrust(EndEntityOrCA endEntityOrCA, const CertPolicyId&, + const SECItem& candidateCert, + /*out*/ TrustLevel* trustLevel) { EXPECT_EQ(endEntityOrCA, EndEntityOrCA::MustBeEndEntity); EXPECT_TRUE(trustLevel); *trustLevel = TrustLevel::InheritsTrust; - return SECSuccess; + return Success; } - virtual SECStatus FindIssuer(const SECItem&, IssuerChecker&, PRTime) + virtual Result FindIssuer(const SECItem&, IssuerChecker&, PRTime) { ADD_FAILURE(); - PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0); - return SECFailure; + return Result::FATAL_ERROR_LIBRARY_FAILURE; } - virtual SECStatus CheckRevocation(EndEntityOrCA endEntityOrCA, const CertID&, - PRTime time, - /*optional*/ const SECItem*, - /*optional*/ const SECItem*) + virtual Result CheckRevocation(EndEntityOrCA endEntityOrCA, const CertID&, + PRTime time, /*optional*/ const SECItem*, + /*optional*/ const SECItem*) { // TODO: I guess mozilla::pkix should support revocation of designated // OCSP responder eventually, but we don't now, so this function should // never get called. ADD_FAILURE(); - PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0); - return SECFailure; + return Result::FATAL_ERROR_LIBRARY_FAILURE; } - virtual SECStatus IsChainValid(const DERArray&) + virtual Result IsChainValid(const DERArray&) { ADD_FAILURE(); - PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0); - return SECFailure; + return Result::FATAL_ERROR_LIBRARY_FAILURE; } - virtual SECStatus VerifySignedData(const SignedDataWithSignature& signedData, - const SECItem& subjectPublicKeyInfo) + virtual Result VerifySignedData(const SignedDataWithSignature& signedData, + const SECItem& subjectPublicKeyInfo) { return ::mozilla::pkix::VerifySignedData(signedData, subjectPublicKeyInfo, nullptr); } - virtual SECStatus DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf, - size_t digestBufLen) + virtual Result DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf, + size_t digestBufLen) { return ::mozilla::pkix::DigestBuf(item, digestBuf, digestBufLen); } - virtual SECStatus CheckPublicKey(const SECItem& subjectPublicKeyInfo) + virtual Result CheckPublicKey(const SECItem& subjectPublicKeyInfo) { return ::mozilla::pkix::CheckPublicKey(subjectPublicKeyInfo); } private: OCSPTestTrustDomain(const OCSPTestTrustDomain&) /*delete*/; void operator=(const OCSPTestTrustDomain&) /*delete*/; }; @@ -168,29 +165,29 @@ public: /*static*/ long pkixocsp_VerifyEncodedResponse::rootIssuedCount = 0; /////////////////////////////////////////////////////////////////////////////// // responseStatus struct WithoutResponseBytes { uint8_t responseStatus; - PRErrorCode expectedError; + Result expectedError; }; static const WithoutResponseBytes WITHOUT_RESPONSEBYTES[] = { - { OCSPResponseContext::successful, SEC_ERROR_OCSP_MALFORMED_RESPONSE }, - { OCSPResponseContext::malformedRequest, SEC_ERROR_OCSP_MALFORMED_REQUEST }, - { OCSPResponseContext::internalError, SEC_ERROR_OCSP_SERVER_ERROR }, - { OCSPResponseContext::tryLater, SEC_ERROR_OCSP_TRY_SERVER_LATER }, - { 4/*unused*/, SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS }, - { OCSPResponseContext::sigRequired, SEC_ERROR_OCSP_REQUEST_NEEDS_SIG }, - { OCSPResponseContext::unauthorized, SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST }, + { OCSPResponseContext::successful, Result::ERROR_OCSP_MALFORMED_RESPONSE }, + { OCSPResponseContext::malformedRequest, Result::ERROR_OCSP_MALFORMED_REQUEST }, + { OCSPResponseContext::internalError, Result::ERROR_OCSP_SERVER_ERROR }, + { OCSPResponseContext::tryLater, Result::ERROR_OCSP_TRY_SERVER_LATER }, + { 4/*unused*/, Result::ERROR_OCSP_UNKNOWN_RESPONSE_STATUS }, + { OCSPResponseContext::sigRequired, Result::ERROR_OCSP_REQUEST_NEEDS_SIG }, + { OCSPResponseContext::unauthorized, Result::ERROR_OCSP_UNAUTHORIZED_REQUEST }, { OCSPResponseContext::unauthorized + 1, - SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS + Result::ERROR_OCSP_UNKNOWN_RESPONSE_STATUS }, }; class pkixocsp_VerifyEncodedResponse_WithoutResponseBytes : public pkixocsp_VerifyEncodedResponse , public ::testing::WithParamInterface<WithoutResponseBytes> { protected: @@ -207,20 +204,20 @@ protected: }; TEST_P(pkixocsp_VerifyEncodedResponse_WithoutResponseBytes, CorrectErrorCode) { SECItem* response(CreateEncodedOCSPErrorResponse( GetParam().responseStatus)); ASSERT_TRUE(response); bool expired; - ASSERT_SECFailure(GetParam().expectedError, - VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, - END_ENTITY_MAX_LIFETIME_IN_DAYS, - *response, expired)); + ASSERT_EQ(GetParam().expectedError, + VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, + END_ENTITY_MAX_LIFETIME_IN_DAYS, + *response, expired)); } INSTANTIATE_TEST_CASE_P(pkixocsp_VerifyEncodedResponse_WithoutResponseBytes, pkixocsp_VerifyEncodedResponse_WithoutResponseBytes, testing::ValuesIn(WITHOUT_RESPONSEBYTES)); /////////////////////////////////////////////////////////////////////////////// // "successful" responses @@ -283,77 +280,80 @@ public: TEST_F(pkixocsp_VerifyEncodedResponse_successful, good_byKey) { SECItem* response(CreateEncodedOCSPSuccessfulResponse( OCSPResponseContext::good, *endEntityCertID, byKey, rootPrivateKey, oneDayBeforeNow, oneDayBeforeNow, &oneDayAfterNow)); ASSERT_TRUE(response); bool expired; - ASSERT_SECSuccess(VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, - END_ENTITY_MAX_LIFETIME_IN_DAYS, - *response, expired)); + ASSERT_EQ(Success, + VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, + now, END_ENTITY_MAX_LIFETIME_IN_DAYS, + *response, expired)); ASSERT_FALSE(expired); } TEST_F(pkixocsp_VerifyEncodedResponse_successful, good_byName) { SECItem* response(CreateEncodedOCSPSuccessfulResponse( OCSPResponseContext::good, *endEntityCertID, rootName, rootPrivateKey, oneDayBeforeNow, oneDayBeforeNow, &oneDayAfterNow)); ASSERT_TRUE(response); bool expired; - ASSERT_SECSuccess(VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, - END_ENTITY_MAX_LIFETIME_IN_DAYS, - *response, expired)); + ASSERT_EQ(Success, + VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, + END_ENTITY_MAX_LIFETIME_IN_DAYS, + *response, expired)); ASSERT_FALSE(expired); } TEST_F(pkixocsp_VerifyEncodedResponse_successful, good_byKey_without_nextUpdate) { SECItem* response(CreateEncodedOCSPSuccessfulResponse( OCSPResponseContext::good, *endEntityCertID, byKey, rootPrivateKey, oneDayBeforeNow, oneDayBeforeNow, nullptr)); ASSERT_TRUE(response); bool expired; - ASSERT_SECSuccess(VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, - END_ENTITY_MAX_LIFETIME_IN_DAYS, - *response, expired)); + ASSERT_EQ(Success, + VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, + END_ENTITY_MAX_LIFETIME_IN_DAYS, + *response, expired)); ASSERT_FALSE(expired); } TEST_F(pkixocsp_VerifyEncodedResponse_successful, revoked) { SECItem* response(CreateEncodedOCSPSuccessfulResponse( OCSPResponseContext::revoked, *endEntityCertID, byKey, rootPrivateKey, oneDayBeforeNow, oneDayBeforeNow, &oneDayAfterNow)); ASSERT_TRUE(response); bool expired; - ASSERT_SECFailure(SEC_ERROR_REVOKED_CERTIFICATE, - VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, - END_ENTITY_MAX_LIFETIME_IN_DAYS, - *response, expired)); + ASSERT_EQ(Result::ERROR_REVOKED_CERTIFICATE, + VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, + END_ENTITY_MAX_LIFETIME_IN_DAYS, + *response, expired)); ASSERT_FALSE(expired); } TEST_F(pkixocsp_VerifyEncodedResponse_successful, unknown) { SECItem* response(CreateEncodedOCSPSuccessfulResponse( OCSPResponseContext::unknown, *endEntityCertID, byKey, rootPrivateKey, oneDayBeforeNow, oneDayBeforeNow, &oneDayAfterNow)); ASSERT_TRUE(response); bool expired; - ASSERT_SECFailure(SEC_ERROR_OCSP_UNKNOWN_CERT, - VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, - END_ENTITY_MAX_LIFETIME_IN_DAYS, - *response, expired)); + ASSERT_EQ(Result::ERROR_OCSP_UNKNOWN_CERT, + VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, + END_ENTITY_MAX_LIFETIME_IN_DAYS, + *response, expired)); ASSERT_FALSE(expired); } /////////////////////////////////////////////////////////////////////////////// // indirect responses (signed by a delegated OCSP responder cert) class pkixocsp_VerifyEncodedResponse_DelegatedResponder : public pkixocsp_VerifyEncodedResponse_successful @@ -453,72 +453,74 @@ protected: TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_byKey) { SECItem* response(CreateEncodedIndirectOCSPSuccessfulResponse( "CN=good_indirect_byKey", OCSPResponseContext::good, byKey)); ASSERT_TRUE(response); bool expired; - ASSERT_SECSuccess(VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, - END_ENTITY_MAX_LIFETIME_IN_DAYS, - *response, expired)); + ASSERT_EQ(Success, + VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, + END_ENTITY_MAX_LIFETIME_IN_DAYS, + *response, expired)); ASSERT_FALSE(expired); } TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_byName) { SECItem* response(CreateEncodedIndirectOCSPSuccessfulResponse( "CN=good_indirect_byName", OCSPResponseContext::good, "CN=good_indirect_byName")); ASSERT_TRUE(response); bool expired; - ASSERT_SECSuccess(VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, - END_ENTITY_MAX_LIFETIME_IN_DAYS, - *response, expired)); + ASSERT_EQ(Success, + VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, + END_ENTITY_MAX_LIFETIME_IN_DAYS, + *response, expired)); ASSERT_FALSE(expired); } TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_byKey_missing_signer) { ScopedSECKEYPublicKey missingSignerPublicKey; ScopedSECKEYPrivateKey missingSignerPrivateKey; ASSERT_SECSuccess(GenerateKeyPair(missingSignerPublicKey, missingSignerPrivateKey)); SECItem* response(CreateEncodedOCSPSuccessfulResponse( OCSPResponseContext::good, *endEntityCertID, byKey, missingSignerPrivateKey, oneDayBeforeNow, oneDayBeforeNow, nullptr)); ASSERT_TRUE(response); bool expired; - ASSERT_SECFailure(SEC_ERROR_OCSP_INVALID_SIGNING_CERT, - VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, - END_ENTITY_MAX_LIFETIME_IN_DAYS, - *response, expired)); + ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT, + VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, + END_ENTITY_MAX_LIFETIME_IN_DAYS, + *response, expired)); ASSERT_FALSE(expired); } TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_byName_missing_signer) { ScopedSECKEYPublicKey missingSignerPublicKey; ScopedSECKEYPrivateKey missingSignerPrivateKey; ASSERT_SECSuccess(GenerateKeyPair(missingSignerPublicKey, missingSignerPrivateKey)); SECItem* response(CreateEncodedOCSPSuccessfulResponse( OCSPResponseContext::good, *endEntityCertID, "CN=missing", missingSignerPrivateKey, oneDayBeforeNow, oneDayBeforeNow, nullptr)); ASSERT_TRUE(response); bool expired; - ASSERT_SECFailure(SEC_ERROR_OCSP_INVALID_SIGNING_CERT, - VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, - END_ENTITY_MAX_LIFETIME_IN_DAYS, - *response, expired)); + ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT, + VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, + END_ENTITY_MAX_LIFETIME_IN_DAYS, + *response, expired)); ASSERT_FALSE(expired); } TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_expired) { static const SECOidTag signerEKU = SEC_OID_OCSP_RESPONDER; static const char* signerName = "CN=good_indirect_expired"; @@ -541,20 +543,20 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De SECItem* response(CreateEncodedOCSPSuccessfulResponse( OCSPResponseContext::good, *endEntityCertID, signerName, signerPrivateKey, oneDayBeforeNow, oneDayBeforeNow, &oneDayAfterNow, certs)); ASSERT_TRUE(response); bool expired; - ASSERT_SECFailure(SEC_ERROR_OCSP_INVALID_SIGNING_CERT, - VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, - END_ENTITY_MAX_LIFETIME_IN_DAYS, - *response, expired)); + ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT, + VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, + END_ENTITY_MAX_LIFETIME_IN_DAYS, + *response, expired)); } TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_future) { static const SECOidTag signerEKU = SEC_OID_OCSP_RESPONDER; static const char* signerName = "CN=good_indirect_future"; const SECItem* extensions[] = { @@ -575,49 +577,49 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De SECItem const* const certs[] = { signerDER, nullptr }; SECItem* response(CreateEncodedOCSPSuccessfulResponse( OCSPResponseContext::good, *endEntityCertID, signerName, signerPrivateKey, oneDayBeforeNow, oneDayBeforeNow, &oneDayAfterNow, certs)); ASSERT_TRUE(response); bool expired; - ASSERT_SECFailure(SEC_ERROR_OCSP_INVALID_SIGNING_CERT, - VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, - END_ENTITY_MAX_LIFETIME_IN_DAYS, - *response, expired)); + ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT, + VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, + END_ENTITY_MAX_LIFETIME_IN_DAYS, + *response, expired)); ASSERT_FALSE(expired); } TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_no_eku) { SECItem* response(CreateEncodedIndirectOCSPSuccessfulResponse( "CN=good_indirect_wrong_eku", OCSPResponseContext::good, byKey, SEC_OID_UNKNOWN)); ASSERT_TRUE(response); bool expired; - ASSERT_SECFailure(SEC_ERROR_OCSP_INVALID_SIGNING_CERT, - VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, - END_ENTITY_MAX_LIFETIME_IN_DAYS, - *response, expired)); + ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT, + VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, + END_ENTITY_MAX_LIFETIME_IN_DAYS, + *response, expired)); ASSERT_FALSE(expired); } TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_indirect_wrong_eku) { SECItem* response(CreateEncodedIndirectOCSPSuccessfulResponse( "CN=good_indirect_wrong_eku", OCSPResponseContext::good, byKey, SEC_OID_EXT_KEY_USAGE_SERVER_AUTH)); ASSERT_TRUE(response); bool expired; - ASSERT_SECFailure(SEC_ERROR_OCSP_INVALID_SIGNING_CERT, - VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, - END_ENTITY_MAX_LIFETIME_IN_DAYS, - *response, expired)); + ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT, + VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, + END_ENTITY_MAX_LIFETIME_IN_DAYS, + *response, expired)); ASSERT_FALSE(expired); } // Test that signature of OCSP response signer cert is verified TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_tampered_eku) { SECItem* response(CreateEncodedIndirectOCSPSuccessfulResponse( "CN=good_indirect_tampered_eku", @@ -632,20 +634,20 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De static const uint8_t EKU_SERVER_AUTH[] = { EKU_PREFIX, 0x01 }; // serverAuth static const uint8_t EKU_OCSP_SIGNER[] = { EKU_PREFIX, 0x09 }; // OCSPSigning #undef EKU_PREFIX ASSERT_SECSuccess(TamperOnce(*response, EKU_SERVER_AUTH, PR_ARRAY_SIZE(EKU_SERVER_AUTH), EKU_OCSP_SIGNER, PR_ARRAY_SIZE(EKU_OCSP_SIGNER))); bool expired; - ASSERT_SECFailure(SEC_ERROR_OCSP_INVALID_SIGNING_CERT, - VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, - END_ENTITY_MAX_LIFETIME_IN_DAYS, - *response, expired)); + ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT, + VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, + END_ENTITY_MAX_LIFETIME_IN_DAYS, + *response, expired)); ASSERT_FALSE(expired); } TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_unknown_issuer) { static const char* subCAName = "CN=good_indirect_unknown_issuer sub-CA"; static const char* signerName = "CN=good_indirect_unknown_issuer OCSP signer"; @@ -672,20 +674,20 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De SECItem const* const certs[] = { signerDER, nullptr }; SECItem* response(CreateEncodedOCSPSuccessfulResponse( OCSPResponseContext::good, *endEntityCertID, signerName, signerPrivateKey, oneDayBeforeNow, oneDayBeforeNow, &oneDayAfterNow, certs)); ASSERT_TRUE(response); bool expired; - ASSERT_SECFailure(SEC_ERROR_OCSP_INVALID_SIGNING_CERT, - VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, - END_ENTITY_MAX_LIFETIME_IN_DAYS, - *response, expired)); + ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT, + VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, + END_ENTITY_MAX_LIFETIME_IN_DAYS, + *response, expired)); ASSERT_FALSE(expired); } // The CA that issued the OCSP responder cert is a sub-CA of the issuer of // the certificate that the OCSP response is for. That sub-CA cert is included // in the OCSP response before the OCSP responder cert. TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_indirect_subca_1_first) @@ -729,20 +731,20 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De SECItem* response(CreateEncodedOCSPSuccessfulResponse( OCSPResponseContext::good, *endEntityCertID, signerName, signerPrivateKey, oneDayBeforeNow, oneDayBeforeNow, &oneDayAfterNow, certs)); ASSERT_TRUE(response); bool expired; - ASSERT_SECFailure(SEC_ERROR_OCSP_INVALID_SIGNING_CERT, - VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, - END_ENTITY_MAX_LIFETIME_IN_DAYS, - *response, expired)); + ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT, + VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, + END_ENTITY_MAX_LIFETIME_IN_DAYS, + *response, expired)); ASSERT_FALSE(expired); } // The CA that issued the OCSP responder cert is a sub-CA of the issuer of // the certificate that the OCSP response is for. That sub-CA cert is included // in the OCSP response after the OCSP responder cert. TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_indirect_subca_1_second) @@ -785,20 +787,20 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De SECItem const* const certs[] = { signerDER, subCADER, nullptr }; SECItem* response(CreateEncodedOCSPSuccessfulResponse( OCSPResponseContext::good, *endEntityCertID, signerName, signerPrivateKey, oneDayBeforeNow, oneDayBeforeNow, &oneDayAfterNow, certs)); ASSERT_TRUE(response); bool expired; - ASSERT_SECFailure(SEC_ERROR_OCSP_INVALID_SIGNING_CERT, - VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, - END_ENTITY_MAX_LIFETIME_IN_DAYS, - *response, expired)); + ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT, + VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, + END_ENTITY_MAX_LIFETIME_IN_DAYS, + *response, expired)); ASSERT_FALSE(expired); } class pkixocsp_VerifyEncodedResponse_GetCertTrust : public pkixocsp_VerifyEncodedResponse_DelegatedResponder { public: pkixocsp_VerifyEncodedResponse_GetCertTrust() : signerCertDER(nullptr) @@ -828,63 +830,65 @@ public: bool SetCertTrust(const SECItem* certDER, TrustLevel certTrustLevel) { this->certDER = certDER; this->certTrustLevel = certTrustLevel; return true; } private: - virtual SECStatus GetCertTrust(EndEntityOrCA endEntityOrCA, - const CertPolicyId&, - const SECItem& candidateCert, - /*out*/ TrustLevel* trustLevel) + virtual Result GetCertTrust(EndEntityOrCA endEntityOrCA, + const CertPolicyId&, + const SECItem& candidateCert, + /*out*/ TrustLevel* trustLevel) { EXPECT_EQ(endEntityOrCA, EndEntityOrCA::MustBeEndEntity); EXPECT_TRUE(trustLevel); EXPECT_TRUE(certDER); EXPECT_TRUE(SECITEM_ItemsAreEqual(certDER, &candidateCert)); *trustLevel = certTrustLevel; - return SECSuccess; + return Success; } const SECItem* certDER; // weak pointer TrustLevel certTrustLevel; }; TrustDomain trustDomain; const SECItem* signerCertDER; // owned by arena SECItem* response; // owned by arena }; TEST_F(pkixocsp_VerifyEncodedResponse_GetCertTrust, InheritTrust) { ASSERT_TRUE(trustDomain.SetCertTrust(signerCertDER, TrustLevel::InheritsTrust)); bool expired; - ASSERT_SECSuccess(VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, - END_ENTITY_MAX_LIFETIME_IN_DAYS, - *response, expired)); + ASSERT_EQ(Success, + VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, + END_ENTITY_MAX_LIFETIME_IN_DAYS, + *response, expired)); ASSERT_FALSE(expired); } TEST_F(pkixocsp_VerifyEncodedResponse_GetCertTrust, TrustAnchor) { ASSERT_TRUE(trustDomain.SetCertTrust(signerCertDER, TrustLevel::TrustAnchor)); bool expired; - ASSERT_SECSuccess(VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, - END_ENTITY_MAX_LIFETIME_IN_DAYS, - *response, expired)); + ASSERT_EQ(Success, + VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, + END_ENTITY_MAX_LIFETIME_IN_DAYS, + *response, expired)); ASSERT_FALSE(expired); } TEST_F(pkixocsp_VerifyEncodedResponse_GetCertTrust, ActivelyDistrusted) { ASSERT_TRUE(trustDomain.SetCertTrust(signerCertDER, TrustLevel::ActivelyDistrusted)); bool expired; - ASSERT_SECFailure(SEC_ERROR_OCSP_INVALID_SIGNING_CERT, - VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, - END_ENTITY_MAX_LIFETIME_IN_DAYS, - *response, expired)); + ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT, + VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now, + END_ENTITY_MAX_LIFETIME_IN_DAYS, + *response, expired)); ASSERT_FALSE(expired); }
--- a/security/pkix/test/lib/pkixtestutil.cpp +++ b/security/pkix/test/lib/pkixtestutil.cpp @@ -28,19 +28,21 @@ #include <limits> #include <new> #include "cryptohi.h" #include "hasht.h" #include "pk11pub.h" #include "pkixcheck.h" #include "pkixder.h" +#include "prerror.h" #include "prinit.h" #include "prprf.h" #include "secder.h" +#include "secerr.h" using namespace std; namespace mozilla { namespace pkix { namespace test { const PRTime ONE_DAY = PRTime(24) * PRTime(60) * PRTime(60) * PR_USEC_PER_SEC; namespace { @@ -158,20 +160,20 @@ public: // Makes a shallow copy of the input item. All input items must have a // lifetime that extends at least to where Squash is called. Result Add(const SECItem* item) { PR_ASSERT(item); PR_ASSERT(item->data); if (numItems >= MaxSequenceItems) { - return Fail(SEC_ERROR_INVALID_ARGS); + return Result::FATAL_ERROR_INVALID_ARGS; } if (length + item->len > 65535) { - return Fail(SEC_ERROR_INVALID_ARGS); + return Result::FATAL_ERROR_INVALID_ARGS; } contents[numItems] = item; numItems++; length += item->len; return Success; }