author | Brian Smith <brian@briansmith.org> |
Sat, 07 Feb 2015 12:14:31 -0800 | |
changeset 228592 | 5e39cbc525ad091f8ee8cd2a9fbfcf49f3e89c36 |
parent 228591 | 8ed8507adfcf2c72c30c5715243efdaa28497cb4 |
child 228593 | 20f6c0ee944d287300eae04e2d32ab167d1a4668 |
push id | 28264 |
push user | cbook@mozilla.com |
push date | Wed, 11 Feb 2015 13:58:35 +0000 |
treeherder | mozilla-central@38058cb42a0e [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | keeler |
bugs | 1130754 |
milestone | 38.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/config/external/nss/nss.def +++ b/config/external/nss/nss.def @@ -672,12 +672,12 @@ SSL_VersionRangeSet SSL_VersionRangeSetDefault UTIL_SetForkState VFY_Begin VFY_CreateContext VFY_DestroyContext VFY_End VFY_Update VFY_VerifyData -VFY_VerifyDataDirect VFY_VerifyDataWithAlgorithmID +VFY_VerifyDigestDirect _SGN_VerifyPKCS1DigestInfo PK11_PQG_ParamGenV2
--- a/security/apps/AppTrustDomain.cpp +++ b/security/apps/AppTrustDomain.cpp @@ -209,27 +209,22 @@ AppTrustDomain::GetCertTrust(EndEntityOr return Success; } trustLevel = TrustLevel::InheritsTrust; return Success; } Result -AppTrustDomain::VerifySignedData(const SignedDataWithSignature& signedData, - Input subjectPublicKeyInfo) -{ - return VerifySignedDataNSS(signedData, subjectPublicKeyInfo, mPinArg); -} - -Result -AppTrustDomain::DigestBuf(Input item, /*out*/ uint8_t* digestBuf, +AppTrustDomain::DigestBuf(Input item, + DigestAlgorithm digestAlg, + /*out*/ uint8_t* digestBuf, size_t digestBufLen) { - return DigestBufNSS(item, digestBuf, digestBufLen); + return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen); } Result AppTrustDomain::CheckRevocation(EndEntityOrCA, const CertID&, Time, /*optional*/ const Input*, /*optional*/ const Input*) { // We don't currently do revocation checking. If we need to distrust an Apps @@ -254,22 +249,38 @@ AppTrustDomain::CheckRSAPublicKeyModulus { if (modulusSizeInBits < mMinimumNonECCBits) { return Result::ERROR_INADEQUATE_KEY_SIZE; } return Success; } Result +AppTrustDomain::VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) +{ + return VerifyRSAPKCS1SignedDigestNSS(signedDigest, subjectPublicKeyInfo, + mPinArg); +} + +Result AppTrustDomain::CheckECDSACurveIsAcceptable(EndEntityOrCA /*endEntityOrCA*/, NamedCurve curve) { switch (curve) { case NamedCurve::secp256r1: // fall through case NamedCurve::secp384r1: // fall through case NamedCurve::secp521r1: return Success; } return Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE; } +Result +AppTrustDomain::VerifyECDSASignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) +{ + return VerifyECDSASignedDigestNSS(signedDigest, subjectPublicKeyInfo, + mPinArg); +} + } } // namespace mozilla::psm
--- a/security/apps/AppTrustDomain.h +++ b/security/apps/AppTrustDomain.h @@ -36,23 +36,27 @@ public: mozilla::pkix::Time time, /*optional*/ const mozilla::pkix::Input* stapledOCSPresponse, /*optional*/ const mozilla::pkix::Input* aiaExtension) MOZ_OVERRIDE; virtual Result IsChainValid(const mozilla::pkix::DERArray& certChain, mozilla::pkix::Time time) MOZ_OVERRIDE; virtual Result CheckRSAPublicKeyModulusSizeInBits( mozilla::pkix::EndEntityOrCA endEntityOrCA, unsigned int modulusSizeInBits) MOZ_OVERRIDE; + virtual Result VerifyRSAPKCS1SignedDigest( + const mozilla::pkix::SignedDigest& signedDigest, + mozilla::pkix::Input subjectPublicKeyInfo) MOZ_OVERRIDE; virtual Result CheckECDSACurveIsAcceptable( mozilla::pkix::EndEntityOrCA endEntityOrCA, mozilla::pkix::NamedCurve curve) MOZ_OVERRIDE; - virtual Result VerifySignedData( - const mozilla::pkix::SignedDataWithSignature& signedData, - mozilla::pkix::Input subjectPublicKeyInfo) MOZ_OVERRIDE; + virtual Result VerifyECDSASignedDigest( + const mozilla::pkix::SignedDigest& signedDigest, + mozilla::pkix::Input subjectPublicKeyInfo) MOZ_OVERRIDE; virtual Result DigestBuf(mozilla::pkix::Input item, + mozilla::pkix::DigestAlgorithm digestAlg, /*out*/ uint8_t* digestBuf, size_t digestBufLen) MOZ_OVERRIDE; private: /*out*/ ScopedCERTCertList& mCertChain; void* mPinArg; // non-owning! ScopedCERTCertificate mTrustedRoot; unsigned int mMinimumNonECCBits;
--- a/security/certverifier/NSSCertDBTrustDomain.cpp +++ b/security/certverifier/NSSCertDBTrustDomain.cpp @@ -252,27 +252,20 @@ NSSCertDBTrustDomain::GetCertTrust(EndEn } } trustLevel = TrustLevel::InheritsTrust; return Success; } Result -NSSCertDBTrustDomain::VerifySignedData(const SignedDataWithSignature& signedData, - Input subjectPublicKeyInfo) -{ - return VerifySignedDataNSS(signedData, subjectPublicKeyInfo, mPinArg); -} - -Result -NSSCertDBTrustDomain::DigestBuf(Input item, +NSSCertDBTrustDomain::DigestBuf(Input item, DigestAlgorithm digestAlg, /*out*/ uint8_t* digestBuf, size_t digestBufLen) { - return DigestBufNSS(item, digestBuf, digestBufLen); + return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen); } static PRIntervalTime OCSPFetchingTypeToTimeoutTime(NSSCertDBTrustDomain::OCSPFetching ocspFetching) { switch (ocspFetching) { case NSSCertDBTrustDomain::FetchOCSPForDVSoftFail: @@ -725,29 +718,46 @@ NSSCertDBTrustDomain::CheckRSAPublicKeyM { if (modulusSizeInBits < mMinimumNonECCBits) { return Result::ERROR_INADEQUATE_KEY_SIZE; } return Success; } Result +NSSCertDBTrustDomain::VerifyRSAPKCS1SignedDigest( + const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) +{ + return VerifyRSAPKCS1SignedDigestNSS(signedDigest, subjectPublicKeyInfo, + mPinArg); +} + +Result NSSCertDBTrustDomain::CheckECDSACurveIsAcceptable( EndEntityOrCA /*endEntityOrCA*/, NamedCurve curve) { switch (curve) { case NamedCurve::secp256r1: // fall through case NamedCurve::secp384r1: // fall through case NamedCurve::secp521r1: return Success; } return Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE; } +Result +NSSCertDBTrustDomain::VerifyECDSASignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) +{ + return VerifyECDSASignedDigestNSS(signedDigest, subjectPublicKeyInfo, + mPinArg); +} + namespace { static char* nss_addEscape(const char* string, char quote) { char* newString = 0; size_t escapes = 0, size = 0; const char* src;
--- a/security/certverifier/NSSCertDBTrustDomain.h +++ b/security/certverifier/NSSCertDBTrustDomain.h @@ -69,26 +69,30 @@ public: mozilla::pkix::Input candidateCertDER, /*out*/ mozilla::pkix::TrustLevel& trustLevel) MOZ_OVERRIDE; virtual Result CheckRSAPublicKeyModulusSizeInBits( mozilla::pkix::EndEntityOrCA endEntityOrCA, unsigned int modulusSizeInBits) MOZ_OVERRIDE; + virtual Result VerifyRSAPKCS1SignedDigest( + const mozilla::pkix::SignedDigest& signedDigest, + mozilla::pkix::Input subjectPublicKeyInfo) MOZ_OVERRIDE; + virtual Result CheckECDSACurveIsAcceptable( mozilla::pkix::EndEntityOrCA endEntityOrCA, mozilla::pkix::NamedCurve curve) MOZ_OVERRIDE; - virtual Result VerifySignedData( - const mozilla::pkix::SignedDataWithSignature& signedData, - mozilla::pkix::Input subjectPublicKeyInfo) - MOZ_OVERRIDE; + virtual Result VerifyECDSASignedDigest( + const mozilla::pkix::SignedDigest& signedDigest, + mozilla::pkix::Input subjectPublicKeyInfo) MOZ_OVERRIDE; virtual Result DigestBuf(mozilla::pkix::Input item, + mozilla::pkix::DigestAlgorithm digestAlg, /*out*/ uint8_t* digestBuf, size_t digestBufLen) MOZ_OVERRIDE; virtual Result CheckRevocation( mozilla::pkix::EndEntityOrCA endEntityOrCA, const mozilla::pkix::CertID& certID, mozilla::pkix::Time time, /*optional*/ const mozilla::pkix::Input* stapledOCSPResponse,
--- a/security/pkix/include/pkix/pkixnss.h +++ b/security/pkix/include/pkix/pkixnss.h @@ -26,33 +26,42 @@ #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 VerifySignedDataNSS(const SignedDataWithSignature& sd, - Input subjectPublicKeyInfo, - void* pkcs11PinArg); +// Verifies the PKCS#1.5 signature on the given data using the given RSA public +// key. +Result VerifyRSAPKCS1SignedDigestNSS(const SignedDigest& sd, + Input subjectPublicKeyInfo, + void* pkcs11PinArg); -// Computes the SHA-1 hash of the data in the current item. +// Verifies the ECDSA signature on the given data using the given ECC public +// key. +Result VerifyECDSASignedDigestNSS(const SignedDigest& sd, + Input subjectPublicKeyInfo, + void* pkcs11PinArg); + +// Computes the digest of the given data using the given digest algorithm. // // 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). +// digestBuf must point to a buffer to where the digest will be written. +// digestBufLen must be the size of the buffer, which must be exactly equal +// to the size of the digest output (20 for SHA-1, 32 for SHA-256, +// etc.) // -// 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 DigestBufNSS(Input item, /*out*/ uint8_t* digestBuf, +Result DigestBufNSS(Input item, + DigestAlgorithm digestAlg, + /*out*/ uint8_t* digestBuf, size_t digestBufLen); Result MapPRErrorCodeToResult(PRErrorCode errorCode); PRErrorCode MapResultToPRErrorCode(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 @@ -78,14 +87,16 @@ void RegisterErrorTable(); inline SECItem UnsafeMapInputToSECItem(Input input) { SECItem result = { siBuffer, const_cast<uint8_t*>(input.UnsafeGetData()), input.GetLength() }; + static_assert(sizeof(decltype(input.GetLength())) <= sizeof(result.len), + "input.GetLength() must fit in a SECItem"); return result; } } } // namespace mozilla::pkix #endif // mozilla_pkix_pkixnss_h
--- a/security/pkix/include/pkix/pkixtypes.h +++ b/security/pkix/include/pkix/pkixtypes.h @@ -46,54 +46,23 @@ enum class NamedCurve // secp384r1 (OID 1.3.132.0.34, RFC 5480) secp384r1 = 2, // secp256r1 (OID 1.2.840.10045.3.1.7, RFC 5480) secp256r1 = 3, }; -enum class SignatureAlgorithm +struct SignedDigest final { - // ecdsa-with-SHA512 (OID 1.2.840.10045.4.3.4, RFC 5758 Section 3.2) - ecdsa_with_sha512 = 1, - - // ecdsa-with-SHA384 (OID 1.2.840.10045.4.3.3, RFC 5758 Section 3.2) - ecdsa_with_sha384 = 4, - - // ecdsa-with-SHA256 (OID 1.2.840.10045.4.3.2, RFC 5758 Section 3.2) - ecdsa_with_sha256 = 7, - - // ecdsa-with-SHA1 (OID 1.2.840.10045.4.1, RFC 3279 Section 2.2.3) - ecdsa_with_sha1 = 10, - - // sha512WithRSAEncryption (OID 1.2.840.113549.1.1.13, RFC 4055 Section 5) - rsa_pkcs1_with_sha512 = 13, - - // sha384WithRSAEncryption (OID 1.2.840.113549.1.1.12, RFC 4055 Section 5) - rsa_pkcs1_with_sha384 = 14, - - // sha256WithRSAEncryption (OID 1.2.840.113549.1.1.11, RFC 4055 Section 5) - rsa_pkcs1_with_sha256 = 15, - - // sha-1WithRSAEncryption (OID 1.2.840.113549.1.1.5, RFC 3279 Section 2.2.1) - rsa_pkcs1_with_sha1 = 16, - - // Used to indicate any unsupported algorithm. - unsupported_algorithm = 19, -}; - -struct SignedDataWithSignature final -{ -public: - Input data; - SignatureAlgorithm algorithm; + Input digest; + DigestAlgorithm digestAlgorithm; Input signature; - void operator=(const SignedDataWithSignature&) = delete; + void operator=(const SignedDigest&) = delete; }; enum class EndEntityOrCA { MustBeEndEntity = 0, MustBeCA = 1 }; enum class KeyUsage : uint8_t { digitalSignature = 0, nonRepudiation = 1, @@ -306,49 +275,58 @@ public: // // Return Success if the key size is acceptable, // Result::ERROR_INADEQUATE_KEY_SIZE if the key size is not acceptable, // or another error code if another error occurred. virtual Result CheckRSAPublicKeyModulusSizeInBits( EndEntityOrCA endEntityOrCA, unsigned int modulusSizeInBits) = 0; + // Verify the given RSA PKCS#1.5 signature on the given digest using the + // given RSA public key. + // + // CheckRSAPublicKeyModulusSizeInBits will be called before calling this + // function, so it is not necessary to repeat those checks here. However, + // VerifyRSAPKCS1SignedDigest *is* responsible for doing the mathematical + // verification of the public key validity as specified in NIST SP 800-56A. + virtual Result VerifyRSAPKCS1SignedDigest( + const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) = 0; + // Check that the given named ECC curve is acceptable for ECDSA signatures. // // Return Success if the curve is acceptable, // Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE if the curve is not acceptable, // or another error code if another error occurred. virtual Result CheckECDSACurveIsAcceptable(EndEntityOrCA endEntityOrCA, NamedCurve curve) = 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. + // Verify the given ECDSA signature on the given digest using the given ECC + // public key. // - // CheckRSAPublicKeyModulusSizeInBits or CheckECDSACurveIsAcceptable will - // be called before calling this function, so it is not necessary to repeat - // those checks in VerifySignedData. However, VerifySignedData *is* - // responsible for doing the mathematical verification of the public key - // validity as specified in NIST SP 800-56A. - virtual Result VerifySignedData(const SignedDataWithSignature& signedData, - Input subjectPublicKeyInfo) = 0; + // CheckECDSACurveIsAcceptable will be called before calling this function, + // so it is not necessary to repeat that check here. However, + // VerifyECDSASignedDigest *is* responsible for doing the mathematical + // verification of the public key validity as specified in NIST SP 800-56A. + virtual Result VerifyECDSASignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) = 0; - // Compute the SHA-1 hash of the data in the current item. + // Compute a digest of the data in item using the given digest algorithm. // // 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). + // digestBuf points to a buffer to where the digest will be written. + // digestBufLen will be the size of the digest output (20 for SHA-1, + // 32 for SHA-256, etc.). // - // 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 Result DigestBuf(Input item, /*out*/ uint8_t* digestBuf, + virtual Result DigestBuf(Input item, + DigestAlgorithm digestAlg, + /*out*/ uint8_t* digestBuf, size_t digestBufLen) = 0; protected: TrustDomain() { } TrustDomain(const TrustDomain&) = delete; void operator=(const TrustDomain&) = delete; };
--- a/security/pkix/lib/pkixbuild.cpp +++ b/security/pkix/lib/pkixbuild.cpp @@ -75,16 +75,21 @@ private: const BackCert& subject; const Time time; const KeyPurposeId requiredEKUIfPresent; const CertPolicyId& requiredPolicy; /*optional*/ Input const* const stapledOCSPResponse; const unsigned int subCACount; const Result deferredSubjectError; + // Initialized lazily. + uint8_t subjectSignatureDigestBuf[MAX_DIGEST_SIZE_IN_BYTES]; + der::PublicKeyAlgorithm subjectSignaturePublicKeyAlg; + SignedDigest subjectSignature; + Result RecordResult(Result currentResult, /*out*/ bool& keepGoing); Result result; bool resultWasSet; PathBuildingStep(const PathBuildingStep&) = delete; void operator=(const PathBuildingStep&) = delete; }; @@ -187,18 +192,32 @@ PathBuildingStep::Check(Input potentialI // 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(rv, keepGoing); } - rv = WrappedVerifySignedData(trustDomain, subject.GetSignedData(), - potentialIssuer.GetSubjectPublicKeyInfo()); + // Calculate the digest of the subject's signed data if we haven't already + // done so. We do this lazily to avoid doing it at all if we backtrack before + // getting to this point. We cache the result to avoid recalculating it if we + // backtrack after getting to this point. + if (subjectSignature.digest.GetLength() == 0) { + rv = DigestSignedData(trustDomain, subject.GetSignedData(), + subjectSignatureDigestBuf, + subjectSignaturePublicKeyAlg, subjectSignature); + if (rv != Success) { + return rv; + } + } + + rv = VerifySignedDigest(trustDomain, subjectSignaturePublicKeyAlg, + subjectSignature, + potentialIssuer.GetSubjectPublicKeyInfo()); if (rv != Success) { return RecordResult(rv, keepGoing); } // We avoid doing revocation checking for expired certificates because OCSP // responders are allowed to forget about expired certificates, and many OCSP // responders return an error when asked for the status of an expired // certificate.
--- a/security/pkix/lib/pkixcert.cpp +++ b/security/pkix/lib/pkixcert.cpp @@ -77,18 +77,17 @@ BackCert::Init() } rv = der::CertificateSerialNumber(tbsCertificate, serialNumber); if (rv != Success) { return rv; } // XXX: Ignored. What are we supposed to check? This seems totally redundant // with Certificate.signatureAlgorithm. Is it important to check that they // are consistent with each other? It doesn't seem to matter! - SignatureAlgorithm signature; - rv = der::SignatureAlgorithmIdentifier(tbsCertificate, signature); + rv = der::ExpectTagAndGetValue(tbsCertificate, der::SEQUENCE, signature); if (rv != Success) { return rv; } rv = der::ExpectTagAndGetTLV(tbsCertificate, der::SEQUENCE, issuer); if (rv != Success) { return rv; } rv = der::ExpectTagAndGetValue(tbsCertificate, der::SEQUENCE, validity);
--- a/security/pkix/lib/pkixder.cpp +++ b/security/pkix/lib/pkixder.cpp @@ -90,58 +90,38 @@ OptionalNull(Reader& input) return Null(input); } return Success; } namespace { Result -DigestAlgorithmOIDValue(Reader& algorithmID, - /*out*/ DigestAlgorithm& algorithm) +AlgorithmIdentifierValue(Reader& input, /*out*/ Reader& algorithmOIDValue) { - // RFC 4055 Section 2.1 - // python DottedOIDToCode.py id-sha1 1.3.14.3.2.26 - static const uint8_t id_sha1[] = { - 0x2b, 0x0e, 0x03, 0x02, 0x1a - }; - // python DottedOIDToCode.py id-sha256 2.16.840.1.101.3.4.2.1 - static const uint8_t id_sha256[] = { - 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 - }; - // python DottedOIDToCode.py id-sha384 2.16.840.1.101.3.4.2.2 - static const uint8_t id_sha384[] = { - 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02 - }; - // python DottedOIDToCode.py id-sha512 2.16.840.1.101.3.4.2.3 - static const uint8_t id_sha512[] = { - 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03 - }; + Result rv = ExpectTagAndGetValue(input, der::OIDTag, algorithmOIDValue); + if (rv != Success) { + return rv; + } + return OptionalNull(input); +} + +} // unnamed namespace - // Matching is attempted based on a rough estimate of the commonality of the - // algorithm, to minimize the number of MatchRest calls. - if (algorithmID.MatchRest(id_sha1)) { - 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 Result::ERROR_INVALID_ALGORITHM; +Result +SignatureAlgorithmIdentifierValue(Reader& input, + /*out*/ PublicKeyAlgorithm& publicKeyAlgorithm, + /*out*/ DigestAlgorithm& digestAlgorithm) +{ + Reader algorithmID; + Result rv = AlgorithmIdentifierValue(input, algorithmID); + if (rv != Success) { + return rv; } - return Success; -} - -Result -SignatureAlgorithmOIDValue(Reader& algorithmID, - /*out*/ SignatureAlgorithm& algorithm) -{ // RFC 5758 Section 3.2 (ecdsa-with-SHA224 is intentionally excluded) // python DottedOIDToCode.py ecdsa-with-SHA256 1.2.840.10045.4.3.2 static const uint8_t ecdsa_with_SHA256[] = { 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02 }; // python DottedOIDToCode.py ecdsa-with-SHA384 1.2.840.10045.4.3.3 static const uint8_t ecdsa_with_SHA384[] = { 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x03 @@ -193,83 +173,95 @@ SignatureAlgorithmOIDValue(Reader& algor // RFC 4055 Section 5 and RFC 3279 Section 2.2.1 both say that parameters for // RSA must be encoded as NULL; we relax that requirement by allowing the // NULL to be omitted, to match all the other signature algorithms we support // and for compatibility. // Matching is attempted based on a rough estimate of the commonality of the // algorithm, to minimize the number of MatchRest calls. if (algorithmID.MatchRest(sha256WithRSAEncryption)) { - algorithm = SignatureAlgorithm::rsa_pkcs1_with_sha256; + publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1; + digestAlgorithm = DigestAlgorithm::sha256; } else if (algorithmID.MatchRest(ecdsa_with_SHA256)) { - algorithm = SignatureAlgorithm::ecdsa_with_sha256; + publicKeyAlgorithm = PublicKeyAlgorithm::ECDSA; + digestAlgorithm = DigestAlgorithm::sha256; } else if (algorithmID.MatchRest(sha_1WithRSAEncryption)) { - algorithm = SignatureAlgorithm::rsa_pkcs1_with_sha1; + publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1; + digestAlgorithm = DigestAlgorithm::sha1; } else if (algorithmID.MatchRest(ecdsa_with_SHA1)) { - algorithm = SignatureAlgorithm::ecdsa_with_sha1; + publicKeyAlgorithm = PublicKeyAlgorithm::ECDSA; + digestAlgorithm = DigestAlgorithm::sha1; } else if (algorithmID.MatchRest(ecdsa_with_SHA384)) { - algorithm = SignatureAlgorithm::ecdsa_with_sha384; + publicKeyAlgorithm = PublicKeyAlgorithm::ECDSA; + digestAlgorithm = DigestAlgorithm::sha384; } else if (algorithmID.MatchRest(ecdsa_with_SHA512)) { - algorithm = SignatureAlgorithm::ecdsa_with_sha512; + publicKeyAlgorithm = PublicKeyAlgorithm::ECDSA; + digestAlgorithm = DigestAlgorithm::sha512; } else if (algorithmID.MatchRest(sha384WithRSAEncryption)) { - algorithm = SignatureAlgorithm::rsa_pkcs1_with_sha384; + publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1; + digestAlgorithm = DigestAlgorithm::sha384; } else if (algorithmID.MatchRest(sha512WithRSAEncryption)) { - algorithm = SignatureAlgorithm::rsa_pkcs1_with_sha512; + publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1; + digestAlgorithm = DigestAlgorithm::sha512; } else if (algorithmID.MatchRest(sha1WithRSASignature)) { // XXX(bug 1042479): recognize this old OID for compatibility. - algorithm = SignatureAlgorithm::rsa_pkcs1_with_sha1; + publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1; + digestAlgorithm = DigestAlgorithm::sha1; } else { - algorithm = SignatureAlgorithm::unsupported_algorithm; + return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED; } return Success; } -template <typename OidValueParser, typename Algorithm> -Result -AlgorithmIdentifier(OidValueParser oidValueParser, Reader& input, - /*out*/ Algorithm& algorithm) -{ - Reader value; - Result rv = ExpectTagAndGetValue(input, SEQUENCE, value); - if (rv != Success) { - return rv; - } - - Reader algorithmID; - rv = ExpectTagAndGetValue(value, der::OIDTag, algorithmID); - if (rv != Success) { - return rv; - } - rv = oidValueParser(algorithmID, algorithm); - if (rv != Success) { - return rv; - } - - rv = OptionalNull(value); - if (rv != Success) { - return rv; - } - - return End(value); -} - -} // unnamed namespace - -Result -SignatureAlgorithmIdentifier(Reader& input, - /*out*/ SignatureAlgorithm& algorithm) -{ - return AlgorithmIdentifier(SignatureAlgorithmOIDValue, input, algorithm); -} - Result DigestAlgorithmIdentifier(Reader& input, /*out*/ DigestAlgorithm& algorithm) { - return AlgorithmIdentifier(DigestAlgorithmOIDValue, input, algorithm); + Reader r; + return der::Nested(input, SEQUENCE, [&algorithm](Reader& r) -> Result { + Reader algorithmID; + Result rv = AlgorithmIdentifierValue(r, algorithmID); + if (rv != Success) { + return rv; + } + + // RFC 4055 Section 2.1 + // python DottedOIDToCode.py id-sha1 1.3.14.3.2.26 + static const uint8_t id_sha1[] = { + 0x2b, 0x0e, 0x03, 0x02, 0x1a + }; + // python DottedOIDToCode.py id-sha256 2.16.840.1.101.3.4.2.1 + static const uint8_t id_sha256[] = { + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 + }; + // python DottedOIDToCode.py id-sha384 2.16.840.1.101.3.4.2.2 + static const uint8_t id_sha384[] = { + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02 + }; + // python DottedOIDToCode.py id-sha512 2.16.840.1.101.3.4.2.3 + static const uint8_t id_sha512[] = { + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03 + }; + + // Matching is attempted based on a rough estimate of the commonality of the + // algorithm, to minimize the number of MatchRest calls. + if (algorithmID.MatchRest(id_sha1)) { + 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 Result::ERROR_INVALID_ALGORITHM; + } + + return Success; + }); } Result SignedData(Reader& input, /*out*/ Reader& tbs, /*out*/ SignedDataWithSignature& signedData) { Reader::Mark mark(input.GetMark()); @@ -279,17 +271,17 @@ SignedData(Reader& input, /*out*/ Reader return rv; } rv = input.GetInput(mark, signedData.data); if (rv != Success) { return rv; } - rv = SignatureAlgorithmIdentifier(input, signedData.algorithm); + rv = ExpectTagAndGetValue(input, der::SEQUENCE, signedData.algorithm); if (rv != Success) { return rv; } rv = BitStringWithNoUnusedBits(input, signedData.signature); if (rv == Result::ERROR_BAD_DER) { rv = Result::ERROR_BAD_SIGNATURE; }
--- a/security/pkix/lib/pkixder.h +++ b/security/pkix/lib/pkixder.h @@ -595,25 +595,45 @@ OptionalExtensions(Reader& input, uint8_ } return Success; } Result DigestAlgorithmIdentifier(Reader& input, /*out*/ DigestAlgorithm& algorithm); -Result SignatureAlgorithmIdentifier(Reader& input, - /*out*/ SignatureAlgorithm& algorithm); +enum PublicKeyAlgorithm +{ + RSA_PKCS1, + ECDSA, +}; + +Result SignatureAlgorithmIdentifierValue( + Reader& input, + /*out*/ PublicKeyAlgorithm& publicKeyAlgorithm, + /*out*/ DigestAlgorithm& digestAlgorithm); + +struct SignedDataWithSignature final +{ +public: + Input data; + Input algorithm; + Input signature; + + void operator=(const SignedDataWithSignature&) = delete; +}; // Parses a SEQUENCE into tbs and then parses an AlgorithmIdentifier followed // by a BIT STRING into signedData. This handles the commonality between // parsing the signed/signature fields of certificates and OCSP responses. In // the case of an OCSP response, the caller needs to parse the certs // separately. // +// Note that signatureAlgorithm is NOT parsed or validated. +// // Certificate ::= SEQUENCE { // tbsCertificate TBSCertificate, // signatureAlgorithm AlgorithmIdentifier, // signatureValue BIT STRING } // // BasicOCSPResponse ::= SEQUENCE { // tbsResponseData ResponseData, // signatureAlgorithm AlgorithmIdentifier,
--- a/security/pkix/lib/pkixnss.cpp +++ b/security/pkix/lib/pkixnss.cpp @@ -32,110 +32,105 @@ #include "pkix/pkix.h" #include "pkix/ScopedPtr.h" #include "pkixutil.h" #include "secerr.h" #include "sslerr.h" namespace mozilla { namespace pkix { +namespace { + Result -VerifySignedDataNSS(const SignedDataWithSignature& sd, - Input subjectPublicKeyInfo, void* pkcs11PinArg) +VerifySignedDigest(const SignedDigest& sd, + Input subjectPublicKeyInfo, + SECOidTag pubKeyAlg, + void* pkcs11PinArg) { - 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::unsupported_algorithm: // fall through - return NotReached("unknown signature algorithm", - Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED); + switch (sd.digestAlgorithm) { + case DigestAlgorithm::sha512: digestAlg = SEC_OID_SHA512; break; + case DigestAlgorithm::sha384: digestAlg = SEC_OID_SHA384; break; + case DigestAlgorithm::sha256: digestAlg = SEC_OID_SHA256; break; + case DigestAlgorithm::sha1: digestAlg = SEC_OID_SHA1; break; MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM } SECItem subjectPublicKeyInfoSECItem = UnsafeMapInputToSECItem(subjectPublicKeyInfo); ScopedPtr<CERTSubjectPublicKeyInfo, SECKEY_DestroySubjectPublicKeyInfo> spki(SECKEY_DecodeDERSubjectPublicKeyInfo(&subjectPublicKeyInfoSECItem)); if (!spki) { return MapPRErrorCodeToResult(PR_GetError()); } ScopedPtr<SECKEYPublicKey, SECKEY_DestroyPublicKey> pubKey(SECKEY_ExtractPublicKey(spki.get())); if (!pubKey) { return MapPRErrorCodeToResult(PR_GetError()); } - // The static_cast is safe as long as the length of the data in sd.data can - // fit in an int. Right now that length is stored as a uint16_t, so this - // works. In the future this may change, hence the assertion. - // See also bug 921585. - static_assert(sizeof(decltype(sd.data.GetLength())) < sizeof(int), - "sd.data.GetLength() must fit in an int"); - SECItem dataSECItem(UnsafeMapInputToSECItem(sd.data)); + SECItem digestSECItem(UnsafeMapInputToSECItem(sd.digest)); SECItem signatureSECItem(UnsafeMapInputToSECItem(sd.signature)); - SECStatus srv = VFY_VerifyDataDirect(dataSECItem.data, - static_cast<int>(dataSECItem.len), - pubKey.get(), &signatureSECItem, - pubKeyAlg, digestAlg, nullptr, - pkcs11PinArg); + SECStatus srv = VFY_VerifyDigestDirect(&digestSECItem, pubKey.get(), + &signatureSECItem, pubKeyAlg, + digestAlg, pkcs11PinArg); if (srv != SECSuccess) { return MapPRErrorCodeToResult(PR_GetError()); } return Success; } +} // unnamed namespace + Result -DigestBufNSS(Input item, /*out*/ uint8_t* digestBuf, size_t digestBufLen) +VerifyRSAPKCS1SignedDigestNSS(const SignedDigest& sd, + Input subjectPublicKeyInfo, + void* pkcs11PinArg) +{ + return VerifySignedDigest(sd, subjectPublicKeyInfo, + SEC_OID_PKCS1_RSA_ENCRYPTION, pkcs11PinArg); +} + +Result +VerifyECDSASignedDigestNSS(const SignedDigest& sd, + Input subjectPublicKeyInfo, + void* pkcs11PinArg) { - static_assert(TrustDomain::DIGEST_LENGTH == SHA1_LENGTH, - "TrustDomain::DIGEST_LENGTH must be 20 (SHA-1 digest length)"); - if (digestBufLen != TrustDomain::DIGEST_LENGTH) { - return NotReached("invalid hash length", Result::FATAL_ERROR_INVALID_ARGS); + return VerifySignedDigest(sd, subjectPublicKeyInfo, + SEC_OID_ANSIX962_EC_PUBLIC_KEY, pkcs11PinArg); +} + +Result +DigestBufNSS(Input item, + DigestAlgorithm digestAlg, + /*out*/ uint8_t* digestBuf, + size_t digestBufLen) +{ + SECOidTag oid; + size_t bits; + switch (digestAlg) { + case DigestAlgorithm::sha512: oid = SEC_OID_SHA512; bits = 512; break; + case DigestAlgorithm::sha384: oid = SEC_OID_SHA384; bits = 384; break; + case DigestAlgorithm::sha256: oid = SEC_OID_SHA256; bits = 256; break; + case DigestAlgorithm::sha1: oid = SEC_OID_SHA1; bits = 160; break; + MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM } + if (digestBufLen != bits / 8) { + return Result::FATAL_ERROR_INVALID_ARGS; + } + SECItem itemSECItem = UnsafeMapInputToSECItem(item); if (itemSECItem.len > static_cast<decltype(itemSECItem.len)>( std::numeric_limits<int32_t>::max())) { PR_NOT_REACHED("large items should not be possible here"); return Result::FATAL_ERROR_INVALID_ARGS; } - SECStatus srv = PK11_HashBuf(SEC_OID_SHA1, digestBuf, itemSECItem.data, + SECStatus srv = PK11_HashBuf(oid, digestBuf, itemSECItem.data, static_cast<int32_t>(itemSECItem.len)); if (srv != SECSuccess) { return MapPRErrorCodeToResult(PR_GetError()); } return Success; } Result
--- a/security/pkix/lib/pkixocsp.cpp +++ b/security/pkix/lib/pkixocsp.cpp @@ -22,17 +22,22 @@ * limitations under the License. */ #include <limits> #include "pkix/pkix.h" #include "pkixcheck.h" #include "pkixutil.h" -#include "pkixder.h" + +namespace { + +const size_t SHA1_DIGEST_LENGTH = 160 / 8; + +} // unnamed namespace namespace mozilla { namespace pkix { // These values correspond to the tag values in the ASN.1 CertStatus enum class CertStatus : uint8_t { Good = der::CONTEXT_SPECIFIC | 0, Revoked = der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1, Unknown = der::CONTEXT_SPECIFIC | 2 @@ -123,18 +128,19 @@ CheckOCSPResponseSignerCert(TrustDomain& // XXX(bug 926270) XXX(bug 1008133) XXX(bug 980163): Improve name // comparison. // TODO: needs test if (!InputsAreEqual(potentialSigner.GetIssuer(), issuerSubject)) { return Result::ERROR_OCSP_RESPONDER_CERT_INVALID; } // TODO(bug 926260): check name constraints - rv = WrappedVerifySignedData(trustDomain, potentialSigner.GetSignedData(), - issuerSubjectPublicKeyInfo); + + rv = VerifySignedData(trustDomain, 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 rv; } @@ -145,17 +151,17 @@ enum class ResponderIDType : uint8_t }; static inline Result OCSPResponse(Reader&, Context&); static inline Result ResponseBytes(Reader&, Context&); static inline Result BasicResponse(Reader&, Context&); static inline Result ResponseData( Reader& tbsResponseData, Context& context, - const SignedDataWithSignature& signedResponseData, + const der::SignedDataWithSignature& signedResponseData, const DERArray& certs); static inline Result SingleResponse(Reader& input, Context& context); static Result ExtensionNotUnderstood(Reader& extnID, Input extnValue, bool critical, /*out*/ bool& understood); static inline Result CertID(Reader& input, const Context& context, /*out*/ bool& match); static Result MatchKeyHash(TrustDomain& trustDomain, @@ -196,36 +202,36 @@ MatchResponderID(TrustDomain& trustDomai } MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM } } static Result VerifyOCSPSignedData(TrustDomain& trustDomain, - const SignedDataWithSignature& signedResponseData, + const der::SignedDataWithSignature& signedResponseData, Input spki) { - Result rv = WrappedVerifySignedData(trustDomain, signedResponseData, spki); + Result rv = VerifySignedData(trustDomain, signedResponseData, spki); if (rv == Result::ERROR_BAD_SIGNATURE) { rv = Result::ERROR_OCSP_BAD_SIGNATURE; } 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. static Result VerifySignature(Context& context, ResponderIDType responderIDType, Input responderID, const DERArray& certs, - const SignedDataWithSignature& signedResponseData) + const der::SignedDataWithSignature& signedResponseData) { bool match; Result rv = MatchResponderID(context.trustDomain, responderIDType, responderID, context.certID.issuer, context.certID.issuerSubjectPublicKeyInfo, match); if (rv != Success) { return rv; @@ -386,17 +392,17 @@ ResponseBytes(Reader& input, Context& co // tbsResponseData ResponseData, // signatureAlgorithm AlgorithmIdentifier, // signature BIT STRING, // certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } Result BasicResponse(Reader& input, Context& context) { Reader tbsResponseData; - SignedDataWithSignature signedData; + der::SignedDataWithSignature signedData; Result rv = der::SignedData(input, tbsResponseData, signedData); if (rv != Success) { if (rv == Result::ERROR_BAD_SIGNATURE) { return Result::ERROR_OCSP_BAD_SIGNATURE; } return rv; } @@ -443,17 +449,17 @@ BasicResponse(Reader& input, Context& co // ResponseData ::= SEQUENCE { // version [0] EXPLICIT Version DEFAULT v1, // responderID ResponderID, // producedAt GeneralizedTime, // responses SEQUENCE OF SingleResponse, // responseExtensions [1] EXPLICIT Extensions OPTIONAL } static inline Result ResponseData(Reader& input, Context& context, - const SignedDataWithSignature& signedResponseData, + const der::SignedDataWithSignature& signedResponseData, const DERArray& certs) { der::Version version; Result rv = der::OptionalVersion(input, version); if (rv != Success) { return rv; } if (version != der::Version::v1) { @@ -713,25 +719,26 @@ CertID(Reader& input, const Context& con // TODO: support SHA-2 hashes. if (hashAlgorithm != DigestAlgorithm::sha1) { // Again, not interested in this response. Consume input, return success. input.SkipToEnd(); return Success; } - if (issuerNameHash.GetLength() != TrustDomain::DIGEST_LENGTH) { + if (issuerNameHash.GetLength() != SHA1_DIGEST_LENGTH) { 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]; - rv = context.trustDomain.DigestBuf(context.certID.issuer, hashBuf, + uint8_t hashBuf[SHA1_DIGEST_LENGTH]; + rv = context.trustDomain.DigestBuf(context.certID.issuer, + DigestAlgorithm::sha1, hashBuf, sizeof(hashBuf)); if (rv != Success) { return rv; } Input computed(hashBuf); if (!InputsAreEqual(computed, issuerNameHash)) { // Again, not interested in this response. Consume input, return success. input.SkipToEnd(); @@ -751,36 +758,36 @@ CertID(Reader& input, const Context& con // -- (i.e., the SHA-1 hash of the value of the // -- BIT STRING subjectPublicKey [excluding // -- the tag, length, and number of unused // -- bits] in the responder's certificate) static Result MatchKeyHash(TrustDomain& trustDomain, Input keyHash, const Input subjectPublicKeyInfo, /*out*/ bool& match) { - if (keyHash.GetLength() != TrustDomain::DIGEST_LENGTH) { + if (keyHash.GetLength() != SHA1_DIGEST_LENGTH) { return Result::ERROR_OCSP_MALFORMED_RESPONSE; } - static uint8_t hashBuf[TrustDomain::DIGEST_LENGTH]; + static uint8_t hashBuf[SHA1_DIGEST_LENGTH]; Result rv = KeyHash(trustDomain, subjectPublicKeyInfo, hashBuf, sizeof hashBuf); if (rv != Success) { return rv; } Input computed(hashBuf); match = InputsAreEqual(computed, keyHash); return Success; } // TODO(bug 966856): support SHA-2 hashes Result KeyHash(TrustDomain& trustDomain, const Input subjectPublicKeyInfo, /*out*/ uint8_t* hashBuf, size_t hashBufSize) { - if (!hashBuf || hashBufSize != TrustDomain::DIGEST_LENGTH) { + if (!hashBuf || hashBufSize != SHA1_DIGEST_LENGTH) { return Result::FATAL_ERROR_LIBRARY_FAILURE; } // RFC 5280 Section 4.1 // // SubjectPublicKeyInfo ::= SEQUENCE { // algorithm AlgorithmIdentifier, // subjectPublicKey BIT STRING } @@ -803,17 +810,18 @@ KeyHash(TrustDomain& trustDomain, const if (rv != Success) { return rv; } rv = der::End(spki); if (rv != Success) { return rv; } - return trustDomain.DigestBuf(subjectPublicKey, hashBuf, hashBufSize); + return trustDomain.DigestBuf(subjectPublicKey, DigestAlgorithm::sha1, + hashBuf, hashBufSize); } Result ExtensionNotUnderstood(Reader& /*extnID*/, Input /*extnValue*/, bool /*critical*/, /*out*/ bool& understood) { understood = false; return Success; @@ -863,17 +871,17 @@ CreateEncodedOCSPRequest(TrustDomain& tr // Since we don't know whether the OCSP responder supports anything other // than SHA-1, we have no choice but to use SHA-1 for issuerNameHash and // issuerKeyHash. static const uint8_t hashAlgorithm[11] = { 0x30, 0x09, // SEQUENCE 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, // OBJECT IDENTIFIER id-sha1 0x05, 0x00, // NULL }; - static const uint8_t hashLen = TrustDomain::DIGEST_LENGTH; + static const uint8_t hashLen = 160 / 8; static const unsigned int totalLenWithoutSerialNumberData = 2 // OCSPRequest + 2 // tbsRequest + 2 // requestList + 2 // Request + 2 // reqCert (CertID) + sizeof(hashAlgorithm) // hashAlgorithm @@ -908,17 +916,18 @@ CreateEncodedOCSPRequest(TrustDomain& tr // reqCert.hashAlgorithm for (size_t i = 0; i < sizeof(hashAlgorithm); ++i) { *d++ = hashAlgorithm[i]; } // reqCert.issuerNameHash (OCTET STRING) *d++ = 0x04; *d++ = hashLen; - Result rv = trustDomain.DigestBuf(certID.issuer, d, hashLen); + Result rv = trustDomain.DigestBuf(certID.issuer, DigestAlgorithm::sha1, d, + hashLen); if (rv != Success) { return rv; } d += hashLen; // reqCert.issuerKeyHash (OCTET STRING) *d++ = 0x04; *d++ = hashLen;
--- a/security/pkix/lib/pkixutil.h +++ b/security/pkix/lib/pkixutil.h @@ -49,17 +49,19 @@ public: , childCert(childCert) { } Result Init(); const Input GetDER() const { return der; } der::Version GetVersion() const { return version; } - const SignedDataWithSignature& GetSignedData() const { return signedData; } + const der::SignedDataWithSignature& GetSignedData() const { + return signedData; + } const Input GetIssuer() const { return issuer; } // XXX: "validity" is a horrible name for the structure that holds // notBefore & notAfter, but that is the name used in RFC 5280 and we use the // RFC 5280 names for everything. const Input GetValidity() const { return validity; } const Input GetSerialNumber() const { return serialNumber; } const Input GetSubject() const { return subject; } const Input GetSubjectPublicKeyInfo() const @@ -102,36 +104,37 @@ public: private: const Input der; public: const EndEntityOrCA endEntityOrCA; BackCert const* const childCert; private: - der::Version version; - // When parsing certificates in BackCert::Init, we don't accept empty // extensions. Consequently, we don't have to store a distinction between // empty extensions and extensions that weren't included. However, when // *processing* extensions, we distinguish between whether an extension was // included or not based on whetehr the GetXXX function for the extension // returns nullptr. static inline const Input* MaybeInput(const Input& item) { return item.GetLength() > 0 ? &item : nullptr; } - SignedDataWithSignature signedData; + der::SignedDataWithSignature signedData; + + der::Version version; + Input serialNumber; + Input signature; Input issuer; // XXX: "validity" is a horrible name for the structure that holds // notBefore & notAfter, but that is the name used in RFC 5280 and we use the // RFC 5280 names for everything. Input validity; - Input serialNumber; Input subject; Input subjectPublicKeyInfo; Input authorityInfoAccess; Input basicConstraints; Input certificatePolicies; Input extKeyUsage; Input inhibitAnyPolicy; @@ -192,29 +195,33 @@ DaysBeforeYear(unsigned int year) { assert(year <= 9999); return ((year - 1u) * 365u) + ((year - 1u) / 4u) // leap years are every 4 years, - ((year - 1u) / 100u) // except years divisible by 100, + ((year - 1u) / 400u); // except years divisible by 400. } -// Ensures that we do not call the TrustDomain's VerifySignedData function if -// the algorithm is unsupported. -inline Result -WrappedVerifySignedData(TrustDomain& trustDomain, - const SignedDataWithSignature& signedData, - Input subjectPublicKeyInfo) -{ - if (signedData.algorithm == SignatureAlgorithm::unsupported_algorithm) { - return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED; - } +static const size_t MAX_DIGEST_SIZE_IN_BYTES = 512 / 8; // sha-512 + +Result DigestSignedData(TrustDomain& trustDomain, + const der::SignedDataWithSignature& signedData, + /*out*/ uint8_t(&digestBuf)[MAX_DIGEST_SIZE_IN_BYTES], + /*out*/ der::PublicKeyAlgorithm& publicKeyAlg, + /*out*/ SignedDigest& signedDigest); - return trustDomain.VerifySignedData(signedData, subjectPublicKeyInfo); -} +Result VerifySignedDigest(TrustDomain& trustDomain, + der::PublicKeyAlgorithm publicKeyAlg, + const SignedDigest& signedDigest, + Input signerSubjectPublicKeyInfo); + +// Combines DigestSignedData and VerifySignedDigest +Result VerifySignedData(TrustDomain& trustDomain, + const der::SignedDataWithSignature& signedData, + Input signerSubjectPublicKeyInfo); // In a switch over an enum, sometimes some compilers are not satisfied that // all control flow paths have been considered unless there is a default case. // However, in our code, such a default case is almost always unreachable dead // code. That can be particularly problematic when the compiler wants the code // to choose a value, such as a return value, for the default case, but there's // no appropriate "impossible case" value to choose. //
new file mode 100644 --- /dev/null +++ b/security/pkix/lib/pkixverify.cpp @@ -0,0 +1,103 @@ +/* -*- 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 2015 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 "pkixutil.h" + +namespace mozilla { namespace pkix { + +Result +DigestSignedData(TrustDomain& trustDomain, + const der::SignedDataWithSignature& signedData, + /*out*/ uint8_t(&digestBuf)[MAX_DIGEST_SIZE_IN_BYTES], + /*out*/ der::PublicKeyAlgorithm& publicKeyAlg, + /*out*/ SignedDigest& signedDigest) +{ + Reader signatureAlg(signedData.algorithm); + Result rv = der::SignatureAlgorithmIdentifierValue( + signatureAlg, publicKeyAlg, signedDigest.digestAlgorithm); + if (rv != Success) { + return rv; + } + if (!signatureAlg.AtEnd()) { + return Result::ERROR_BAD_DER; + } + + size_t digestLen; + switch (signedDigest.digestAlgorithm) { + case DigestAlgorithm::sha512: digestLen = 512 / 8; break; + case DigestAlgorithm::sha384: digestLen = 384 / 8; break; + case DigestAlgorithm::sha256: digestLen = 256 / 8; break; + case DigestAlgorithm::sha1: digestLen = 160 / 8; break; + MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM + } + assert(digestLen <= sizeof(digestBuf)); + + rv = trustDomain.DigestBuf(signedData.data, signedDigest.digestAlgorithm, + digestBuf, digestLen); + if (rv != Success) { + return rv; + } + rv = signedDigest.digest.Init(digestBuf, digestLen); + if (rv != Success) { + return rv; + } + + return signedDigest.signature.Init(signedData.signature); +} + +Result +VerifySignedDigest(TrustDomain& trustDomain, + der::PublicKeyAlgorithm publicKeyAlg, + const SignedDigest& signedDigest, + Input signerSubjectPublicKeyInfo) +{ + switch (publicKeyAlg) { + case der::PublicKeyAlgorithm::ECDSA: + return trustDomain.VerifyECDSASignedDigest(signedDigest, + signerSubjectPublicKeyInfo); + case der::PublicKeyAlgorithm::RSA_PKCS1: + return trustDomain.VerifyRSAPKCS1SignedDigest(signedDigest, + signerSubjectPublicKeyInfo); + MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM + } +} + +Result +VerifySignedData(TrustDomain& trustDomain, + const der::SignedDataWithSignature& signedData, + Input signerSubjectPublicKeyInfo) +{ + uint8_t digestBuf[MAX_DIGEST_SIZE_IN_BYTES]; + der::PublicKeyAlgorithm publicKeyAlg; + SignedDigest signedDigest; + Result rv = DigestSignedData(trustDomain, signedData, digestBuf, + publicKeyAlg, signedDigest); + if (rv != Success) { + return rv; + } + return VerifySignedDigest(trustDomain, publicKeyAlg, signedDigest, + signerSubjectPublicKeyInfo); +} + +} } // namespace mozilla::pkix
--- a/security/pkix/moz.build +++ b/security/pkix/moz.build @@ -9,16 +9,17 @@ SOURCES += [ 'lib/pkixcert.cpp', 'lib/pkixcheck.cpp', 'lib/pkixder.cpp', 'lib/pkixnames.cpp', 'lib/pkixnss.cpp', 'lib/pkixocsp.cpp', 'lib/pkixresult.cpp', 'lib/pkixtime.cpp', + 'lib/pkixverify.cpp', ] LOCAL_INCLUDES += [ 'include', ] TEST_DIRS += [ 'test/gtest',
--- a/security/pkix/test/gtest/pkixbuild_tests.cpp +++ b/security/pkix/test/gtest/pkixbuild_tests.cpp @@ -148,39 +148,45 @@ private: return Success; } Result IsChainValid(const DERArray&, Time) override { return Success; } - Result VerifySignedData(const SignedDataWithSignature& signedData, - Input subjectPublicKeyInfo) override + Result DigestBuf(Input input, DigestAlgorithm digestAlg, + /*out*/ uint8_t* digestBuf, size_t digestLen) override { - return TestVerifySignedData(signedData, subjectPublicKeyInfo); - } - - Result DigestBuf(Input, /*out*/ uint8_t*, size_t) override - { - ADD_FAILURE(); - return Result::FATAL_ERROR_LIBRARY_FAILURE; + return TestDigestBuf(input, digestAlg, digestBuf, digestLen); } Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int) override { return Success; } + Result VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) override + { + return TestVerifyRSAPKCS1SignedDigest(signedDigest, subjectPublicKeyInfo); + } + Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override { return Success; } + Result VerifyECDSASignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) override + { + return TestVerifyECDSASignedDigest(signedDigest, subjectPublicKeyInfo); + } + std::map<ByteString, ByteString> subjectDERToCertDER; ByteString leafCACertDER; ByteString rootCACertDER; }; class pkixbuild : public ::testing::Test { @@ -317,39 +323,45 @@ public: return Result::FATAL_ERROR_LIBRARY_FAILURE; } Result IsChainValid(const DERArray&, Time) override { return Success; } - Result VerifySignedData(const SignedDataWithSignature& signedData, - Input subjectPublicKeyInfo) override + Result DigestBuf(Input input, DigestAlgorithm digestAlg, + /*out*/ uint8_t* digestBuf, size_t digestLen) override { - return TestVerifySignedData(signedData, subjectPublicKeyInfo); - } - - Result DigestBuf(Input, /*out*/uint8_t*, size_t) override - { - ADD_FAILURE(); - return Result::FATAL_ERROR_LIBRARY_FAILURE; + return TestDigestBuf(input, digestAlg, digestBuf, digestLen); } Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int) override { return Success; } + Result VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) override + { + return TestVerifyRSAPKCS1SignedDigest(signedDigest, subjectPublicKeyInfo); + } + Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override { return Success; } + Result VerifyECDSASignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) override + { + return TestVerifyECDSASignedDigest(signedDigest, subjectPublicKeyInfo); + } + private: ByteString rootDER; }; TEST_F(pkixbuild, NoRevocationCheckingForExpiredCert) { const char* rootCN = "Root CA"; ByteString rootDER(CreateCert(rootCN, rootCN, EndEntityOrCA::MustBeCA, @@ -406,37 +418,45 @@ public: return Result::FATAL_ERROR_LIBRARY_FAILURE; } Result IsChainValid(const DERArray&, Time) override { return Success; } - Result VerifySignedData(const SignedDataWithSignature&, Input) override - { - ADD_FAILURE(); - return Result::FATAL_ERROR_LIBRARY_FAILURE; - } - - Result DigestBuf(Input, /*out*/uint8_t*, size_t) override + Result DigestBuf(Input, DigestAlgorithm, /*out*/uint8_t*, size_t) override { ADD_FAILURE(); return Result::FATAL_ERROR_LIBRARY_FAILURE; } Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int) override { - return Success; + ADD_FAILURE(); + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + + Result VerifyRSAPKCS1SignedDigest(const SignedDigest&, Input) override + { + ADD_FAILURE(); + return Result::FATAL_ERROR_LIBRARY_FAILURE; } Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override { - return Success; + ADD_FAILURE(); + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + + Result VerifyECDSASignedDigest(const SignedDigest&, Input) override + { + ADD_FAILURE(); + return Result::FATAL_ERROR_LIBRARY_FAILURE; } }; class pkixbuild_DSS : public ::testing::Test { }; TEST_F(pkixbuild_DSS, DSSEndEntityKeyNotAccepted) { DSSTrustDomain trustDomain; @@ -509,39 +529,45 @@ public: return Success; } Result IsChainValid(const DERArray&, Time) override { return Success; } - Result VerifySignedData(const SignedDataWithSignature& signedData, - Input subjectPublicKeyInfo) override + Result DigestBuf(Input input, DigestAlgorithm digestAlg, + /*out*/ uint8_t* digestBuf, size_t digestLen) override { - return TestVerifySignedData(signedData, subjectPublicKeyInfo); - } - - Result DigestBuf(Input, /*out*/uint8_t*, size_t) override - { - ADD_FAILURE(); - return Result::FATAL_ERROR_LIBRARY_FAILURE; + return TestDigestBuf(input, digestAlg, digestBuf, digestLen); } Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int) override { return Success; } + Result VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) override + { + return TestVerifyRSAPKCS1SignedDigest(signedDigest, subjectPublicKeyInfo); + } + Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override { return Success; } + Result VerifyECDSASignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) override + { + return TestVerifyECDSASignedDigest(signedDigest, subjectPublicKeyInfo); + } + private: const ByteString issuer; const bool expectedKeepGoing; }; struct IssuerNameCheckParams { const char* subjectIssuerCN; // null means "empty name"
--- a/security/pkix/test/gtest/pkixcert_extension_tests.cpp +++ b/security/pkix/test/gtest/pkixcert_extension_tests.cpp @@ -84,38 +84,45 @@ private: return Success; } Result IsChainValid(const DERArray&, Time) override { return Success; } - Result VerifySignedData(const SignedDataWithSignature& signedData, - Input subjectPublicKeyInfo) override + Result DigestBuf(Input input, DigestAlgorithm digestAlg, + /*out*/ uint8_t* digestBuf, size_t digestLen) override { - return TestVerifySignedData(signedData, subjectPublicKeyInfo); - } - - Result DigestBuf(Input, /*out*/ uint8_t*, size_t) override - { - ADD_FAILURE(); - return Result::FATAL_ERROR_LIBRARY_FAILURE; + return TestDigestBuf(input, digestAlg, digestBuf, digestLen); } Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int) override { return Success; } + Result VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) override + { + return TestVerifyRSAPKCS1SignedDigest(signedDigest, subjectPublicKeyInfo); + } + Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override { return Success; } + + Result VerifyECDSASignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) override + { + return TestVerifyECDSASignedDigest(signedDigest, subjectPublicKeyInfo); + } + }; // python DottedOIDToCode.py --tlv unknownExtensionOID 1.3.6.1.4.1.13769.666.666.666.1.500.9.3 static const uint8_t tlv_unknownExtensionOID[] = { 0x06, 0x12, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xeb, 0x49, 0x85, 0x1a, 0x85, 0x1a, 0x85, 0x1a, 0x01, 0x83, 0x74, 0x09, 0x03 };
--- a/security/pkix/test/gtest/pkixcert_signature_algorithm_tests.cpp +++ b/security/pkix/test/gtest/pkixcert_signature_algorithm_tests.cpp @@ -98,40 +98,45 @@ private: return Success; } Result IsChainValid(const DERArray&, Time) override { return Success; } - Result VerifySignedData(const SignedDataWithSignature& signedData, - Input subjectPublicKeyInfo) override + Result DigestBuf(Input input, DigestAlgorithm digestAlg, + /*out*/ uint8_t* digestBuf, size_t digestLen) override { - EXPECT_NE(SignatureAlgorithm::unsupported_algorithm, signedData.algorithm); - return TestVerifySignedData(signedData, subjectPublicKeyInfo); - } - - Result DigestBuf(Input, uint8_t*, size_t) override - { - ADD_FAILURE(); - return Result::FATAL_ERROR_LIBRARY_FAILURE; + return TestDigestBuf(input, digestAlg, digestBuf, digestLen); } Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int) override { return Success; } + Result VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) override + { + return TestVerifyRSAPKCS1SignedDigest(signedDigest, subjectPublicKeyInfo); + } + Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override { return Success; } + Result VerifyECDSASignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) override + { + return TestVerifyECDSASignedDigest(signedDigest, subjectPublicKeyInfo); + } + ByteString rootDER; ByteString rootSubjectDER; ByteString intDER; ByteString intSubjectDER; }; static const ByteString NO_INTERMEDIATE; // empty
--- a/security/pkix/test/gtest/pkixder_pki_types_tests.cpp +++ b/security/pkix/test/gtest/pkixder_pki_types_tests.cpp @@ -185,33 +185,38 @@ TEST_F(pkixder_pki_types_tests, Optional der::Version version = der::Version::v3; ASSERT_EQ(Success, OptionalVersion(reader, version)); ASSERT_EQ(der::Version::v1, version); } static const size_t MAX_ALGORITHM_OID_DER_LENGTH = 13; -template <typename T> -struct AlgorithmIdentifierTestInfo +struct InvalidAlgorithmIdentifierTestInfo { - T algorithm; uint8_t der[MAX_ALGORITHM_OID_DER_LENGTH]; size_t derLength; }; -class pkixder_DigestAlgorithmIdentifier +struct ValidDigestAlgorithmIdentifierTestInfo +{ + DigestAlgorithm algorithm; + uint8_t der[MAX_ALGORITHM_OID_DER_LENGTH]; + size_t derLength; +}; + +class pkixder_DigestAlgorithmIdentifier_Valid : public ::testing::Test - , public ::testing::WithParamInterface< - AlgorithmIdentifierTestInfo<DigestAlgorithm>> + , public ::testing::WithParamInterface<ValidDigestAlgorithmIdentifierTestInfo> { }; -static const AlgorithmIdentifierTestInfo<DigestAlgorithm> -DIGEST_ALGORITHM_TEST_INFO[] = { +static const ValidDigestAlgorithmIdentifierTestInfo + VALID_DIGEST_ALGORITHM_TEST_INFO[] = +{ { DigestAlgorithm::sha512, { 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03 }, 13 }, { DigestAlgorithm::sha384, { 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02 }, @@ -224,19 +229,19 @@ DIGEST_ALGORITHM_TEST_INFO[] = { }, { DigestAlgorithm::sha1, { 0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a }, 9 }, }; -TEST_P(pkixder_DigestAlgorithmIdentifier, Valid) +TEST_P(pkixder_DigestAlgorithmIdentifier_Valid, Valid) { - const AlgorithmIdentifierTestInfo<DigestAlgorithm>& param(GetParam()); + const ValidDigestAlgorithmIdentifierTestInfo& param(GetParam()); { Input input; ASSERT_EQ(Success, input.Init(param.der, param.derLength)); Reader reader(input); DigestAlgorithm alg; ASSERT_EQ(Success, DigestAlgorithmIdentifier(reader, alg)); ASSERT_EQ(param.algorithm, alg); @@ -255,170 +260,223 @@ TEST_P(pkixder_DigestAlgorithmIdentifier Reader reader(input); DigestAlgorithm alg; ASSERT_EQ(Success, DigestAlgorithmIdentifier(reader, alg)); ASSERT_EQ(param.algorithm, alg); ASSERT_EQ(Success, End(reader)); } } -INSTANTIATE_TEST_CASE_P(pkixder_DigestAlgorithmIdentifier, - pkixder_DigestAlgorithmIdentifier, - testing::ValuesIn(DIGEST_ALGORITHM_TEST_INFO)); +INSTANTIATE_TEST_CASE_P(pkixder_DigestAlgorithmIdentifier_Valid, + pkixder_DigestAlgorithmIdentifier_Valid, + testing::ValuesIn(VALID_DIGEST_ALGORITHM_TEST_INFO)); -TEST_F(pkixder_DigestAlgorithmIdentifier, Invalid_MD5) +class pkixder_DigestAlgorithmIdentifier_Invalid + : public ::testing::Test + , public ::testing::WithParamInterface<InvalidAlgorithmIdentifierTestInfo> { - // The OID identifies MD5 (1.2.840.113549.2.5). It is invalid because we - // don't accept MD5 as a hash algorithm. - static const uint8_t DER[] = { - 0x30, 0x0a, 0x06, 0x08, - 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05 - }; - Input input(DER); - Reader reader(input); +}; - DigestAlgorithm alg; - ASSERT_EQ(Result::ERROR_INVALID_ALGORITHM, - DigestAlgorithmIdentifier(reader, alg)); -} - -TEST_F(pkixder_DigestAlgorithmIdentifier, Invalid_Digest_ECDSA_WITH_SHA256) +static const InvalidAlgorithmIdentifierTestInfo + INVALID_DIGEST_ALGORITHM_TEST_INFO[] = { - // 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(DER); + { // MD5 + { 0x30, 0x0a, 0x06, 0x08, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05 }, + 12, + }, + { // ecdsa-with-SHA256 (1.2.840.10045.4.3.2) (not a hash algorithm) + { 0x30, 0x0a, 0x06, 0x08, + 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02 }, + 12, + }, +}; + +TEST_P(pkixder_DigestAlgorithmIdentifier_Invalid, Invalid) +{ + const InvalidAlgorithmIdentifierTestInfo& param(GetParam()); + Input input; + ASSERT_EQ(Success, input.Init(param.der, param.derLength)); Reader reader(input); - DigestAlgorithm alg; ASSERT_EQ(Result::ERROR_INVALID_ALGORITHM, DigestAlgorithmIdentifier(reader, alg)); } -static const AlgorithmIdentifierTestInfo<SignatureAlgorithm> - SIGNATURE_ALGORITHM_TEST_INFO[] = +INSTANTIATE_TEST_CASE_P(pkixder_DigestAlgorithmIdentifier_Invalid, + pkixder_DigestAlgorithmIdentifier_Invalid, + testing::ValuesIn(INVALID_DIGEST_ALGORITHM_TEST_INFO)); + +struct ValidSignatureAlgorithmIdentifierValueTestInfo { - { SignatureAlgorithm::ecdsa_with_sha512, - { 0x30, 0x0a, 0x06, 0x08, + PublicKeyAlgorithm publicKeyAlg; + DigestAlgorithm digestAlg; + uint8_t der[MAX_ALGORITHM_OID_DER_LENGTH]; + size_t derLength; +}; + +static const ValidSignatureAlgorithmIdentifierValueTestInfo + VALID_SIGNATURE_ALGORITHM_VALUE_TEST_INFO[] = +{ + // ECDSA + { PublicKeyAlgorithm::ECDSA, + DigestAlgorithm::sha512, + { 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04 }, - 12, + 10, }, - { SignatureAlgorithm::ecdsa_with_sha384, - { 0x30, 0x0a, 0x06, 0x08, + { PublicKeyAlgorithm::ECDSA, + DigestAlgorithm::sha384, + { 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x03 }, - 12, + 10, }, - { SignatureAlgorithm::ecdsa_with_sha256, - { 0x30, 0x0a, 0x06, 0x08, + { PublicKeyAlgorithm::ECDSA, + DigestAlgorithm::sha256, + { 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02 }, - 12, + 10, }, - { SignatureAlgorithm::ecdsa_with_sha1, - { 0x30, 0x09, 0x06, 0x07, + { PublicKeyAlgorithm::ECDSA, + DigestAlgorithm::sha1, + { 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x01 }, - 11, + 9, }, // RSA - { SignatureAlgorithm::rsa_pkcs1_with_sha512, - { 0x30, 0x0b, 0x06, 0x09, + { PublicKeyAlgorithm::RSA_PKCS1, + DigestAlgorithm::sha512, + { 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0d }, - 13, - }, - { SignatureAlgorithm::rsa_pkcs1_with_sha384, - { 0x30, 0x0b, 0x06, 0x09, - 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0c }, - 13, - }, - { SignatureAlgorithm::rsa_pkcs1_with_sha256, - { 0x30, 0x0b, 0x06, 0x09, - 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b }, - 13, + 11, }, - { SignatureAlgorithm::rsa_pkcs1_with_sha1, - // IETF Standard OID - { 0x30, 0x0b, 0x06, 0x09, - 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05 }, - 13, - }, - { SignatureAlgorithm::rsa_pkcs1_with_sha1, - // Legacy OIW OID (bug 1042479) - { 0x30, 0x07, 0x06, 0x05, - 0x2b, 0x0e, 0x03, 0x02, 0x1d }, - 9, - }, - - // id-dsa-with-sha256 (2.16.840.1.101.3.4.3.2) - { SignatureAlgorithm::unsupported_algorithm, - { 0x30, 0x0b, 0x06, 0x09, - 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x02 }, - 13, - }, - - // id-dsa-with-sha1 (1.2.840.10040.4.3) - { SignatureAlgorithm::unsupported_algorithm, - { 0x30, 0x09, 0x06, 0x07, - 0x2a, 0x86, 0x48, 0xce, 0x38, 0x04, 0x03 }, + { PublicKeyAlgorithm::RSA_PKCS1, + DigestAlgorithm::sha384, + { 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0c }, 11, }, - - // RSA-with-MD5 (1.2.840.113549.1.1.4) - { SignatureAlgorithm::unsupported_algorithm, - { 0x30, 0x0b, 0x06, 0x09, - 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x04 }, - 13, + { PublicKeyAlgorithm::RSA_PKCS1, + DigestAlgorithm::sha256, + { 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b }, + 11, }, - - // id-sha256 (2.16.840.1.101.3.4.2.1). It is invalid because SHA-256 is not - // a signature algorithm. - { SignatureAlgorithm::unsupported_algorithm, - { 0x30, 0x0b, 0x06, 0x09, - 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 }, - 13, + { PublicKeyAlgorithm::RSA_PKCS1, + DigestAlgorithm::sha1, + // IETF Standard OID + { 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05 }, + 11, + }, + { PublicKeyAlgorithm::RSA_PKCS1, + DigestAlgorithm::sha1, + // Legacy OIW OID (bug 1042479) + { 0x06, 0x05, + 0x2b, 0x0e, 0x03, 0x02, 0x1d }, + 7, }, }; -class pkixder_SignatureAlgorithmIdentifier +class pkixder_SignatureAlgorithmIdentifierValue_Valid : public ::testing::Test , public ::testing::WithParamInterface< - AlgorithmIdentifierTestInfo<SignatureAlgorithm>> + ValidSignatureAlgorithmIdentifierValueTestInfo> { }; -TEST_P(pkixder_SignatureAlgorithmIdentifier, ValidAndInvalid) +TEST_P(pkixder_SignatureAlgorithmIdentifierValue_Valid, Valid) { - const AlgorithmIdentifierTestInfo<SignatureAlgorithm>& param(GetParam()); + const ValidSignatureAlgorithmIdentifierValueTestInfo& param(GetParam()); { Input input; ASSERT_EQ(Success, input.Init(param.der, param.derLength)); Reader reader(input); - SignatureAlgorithm alg; - ASSERT_EQ(Success, SignatureAlgorithmIdentifier(reader, alg)); - ASSERT_EQ(param.algorithm, alg); + PublicKeyAlgorithm publicKeyAlg; + DigestAlgorithm digestAlg; + ASSERT_EQ(Success, + SignatureAlgorithmIdentifierValue(reader, publicKeyAlg, + digestAlg)); + ASSERT_EQ(param.publicKeyAlg, publicKeyAlg); + ASSERT_EQ(param.digestAlg, digestAlg); ASSERT_EQ(Success, End(reader)); } { uint8_t derWithNullParam[MAX_ALGORITHM_OID_DER_LENGTH + 2]; memcpy(derWithNullParam, param.der, param.derLength); - derWithNullParam[1] += 2; // we're going to expand the value by 2 bytes derWithNullParam[param.derLength] = 0x05; // NULL tag derWithNullParam[param.derLength + 1] = 0x00; // length zero Input input; ASSERT_EQ(Success, input.Init(derWithNullParam, param.derLength + 2)); Reader reader(input); - SignatureAlgorithm alg; - ASSERT_EQ(Success, SignatureAlgorithmIdentifier(reader, alg)); - ASSERT_EQ(param.algorithm, alg); + PublicKeyAlgorithm publicKeyAlg; + DigestAlgorithm digestAlg; + ASSERT_EQ(Success, + SignatureAlgorithmIdentifierValue(reader, publicKeyAlg, + digestAlg)); + ASSERT_EQ(param.publicKeyAlg, publicKeyAlg); + ASSERT_EQ(param.digestAlg, digestAlg); ASSERT_EQ(Success, End(reader)); } } -INSTANTIATE_TEST_CASE_P(pkixder_SignatureAlgorithmIdentifier, - pkixder_SignatureAlgorithmIdentifier, - testing::ValuesIn(SIGNATURE_ALGORITHM_TEST_INFO)); +INSTANTIATE_TEST_CASE_P( + pkixder_SignatureAlgorithmIdentifierValue_Valid, + pkixder_SignatureAlgorithmIdentifierValue_Valid, + testing::ValuesIn(VALID_SIGNATURE_ALGORITHM_VALUE_TEST_INFO)); + +static const InvalidAlgorithmIdentifierTestInfo + INVALID_SIGNATURE_ALGORITHM_VALUE_TEST_INFO[] = +{ + // id-dsa-with-sha256 (2.16.840.1.101.3.4.3.2) + { { 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x02 }, + 11, + }, + + // id-dsa-with-sha1 (1.2.840.10040.4.3) + { { 0x06, 0x07, + 0x2a, 0x86, 0x48, 0xce, 0x38, 0x04, 0x03 }, + 9, + }, + + // RSA-with-MD5 (1.2.840.113549.1.1.4) + { { 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x04 }, + 11, + }, + + // id-sha256 (2.16.840.1.101.3.4.2.1). It is invalid because SHA-256 is not + // a signature algorithm. + { { 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 }, + 11, + }, +}; + +class pkixder_SignatureAlgorithmIdentifier_Invalid + : public ::testing::Test + , public ::testing::WithParamInterface<InvalidAlgorithmIdentifierTestInfo> +{ +}; + +TEST_P(pkixder_SignatureAlgorithmIdentifier_Invalid, Invalid) +{ + const InvalidAlgorithmIdentifierTestInfo& param(GetParam()); + Input input; + ASSERT_EQ(Success, input.Init(param.der, param.derLength)); + Reader reader(input); + der::PublicKeyAlgorithm publicKeyAlg; + DigestAlgorithm digestAlg; + ASSERT_EQ(Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED, + SignatureAlgorithmIdentifierValue(reader, publicKeyAlg, digestAlg)); +} + +INSTANTIATE_TEST_CASE_P( + pkixder_SignatureAlgorithmIdentifier_Invalid, + pkixder_SignatureAlgorithmIdentifier_Invalid, + testing::ValuesIn(INVALID_SIGNATURE_ALGORITHM_VALUE_TEST_INFO)); } // unnamed namespace
--- a/security/pkix/test/gtest/pkixocsp_CreateEncodedOCSPRequest_tests.cpp +++ b/security/pkix/test/gtest/pkixocsp_CreateEncodedOCSPRequest_tests.cpp @@ -54,37 +54,46 @@ private: } Result IsChainValid(const DERArray&, Time) override { ADD_FAILURE(); return Result::FATAL_ERROR_LIBRARY_FAILURE; } - Result VerifySignedData(const SignedDataWithSignature&, Input) override + Result DigestBuf(Input item, DigestAlgorithm digestAlg, + /*out*/ uint8_t *digestBuf, size_t digestBufLen) + override + { + return TestDigestBuf(item, digestAlg, digestBuf, digestBufLen); + } + + Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int) + final override { ADD_FAILURE(); return Result::FATAL_ERROR_LIBRARY_FAILURE; } - Result DigestBuf(Input item, /*out*/ uint8_t *digestBuf, size_t digestBufLen) - override + Result VerifyRSAPKCS1SignedDigest(const SignedDigest&, Input) override { - return TestDigestBuf(item, digestBuf, digestBufLen); + ADD_FAILURE(); + return Result::FATAL_ERROR_LIBRARY_FAILURE; } - Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int) - override + Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) final override { - return Success; + ADD_FAILURE(); + return Result::FATAL_ERROR_LIBRARY_FAILURE; } - Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override + Result VerifyECDSASignedDigest(const SignedDigest&, Input) override { - return Success; + ADD_FAILURE(); + return Result::FATAL_ERROR_LIBRARY_FAILURE; } }; class pkixocsp_CreateEncodedOCSPRequest : public ::testing::Test { protected: void MakeIssuerCertIDComponents(const char* issuerASCII, /*out*/ ByteString& issuerDER,
--- a/security/pkix/test/gtest/pkixocsp_VerifyEncodedOCSPResponse.cpp +++ b/security/pkix/test/gtest/pkixocsp_VerifyEncodedOCSPResponse.cpp @@ -65,39 +65,46 @@ public: } Result IsChainValid(const DERArray&, Time) final override { ADD_FAILURE(); return Result::FATAL_ERROR_LIBRARY_FAILURE; } - Result VerifySignedData(const SignedDataWithSignature& signedData, - Input subjectPublicKeyInfo) final override - { - return TestVerifySignedData(signedData, subjectPublicKeyInfo); - } - - Result DigestBuf(Input item, /*out*/ uint8_t* digestBuf, size_t digestBufLen) + Result DigestBuf(Input item, DigestAlgorithm digestAlg, + /*out*/ uint8_t* digestBuf, size_t digestBufLen) final override { - return TestDigestBuf(item, digestBuf, digestBufLen); + return TestDigestBuf(item, digestAlg, digestBuf, digestBufLen); } Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int) final override { return Success; } + Result VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) override + { + return TestVerifyRSAPKCS1SignedDigest(signedDigest, subjectPublicKeyInfo); + } + Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) final override { return Success; } + Result VerifyECDSASignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) override + { + return TestVerifyECDSASignedDigest(signedDigest, subjectPublicKeyInfo); + } + OCSPTestTrustDomain(const OCSPTestTrustDomain&) = delete; void operator=(const OCSPTestTrustDomain&) = delete; }; namespace { char const* const rootName = "Test CA 1"; void deleteCertID(CertID* certID) { delete certID; } } // unnamed namespace
--- a/security/pkix/test/lib/pkixtestnss.cpp +++ b/security/pkix/test/lib/pkixtestnss.cpp @@ -380,23 +380,36 @@ SHA1(const ByteString& toHash) static_cast<int32_t>(toHash.length())); if (srv != SECSuccess) { return ByteString(); } return ByteString(digestBuf, sizeof(digestBuf)); } Result -TestVerifySignedData(const SignedDataWithSignature& signedData, - Input subjectPublicKeyInfo) +TestVerifyECDSASignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) { InitNSSIfNeeded(); - return VerifySignedDataNSS(signedData, subjectPublicKeyInfo, nullptr); + return VerifyECDSASignedDigestNSS(signedDigest, subjectPublicKeyInfo, + nullptr); } Result -TestDigestBuf(Input item, /*out*/ uint8_t* digestBuf, size_t digestBufLen) +TestVerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) { InitNSSIfNeeded(); - return DigestBufNSS(item, digestBuf, digestBufLen); + return VerifyRSAPKCS1SignedDigestNSS(signedDigest, subjectPublicKeyInfo, + nullptr); +} + +Result +TestDigestBuf(Input item, + DigestAlgorithm digestAlg, + /*out*/ uint8_t* digestBuf, + size_t digestBufLen) +{ + InitNSSIfNeeded(); + return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen); } } } } // namespace mozilla::pkix::test
--- a/security/pkix/test/lib/pkixtestutil.h +++ b/security/pkix/test/lib/pkixtestutil.h @@ -267,21 +267,22 @@ protected: TestKeyPair* CloneReusedKeyPair(); TestKeyPair* GenerateKeyPair(); TestKeyPair* GenerateDSSKeyPair(); inline void DeleteTestKeyPair(TestKeyPair* keyPair) { delete keyPair; } typedef ScopedPtr<TestKeyPair, DeleteTestKeyPair> ScopedTestKeyPair; ByteString SHA1(const ByteString& toHash); -Result TestCheckPublicKey(Input subjectPublicKeyInfo); -Result TestVerifySignedData(const SignedDataWithSignature& signedData, - Input subjectPublicKeyInfo); -Result TestDigestBuf(Input item, /*out*/ uint8_t* digestBuf, - size_t digestBufLen); +Result TestVerifyECDSASignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo); +Result TestVerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo); +Result TestDigestBuf(Input item, DigestAlgorithm digestAlg, + /*out*/ uint8_t* digestBuf, size_t digestBufLen); // Replace one substring in item with another of the same length, but only if // the substring was found exactly once. The "same length" restriction is // useful for avoiding invalidating lengths encoded within the item. The // "only once" restriction is helpful for avoiding making accidental changes. // // The string to search for must be 8 or more bytes long so that it is // extremely unlikely that there will ever be any false positive matches