/* -*- 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 "pkixgtest.h" #include "mozpkix/pkixder.h" using namespace mozilla::pkix; using namespace mozilla::pkix::test; namespace mozilla { namespace pkix { extern Result CheckSignatureAlgorithm( TrustDomain& trustDomain, EndEntityOrCA endEntityOrCA, Time notBefore, const der::SignedDataWithSignature& signedData, Input signatureValue); } } // namespace mozilla::pkix struct CheckSignatureAlgorithmTestParams { ByteString signatureAlgorithmValue; ByteString signatureValue; unsigned int signatureLengthInBytes; Result expectedResult; }; ::std::ostream& operator<<(::std::ostream& os, const CheckSignatureAlgorithmTestParams&) { return os << "TODO (bug 1318770)"; } #define BS(s) ByteString(s, MOZILLA_PKIX_ARRAY_LENGTH(s)) // python DottedOIDToCode.py --tlv sha256WithRSAEncryption 1.2.840.113549.1.1.11 static const uint8_t tlv_sha256WithRSAEncryption[] = { 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b }; // Same as tlv_sha256WithRSAEncryption, except one without the "0x0b" and with // the DER length decreased accordingly. static const uint8_t tlv_sha256WithRSAEncryption_truncated[] = { 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01 }; // python DottedOIDToCode.py --tlv sha-1WithRSAEncryption 1.2.840.113549.1.1.5 static const uint8_t tlv_sha_1WithRSAEncryption[] = { 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05 }; // python DottedOIDToCode.py --tlv sha1WithRSASignature 1.3.14.3.2.29 static const uint8_t tlv_sha1WithRSASignature[] = { 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1d }; // python DottedOIDToCode.py --tlv md5WithRSAEncryption 1.2.840.113549.1.1.4 static const uint8_t tlv_md5WithRSAEncryption[] = { 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x04 }; // CA/B Forum BR 1.8.1 Section 7.1.3.2.1 // Params for RSA-PSS with SHA-256, MGF-1 with SHA-256, and a salt length // of 32 bytes: static const uint8_t rsaPSSWithSHA256[] = { 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0a, 0x30, 0x34, 0xa0, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0xa1, 0x1c, 0x30, 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x08, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0xa2, 0x03, 0x02, 0x01, 0x20 }; static const CheckSignatureAlgorithmTestParams CHECKSIGNATUREALGORITHM_TEST_PARAMS[] = { { // Both algorithm IDs are empty ByteString(), ByteString(), 2048 / 8, Result::ERROR_BAD_DER, }, { // signatureAlgorithm is empty, signature is supported. ByteString(), BS(tlv_sha256WithRSAEncryption), 2048 / 8, Result::ERROR_BAD_DER, }, { // signatureAlgorithm is supported, signature is empty. BS(tlv_sha256WithRSAEncryption), ByteString(), 2048 / 8, Result::ERROR_BAD_DER, }, { // Algorithms match, both are supported. BS(tlv_sha256WithRSAEncryption), BS(tlv_sha256WithRSAEncryption), 2048 / 8, Success }, { // Algorithms do not match because signatureAlgorithm is truncated. BS(tlv_sha256WithRSAEncryption_truncated), BS(tlv_sha256WithRSAEncryption), 2048 / 8, Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED }, { // Algorithms do not match because signature is truncated. BS(tlv_sha256WithRSAEncryption), BS(tlv_sha256WithRSAEncryption_truncated), 2048 / 8, Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED }, { // Algorithms do not match, both are supported. BS(tlv_sha_1WithRSAEncryption), BS(tlv_sha256WithRSAEncryption), 2048 / 8, Result::ERROR_SIGNATURE_ALGORITHM_MISMATCH, }, { // Algorithms do not match, both are supported. BS(tlv_sha256WithRSAEncryption), BS(tlv_sha_1WithRSAEncryption), 2048 / 8, Result::ERROR_SIGNATURE_ALGORITHM_MISMATCH, }, { // Algorithms match, both are unsupported. BS(tlv_md5WithRSAEncryption), BS(tlv_md5WithRSAEncryption), 2048 / 8, Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED }, { // signatureAlgorithm is unsupported, signature is supported. BS(tlv_md5WithRSAEncryption), BS(tlv_sha256WithRSAEncryption), 2048 / 8, Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED }, { // signatureAlgorithm is supported, signature is unsupported. BS(tlv_sha256WithRSAEncryption), BS(tlv_md5WithRSAEncryption), 2048 / 8, Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED }, { // Both have the optional NULL parameter. BS(tlv_sha256WithRSAEncryption) + TLV(der::NULLTag, ByteString()), BS(tlv_sha256WithRSAEncryption) + TLV(der::NULLTag, ByteString()), 2048 / 8, Success }, { // signatureAlgorithm has the optional NULL parameter, signature doesn't. BS(tlv_sha256WithRSAEncryption) + TLV(der::NULLTag, ByteString()), BS(tlv_sha256WithRSAEncryption), 2048 / 8, Success }, { // signatureAlgorithm does not have the optional NULL parameter, signature // does. BS(tlv_sha256WithRSAEncryption), BS(tlv_sha256WithRSAEncryption) + TLV(der::NULLTag, ByteString()), 2048 / 8, Success }, { // The different OIDs for RSA-with-SHA1 we support are semantically // equivalent. BS(tlv_sha1WithRSASignature), BS(tlv_sha_1WithRSAEncryption), 2048 / 8, Success, }, { // The different OIDs for RSA-with-SHA1 we support are semantically // equivalent (opposite order). BS(tlv_sha_1WithRSAEncryption), BS(tlv_sha1WithRSASignature), 2048 / 8, Success, }, { // Algorithms match, both are supported, key size is not a multile of 128 // bits. This test verifies that we're not wrongly rounding up the // signature size like we did in the original patch for bug 1131767. BS(tlv_sha256WithRSAEncryption), BS(tlv_sha256WithRSAEncryption), (2048 / 8) - 1, Success }, { // signatureAlgorithm and signature are RSA-PSS BS(rsaPSSWithSHA256), BS(rsaPSSWithSHA256), 2048 / 8, Success }, { // signatureAlgorithm is RSA-PSS, signature is RSA PKCS#1v1.5 BS(rsaPSSWithSHA256), BS(tlv_sha256WithRSAEncryption), 2048 / 8, Result::ERROR_SIGNATURE_ALGORITHM_MISMATCH }, }; class pkixcheck_CheckSignatureAlgorithm : public ::testing::Test , public ::testing::WithParamInterface { }; class pkixcheck_CheckSignatureAlgorithm_TrustDomain final : public EverythingFailsByDefaultTrustDomain { public: explicit pkixcheck_CheckSignatureAlgorithm_TrustDomain( unsigned int aPublicKeySizeInBits) : publicKeySizeInBits(aPublicKeySizeInBits) , checkedDigestAlgorithm(false) , checkedModulusSizeInBits(false) { } Result CheckSignatureDigestAlgorithm(DigestAlgorithm, EndEntityOrCA, Time) override { checkedDigestAlgorithm = true; return Success; } Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA endEntityOrCA, unsigned int modulusSizeInBits) override { EXPECT_EQ(EndEntityOrCA::MustBeEndEntity, endEntityOrCA); EXPECT_EQ(publicKeySizeInBits, modulusSizeInBits); checkedModulusSizeInBits = true; return Success; } const unsigned int publicKeySizeInBits; bool checkedDigestAlgorithm; bool checkedModulusSizeInBits; }; TEST_P(pkixcheck_CheckSignatureAlgorithm, CheckSignatureAlgorithm) { const Time now(Now()); const CheckSignatureAlgorithmTestParams& params(GetParam()); Input signatureValueInput; ASSERT_EQ(Success, signatureValueInput.Init(params.signatureValue.data(), params.signatureValue.length())); pkixcheck_CheckSignatureAlgorithm_TrustDomain trustDomain(params.signatureLengthInBytes * 8); der::SignedDataWithSignature signedData; ASSERT_EQ(Success, signedData.algorithm.Init(params.signatureAlgorithmValue.data(), params.signatureAlgorithmValue.length())); ByteString dummySignature(params.signatureLengthInBytes, 0xDE); ASSERT_EQ(Success, signedData.signature.Init(dummySignature.data(), dummySignature.length())); ASSERT_EQ(params.expectedResult, CheckSignatureAlgorithm(trustDomain, EndEntityOrCA::MustBeEndEntity, now, signedData, signatureValueInput)); ASSERT_EQ(params.expectedResult == Success, trustDomain.checkedDigestAlgorithm); ASSERT_EQ(params.expectedResult == Success, trustDomain.checkedModulusSizeInBits); } INSTANTIATE_TEST_SUITE_P( pkixcheck_CheckSignatureAlgorithm, pkixcheck_CheckSignatureAlgorithm, testing::ValuesIn(CHECKSIGNATUREALGORITHM_TEST_PARAMS)); class pkixcheck_CheckSignatureAlgorithm_BuildCertChain_TrustDomain : public DefaultCryptoTrustDomain { public: explicit pkixcheck_CheckSignatureAlgorithm_BuildCertChain_TrustDomain( const ByteString& aIssuer) : issuer(aIssuer) { } Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input cert, /*out*/ TrustLevel& trustLevel) override { trustLevel = InputEqualsByteString(cert, issuer) ? TrustLevel::TrustAnchor : TrustLevel::InheritsTrust; return Success; } Result FindIssuer(Input, IssuerChecker& checker, Time) override { EXPECT_FALSE(ENCODING_FAILED(issuer)); Input issuerInput; EXPECT_EQ(Success, issuerInput.Init(issuer.data(), issuer.length())); bool keepGoing; EXPECT_EQ(Success, checker.Check(issuerInput, nullptr, keepGoing)); EXPECT_FALSE(keepGoing); return Success; } Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration, /*optional*/ const Input*, /*optional*/ const Input*, /*optional*/ const Input*) override { return Success; } Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override { return Success; } ByteString issuer; }; // Test that CheckSignatureAlgorithm actually gets called at some point when // BuildCertChain is called. TEST_F(pkixcheck_CheckSignatureAlgorithm, BuildCertChain) { ScopedTestKeyPair keyPair(CloneReusedKeyPair()); ASSERT_TRUE(keyPair.get()); ByteString issuerExtensions[2]; issuerExtensions[0] = CreateEncodedBasicConstraints(true, nullptr, Critical::No); ASSERT_FALSE(ENCODING_FAILED(issuerExtensions[0])); ByteString issuer(CreateEncodedCertificate(3, sha256WithRSAEncryption(), CreateEncodedSerialNumber(1), CNToDERName("issuer"), oneDayBeforeNow, oneDayAfterNow, CNToDERName("issuer"), *keyPair, issuerExtensions, *keyPair, sha256WithRSAEncryption())); ASSERT_FALSE(ENCODING_FAILED(issuer)); ByteString subject(CreateEncodedCertificate(3, sha1WithRSAEncryption(), CreateEncodedSerialNumber(2), CNToDERName("issuer"), oneDayBeforeNow, oneDayAfterNow, CNToDERName("subject"), *keyPair, nullptr, *keyPair, sha256WithRSAEncryption())); ASSERT_FALSE(ENCODING_FAILED(subject)); Input subjectInput; ASSERT_EQ(Success, subjectInput.Init(subject.data(), subject.length())); pkixcheck_CheckSignatureAlgorithm_BuildCertChain_TrustDomain trustDomain(issuer); Result rv = BuildCertChain(trustDomain, subjectInput, Now(), EndEntityOrCA::MustBeEndEntity, KeyUsage::noParticularKeyUsageRequired, KeyPurposeId::anyExtendedKeyUsage, CertPolicyId::anyPolicy, nullptr); ASSERT_EQ(Result::ERROR_SIGNATURE_ALGORITHM_MISMATCH, rv); }