Source code
Revision control
Copy as Markdown
Other Tools
/* -*- 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
*/
/* 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
*
*
* 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&)
{
}
#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
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<CheckSignatureAlgorithmTestParams>
{
};
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);
}