From 40f92302ca27bc338fe056494d7d727eb6e18222 Mon Sep 17 00:00:00 2001 From: Dana Keeler Date: Wed, 23 Mar 2022 18:46:42 +0000 Subject: Bug 1005084 - support specific RSA-PSS parameters in mozilla::pkix r=jschanck This patch adds support to mozilla::pkix for certificates signed with RSA-PSS using one of the following parameters permitted by the CA/Browser Forum Baseline Requirements 1.8.1: * SHA-256, MGF-1 with SHA-256, and a salt length of 32 bytes * SHA-384, MGF-1 with SHA-384, and a salt length of 48 bytes * SHA-512, MGF-1 with SHA-512, and a salt length of 64 bytes Differential Revision: https://phabricator.services.mozilla.com/D141539 --- .../pkixcheck_CheckSignatureAlgorithm_tests.cpp | 26 +++ gtests/mozpkix_gtest/pkixder_pki_types_tests.cpp | 110 +++++++++-- gtests/mozpkix_gtest/pkixgtest.h | 6 + gtests/mozpkix_gtest/pkixnss_tests.cpp | 217 +++++++++++++++++++++ lib/mozpkix/include/pkix/pkixder.h | 2 +- lib/mozpkix/include/pkix/pkixnss.h | 6 + lib/mozpkix/include/pkix/pkixtypes.h | 10 + lib/mozpkix/lib/pkixc.cpp | 7 + lib/mozpkix/lib/pkixcheck.cpp | 9 +- lib/mozpkix/lib/pkixder.cpp | 131 ++++++++++--- lib/mozpkix/lib/pkixnss.cpp | 56 +++++- lib/mozpkix/lib/pkixverify.cpp | 3 + lib/mozpkix/test-lib/pkixtestnss.cpp | 9 + 13 files changed, 542 insertions(+), 50 deletions(-) diff --git a/gtests/mozpkix_gtest/pkixcheck_CheckSignatureAlgorithm_tests.cpp b/gtests/mozpkix_gtest/pkixcheck_CheckSignatureAlgorithm_tests.cpp index 90bd6d777..bd61ef2a4 100644 --- a/gtests/mozpkix_gtest/pkixcheck_CheckSignatureAlgorithm_tests.cpp +++ b/gtests/mozpkix_gtest/pkixcheck_CheckSignatureAlgorithm_tests.cpp @@ -81,6 +81,18 @@ 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[] = { @@ -191,6 +203,20 @@ static const CheckSignatureAlgorithmTestParams (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 diff --git a/gtests/mozpkix_gtest/pkixder_pki_types_tests.cpp b/gtests/mozpkix_gtest/pkixder_pki_types_tests.cpp index bdde10ebb..fa5c55123 100644 --- a/gtests/mozpkix_gtest/pkixder_pki_types_tests.cpp +++ b/gtests/mozpkix_gtest/pkixder_pki_types_tests.cpp @@ -189,7 +189,7 @@ TEST_F(pkixder_pki_types_tests, OptionalVersionMissing) ASSERT_EQ(der::Version::v1, version); } -static const size_t MAX_ALGORITHM_OID_DER_LENGTH = 13; +static const size_t MAX_ALGORITHM_OID_DER_LENGTH = 65; struct InvalidAlgorithmIdentifierTestInfo { @@ -312,6 +312,7 @@ struct ValidSignatureAlgorithmIdentifierValueTestInfo DigestAlgorithm digestAlg; uint8_t der[MAX_ALGORITHM_OID_DER_LENGTH]; size_t derLength; + bool explicitNullAllowed; }; static const ValidSignatureAlgorithmIdentifierValueTestInfo @@ -323,44 +324,51 @@ static const ValidSignatureAlgorithmIdentifierValueTestInfo { 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04 }, 10, + true, }, { PublicKeyAlgorithm::ECDSA, DigestAlgorithm::sha384, { 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x03 }, 10, + true, }, { PublicKeyAlgorithm::ECDSA, DigestAlgorithm::sha256, { 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02 }, 10, + true, }, { PublicKeyAlgorithm::ECDSA, DigestAlgorithm::sha1, { 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x01 }, 9, + true, }, - // RSA + // RSA PKCS#1 1.5 { PublicKeyAlgorithm::RSA_PKCS1, DigestAlgorithm::sha512, { 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0d }, 11, + true, }, { PublicKeyAlgorithm::RSA_PKCS1, DigestAlgorithm::sha384, { 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0c }, 11, + true, }, { PublicKeyAlgorithm::RSA_PKCS1, DigestAlgorithm::sha256, { 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b }, 11, + true, }, { PublicKeyAlgorithm::RSA_PKCS1, DigestAlgorithm::sha1, @@ -368,6 +376,7 @@ static const ValidSignatureAlgorithmIdentifierValueTestInfo { 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05 }, 11, + true, }, { PublicKeyAlgorithm::RSA_PKCS1, DigestAlgorithm::sha1, @@ -375,6 +384,44 @@ static const ValidSignatureAlgorithmIdentifierValueTestInfo { 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1d }, 7, + true, + }, + + // RSA-PSS + { PublicKeyAlgorithm::RSA_PSS, + DigestAlgorithm::sha256, + { 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 }, + 65, + false, + }, + + { PublicKeyAlgorithm::RSA_PSS, + DigestAlgorithm::sha384, + { 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, 0x02, 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, 0x02, 0x05, 0x00, + 0xa2, 0x03, 0x02, 0x01, 0x30 }, + 65, + false, + }, + + { PublicKeyAlgorithm::RSA_PSS, + DigestAlgorithm::sha512, + { 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, 0x03, 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, 0x03, 0x05, 0x00, + 0xa2, 0x03, 0x02, 0x01, 0x40 }, + 65, + false, }, }; @@ -403,20 +450,22 @@ TEST_P(pkixder_SignatureAlgorithmIdentifierValue_Valid, Valid) ASSERT_EQ(Success, End(reader)); } - { - uint8_t derWithNullParam[MAX_ALGORITHM_OID_DER_LENGTH + 2]; - memcpy(derWithNullParam, param.der, param.derLength); - derWithNullParam[param.derLength] = 0x05; // NULL tag - derWithNullParam[param.derLength + 1] = 0x00; // length zero + uint8_t derWithNullParam[MAX_ALGORITHM_OID_DER_LENGTH + 2]; + memcpy(derWithNullParam, param.der, param.derLength); + 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); - PublicKeyAlgorithm publicKeyAlg; - DigestAlgorithm digestAlg; - ASSERT_EQ(Success, - SignatureAlgorithmIdentifierValue(reader, publicKeyAlg, - digestAlg)); + Input input; + ASSERT_EQ(Success, input.Init(derWithNullParam, param.derLength + 2)); + Reader reader(input); + PublicKeyAlgorithm publicKeyAlg; + DigestAlgorithm digestAlg; + ASSERT_EQ(param.explicitNullAllowed + ? Success + : Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED, + SignatureAlgorithmIdentifierValue(reader, publicKeyAlg, + digestAlg)); + if (param.explicitNullAllowed) { ASSERT_EQ(param.publicKeyAlg, publicKeyAlg); ASSERT_EQ(param.digestAlg, digestAlg); ASSERT_EQ(Success, End(reader)); @@ -455,6 +504,37 @@ static const InvalidAlgorithmIdentifierTestInfo 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 }, 11, }, + + // RSA-PSS with SHA-256, MGF-1 with SHA-256, and a salt length of 48 bytes + { { 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, 0x30 }, + 65, + }, + + // RSA-PSS with SHA-512, MGF-1 with SHA-256, and a salt length of 32 bytes + { { 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, 0x03, 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 }, + 65, + }, + + // RSA-PSS with omitted parameters + { { 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0a }, + 11, + }, + + // RSA-PSS with NULL parameters + { { 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0a, + 0x05, 0x00 }, + 13, + }, }; class pkixder_SignatureAlgorithmIdentifier_Invalid diff --git a/gtests/mozpkix_gtest/pkixgtest.h b/gtests/mozpkix_gtest/pkixgtest.h index 92f69350f..16b7b4983 100644 --- a/gtests/mozpkix_gtest/pkixgtest.h +++ b/gtests/mozpkix_gtest/pkixgtest.h @@ -155,6 +155,12 @@ class EverythingFailsByDefaultTrustDomain : public TrustDomain { Result::FATAL_ERROR_LIBRARY_FAILURE); } + Result VerifyRSAPSSSignedData(Input, DigestAlgorithm, Input, Input) override { + ADD_FAILURE(); + return NotReached("VerifyRSAPSSSignedData should not be called", + Result::FATAL_ERROR_LIBRARY_FAILURE); + } + Result CheckValidityIsAcceptable(Time, Time, EndEntityOrCA, KeyPurposeId) override { ADD_FAILURE(); diff --git a/gtests/mozpkix_gtest/pkixnss_tests.cpp b/gtests/mozpkix_gtest/pkixnss_tests.cpp index 7a556284a..ba14fa9c3 100644 --- a/gtests/mozpkix_gtest/pkixnss_tests.cpp +++ b/gtests/mozpkix_gtest/pkixnss_tests.cpp @@ -189,6 +189,140 @@ static const uint8_t kRsaPkcs1Sha512Signature[] = { 0xc2, 0xe9, 0x62, 0x07 }; +static const uint8_t kRsaPssSubjectPublicKeyInfo[] = { + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x0a, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, + 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xdb, 0x75, 0x02, + 0x7b, 0xeb, 0xf7, 0x3b, 0x31, 0x03, 0x71, 0x77, 0x34, 0x88, 0x8f, 0xb2, + 0x0d, 0xa6, 0xbe, 0x7d, 0xa7, 0xdd, 0xac, 0x0e, 0x99, 0x50, 0x46, 0x69, + 0x90, 0xe6, 0x7c, 0x3a, 0xa6, 0xf9, 0x3e, 0x02, 0x15, 0x3c, 0xf7, 0xb9, + 0xf4, 0xab, 0x3d, 0x54, 0x2c, 0x0d, 0x84, 0x94, 0x37, 0x95, 0xbc, 0x2b, + 0x56, 0x05, 0x00, 0xfa, 0xa2, 0x08, 0xf9, 0xcd, 0xc3, 0x2b, 0x9a, 0x58, + 0x80, 0x11, 0x49, 0xe0, 0x69, 0xf9, 0x81, 0x08, 0x52, 0x75, 0xb4, 0xc1, + 0x94, 0xa2, 0x67, 0x22, 0x5b, 0xfb, 0xe4, 0x74, 0xaa, 0x24, 0xb7, 0xa3, + 0x5e, 0x2c, 0x6b, 0xda, 0x20, 0x09, 0x5a, 0x5e, 0x4f, 0x95, 0xe8, 0x24, + 0x71, 0x64, 0x65, 0x29, 0x2c, 0x44, 0xb5, 0x17, 0xec, 0xe4, 0x68, 0xc3, + 0x69, 0x6b, 0x53, 0x6d, 0xa1, 0xa0, 0xb1, 0x74, 0xe2, 0x28, 0x03, 0xda, + 0x20, 0xca, 0xa4, 0x45, 0x1e, 0xf6, 0xab, 0xc7, 0xe9, 0xcb, 0xe3, 0x9a, + 0x16, 0x34, 0x8f, 0xd7, 0xf3, 0x66, 0x74, 0xea, 0xe7, 0x32, 0xf3, 0xd2, + 0x55, 0x6c, 0x8f, 0x38, 0xb8, 0x1b, 0x38, 0x08, 0x4c, 0x1f, 0x41, 0x74, + 0x35, 0x9e, 0x2d, 0x29, 0xed, 0x72, 0xe3, 0xda, 0x18, 0x01, 0xf4, 0x5f, + 0x8d, 0x9d, 0x72, 0x13, 0x18, 0x09, 0x1f, 0xbe, 0xb0, 0x20, 0x90, 0xc4, + 0x3d, 0x2c, 0x4f, 0xf2, 0xdc, 0x99, 0x8a, 0xae, 0x02, 0xd6, 0xef, 0x5a, + 0x88, 0x08, 0x15, 0x85, 0xdd, 0xaa, 0xce, 0xe4, 0x4b, 0x3f, 0xe9, 0xf4, + 0xfa, 0x54, 0xde, 0xb0, 0x30, 0xdf, 0x8f, 0x14, 0x2c, 0x49, 0x69, 0x24, + 0xe4, 0xa9, 0xeb, 0x62, 0x15, 0xf8, 0x8a, 0xd8, 0xe4, 0x8a, 0x99, 0x2b, + 0xdb, 0x68, 0x8b, 0x2a, 0x61, 0xbd, 0xc0, 0x57, 0xff, 0x5f, 0xee, 0xe9, + 0xac, 0x06, 0x77, 0x13, 0x7b, 0x2e, 0xd1, 0x76, 0x6c, 0xe8, 0x6c, 0x73, + 0x1f, 0x02, 0x03, 0x01, 0x00, 0x01 +}; + +// Use `openssl dgst -binary -sha1` to obtain the hash of the data, and then +// `openssl pkeyutl` with `-pkeyopt rsa_padding_mode:pss`, `-pkeyopt +// digest:sha1`, and `-pkeyopt rsa_pss_saltlen:-1` to create the signature. +static const uint8_t kRsaPssSha1Signature[] = { + 0xa9, 0xd9, 0x36, 0x19, 0x90, 0x3d, 0x58, 0xa6, 0x66, 0xae, 0x8e, 0xab, + 0xb5, 0x7d, 0xe7, 0x8c, 0xcf, 0x3c, 0x69, 0xd8, 0xba, 0x22, 0xe1, 0x35, + 0x9a, 0x88, 0xfd, 0xf1, 0x01, 0x75, 0x71, 0x05, 0x60, 0xcf, 0x05, 0x76, + 0x18, 0x38, 0xa6, 0x74, 0xaf, 0x36, 0x90, 0xb5, 0xad, 0x3c, 0xde, 0xe3, + 0x5f, 0x86, 0x3b, 0x38, 0x05, 0xa0, 0xae, 0xa2, 0x5c, 0xa9, 0xc8, 0x35, + 0xa1, 0x86, 0x95, 0xcb, 0xb0, 0xd8, 0x91, 0x27, 0x7f, 0x9b, 0x5f, 0xbe, + 0xd7, 0x99, 0x39, 0x88, 0x9a, 0xe8, 0xeb, 0x8a, 0x8b, 0xf2, 0xd3, 0x39, + 0xca, 0xbc, 0x29, 0x9c, 0xf4, 0x8d, 0x2d, 0x86, 0xbc, 0x13, 0xfc, 0xc4, + 0x9f, 0x1f, 0x65, 0x7c, 0x86, 0x10, 0x36, 0x8d, 0x43, 0xf5, 0x55, 0xa4, + 0xd2, 0x5e, 0x0a, 0x8f, 0x6a, 0x2e, 0x9e, 0x2f, 0xe2, 0xb9, 0x32, 0x4a, + 0x00, 0x5a, 0xc6, 0x1a, 0x75, 0x44, 0x59, 0x5f, 0xfc, 0xe3, 0xf6, 0xfb, + 0xc1, 0x95, 0x10, 0x18, 0x2c, 0xed, 0xfd, 0x23, 0x0c, 0x48, 0x14, 0xf9, + 0x5c, 0x04, 0x9b, 0xb7, 0x7a, 0x23, 0xf6, 0x82, 0x78, 0x92, 0xe9, 0xa8, + 0xc6, 0x1b, 0xa9, 0xad, 0xcc, 0x4a, 0xac, 0x56, 0x01, 0x02, 0x81, 0xa9, + 0xa4, 0x34, 0xb2, 0xdd, 0xd3, 0xc4, 0x01, 0x97, 0x84, 0x3f, 0x85, 0x2c, + 0x7e, 0x66, 0x4a, 0x98, 0x1f, 0xf5, 0x1e, 0x2e, 0x62, 0x09, 0x4e, 0x47, + 0xd1, 0x39, 0x93, 0x13, 0x53, 0x48, 0x90, 0xb4, 0xb8, 0x3f, 0x3c, 0x6b, + 0x87, 0x36, 0x24, 0x8f, 0xf5, 0xb8, 0x9d, 0xb2, 0x58, 0x8f, 0x55, 0xbd, + 0x2f, 0xf6, 0xce, 0x05, 0xd3, 0xc5, 0x6b, 0x84, 0x6c, 0x1b, 0x77, 0x00, + 0x7e, 0xfb, 0xa4, 0x01, 0x0f, 0xf5, 0x9d, 0x9a, 0xc7, 0xe3, 0xa4, 0xdb, + 0xac, 0x87, 0x3b, 0x50, 0x66, 0xd2, 0xf5, 0xaf, 0x51, 0x28, 0xb4, 0x5e, + 0x6e, 0xca, 0x74, 0xaa +}; + +// As above, but with sha256. +static const uint8_t kRsaPssSha256Signature[] = { + 0x34, 0x01, 0x2e, 0x0b, 0xba, 0x00, 0x2a, 0x66, 0x50, 0x5a, 0x6c, 0xf8, + 0x3b, 0x7f, 0xda, 0x43, 0xd1, 0x56, 0x75, 0xc8, 0xa9, 0x7a, 0xa9, 0x53, + 0xc8, 0xb3, 0xca, 0x92, 0x68, 0x8d, 0x68, 0x90, 0x93, 0xf3, 0xfd, 0xb3, + 0x66, 0x3d, 0x81, 0xe4, 0x6e, 0x7d, 0x46, 0x57, 0x86, 0x0d, 0xd5, 0x58, + 0x46, 0x27, 0x4f, 0xae, 0xab, 0x55, 0x1f, 0x25, 0xdd, 0x43, 0xac, 0x72, + 0x50, 0x0c, 0x24, 0x87, 0xbb, 0xf9, 0x08, 0x4a, 0x47, 0x43, 0xff, 0x7a, + 0x40, 0x8a, 0xd6, 0x69, 0x99, 0xe8, 0x65, 0x66, 0x4d, 0x73, 0x9f, 0xc5, + 0x43, 0x60, 0x44, 0xc6, 0xf8, 0x8a, 0xbd, 0xf2, 0x8a, 0xfc, 0x9f, 0x22, + 0xa6, 0x10, 0x4a, 0xa4, 0x8b, 0x70, 0x5b, 0xa1, 0x7c, 0x8c, 0x1a, 0xc3, + 0xf2, 0x84, 0xde, 0x03, 0x41, 0x7d, 0xe1, 0x38, 0x09, 0xa2, 0x78, 0x14, + 0xf4, 0x2c, 0xa6, 0xf6, 0x8a, 0x1e, 0xcc, 0x91, 0xbf, 0x6c, 0x14, 0xe4, + 0xc1, 0x65, 0xc7, 0x74, 0x89, 0x49, 0x24, 0x5c, 0xc6, 0x85, 0x58, 0x35, + 0x76, 0x9b, 0x9c, 0xa1, 0x3e, 0xef, 0xf7, 0x57, 0xc7, 0x18, 0x70, 0x98, + 0x1b, 0xa1, 0x12, 0xae, 0x69, 0x85, 0x1d, 0x0d, 0xa6, 0xec, 0x94, 0x7f, + 0xab, 0x77, 0x08, 0xfc, 0x89, 0x69, 0x9c, 0x51, 0xa1, 0x01, 0xd6, 0xc8, + 0xc2, 0xbf, 0xc7, 0x63, 0xa3, 0xce, 0xca, 0xc9, 0x11, 0xfd, 0x78, 0x62, + 0x5d, 0x80, 0x15, 0x10, 0x75, 0xcd, 0xd8, 0xc9, 0x7f, 0xd2, 0xcb, 0x32, + 0x35, 0x0d, 0xc0, 0x56, 0x0e, 0xa4, 0xa5, 0x2d, 0xe3, 0xeb, 0x1e, 0x30, + 0x60, 0x35, 0x1d, 0xe8, 0xb5, 0x27, 0xa1, 0x46, 0x61, 0x74, 0xb7, 0xf6, + 0x2c, 0x50, 0x4f, 0x23, 0xb3, 0xb0, 0x6b, 0xaf, 0x8d, 0x1a, 0x74, 0x27, + 0x58, 0x58, 0x25, 0xc9, 0x45, 0x84, 0xd8, 0x5d, 0xe8, 0x7e, 0xc6, 0x3e, + 0xd4, 0xf2, 0xb1, 0x3f +}; + +// As above, but with sha384. +static const uint8_t kRsaPssSha384Signature[] = { + 0x42, 0xd1, 0x90, 0xb2, 0x78, 0x76, 0x97, 0x5f, 0xf1, 0x4c, 0xab, 0x1b, + 0xe0, 0x80, 0xd6, 0x32, 0xac, 0x57, 0x93, 0x2e, 0x03, 0xb7, 0xec, 0x9f, + 0x48, 0x72, 0x91, 0x12, 0xb7, 0x2b, 0xaf, 0x69, 0xc6, 0x2d, 0xde, 0x6a, + 0xdc, 0xcb, 0xaf, 0xf8, 0x84, 0x2b, 0x13, 0xf9, 0xb9, 0x60, 0x76, 0x36, + 0x13, 0x63, 0xcb, 0xdd, 0x22, 0xed, 0xa2, 0x07, 0xac, 0x44, 0xee, 0x40, + 0x01, 0x19, 0x0e, 0xdd, 0x1d, 0xeb, 0x0a, 0xe7, 0xd2, 0x71, 0x1d, 0xea, + 0x66, 0x9f, 0x61, 0x76, 0xee, 0xbf, 0x50, 0xa1, 0x47, 0x17, 0x32, 0x58, + 0x03, 0x32, 0xd5, 0x39, 0x37, 0xee, 0x30, 0x9c, 0x87, 0xed, 0xc3, 0x07, + 0xde, 0x36, 0xc6, 0x65, 0x55, 0xd4, 0xd3, 0xbc, 0x4a, 0x83, 0x70, 0xd3, + 0x8d, 0xd2, 0xe0, 0x65, 0x8c, 0xe1, 0xd8, 0x40, 0xe2, 0x83, 0x52, 0x4f, + 0xf2, 0xe5, 0x16, 0x9a, 0x9b, 0x9b, 0xf2, 0x51, 0x97, 0xb0, 0x64, 0xf9, + 0x78, 0x07, 0xcc, 0x48, 0xe4, 0xe5, 0xeb, 0x69, 0x31, 0xe7, 0x10, 0xb9, + 0xa9, 0xdd, 0x06, 0xb0, 0x9e, 0x06, 0xed, 0xe1, 0x21, 0x3d, 0xcb, 0xfb, + 0x0b, 0x84, 0x78, 0xbb, 0x1c, 0x3c, 0x5e, 0x56, 0x29, 0xd8, 0x85, 0xe1, + 0x6b, 0x14, 0xee, 0xad, 0xf8, 0x8c, 0xb7, 0xfb, 0xdb, 0x48, 0x89, 0xa2, + 0x2f, 0x98, 0x4c, 0xcc, 0x62, 0xbe, 0x1d, 0x5e, 0xe2, 0x59, 0x11, 0x4b, + 0xfd, 0x93, 0xd0, 0xe2, 0x93, 0x9a, 0x8e, 0xc6, 0x97, 0x99, 0x6b, 0x9f, + 0x81, 0x96, 0x76, 0x78, 0x6f, 0xe9, 0xf3, 0x3d, 0xe2, 0xe2, 0xd6, 0x8a, + 0x3e, 0xb3, 0xff, 0x33, 0xfb, 0x1d, 0x54, 0xa9, 0xcd, 0xe8, 0x58, 0x34, + 0xd7, 0x9e, 0x5f, 0x0a, 0xcd, 0x38, 0xbd, 0x67, 0x2c, 0x4e, 0xbd, 0x11, + 0xed, 0x4a, 0xfd, 0x15, 0x35, 0x14, 0x0d, 0xfa, 0xd9, 0xa4, 0xf7, 0x6c, + 0x5f, 0x19, 0xbd, 0x1c +}; + +// As above, but with sha512. +static const uint8_t kRsaPssSha512Signature[] = { + 0xbf, 0xbc, 0xa6, 0x9d, 0x9b, 0x25, 0xd0, 0x0f, 0x13, 0x2c, 0xa4, 0x36, + 0x5d, 0xab, 0x06, 0xba, 0x7e, 0x0e, 0xc8, 0x30, 0x6e, 0x8b, 0x36, 0x19, + 0x28, 0xdd, 0x41, 0xc7, 0xba, 0x94, 0x0c, 0x0a, 0x46, 0x83, 0xdd, 0xdf, + 0xf0, 0x99, 0xd6, 0x07, 0xa9, 0xc4, 0x93, 0xd9, 0x1e, 0x98, 0xac, 0xa0, + 0xb2, 0x35, 0x95, 0x98, 0x89, 0x4f, 0x6b, 0x81, 0x0e, 0x4d, 0xc1, 0x94, + 0xb8, 0xc2, 0x4c, 0x33, 0xd2, 0x2b, 0xd6, 0xbd, 0xb8, 0x2f, 0x04, 0x24, + 0x64, 0x94, 0xfe, 0x02, 0x36, 0x83, 0xce, 0xe1, 0x47, 0xf3, 0xb3, 0x4a, + 0xc0, 0x7e, 0xe4, 0x6b, 0x3b, 0x0d, 0xcd, 0x83, 0x6e, 0x93, 0xd2, 0x5e, + 0x40, 0xdb, 0xe1, 0xe2, 0x08, 0xd1, 0xc3, 0x7a, 0xfb, 0xdf, 0xe5, 0x91, + 0x01, 0xb2, 0xd9, 0xcd, 0x15, 0xeb, 0x15, 0xcc, 0xf4, 0xa6, 0xdd, 0x87, + 0x31, 0x9b, 0xcb, 0x19, 0x38, 0x00, 0x99, 0xfe, 0xcb, 0xfb, 0x41, 0xda, + 0xce, 0x45, 0xf2, 0x65, 0x5b, 0x3b, 0x9d, 0x34, 0x7e, 0x48, 0x40, 0x63, + 0x67, 0xfa, 0xbe, 0x34, 0xaf, 0xf5, 0x69, 0xa3, 0x40, 0x8d, 0x1b, 0xae, + 0x84, 0x0f, 0x3b, 0x1f, 0xc4, 0x00, 0x8b, 0x8a, 0x64, 0x9f, 0xca, 0xfc, + 0x0c, 0x58, 0x6e, 0xfd, 0x8a, 0xb5, 0x11, 0x3d, 0x2b, 0x7d, 0xf1, 0xdd, + 0x08, 0xe9, 0x1d, 0x27, 0x18, 0x1e, 0x31, 0xc1, 0xec, 0x10, 0xe6, 0x5b, + 0x39, 0xcc, 0x9f, 0x2e, 0x9c, 0x41, 0x4c, 0x47, 0x77, 0x36, 0x5f, 0x2c, + 0x5b, 0x8c, 0x16, 0x96, 0x7f, 0x1f, 0xa8, 0x30, 0x3b, 0x34, 0x2f, 0xa2, + 0x44, 0x41, 0x49, 0x68, 0x3a, 0x75, 0xa5, 0xb4, 0xde, 0xd2, 0x89, 0x72, + 0x7c, 0x6e, 0x83, 0x54, 0x30, 0x9c, 0x1e, 0x9b, 0x58, 0xb0, 0xa3, 0x2c, + 0xdf, 0x3d, 0x69, 0xff, 0xe1, 0x4c, 0x28, 0x4a, 0x19, 0xec, 0x0b, 0x4c, + 0x0a, 0x7e, 0xb7, 0xd4 +}; + static const uint8_t kEC256SubjectPublicKeyInfo[] = { 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, @@ -311,6 +445,39 @@ static const VerifySignedDataNSSTestParams BS(kRsaSubjectPublicKeyInfo), Success, }, + // SHA-1 is not allowed for RSA-PSS + { + BS(kData), + DigestAlgorithm::sha1, + BS(kRsaPssSha1Signature), + PublicKeyAlgorithm::RSA_PSS, + BS(kRsaPssSubjectPublicKeyInfo), + Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED, + }, + { + BS(kData), + DigestAlgorithm::sha256, + BS(kRsaPssSha256Signature), + PublicKeyAlgorithm::RSA_PSS, + BS(kRsaPssSubjectPublicKeyInfo), + Success, + }, + { + BS(kData), + DigestAlgorithm::sha384, + BS(kRsaPssSha384Signature), + PublicKeyAlgorithm::RSA_PSS, + BS(kRsaPssSubjectPublicKeyInfo), + Success, + }, + { + BS(kData), + DigestAlgorithm::sha512, + BS(kRsaPssSha512Signature), + PublicKeyAlgorithm::RSA_PSS, + BS(kRsaPssSubjectPublicKeyInfo), + Success, + }, { BS(kData), DigestAlgorithm::sha1, @@ -352,6 +519,15 @@ static const VerifySignedDataNSSTestParams BS(kRsaSubjectPublicKeyInfo), Result::ERROR_BAD_SIGNATURE, }, + // Wrong digest algorithm - RSA PSS + { + BS(kData), + DigestAlgorithm::sha512, + BS(kRsaPssSha384Signature), + PublicKeyAlgorithm::RSA_PSS, + BS(kRsaPssSubjectPublicKeyInfo), + Result::ERROR_BAD_SIGNATURE, + }, // Wrong digest algorithm - ECDSA { BS(kData), @@ -379,6 +555,42 @@ static const VerifySignedDataNSSTestParams BS(kRsaSubjectPublicKeyInfo), Result::ERROR_BAD_SIGNATURE, }, + // RSA PKCS#1 key for RSA PSS signature + { + BS(kData), + DigestAlgorithm::sha256, + BS(kRsaPssSha256Signature), + PublicKeyAlgorithm::RSA_PKCS1, + BS(kRsaSubjectPublicKeyInfo), + Result::ERROR_BAD_SIGNATURE, + }, + // RSA PSS key for RSA PKCS#1 signature + { + BS(kData), + DigestAlgorithm::sha256, + BS(kRsaPkcs1Sha256Signature), + PublicKeyAlgorithm::RSA_PSS, + BS(kRsaPssSubjectPublicKeyInfo), + Result::ERROR_BAD_SIGNATURE, + }, + // ECDSA key for RSA PSS signature + { + BS(kData), + DigestAlgorithm::sha256, + BS(kRsaPssSha256Signature), + PublicKeyAlgorithm::ECDSA, + BS(kEC256SubjectPublicKeyInfo), + Result::ERROR_BAD_SIGNATURE, + }, + // RSA PSS key for ECDSA signature + { + BS(kData), + DigestAlgorithm::sha256, + BS(kEC256Signature), + PublicKeyAlgorithm::RSA_PSS, + BS(kRsaPssSubjectPublicKeyInfo), + Result::ERROR_BAD_SIGNATURE, + }, // Wrong data. { BS(kRsaSubjectPublicKeyInfo), @@ -406,6 +618,11 @@ void CheckVerifySignedData(PublicKeyAlgorithm publicKeyAlgorithm, Input data, VerifyRSAPKCS1SignedDataNSS(data, digestAlgorithm, signature, subjectPublicKeyInfo, nullptr)); break; + case PublicKeyAlgorithm::RSA_PSS: + ASSERT_EQ(expectedResult, + VerifyRSAPSSSignedDataNSS(data, digestAlgorithm, signature, + subjectPublicKeyInfo, nullptr)); + break; case PublicKeyAlgorithm::ECDSA: ASSERT_EQ(expectedResult, VerifyECDSASignedDataNSS(data, digestAlgorithm, signature, diff --git a/lib/mozpkix/include/pkix/pkixder.h b/lib/mozpkix/include/pkix/pkixder.h index 1b0d08e0d..ee3003b58 100644 --- a/lib/mozpkix/include/pkix/pkixder.h +++ b/lib/mozpkix/include/pkix/pkixder.h @@ -488,7 +488,7 @@ inline Result OptionalExtensions(Reader& input, uint8_t tag, Result DigestAlgorithmIdentifier(Reader& input, /*out*/ DigestAlgorithm& algorithm); -enum class PublicKeyAlgorithm { RSA_PKCS1, ECDSA }; +enum class PublicKeyAlgorithm { RSA_PKCS1, RSA_PSS, ECDSA }; Result SignatureAlgorithmIdentifierValue( Reader& input, diff --git a/lib/mozpkix/include/pkix/pkixnss.h b/lib/mozpkix/include/pkix/pkixnss.h index 4c5f0a322..c1050c649 100644 --- a/lib/mozpkix/include/pkix/pkixnss.h +++ b/lib/mozpkix/include/pkix/pkixnss.h @@ -38,6 +38,12 @@ Result VerifyRSAPKCS1SignedDataNSS(Input data, DigestAlgorithm digestAlgorithm, Input signature, Input subjectPublicKeyInfo, void* pkcs11PinArg); +// Verifies the RSA-PSS signature on the given data using the given RSA +// public key. +Result VerifyRSAPSSSignedDataNSS(Input data, DigestAlgorithm digestAlgorithm, + Input signature, Input subjectPublicKeyInfo, + void* pkcs11PinArg); + // Verifies the ECDSA signature on the given data using the given ECC public // key. Result VerifyECDSASignedDataNSS(Input data, DigestAlgorithm digestAlgorithm, diff --git a/lib/mozpkix/include/pkix/pkixtypes.h b/lib/mozpkix/include/pkix/pkixtypes.h index 18f52b472..48c11c3a6 100644 --- a/lib/mozpkix/include/pkix/pkixtypes.h +++ b/lib/mozpkix/include/pkix/pkixtypes.h @@ -304,6 +304,16 @@ class TrustDomain { Input signature, Input subjectPublicKeyInfo) = 0; + // Verify the given RSA-PSS 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. + virtual Result VerifyRSAPSSSignedData(Input data, + DigestAlgorithm digestAlgorithm, + Input signature, + Input subjectPublicKeyInfo) = 0; + // Check that the given named ECC curve is acceptable for ECDSA signatures. // // Return Success if the curve is acceptable, diff --git a/lib/mozpkix/lib/pkixc.cpp b/lib/mozpkix/lib/pkixc.cpp index 5b30af33f..256a5c8fb 100644 --- a/lib/mozpkix/lib/pkixc.cpp +++ b/lib/mozpkix/lib/pkixc.cpp @@ -117,6 +117,13 @@ class CodeSigningTrustDomain final : public TrustDomain { subjectPublicKeyInfo, nullptr); } + virtual Result VerifyRSAPSSSignedData( + Input data, DigestAlgorithm digestAlgorithm, Input signature, + Input subjectPublicKeyInfo) override { + return VerifyRSAPSSSignedDataNSS(data, digestAlgorithm, signature, + subjectPublicKeyInfo, nullptr); + } + virtual Result CheckECDSACurveIsAcceptable(EndEntityOrCA endEntityOrCA, NamedCurve curve) override { switch (curve) { diff --git a/lib/mozpkix/lib/pkixcheck.cpp b/lib/mozpkix/lib/pkixcheck.cpp index a0079f305..9b255b3a1 100644 --- a/lib/mozpkix/lib/pkixcheck.cpp +++ b/lib/mozpkix/lib/pkixcheck.cpp @@ -100,12 +100,13 @@ CheckSignatureAlgorithm(TrustDomain& trustDomain, switch (publicKeyAlg) { case der::PublicKeyAlgorithm::RSA_PKCS1: + case der::PublicKeyAlgorithm::RSA_PSS: { // The RSA computation may give a result that requires fewer bytes to - // encode than the public key (since it is modular arithmetic). However, - // the last step of generating a PKCS#1.5 signature is the I2OSP - // procedure, which pads any such shorter result with zeros so that it - // is exactly the same length as the public key. + // encode than the modulus (since it is modular arithmetic). However, + // the last step of generating a RSA-PKCS#1.5 or -PSS signature is the + // I2OSP procedure, which pads any such shorter result with zeros so that + // it is exactly the same length as the modulus. unsigned int signatureSizeInBits = signedData.signature.GetLength() * 8u; return trustDomain.CheckRSAPublicKeyModulusSizeInBits( endEntityOrCA, signatureSizeInBits); diff --git a/lib/mozpkix/lib/pkixder.cpp b/lib/mozpkix/lib/pkixder.cpp index 9dccc9639..59454c7d3 100644 --- a/lib/mozpkix/lib/pkixder.cpp +++ b/lib/mozpkix/lib/pkixder.cpp @@ -83,29 +83,6 @@ ReadTagAndGetValue(Reader& input, /*out*/ uint8_t& tag, /*out*/ Input& value) return input.Skip(length, value); } -static Result -OptionalNull(Reader& input) -{ - if (input.Peek(NULLTag)) { - return Null(input); - } - return Success; -} - -namespace { - -Result -AlgorithmIdentifierValue(Reader& input, /*out*/ Reader& algorithmOIDValue) -{ - Result rv = ExpectTagAndGetValue(input, der::OIDTag, algorithmOIDValue); - if (rv != Success) { - return rv; - } - return OptionalNull(input); -} - -} // namespace - Result SignatureAlgorithmIdentifierValue(Reader& input, /*out*/ PublicKeyAlgorithm& publicKeyAlgorithm, @@ -116,13 +93,39 @@ SignatureAlgorithmIdentifierValue(Reader& input, // // 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. + // NULL to be omitted. + // + // RFC 8017 Appendix A.2.3 specifies the format of the parameters for + // RSA-PSS. The CA/Browser Forum Baseline Requirements (as of 1.8.1) + // specify three specific parameters that may be used with RSA-PSS: + // * SHA-256, MGF-1 with SHA-256, and a salt length of 32 bytes + // * SHA-384, MGF-1 with SHA-384, and a salt length of 48 bytes + // * SHA-512, MGF-1 with SHA-512, and a salt length of 64 bytes + // as well as the corresponding specific encodings that must be used + // for the entire AlgorithmIdentifier: + // * 304106092a864886f70d01010a3034a00f300d0609608648016503040201 + // 0500a11c301a06092a864886f70d010108300d0609608648016503040201 + // 0500a203020120 + // * 304106092a864886f70d01010a3034a00f300d0609608648016503040202 + // 0500a11c301a06092a864886f70d010108300d0609608648016503040202 + // 0500a203020130 + // * 304106092a864886f70d01010a3034a00f300d0609608648016503040203 + // 0500a11c301a06092a864886f70d010108300d0609608648016503040203 + // 0500a203020140 + // Note that these encodings include the outer SEQUENCE and length bytes; + // in this implementation, the caller has already validated those bytes. + // Currently these are the only sets of parameters mozilla::pkix supports. Reader algorithmID; - Result rv = AlgorithmIdentifierValue(input, algorithmID); + Result rv = ExpectTagAndGetValue(input, der::OIDTag, algorithmID); + if (rv != Success) { + return rv; + } + Input algorithmParamsInput; + rv = input.SkipToEnd(algorithmParamsInput); if (rv != Success) { return rv; } + Reader algorithmParams(algorithmParamsInput); // RFC 5758 Section 3.2 (ecdsa-with-SHA224 is intentionally excluded) // python DottedOIDToCode.py ecdsa-with-SHA256 1.2.840.10045.4.3.2 @@ -173,6 +176,41 @@ SignatureAlgorithmIdentifierValue(Reader& input, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x01 }; + // RFC 8017 Appendix A.2.3 + // python DottedOIDToCode.py id-RSA-PSS 1.2.840.113549.1.1.10 + static const uint8_t id_RSA_PSS[] = { + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0a + }; + + // 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 rsaPSSWithSHA256MGF1WithSHA256Salt32[] = { + 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 + }; + // Params for RSA-PSS with SHA-384, MGF-1 with SHA-384, and a salt length + // of 48 bytes: + static const uint8_t rsaPSSWithSHA384MGF1WithSHA384Salt48[] = { + 0x30, 0x34, 0xa0, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, + 0x65, 0x03, 0x04, 0x02, 0x02, 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, 0x02, 0x05, + 0x00, 0xa2, 0x03, 0x02, 0x01, 0x30 + }; + // Params for RSA-PSS with SHA-512, MGF-1 with SHA-512, and a salt length + // of 64 bytes: + static const uint8_t rsaPSSWithSHA512MGF1WithSHA512Salt64[] = { + 0x30, 0x34, 0xa0, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, + 0x65, 0x03, 0x04, 0x02, 0x03, 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, 0x03, 0x05, + 0x00, 0xa2, 0x03, 0x02, 0x01, 0x40 + }; + // 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)) { @@ -200,25 +238,62 @@ SignatureAlgorithmIdentifierValue(Reader& input, publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1; digestAlgorithm = DigestAlgorithm::sha512; } else if (algorithmID.MatchRest(sha1WithRSASignature)) { - // XXX(bug 1042479): recognize this old OID for compatibility. + // This old OID is recognized for compatibility (see bug 1042479). publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1; digestAlgorithm = DigestAlgorithm::sha1; + } else if (algorithmID.MatchRest(id_RSA_PSS)) { + publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PSS; + if (algorithmParams.MatchRest(rsaPSSWithSHA256MGF1WithSHA256Salt32)) { + digestAlgorithm = DigestAlgorithm::sha256; + } else if (algorithmParams.MatchRest(rsaPSSWithSHA384MGF1WithSHA384Salt48)) { + digestAlgorithm = DigestAlgorithm::sha384; + } else if (algorithmParams.MatchRest(rsaPSSWithSHA512MGF1WithSHA512Salt64)) { + digestAlgorithm = DigestAlgorithm::sha512; + } else { + return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED; + } } else { return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED; } + // Ensure that for non-RSA-PSS algorithms, the params are NULL or omitted. + if (publicKeyAlgorithm != PublicKeyAlgorithm::RSA_PSS) { + if (algorithmParams.Peek(NULLTag)) { + rv = Null(algorithmParams); + if (rv != Success) { + return rv; + } + } + if (!algorithmParams.AtEnd()) { + return Result::ERROR_BAD_DER; + } + } + return Success; } Result DigestAlgorithmIdentifier(Reader& input, /*out*/ DigestAlgorithm& algorithm) { + // AlgorithmIdentifier ::= SEQUENCE { + // algorithm OBJECT IDENTIFIER, + // parameters ANY DEFINED BY algorithm OPTIONAL } return der::Nested(input, SEQUENCE, [&algorithm](Reader& r) -> Result { Reader algorithmID; - Result rv = AlgorithmIdentifierValue(r, algorithmID); + Result rv = ExpectTagAndGetValue(r, der::OIDTag, algorithmID); if (rv != Success) { return rv; } + // The parameters are expected to be NULL or omitted. + if (r.Peek(NULLTag)) { + rv = Null(r); + if (rv != Success) { + return rv; + } + } + if (!r.AtEnd()) { + return Result::ERROR_BAD_DER; + } // RFC 4055 Section 2.1 // python DottedOIDToCode.py id-sha1 1.3.14.3.2.26 diff --git a/lib/mozpkix/lib/pkixnss.cpp b/lib/mozpkix/lib/pkixnss.cpp index 06c1bdcbd..98575c852 100644 --- a/lib/mozpkix/lib/pkixnss.cpp +++ b/lib/mozpkix/lib/pkixnss.cpp @@ -57,10 +57,11 @@ SubjectPublicKeyInfoToSECKEYPublicKey(Input subjectPublicKeyInfo, return Success; } +template Result VerifySignedData(SECKEYPublicKey* publicKey, CK_MECHANISM_TYPE mechanism, SECItem* params, SECItem* signature, SECItem* data, - SECOidTag (&policyTags)[3], void* pkcs11PinArg) + SECOidTag (&policyTags)[N], void* pkcs11PinArg) { // Hash and signature algorithms can be disabled by policy in NSS. However, // the policy engine in NSS is not currently sophisticated enough to, for @@ -75,7 +76,7 @@ VerifySignedData(SECKEYPublicKey* publicKey, CK_MECHANISM_TYPE mechanism, return MapPRErrorCodeToResult(PR_GetError()); } if (!(policyFlags & NSS_USE_ALG_IN_ANY_SIGNATURE)) { - return MapPRErrorCodeToResult(SEC_ERROR_SIGNATURE_ALGORITHM_DISABLED); + return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED; } } SECStatus srv = PK11_VerifyWithMechanism(publicKey, mechanism, params, @@ -132,6 +133,57 @@ VerifyRSAPKCS1SignedDataNSS(Input data, DigestAlgorithm digestAlgorithm, &dataItem, policyTags, pkcs11PinArg); } +Result +VerifyRSAPSSSignedDataNSS(Input data, DigestAlgorithm digestAlgorithm, + Input signature, Input subjectPublicKeyInfo, void* pkcs11PinArg) +{ + ScopedSECKEYPublicKey publicKey; + Result rv = SubjectPublicKeyInfoToSECKEYPublicKey(subjectPublicKeyInfo, + publicKey); + if (rv != Success) { + return rv; + } + SECItem signatureItem(UnsafeMapInputToSECItem(signature)); + SECItem dataItem(UnsafeMapInputToSECItem(data)); + CK_MECHANISM_TYPE mechanism; + SECOidTag signaturePolicyTag = SEC_OID_PKCS1_RSA_PSS_SIGNATURE; + SECOidTag hashPolicyTag; + CK_RSA_PKCS_PSS_PARAMS rsaPSSParams; + switch (digestAlgorithm) { + case DigestAlgorithm::sha512: + mechanism = CKM_SHA512_RSA_PKCS_PSS; + hashPolicyTag = SEC_OID_SHA512; + rsaPSSParams.hashAlg = CKM_SHA512; + rsaPSSParams.mgf = CKG_MGF1_SHA512; + rsaPSSParams.sLen = 64; + break; + case DigestAlgorithm::sha384: + mechanism = CKM_SHA384_RSA_PKCS_PSS; + hashPolicyTag = SEC_OID_SHA384; + rsaPSSParams.hashAlg = CKM_SHA384; + rsaPSSParams.mgf = CKG_MGF1_SHA384; + rsaPSSParams.sLen = 48; + break; + case DigestAlgorithm::sha256: + mechanism = CKM_SHA256_RSA_PKCS_PSS; + hashPolicyTag = SEC_OID_SHA256; + rsaPSSParams.hashAlg = CKM_SHA256; + rsaPSSParams.mgf = CKG_MGF1_SHA256; + rsaPSSParams.sLen = 32; + break; + case DigestAlgorithm::sha1: + return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED; + break; + MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM + } + SECItem params; + params.data = reinterpret_cast(&rsaPSSParams); + params.len = sizeof(CK_RSA_PKCS_PSS_PARAMS); + SECOidTag policyTags[2] = { signaturePolicyTag, hashPolicyTag }; + return VerifySignedData(publicKey.get(), mechanism, ¶ms, &signatureItem, + &dataItem, policyTags, pkcs11PinArg); +} + Result EncodedECDSASignatureToRawPoint(Input signature, const ScopedSECKEYPublicKey& publicKey, ScopedSECItem& result) { diff --git a/lib/mozpkix/lib/pkixverify.cpp b/lib/mozpkix/lib/pkixverify.cpp index 5658386d2..8cb58bf7d 100644 --- a/lib/mozpkix/lib/pkixverify.cpp +++ b/lib/mozpkix/lib/pkixverify.cpp @@ -50,6 +50,9 @@ VerifySignedData(TrustDomain& trustDomain, case der::PublicKeyAlgorithm::RSA_PKCS1: return trustDomain.VerifyRSAPKCS1SignedData(signedData.data, digestAlgorithm, signedData.signature, signerSubjectPublicKeyInfo); + case der::PublicKeyAlgorithm::RSA_PSS: + return trustDomain.VerifyRSAPSSSignedData(signedData.data, + digestAlgorithm, signedData.signature, signerSubjectPublicKeyInfo); MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM } } diff --git a/lib/mozpkix/test-lib/pkixtestnss.cpp b/lib/mozpkix/test-lib/pkixtestnss.cpp index 45edf3ea7..e8e082ab6 100644 --- a/lib/mozpkix/test-lib/pkixtestnss.cpp +++ b/lib/mozpkix/test-lib/pkixtestnss.cpp @@ -352,6 +352,15 @@ TestVerifyRSAPKCS1SignedData(Input data, DigestAlgorithm digestAlgorithm, subjectPublicKeyInfo, nullptr); } +Result +TestVerifyRSAPSSSignedData(Input data, DigestAlgorithm digestAlgorithm, + Input signature, Input subjectPublicKeyInfo) +{ + InitNSSIfNeeded(); + return VerifyRSAPSSSignedDataNSS(data, digestAlgorithm, signature, + subjectPublicKeyInfo, nullptr); +} + Result TestDigestBuf(Input item, DigestAlgorithm digestAlg, -- cgit v1.2.1