/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ #include "pkixgtest.h" #include "mozpkix/pkixder.h" #include "secoid.h" using namespace mozilla::pkix; using namespace mozilla::pkix::test; /* These tests generate invalid certificates on the fly, We want to test * validation of those certificates, not the generation, so we * need to temporarily allow disallowed signature policies before * we do the actual certificate or ocsp signing */ class HashAlgorithmPolicies { static const int numberOfHashes = 4; /* sigh */ static const SECOidTag hashOids[numberOfHashes]; PRUint32 savedPolicy[numberOfHashes]; public: void EnableHashSignaturePolicy(void); void RestoreHashSignaturePolicy(void); }; const SECOidTag HashAlgorithmPolicies::hashOids[numberOfHashes] = { SEC_OID_MD2, SEC_OID_MD4, SEC_OID_MD5, SEC_OID_SHA1 }; void HashAlgorithmPolicies::EnableHashSignaturePolicy(void) { for (int i=0;i < numberOfHashes; i++) { ASSERT_EQ(SECSuccess, NSS_GetAlgorithmPolicy(hashOids[i], &savedPolicy[i])); ASSERT_EQ(SECSuccess, NSS_SetAlgorithmPolicy(hashOids[i], NSS_USE_ALG_IN_SIGNATURE, 0)); } } void HashAlgorithmPolicies::RestoreHashSignaturePolicy(void) { for (int i=0;i < numberOfHashes; i++) { ASSERT_EQ(SECSuccess, NSS_SetAlgorithmPolicy(hashOids[i], savedPolicy[i], NSS_USE_ALG_IN_SIGNATURE)); } } static ByteString CreateCert(const char* issuerCN, const char* subjectCN, EndEntityOrCA endEntityOrCA, const TestSignatureAlgorithm& signatureAlgorithm, /*out*/ ByteString& subjectDER) { static long serialNumberValue = 0; ++serialNumberValue; ByteString serialNumber(CreateEncodedSerialNumber(serialNumberValue)); EXPECT_FALSE(ENCODING_FAILED(serialNumber)); ByteString issuerDER(CNToDERName(issuerCN)); EXPECT_FALSE(ENCODING_FAILED(issuerDER)); subjectDER = CNToDERName(subjectCN); EXPECT_FALSE(ENCODING_FAILED(subjectDER)); ByteString extensions[2]; if (endEntityOrCA == EndEntityOrCA::MustBeCA) { extensions[0] = CreateEncodedBasicConstraints(true, nullptr, Critical::Yes); EXPECT_FALSE(ENCODING_FAILED(extensions[0])); } ScopedTestKeyPair reusedKey(CloneReusedKeyPair()); HashAlgorithmPolicies policies; policies.EnableHashSignaturePolicy(); ByteString certDER(CreateEncodedCertificate(v3, signatureAlgorithm, serialNumber, issuerDER, oneDayBeforeNow, oneDayAfterNow, subjectDER, *reusedKey, extensions, *reusedKey, signatureAlgorithm)); policies.RestoreHashSignaturePolicy(); EXPECT_FALSE(ENCODING_FAILED(certDER)); return certDER; } class AlgorithmTestsTrustDomain final : public DefaultCryptoTrustDomain { public: AlgorithmTestsTrustDomain(const ByteString& aRootDER, const ByteString& aRootSubjectDER, /*optional*/ const ByteString& aIntDER, /*optional*/ const ByteString& aIntSubjectDER) : rootDER(aRootDER) , rootSubjectDER(aRootSubjectDER) , intDER(aIntDER) , intSubjectDER(aIntSubjectDER) { } private: Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input candidateCert, /*out*/ TrustLevel& trustLevel) override { if (InputEqualsByteString(candidateCert, rootDER)) { trustLevel = TrustLevel::TrustAnchor; } else { trustLevel = TrustLevel::InheritsTrust; } return Success; } Result FindIssuer(Input encodedIssuerName, IssuerChecker& checker, Time) override { ByteString* issuerDER = nullptr; if (InputEqualsByteString(encodedIssuerName, rootSubjectDER)) { issuerDER = &rootDER; } else if (InputEqualsByteString(encodedIssuerName, intSubjectDER)) { issuerDER = &intDER; } else { // FindIssuer just returns success if it can't find a potential issuer. return Success; } Input issuerCert; Result rv = issuerCert.Init(issuerDER->data(), issuerDER->length()); if (rv != Success) { return rv; } bool keepGoing; return checker.Check(issuerCert, nullptr, keepGoing); } Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration, const Input*, const Input*, const Input*) override { return Success; } Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override { return Success; } ByteString rootDER; ByteString rootSubjectDER; ByteString intDER; ByteString intSubjectDER; }; static const TestSignatureAlgorithm NO_INTERMEDIATE { TestPublicKeyAlgorithm(ByteString()), TestDigestAlgorithmID::MD2, ByteString(), false }; struct ChainValidity final { ChainValidity(const TestSignatureAlgorithm& aEndEntitySignatureAlgorithm, const TestSignatureAlgorithm& aOptionalIntSignatureAlgorithm, const TestSignatureAlgorithm& aRootSignatureAlgorithm, bool aIsValid) : endEntitySignatureAlgorithm(aEndEntitySignatureAlgorithm) , optionalIntermediateSignatureAlgorithm(aOptionalIntSignatureAlgorithm) , rootSignatureAlgorithm(aRootSignatureAlgorithm) , isValid(aIsValid) { } // In general, a certificate is generated for each of these. However, if // optionalIntermediateSignatureAlgorithm is NO_INTERMEDIATE, then only 2 // certificates are generated. // The certificate generated for the given rootSignatureAlgorithm is the // trust anchor. TestSignatureAlgorithm endEntitySignatureAlgorithm; TestSignatureAlgorithm optionalIntermediateSignatureAlgorithm; TestSignatureAlgorithm rootSignatureAlgorithm; bool isValid; }; static const ChainValidity CHAIN_VALIDITY[] = { // The trust anchor may have a signature with an unsupported signature // algorithm. ChainValidity(sha256WithRSAEncryption(), NO_INTERMEDIATE, md5WithRSAEncryption(), true), ChainValidity(sha256WithRSAEncryption(), NO_INTERMEDIATE, md2WithRSAEncryption(), true), // Certificates that are not trust anchors must not have a signature with an // unsupported signature algorithm. ChainValidity(md5WithRSAEncryption(), NO_INTERMEDIATE, sha256WithRSAEncryption(), false), ChainValidity(md2WithRSAEncryption(), NO_INTERMEDIATE, sha256WithRSAEncryption(), false), ChainValidity(md2WithRSAEncryption(), NO_INTERMEDIATE, md5WithRSAEncryption(), false), ChainValidity(sha256WithRSAEncryption(), md5WithRSAEncryption(), sha256WithRSAEncryption(), false), ChainValidity(sha256WithRSAEncryption(), md2WithRSAEncryption(), sha256WithRSAEncryption(), false), ChainValidity(sha256WithRSAEncryption(), md2WithRSAEncryption(), md5WithRSAEncryption(), false), }; class pkixcert_IsValidChainForAlgorithm : public ::testing::Test , public ::testing::WithParamInterface { }; ::std::ostream& operator<<(::std::ostream& os, const pkixcert_IsValidChainForAlgorithm&) { return os << "TODO (bug 1318770)"; } ::std::ostream& operator<<(::std::ostream& os, const ChainValidity&) { return os << "TODO (bug 1318770)"; } TEST_P(pkixcert_IsValidChainForAlgorithm, IsValidChainForAlgorithm) { const ChainValidity& chainValidity(GetParam()); const char* rootCN = "CN=Root"; ByteString rootSubjectDER; ByteString rootEncoded( CreateCert(rootCN, rootCN, EndEntityOrCA::MustBeCA, chainValidity.rootSignatureAlgorithm, rootSubjectDER)); EXPECT_FALSE(ENCODING_FAILED(rootEncoded)); EXPECT_FALSE(ENCODING_FAILED(rootSubjectDER)); const char* issuerCN = rootCN; const char* intermediateCN = "CN=Intermediate"; ByteString intermediateSubjectDER; ByteString intermediateEncoded; // If the the algorithmIdentifier is empty, then it's NO_INTERMEDIATE. if (!chainValidity.optionalIntermediateSignatureAlgorithm .algorithmIdentifier.empty()) { intermediateEncoded = CreateCert(rootCN, intermediateCN, EndEntityOrCA::MustBeCA, chainValidity.optionalIntermediateSignatureAlgorithm, intermediateSubjectDER); EXPECT_FALSE(ENCODING_FAILED(intermediateEncoded)); EXPECT_FALSE(ENCODING_FAILED(intermediateSubjectDER)); issuerCN = intermediateCN; } AlgorithmTestsTrustDomain trustDomain(rootEncoded, rootSubjectDER, intermediateEncoded, intermediateSubjectDER); const char* endEntityCN = "CN=End Entity"; ByteString endEntitySubjectDER; ByteString endEntityEncoded( CreateCert(issuerCN, endEntityCN, EndEntityOrCA::MustBeEndEntity, chainValidity.endEntitySignatureAlgorithm, endEntitySubjectDER)); EXPECT_FALSE(ENCODING_FAILED(endEntityEncoded)); EXPECT_FALSE(ENCODING_FAILED(endEntitySubjectDER)); Input endEntity; ASSERT_EQ(Success, endEntity.Init(endEntityEncoded.data(), endEntityEncoded.length())); Result expectedResult = chainValidity.isValid ? Success : Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED; ASSERT_EQ(expectedResult, BuildCertChain(trustDomain, endEntity, Now(), EndEntityOrCA::MustBeEndEntity, KeyUsage::noParticularKeyUsageRequired, KeyPurposeId::id_kp_serverAuth, CertPolicyId::anyPolicy, nullptr)); } INSTANTIATE_TEST_SUITE_P(pkixcert_IsValidChainForAlgorithm, pkixcert_IsValidChainForAlgorithm, testing::ValuesIn(CHAIN_VALIDITY));