diff --git a/cmd/certutil/certutil.c b/cmd/certutil/certutil.c index 9bbb940b8f..b56724db49 100644 --- a/cmd/certutil/certutil.c +++ b/cmd/certutil/certutil.c @@ -194,6 +194,8 @@ CertReq(SECKEYPrivateKey *privk, SECKEYPublicKey *pubk, KeyType keyType, PLArenaPool *arena; void *extHandle; SECItem signedReq = { siBuffer, NULL, 0 }; + SECAlgorithmID signAlg; + SECItem *params = NULL; arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (!arena) { @@ -211,11 +213,25 @@ CertReq(SECKEYPrivateKey *privk, SECKEYPublicKey *pubk, KeyType keyType, /* Change cert type to RSA-PSS, if desired. */ if (pssCertificate) { + params = SEC_CreateSignatureAlgorithmParameters(arena, + NULL, + SEC_OID_PKCS1_RSA_PSS_SIGNATURE, + hashAlgTag, + NULL, + privk); + if (!params) { + PORT_FreeArena(arena, PR_FALSE); + SECKEY_DestroySubjectPublicKeyInfo(spki); + SECU_PrintError(progName, "unable to create RSA-PSS parameters"); + return SECFailure; + } + spki->algorithm.parameters.data = NULL; rv = SECOID_SetAlgorithmID(arena, &spki->algorithm, - SEC_OID_PKCS1_RSA_PSS_SIGNATURE, 0); + SEC_OID_PKCS1_RSA_PSS_SIGNATURE, params); if (rv != SECSuccess) { PORT_FreeArena(arena, PR_FALSE); + SECKEY_DestroySubjectPublicKeyInfo(spki); SECU_PrintError(progName, "unable to set algorithm ID"); return SECFailure; } @@ -256,16 +272,34 @@ CertReq(SECKEYPrivateKey *privk, SECKEYPublicKey *pubk, KeyType keyType, return SECFailure; } - /* Sign the request */ - signAlgTag = SEC_GetSignatureAlgorithmOidTag(keyType, hashAlgTag); - if (signAlgTag == SEC_OID_UNKNOWN) { - PORT_FreeArena(arena, PR_FALSE); - SECU_PrintError(progName, "unknown Key or Hash type"); - return SECFailure; + PORT_Memset(&signAlg, 0, sizeof(signAlg)); + if (pssCertificate) { + rv = SECOID_SetAlgorithmID(arena, &signAlg, + SEC_OID_PKCS1_RSA_PSS_SIGNATURE, params); + if (rv != SECSuccess) { + PORT_FreeArena(arena, PR_FALSE); + SECU_PrintError(progName, "unable to set algorithm ID"); + return SECFailure; + } + } else { + signAlgTag = SEC_GetSignatureAlgorithmOidTag(keyType, hashAlgTag); + if (signAlgTag == SEC_OID_UNKNOWN) { + PORT_FreeArena(arena, PR_FALSE); + SECU_PrintError(progName, "unknown Key or Hash type"); + return SECFailure; + } + rv = SECOID_SetAlgorithmID(arena, &signAlg, signAlgTag, 0); + if (rv != SECSuccess) { + PORT_FreeArena(arena, PR_FALSE); + SECU_PrintError(progName, "unable to set algorithm ID"); + return SECFailure; + } } - rv = SEC_DerSignData(arena, &signedReq, encoding->data, encoding->len, - privk, signAlgTag); + /* Sign the request */ + rv = SEC_DerSignDataWithAlgorithmID(arena, &signedReq, + encoding->data, encoding->len, + privk, &signAlg); if (rv) { PORT_FreeArena(arena, PR_FALSE); SECU_PrintError(progName, "signing of data failed"); @@ -1864,47 +1898,120 @@ MakeV1Cert(CERTCertDBHandle *handle, return (cert); } +static SECStatus +SetSignatureAlgorithm(PLArenaPool *arena, + SECAlgorithmID *signAlg, + SECAlgorithmID *spkiAlg, + SECOidTag hashAlgTag, + SECKEYPrivateKey *privKey, + PRBool pssSign) +{ + SECStatus rv; + + if (pssSign || + SECOID_GetAlgorithmTag(spkiAlg) == SEC_OID_PKCS1_RSA_PSS_SIGNATURE) { + SECItem *srcParams; + SECItem *params; + + if (SECOID_GetAlgorithmTag(spkiAlg) == SEC_OID_PKCS1_RSA_PSS_SIGNATURE) { + srcParams = &spkiAlg->parameters; + } else { + /* If the issuer's public key is RSA, the parameter field + * of the SPKI should be NULL, which can't be used as a + * basis of RSA-PSS parameters. */ + srcParams = NULL; + } + params = SEC_CreateSignatureAlgorithmParameters(arena, + NULL, + SEC_OID_PKCS1_RSA_PSS_SIGNATURE, + hashAlgTag, + srcParams, + privKey); + if (!params) { + SECU_PrintError(progName, "Could not create RSA-PSS parameters"); + return SECFailure; + } + rv = SECOID_SetAlgorithmID(arena, signAlg, + SEC_OID_PKCS1_RSA_PSS_SIGNATURE, + params); + if (rv != SECSuccess) { + SECU_PrintError(progName, "Could not set signature algorithm id."); + return rv; + } + } else { + KeyType keyType = SECKEY_GetPrivateKeyType(privKey); + SECOidTag algID; + + algID = SEC_GetSignatureAlgorithmOidTag(keyType, hashAlgTag); + if (algID == SEC_OID_UNKNOWN) { + SECU_PrintError(progName, "Unknown key or hash type for issuer."); + return SECFailure; + } + rv = SECOID_SetAlgorithmID(arena, signAlg, algID, 0); + if (rv != SECSuccess) { + SECU_PrintError(progName, "Could not set signature algorithm id."); + return rv; + } + } + return SECSuccess; +} + static SECStatus SignCert(CERTCertDBHandle *handle, CERTCertificate *cert, PRBool selfsign, SECOidTag hashAlgTag, SECKEYPrivateKey *privKey, char *issuerNickName, - int certVersion, void *pwarg) + int certVersion, PRBool pssSign, void *pwarg) { SECItem der; SECKEYPrivateKey *caPrivateKey = NULL; SECStatus rv; PLArenaPool *arena; - SECOidTag algID; + CERTCertificate *issuer; void *dummy; - if (!selfsign) { - CERTCertificate *issuer = PK11_FindCertFromNickname(issuerNickName, pwarg); + arena = cert->arena; + + if (selfsign) { + issuer = cert; + } else { + issuer = PK11_FindCertFromNickname(issuerNickName, pwarg); if ((CERTCertificate *)NULL == issuer) { SECU_PrintError(progName, "unable to find issuer with nickname %s", issuerNickName); - return SECFailure; + rv = SECFailure; + goto done; } - privKey = caPrivateKey = PK11_FindKeyByAnyCert(issuer, pwarg); - CERT_DestroyCertificate(issuer); if (caPrivateKey == NULL) { SECU_PrintError(progName, "unable to retrieve key %s", issuerNickName); - return SECFailure; + rv = SECFailure; + CERT_DestroyCertificate(issuer); + goto done; } } - arena = cert->arena; - - algID = SEC_GetSignatureAlgorithmOidTag(privKey->keyType, hashAlgTag); - if (algID == SEC_OID_UNKNOWN) { - fprintf(stderr, "Unknown key or hash type for issuer."); + if (pssSign && + (SECKEY_GetPrivateKeyType(privKey) != rsaKey && + SECKEY_GetPrivateKeyType(privKey) != rsaPssKey)) { + SECU_PrintError(progName, "unable to create RSA-PSS signature with key %s", + issuerNickName); rv = SECFailure; + if (!selfsign) { + CERT_DestroyCertificate(issuer); + } goto done; } - rv = SECOID_SetAlgorithmID(arena, &cert->signature, algID, 0); + rv = SetSignatureAlgorithm(arena, + &cert->signature, + &issuer->subjectPublicKeyInfo.algorithm, + hashAlgTag, + privKey, + pssSign); + if (!selfsign) { + CERT_DestroyCertificate(issuer); + } if (rv != SECSuccess) { - fprintf(stderr, "Could not set signature algorithm id."); goto done; } @@ -1923,7 +2030,8 @@ SignCert(CERTCertDBHandle *handle, CERTCertificate *cert, PRBool selfsign, break; default: PORT_SetError(SEC_ERROR_INVALID_ARGS); - return SECFailure; + rv = SECFailure; + goto done; } der.len = 0; @@ -1936,7 +2044,8 @@ SignCert(CERTCertDBHandle *handle, CERTCertificate *cert, PRBool selfsign, goto done; } - rv = SEC_DerSignData(arena, &cert->derCert, der.data, der.len, privKey, algID); + rv = SEC_DerSignDataWithAlgorithmID(arena, &cert->derCert, der.data, der.len, + privKey, &cert->signature); if (rv != SECSuccess) { fprintf(stderr, "Could not sign encoded certificate data.\n"); /* result allocated out of the arena, it will be freed @@ -1969,6 +2078,7 @@ CreateCert( certutilExtnList extnList, const char *extGeneric, int certVersion, + PRBool pssSign, SECItem *certDER) { void *extHandle = NULL; @@ -2029,7 +2139,7 @@ CreateCert( rv = SignCert(handle, subjectCert, selfsign, hashAlgTag, *selfsignprivkey, issuerNickName, - certVersion, pwarg); + certVersion, pssSign, pwarg); if (rv != SECSuccess) break; @@ -2352,6 +2462,7 @@ enum certutilOpts { opt_GenericExtensions, opt_NewNickname, opt_Pss, + opt_PssSign, opt_Help }; @@ -2472,6 +2583,8 @@ static const secuCommandFlag options_init[] = "new-n" }, { /* opt_Pss */ 0, PR_FALSE, 0, PR_FALSE, "pss" }, + { /* opt_PssSign */ 0, PR_FALSE, 0, PR_FALSE, + "pss-sign" }, }; #define NUM_OPTIONS ((sizeof options_init) / (sizeof options_init[0])) @@ -3363,6 +3476,25 @@ certutil_main(int argc, char **argv, PRBool initialize) } } + /* --pss-sign is to sign a certificate with RSA-PSS, even if the + * issuer's key is an RSA key. If the key is an RSA-PSS key, the + * generated signature is always RSA-PSS. */ + if (certutil.options[opt_PssSign].activated) { + if (!certutil.commands[cmd_CreateNewCert].activated && + !certutil.commands[cmd_CreateAndAddCert].activated) { + PR_fprintf(PR_STDERR, + "%s -%c: --pss-sign only works with -C or -S.\n", + progName, commandToRun); + return 255; + } + if (keytype != rsaKey) { + PR_fprintf(PR_STDERR, + "%s -%c: --pss-sign only works with RSA keys.\n", + progName, commandToRun); + return 255; + } + } + /* If we need a list of extensions convert the flags into list format */ if (certutil.commands[cmd_CertReq].activated || certutil.commands[cmd_CreateAndAddCert].activated || @@ -3500,6 +3632,7 @@ certutil_main(int argc, char **argv, PRBool initialize) (certutil.options[opt_GenericExtensions].activated ? certutil.options[opt_GenericExtensions].arg : NULL), certVersion, + certutil.options[opt_PssSign].activated, &certDER); if (rv) goto shutdown; diff --git a/cmd/lib/secutil.c b/cmd/lib/secutil.c index cf52269c3b..cedecee2dd 100644 --- a/cmd/lib/secutil.c +++ b/cmd/lib/secutil.c @@ -1312,15 +1312,12 @@ SECU_PrintAlgorithmID(FILE *out, SECAlgorithmID *a, char *m, int level) return; } - if (algtag == SEC_OID_PKCS1_RSA_PSS_SIGNATURE) { - secu_PrintRSAPSSParams(out, &a->parameters, "Parameters", level + 1); - return; - } - if (a->parameters.len == 0 || (a->parameters.len == 2 && PORT_Memcmp(a->parameters.data, "\005\000", 2) == 0)) { /* No arguments or NULL argument */ + } else if (algtag == SEC_OID_PKCS1_RSA_PSS_SIGNATURE) { + secu_PrintRSAPSSParams(out, &a->parameters, "Parameters", level + 1); } else { /* Print args to algorithm */ SECU_PrintAsHex(out, &a->parameters, "Args", level + 1); diff --git a/gtests/cryptohi_gtest/Makefile b/gtests/cryptohi_gtest/Makefile new file mode 100644 index 0000000000..0d547e0803 --- /dev/null +++ b/gtests/cryptohi_gtest/Makefile @@ -0,0 +1,43 @@ +#! gmake +# +# 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/. + +####################################################################### +# (1) Include initial platform-independent assignments (MANDATORY). # +####################################################################### + +include manifest.mn + +####################################################################### +# (2) Include "global" configuration information. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/config.mk + +####################################################################### +# (3) Include "component" configuration information. (OPTIONAL) # +####################################################################### + + +####################################################################### +# (4) Include "local" platform-dependent assignments (OPTIONAL). # +####################################################################### + +include ../common/gtest.mk + +####################################################################### +# (5) Execute "global" rules. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/rules.mk + +####################################################################### +# (6) Execute "component" rules. (OPTIONAL) # +####################################################################### + + +####################################################################### +# (7) Execute "local" rules. (OPTIONAL). # +####################################################################### diff --git a/gtests/cryptohi_gtest/cryptohi_gtest.gyp b/gtests/cryptohi_gtest/cryptohi_gtest.gyp new file mode 100644 index 0000000000..72c815ecac --- /dev/null +++ b/gtests/cryptohi_gtest/cryptohi_gtest.gyp @@ -0,0 +1,29 @@ +# 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/. +{ + 'includes': [ + '../../coreconf/config.gypi', + '../common/gtest.gypi', + ], + 'targets': [ + { + 'target_name': 'cryptohi_gtest', + 'type': 'executable', + 'sources': [ + 'cryptohi_unittest.cc', + '<(DEPTH)/gtests/common/gtests.cc' + ], + 'dependencies': [ + '<(DEPTH)/exports.gyp:nss_exports', + '<(DEPTH)/gtests/google_test/google_test.gyp:gtest', + '<(DEPTH)/lib/util/util.gyp:nssutil3', + '<(DEPTH)/lib/ssl/ssl.gyp:ssl3', + '<(DEPTH)/lib/nss/nss.gyp:nss3', + ] + } + ], + 'variables': { + 'module': 'nss' + } +} diff --git a/gtests/cryptohi_gtest/cryptohi_unittest.cc b/gtests/cryptohi_gtest/cryptohi_unittest.cc new file mode 100644 index 0000000000..6f79bdd048 --- /dev/null +++ b/gtests/cryptohi_gtest/cryptohi_unittest.cc @@ -0,0 +1,369 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include + +#include "gtest/gtest.h" + +#include "scoped_ptrs.h" +#include "cryptohi.h" +#include "secitem.h" +#include "secerr.h" + +namespace nss_test { + +class SignParamsTestF : public ::testing::Test { + protected: + ScopedPLArenaPool arena_; + ScopedSECKEYPrivateKey privk_; + ScopedSECKEYPublicKey pubk_; + ScopedSECKEYPrivateKey ecPrivk_; + ScopedSECKEYPublicKey ecPubk_; + + void SetUp() { + arena_.reset(PORT_NewArena(2048)); + + SECKEYPublicKey *pubk; + SECKEYPrivateKey *privk = SECKEY_CreateRSAPrivateKey(1024, &pubk, NULL); + ASSERT_NE(nullptr, pubk); + pubk_.reset(pubk); + ASSERT_NE(nullptr, privk); + privk_.reset(privk); + + SECKEYECParams ecParams = {siBuffer, NULL, 0}; + SECOidData *oidData; + oidData = SECOID_FindOIDByTag(SEC_OID_CURVE25519); + ASSERT_NE(nullptr, oidData); + SECITEM_AllocItem(NULL, &ecParams, (2 + oidData->oid.len)); + ecParams.data[0] = SEC_ASN1_OBJECT_ID; /* we have to prepend 0x06 */ + ecParams.data[1] = oidData->oid.len; + memcpy(ecParams.data + 2, oidData->oid.data, oidData->oid.len); + SECKEYPublicKey *ecPubk; + SECKEYPrivateKey *ecPrivk = + SECKEY_CreateECPrivateKey(&ecParams, &ecPubk, NULL); + ASSERT_NE(nullptr, ecPubk); + ecPubk_.reset(ecPubk); + ASSERT_NE(nullptr, ecPrivk); + ecPrivk_.reset(ecPrivk); + } + + void CreatePssParams(SECKEYRSAPSSParams *params, SECOidTag hashAlgTag) { + PORT_Memset(params, 0, sizeof(SECKEYRSAPSSParams)); + + params->hashAlg = (SECAlgorithmID *)PORT_ArenaZAlloc( + arena_.get(), sizeof(SECAlgorithmID)); + ASSERT_NE(nullptr, params->hashAlg); + SECStatus rv = + SECOID_SetAlgorithmID(arena_.get(), params->hashAlg, hashAlgTag, NULL); + ASSERT_EQ(SECSuccess, rv); + } + + void CreatePssParams(SECKEYRSAPSSParams *params, SECOidTag hashAlgTag, + SECOidTag maskHashAlgTag) { + CreatePssParams(params, hashAlgTag); + + SECAlgorithmID maskHashAlg; + PORT_Memset(&maskHashAlg, 0, sizeof(maskHashAlg)); + SECOID_SetAlgorithmID(arena_.get(), &maskHashAlg, maskHashAlgTag, NULL); + + SECItem *maskHashAlgItem = + SEC_ASN1EncodeItem(arena_.get(), NULL, &maskHashAlg, + SEC_ASN1_GET(SECOID_AlgorithmIDTemplate)); + + params->maskAlg = (SECAlgorithmID *)PORT_ArenaZAlloc( + arena_.get(), sizeof(SECAlgorithmID)); + ASSERT_NE(nullptr, params->maskAlg); + + SECStatus rv = SECOID_SetAlgorithmID(arena_.get(), params->maskAlg, + SEC_OID_PKCS1_MGF1, maskHashAlgItem); + ASSERT_EQ(SECSuccess, rv); + } + + void CreatePssParams(SECKEYRSAPSSParams *params, SECOidTag hashAlgTag, + SECOidTag maskHashAlgTag, unsigned long saltLength) { + CreatePssParams(params, hashAlgTag, maskHashAlgTag); + + SECItem *saltLengthItem = + SEC_ASN1EncodeInteger(arena_.get(), ¶ms->saltLength, saltLength); + ASSERT_EQ(¶ms->saltLength, saltLengthItem); + } + + void CheckHashAlg(SECKEYRSAPSSParams *params, SECOidTag hashAlgTag) { + // If hash algorithm is SHA-1, it must be omitted in the parameters + if (hashAlgTag == SEC_OID_SHA1) { + EXPECT_EQ(nullptr, params->hashAlg); + } else { + EXPECT_NE(nullptr, params->hashAlg); + EXPECT_EQ(hashAlgTag, SECOID_GetAlgorithmTag(params->hashAlg)); + } + } + + void CheckMaskAlg(SECKEYRSAPSSParams *params, SECOidTag hashAlgTag) { + SECStatus rv; + + // If hash algorithm is SHA-1, it must be omitted in the parameters + if (hashAlgTag == SEC_OID_SHA1) + EXPECT_EQ(nullptr, params->hashAlg); + else { + EXPECT_NE(nullptr, params->maskAlg); + EXPECT_EQ(SEC_OID_PKCS1_MGF1, SECOID_GetAlgorithmTag(params->maskAlg)); + + SECAlgorithmID hashAlg; + rv = SEC_QuickDERDecodeItem(arena_.get(), &hashAlg, + SEC_ASN1_GET(SECOID_AlgorithmIDTemplate), + ¶ms->maskAlg->parameters); + ASSERT_EQ(SECSuccess, rv); + + EXPECT_EQ(hashAlgTag, SECOID_GetAlgorithmTag(&hashAlg)); + } + } + + void CheckSaltLength(SECKEYRSAPSSParams *params, SECOidTag hashAlg) { + // If the salt length parameter is missing, that means it is 20 (default) + if (!params->saltLength.data) { + return; + } + + unsigned long value; + SECStatus rv = SEC_ASN1DecodeInteger(¶ms->saltLength, &value); + ASSERT_EQ(SECSuccess, rv); + + // The salt length are usually the same as the hash length, + // except for the case where the hash length exceeds the limit + // set by the key length + switch (hashAlg) { + case SEC_OID_SHA1: + EXPECT_EQ(20UL, value); + break; + case SEC_OID_SHA224: + EXPECT_EQ(28UL, value); + break; + case SEC_OID_SHA256: + EXPECT_EQ(32UL, value); + break; + case SEC_OID_SHA384: + EXPECT_EQ(48UL, value); + break; + case SEC_OID_SHA512: + // Truncated from 64, because our private key is 1024-bit + EXPECT_EQ(62UL, value); + break; + default: + FAIL(); + } + } +}; + +class SignParamsTest + : public SignParamsTestF, + public ::testing::WithParamInterface> {}; + +class SignParamsSourceTest : public SignParamsTestF, + public ::testing::WithParamInterface {}; + +TEST_P(SignParamsTest, CreateRsa) { + SECOidTag hashAlg = std::get<0>(GetParam()); + SECOidTag srcHashAlg = std::get<1>(GetParam()); + + SECItem *srcParams; + if (srcHashAlg != SEC_OID_UNKNOWN) { + SECKEYRSAPSSParams pssParams; + ASSERT_NO_FATAL_FAILURE( + CreatePssParams(&pssParams, srcHashAlg, srcHashAlg)); + srcParams = SEC_ASN1EncodeItem(arena_.get(), nullptr, &pssParams, + SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate)); + ASSERT_NE(nullptr, srcParams); + } else { + srcParams = NULL; + } + + SECItem *params = SEC_CreateSignatureAlgorithmParameters( + arena_.get(), nullptr, SEC_OID_PKCS1_RSA_ENCRYPTION, hashAlg, srcParams, + privk_.get()); + + // PKCS#1 RSA actually doesn't take any parameters, but if it is + // given, return a copy of it + if (srcHashAlg != SEC_OID_UNKNOWN) { + EXPECT_EQ(srcParams->len, params->len); + EXPECT_EQ(0, memcmp(params->data, srcParams->data, srcParams->len)); + } else { + EXPECT_EQ(nullptr, params); + } +} + +TEST_P(SignParamsTest, CreateRsaPss) { + SECOidTag hashAlg = std::get<0>(GetParam()); + SECOidTag srcHashAlg = std::get<1>(GetParam()); + + SECItem *srcParams; + if (srcHashAlg != SEC_OID_UNKNOWN) { + SECKEYRSAPSSParams pssParams; + ASSERT_NO_FATAL_FAILURE( + CreatePssParams(&pssParams, srcHashAlg, srcHashAlg)); + srcParams = SEC_ASN1EncodeItem(arena_.get(), nullptr, &pssParams, + SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate)); + ASSERT_NE(nullptr, srcParams); + } else { + srcParams = NULL; + } + + SECItem *params = SEC_CreateSignatureAlgorithmParameters( + arena_.get(), nullptr, SEC_OID_PKCS1_RSA_PSS_SIGNATURE, hashAlg, + srcParams, privk_.get()); + + if (hashAlg != SEC_OID_UNKNOWN && srcHashAlg != SEC_OID_UNKNOWN && + hashAlg != srcHashAlg) { + EXPECT_EQ(nullptr, params); + return; + } + + EXPECT_NE(nullptr, params); + + SECKEYRSAPSSParams pssParams; + PORT_Memset(&pssParams, 0, sizeof(pssParams)); + SECStatus rv = + SEC_QuickDERDecodeItem(arena_.get(), &pssParams, + SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate), params); + ASSERT_EQ(SECSuccess, rv); + + if (hashAlg == SEC_OID_UNKNOWN) { + if (!pssParams.hashAlg) { + hashAlg = SEC_OID_SHA1; + } else { + hashAlg = SECOID_GetAlgorithmTag(pssParams.hashAlg); + } + + if (srcHashAlg == SEC_OID_UNKNOWN) { + // If both hashAlg and srcHashAlg is unset, NSS will decide the hash + // algorithm based on the key length; in this case it's SHA256 + EXPECT_EQ(SEC_OID_SHA256, hashAlg); + } else { + EXPECT_EQ(srcHashAlg, hashAlg); + } + } + + ASSERT_NO_FATAL_FAILURE(CheckHashAlg(&pssParams, hashAlg)); + ASSERT_NO_FATAL_FAILURE(CheckMaskAlg(&pssParams, hashAlg)); + ASSERT_NO_FATAL_FAILURE(CheckSaltLength(&pssParams, hashAlg)); + + // The default trailer field (1) must be omitted + EXPECT_EQ(nullptr, pssParams.trailerField.data); +} + +TEST_P(SignParamsTest, CreateRsaPssWithECPrivateKey) { + SECOidTag hashAlg = std::get<0>(GetParam()); + SECOidTag srcHashAlg = std::get<1>(GetParam()); + + SECItem *srcParams; + if (srcHashAlg != SEC_OID_UNKNOWN) { + SECKEYRSAPSSParams pssParams; + ASSERT_NO_FATAL_FAILURE( + CreatePssParams(&pssParams, srcHashAlg, srcHashAlg)); + srcParams = SEC_ASN1EncodeItem(arena_.get(), nullptr, &pssParams, + SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate)); + ASSERT_NE(nullptr, srcParams); + } else { + srcParams = NULL; + } + + SECItem *params = SEC_CreateSignatureAlgorithmParameters( + arena_.get(), nullptr, SEC_OID_PKCS1_RSA_PSS_SIGNATURE, hashAlg, + srcParams, ecPrivk_.get()); + + EXPECT_EQ(nullptr, params); +} + +TEST_P(SignParamsTest, CreateRsaPssWithInvalidHashAlg) { + SECOidTag srcHashAlg = std::get<1>(GetParam()); + + SECItem *srcParams; + if (srcHashAlg != SEC_OID_UNKNOWN) { + SECKEYRSAPSSParams pssParams; + ASSERT_NO_FATAL_FAILURE( + CreatePssParams(&pssParams, srcHashAlg, srcHashAlg)); + srcParams = SEC_ASN1EncodeItem(arena_.get(), nullptr, &pssParams, + SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate)); + ASSERT_NE(nullptr, srcParams); + } else { + srcParams = NULL; + } + + SECItem *params = SEC_CreateSignatureAlgorithmParameters( + arena_.get(), nullptr, SEC_OID_PKCS1_RSA_PSS_SIGNATURE, SEC_OID_MD5, + srcParams, privk_.get()); + + EXPECT_EQ(nullptr, params); +} + +TEST_P(SignParamsSourceTest, CreateRsaPssWithInvalidHashAlg) { + SECOidTag hashAlg = GetParam(); + + SECItem *srcParams; + SECKEYRSAPSSParams pssParams; + ASSERT_NO_FATAL_FAILURE( + CreatePssParams(&pssParams, SEC_OID_MD5, SEC_OID_MD5)); + srcParams = SEC_ASN1EncodeItem(arena_.get(), nullptr, &pssParams, + SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate)); + ASSERT_NE(nullptr, srcParams); + + SECItem *params = SEC_CreateSignatureAlgorithmParameters( + arena_.get(), nullptr, SEC_OID_PKCS1_RSA_PSS_SIGNATURE, hashAlg, + srcParams, privk_.get()); + + EXPECT_EQ(nullptr, params); +} + +TEST_P(SignParamsSourceTest, CreateRsaPssWithInvalidSaltLength) { + SECOidTag hashAlg = GetParam(); + + SECItem *srcParams; + SECKEYRSAPSSParams pssParams; + ASSERT_NO_FATAL_FAILURE( + CreatePssParams(&pssParams, SEC_OID_SHA512, SEC_OID_SHA512, 100)); + srcParams = SEC_ASN1EncodeItem(arena_.get(), nullptr, &pssParams, + SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate)); + ASSERT_NE(nullptr, srcParams); + + SECItem *params = SEC_CreateSignatureAlgorithmParameters( + arena_.get(), nullptr, SEC_OID_PKCS1_RSA_PSS_SIGNATURE, hashAlg, + srcParams, privk_.get()); + + EXPECT_EQ(nullptr, params); +} + +TEST_P(SignParamsSourceTest, CreateRsaPssWithHashMismatch) { + SECOidTag hashAlg = GetParam(); + + SECItem *srcParams; + SECKEYRSAPSSParams pssParams; + ASSERT_NO_FATAL_FAILURE( + CreatePssParams(&pssParams, SEC_OID_SHA256, SEC_OID_SHA512)); + srcParams = SEC_ASN1EncodeItem(arena_.get(), nullptr, &pssParams, + SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate)); + ASSERT_NE(nullptr, srcParams); + + SECItem *params = SEC_CreateSignatureAlgorithmParameters( + arena_.get(), nullptr, SEC_OID_PKCS1_RSA_PSS_SIGNATURE, hashAlg, + srcParams, privk_.get()); + + EXPECT_EQ(nullptr, params); +} + +INSTANTIATE_TEST_CASE_P( + SignParamsTestCases, SignParamsTest, + ::testing::Combine(::testing::Values(SEC_OID_UNKNOWN, SEC_OID_SHA1, + SEC_OID_SHA224, SEC_OID_SHA256, + SEC_OID_SHA384, SEC_OID_SHA512), + ::testing::Values(SEC_OID_UNKNOWN, SEC_OID_SHA1, + SEC_OID_SHA224, SEC_OID_SHA256, + SEC_OID_SHA384, SEC_OID_SHA512))); + +INSTANTIATE_TEST_CASE_P(SignParamsSourceTestCases, SignParamsSourceTest, + ::testing::Values(SEC_OID_UNKNOWN, SEC_OID_SHA1, + SEC_OID_SHA224, SEC_OID_SHA256, + SEC_OID_SHA384, SEC_OID_SHA512)); + +} // namespace nss_test diff --git a/gtests/cryptohi_gtest/manifest.mn b/gtests/cryptohi_gtest/manifest.mn new file mode 100644 index 0000000000..644463aa6a --- /dev/null +++ b/gtests/cryptohi_gtest/manifest.mn @@ -0,0 +1,22 @@ +# +# 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/. +CORE_DEPTH = ../.. +DEPTH = ../.. +MODULE = nss + +CPPSRCS = \ + cryptohi_unittest.cc \ + $(NULL) + +INCLUDES += -I$(CORE_DEPTH)/gtests/google_test/gtest/include \ + -I$(CORE_DEPTH)/gtests/common \ + -I$(CORE_DEPTH)/cpputil + +REQUIRES = nspr gtest + +PROGRAM = cryptohi_gtest + +EXTRA_LIBS = $(DIST)/lib/$(LIB_PREFIX)gtest.$(LIB_SUFFIX) $(EXTRA_OBJS) \ + $(DIST)/lib/$(LIB_PREFIX)gtestutil.$(LIB_SUFFIX) diff --git a/gtests/manifest.mn b/gtests/manifest.mn index a435f8733c..13048f037c 100644 --- a/gtests/manifest.mn +++ b/gtests/manifest.mn @@ -21,6 +21,7 @@ ifneq ($(NSS_BUILD_UTIL_ONLY),1) NSS_SRCDIRS = \ certdb_gtest \ certhigh_gtest \ + cryptohi_gtest \ der_gtest \ pk11_gtest \ softoken_gtest \ diff --git a/gtests/ssl_gtest/ssl_auth_unittest.cc b/gtests/ssl_gtest/ssl_auth_unittest.cc index dbcbc9aa33..ccff03fe83 100644 --- a/gtests/ssl_gtest/ssl_auth_unittest.cc +++ b/gtests/ssl_gtest/ssl_auth_unittest.cc @@ -29,7 +29,25 @@ TEST_P(TlsConnectGeneric, ServerAuthBigRsa) { } TEST_P(TlsConnectGeneric, ServerAuthRsaChain) { - Reset(TlsAgent::kServerRsaChain); + Reset("rsa_chain"); + Connect(); + CheckKeys(); + size_t chain_length; + EXPECT_TRUE(client_->GetPeerChainLength(&chain_length)); + EXPECT_EQ(2UL, chain_length); +} + +TEST_P(TlsConnectGeneric, ServerAuthRsaPssChain) { + Reset("rsa_pss_chain"); + Connect(); + CheckKeys(); + size_t chain_length; + EXPECT_TRUE(client_->GetPeerChainLength(&chain_length)); + EXPECT_EQ(2UL, chain_length); +} + +TEST_P(TlsConnectGeneric, ServerAuthRsaCARsaPssChain) { + Reset("rsa_ca_rsa_pss_chain"); Connect(); CheckKeys(); size_t chain_length; diff --git a/gtests/ssl_gtest/tls_agent.cc b/gtests/ssl_gtest/tls_agent.cc index 2809d138a0..e84ffc2036 100644 --- a/gtests/ssl_gtest/tls_agent.cc +++ b/gtests/ssl_gtest/tls_agent.cc @@ -36,7 +36,6 @@ const std::string TlsAgent::kServerRsa = "rsa"; // both sign and encrypt const std::string TlsAgent::kServerRsaSign = "rsa_sign"; const std::string TlsAgent::kServerRsaPss = "rsa_pss"; const std::string TlsAgent::kServerRsaDecrypt = "rsa_decrypt"; -const std::string TlsAgent::kServerRsaChain = "rsa_chain"; const std::string TlsAgent::kServerEcdsa256 = "ecdsa256"; const std::string TlsAgent::kServerEcdsa384 = "ecdsa384"; const std::string TlsAgent::kServerEcdsa521 = "ecdsa521"; diff --git a/gtests/ssl_gtest/tls_agent.h b/gtests/ssl_gtest/tls_agent.h index 9344ce0693..2baffbd7fd 100644 --- a/gtests/ssl_gtest/tls_agent.h +++ b/gtests/ssl_gtest/tls_agent.h @@ -66,7 +66,6 @@ class TlsAgent : public PollTarget { static const std::string kServerRsaSign; static const std::string kServerRsaPss; static const std::string kServerRsaDecrypt; - static const std::string kServerRsaChain; // A cert that requires a chain. static const std::string kServerEcdsa256; static const std::string kServerEcdsa384; static const std::string kServerEcdsa521; diff --git a/lib/certdb/certdb.c b/lib/certdb/certdb.c index 7864edc08e..1a676a7207 100644 --- a/lib/certdb/certdb.c +++ b/lib/certdb/certdb.c @@ -1192,6 +1192,7 @@ CERT_CheckKeyUsage(CERTCertificate *cert, unsigned int requiredUsage) case rsaKey: requiredUsage |= KU_KEY_ENCIPHERMENT; break; + case rsaPssKey: case dsaKey: requiredUsage |= KU_DIGITAL_SIGNATURE; break; diff --git a/lib/cryptohi/cryptohi.h b/lib/cryptohi/cryptohi.h index f658daa9ef..e529fa34f5 100644 --- a/lib/cryptohi/cryptohi.h +++ b/lib/cryptohi/cryptohi.h @@ -59,6 +59,14 @@ extern SECItem *DSAU_DecodeDerSigToLen(const SECItem *item, unsigned int len); */ extern SGNContext *SGN_NewContext(SECOidTag alg, SECKEYPrivateKey *privKey); +/* +** Create a new signature context from an algorithmID. +** "alg" the signature algorithm to use +** "privKey" the private key to use +*/ +extern SGNContext *SGN_NewContextWithAlgorithmID(SECAlgorithmID *alg, + SECKEYPrivateKey *privKey); + /* ** Destroy a signature-context object ** "cx" the object @@ -105,6 +113,21 @@ extern SECStatus SEC_SignData(SECItem *result, const unsigned char *buf, int len, SECKEYPrivateKey *pk, SECOidTag algid); +/* +** Sign a single block of data using private key encryption and given +** signature/hash algorithm with parameters from an algorithmID. +** "result" the final signature data (memory is allocated) +** "buf" the input data to sign +** "len" the amount of data to sign +** "pk" the private key to encrypt with +** "algid" the signature/hash algorithm to sign with +** (must be compatible with the key type). +*/ +extern SECStatus SEC_SignDataWithAlgorithmID(SECItem *result, + const unsigned char *buf, int len, + SECKEYPrivateKey *pk, + SECAlgorithmID *algid); + /* ** Sign a pre-digested block of data using private key encryption, encoding ** The given signature/hash algorithm. @@ -131,6 +154,27 @@ extern SECStatus SEC_DerSignData(PLArenaPool *arena, SECItem *result, const unsigned char *buf, int len, SECKEYPrivateKey *pk, SECOidTag algid); +/* +** DER sign a single block of data using private key encryption and +** the given signature/hash algorithm with parameters from an +** algorithmID. This routine first computes a digital signature using +** SEC_SignData, then wraps it with an CERTSignedData and then der +** encodes the result. +** "arena" is the memory arena to use to allocate data from +** "result" the final der encoded data (memory is allocated) +** "buf" the input data to sign +** "len" the amount of data to sign +** "pk" the private key to encrypt with +** "algid" the signature/hash algorithm to sign with +** (must be compatible with the key type). +*/ +extern SECStatus SEC_DerSignDataWithAlgorithmID(PLArenaPool *arena, + SECItem *result, + const unsigned char *buf, + int len, + SECKEYPrivateKey *pk, + SECAlgorithmID *algid); + /* ** Destroy a signed-data object. ** "sd" the object @@ -146,6 +190,23 @@ extern void SEC_DestroySignedData(CERTSignedData *sd, PRBool freeit); extern SECOidTag SEC_GetSignatureAlgorithmOidTag(KeyType keyType, SECOidTag hashAlgTag); +/* +** Create algorithm parameters for signing. Return a new item +** allocated from arena, or NULL on failure. +** "arena" is the memory arena to use to allocate data from +** "result" the encoded parameters (memory is allocated) +** "signAlgTag" is the signing algorithm +** "hashAlgTag" is the preferred hash algorithm +** "params" is the default parameters +** "key" is the private key +*/ +extern SECItem *SEC_CreateSignatureAlgorithmParameters(PLArenaPool *arena, + SECItem *result, + SECOidTag signAlgTag, + SECOidTag hashAlgTag, + const SECItem *params, + const SECKEYPrivateKey *key); + /****************************************/ /* ** Signature verification operations diff --git a/lib/cryptohi/keyi.h b/lib/cryptohi/keyi.h index f8f5f7f7da..ee11fc905e 100644 --- a/lib/cryptohi/keyi.h +++ b/lib/cryptohi/keyi.h @@ -17,6 +17,9 @@ KeyType seckey_GetKeyType(SECOidTag pubKeyOid); SECStatus sec_DecodeSigAlg(const SECKEYPublicKey *key, SECOidTag sigAlg, const SECItem *param, SECOidTag *encalg, SECOidTag *hashalg); +SECStatus sec_RSAPSSParamsToMechanism(CK_RSA_PKCS_PSS_PARAMS *mech, + const SECKEYRSAPSSParams *params); + SEC_END_PROTOS #endif /* _KEYHI_H_ */ diff --git a/lib/cryptohi/seckey.c b/lib/cryptohi/seckey.c index 9ea48b7677..f300522133 100644 --- a/lib/cryptohi/seckey.c +++ b/lib/cryptohi/seckey.c @@ -1048,6 +1048,7 @@ SECKEY_SignatureLen(const SECKEYPublicKey *pubk) switch (pubk->keyType) { case rsaKey: + case rsaPssKey: b0 = pubk->u.rsa.modulus.data[0]; return b0 ? pubk->u.rsa.modulus.len : pubk->u.rsa.modulus.len - 1; case dsaKey: @@ -1974,3 +1975,92 @@ SECKEY_GetECCOid(const SECKEYECParams *params) return oidData->offset; } + +static CK_MECHANISM_TYPE +sec_GetHashMechanismByOidTag(SECOidTag tag) +{ + switch (tag) { + case SEC_OID_SHA512: + return CKM_SHA512; + case SEC_OID_SHA384: + return CKM_SHA384; + case SEC_OID_SHA256: + return CKM_SHA256; + default: + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + /* fallthrough */ + case SEC_OID_SHA1: + break; + } + return CKM_SHA_1; +} + +static CK_RSA_PKCS_MGF_TYPE +sec_GetMgfTypeByOidTag(SECOidTag tag) +{ + switch (tag) { + case SEC_OID_SHA512: + return CKG_MGF1_SHA512; + case SEC_OID_SHA384: + return CKG_MGF1_SHA384; + case SEC_OID_SHA256: + return CKG_MGF1_SHA256; + default: + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + /* fallthrough */ + case SEC_OID_SHA1: + break; + } + return CKG_MGF1_SHA1; +} + +SECStatus +sec_RSAPSSParamsToMechanism(CK_RSA_PKCS_PSS_PARAMS *mech, + const SECKEYRSAPSSParams *params) +{ + SECStatus rv = SECSuccess; + SECOidTag hashAlgTag; + unsigned long saltLength; + + PORT_Memset(mech, 0, sizeof(CK_RSA_PKCS_PSS_PARAMS)); + + if (params->hashAlg) { + hashAlgTag = SECOID_GetAlgorithmTag(params->hashAlg); + } else { + hashAlgTag = SEC_OID_SHA1; /* default, SHA-1 */ + } + mech->hashAlg = sec_GetHashMechanismByOidTag(hashAlgTag); + + if (params->maskAlg) { + SECAlgorithmID maskHashAlg; + SECOidTag maskHashAlgTag; + PORTCheapArenaPool tmpArena; + + if (SECOID_GetAlgorithmTag(params->maskAlg) != SEC_OID_PKCS1_MGF1) { + /* only MGF1 is known to PKCS#11 */ + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + return SECFailure; + } + + PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE); + rv = SEC_QuickDERDecodeItem(&tmpArena.arena, &maskHashAlg, + SEC_ASN1_GET(SECOID_AlgorithmIDTemplate), + ¶ms->maskAlg->parameters); + PORT_DestroyCheapArena(&tmpArena); + if (rv != SECSuccess) { + return rv; + } + maskHashAlgTag = SECOID_GetAlgorithmTag(&maskHashAlg); + mech->mgf = sec_GetMgfTypeByOidTag(maskHashAlgTag); + } else { + mech->mgf = CKG_MGF1_SHA1; /* default, MGF1 with SHA-1 */ + } + + rv = SEC_ASN1DecodeInteger((SECItem *)¶ms->saltLength, &saltLength); + if (rv != SECSuccess) { + return rv; + } + mech->sLen = saltLength; + + return rv; +} diff --git a/lib/cryptohi/secsign.c b/lib/cryptohi/secsign.c index d06cb2e852..693e79c65a 100644 --- a/lib/cryptohi/secsign.c +++ b/lib/cryptohi/secsign.c @@ -22,10 +22,11 @@ struct SGNContextStr { void *hashcx; const SECHashObject *hashobj; SECKEYPrivateKey *key; + SECItem *params; }; -SGNContext * -SGN_NewContext(SECOidTag alg, SECKEYPrivateKey *key) +static SGNContext * +sgn_NewContext(SECOidTag alg, SECItem *params, SECKEYPrivateKey *key) { SGNContext *cx; SECOidTag hashalg, signalg; @@ -40,7 +41,7 @@ SGN_NewContext(SECOidTag alg, SECKEYPrivateKey *key) * it may just support CKM_SHA1_RSA_PKCS and/or CKM_MD5_RSA_PKCS. */ /* we have a private key, not a public key, so don't pass it in */ - rv = sec_DecodeSigAlg(NULL, alg, NULL, &signalg, &hashalg); + rv = sec_DecodeSigAlg(NULL, alg, params, &signalg, &hashalg); if (rv != SECSuccess) { PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); return 0; @@ -49,7 +50,8 @@ SGN_NewContext(SECOidTag alg, SECKEYPrivateKey *key) /* verify our key type */ if (key->keyType != keyType && - !((key->keyType == dsaKey) && (keyType == fortezzaKey))) { + !((key->keyType == dsaKey) && (keyType == fortezzaKey)) && + !((key->keyType == rsaKey) && (keyType == rsaPssKey))) { PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); return 0; } @@ -59,10 +61,24 @@ SGN_NewContext(SECOidTag alg, SECKEYPrivateKey *key) cx->hashalg = hashalg; cx->signalg = signalg; cx->key = key; + cx->params = params; } return cx; } +SGNContext * +SGN_NewContext(SECOidTag alg, SECKEYPrivateKey *key) +{ + return sgn_NewContext(alg, NULL, key); +} + +SGNContext * +SGN_NewContextWithAlgorithmID(SECAlgorithmID *alg, SECKEYPrivateKey *key) +{ + SECOidTag tag = SECOID_GetAlgorithmTag(alg); + return sgn_NewContext(tag, &alg->parameters, key); +} + void SGN_DestroyContext(SGNContext *cx, PRBool freeit) { @@ -148,6 +164,7 @@ SGN_End(SGNContext *cx, SECItem *result) result->data = 0; digder.data = 0; + sigitem.data = 0; /* Finish up digest function */ if (cx->hashcx == NULL) { @@ -156,7 +173,8 @@ SGN_End(SGNContext *cx, SECItem *result) } (*cx->hashobj->end)(cx->hashcx, digest, &part1, sizeof(digest)); - if (privKey->keyType == rsaKey) { + if (privKey->keyType == rsaKey && + cx->signalg != SEC_OID_PKCS1_RSA_PSS_SIGNATURE) { arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (!arena) { @@ -200,26 +218,65 @@ SGN_End(SGNContext *cx, SECItem *result) goto loser; } - rv = PK11_Sign(privKey, &sigitem, &digder); - if (rv != SECSuccess) { - PORT_Free(sigitem.data); - sigitem.data = NULL; - goto loser; + if (cx->signalg == SEC_OID_PKCS1_RSA_PSS_SIGNATURE) { + CK_RSA_PKCS_PSS_PARAMS mech; + SECItem mechItem = { siBuffer, (unsigned char *)&mech, sizeof(mech) }; + + PORT_Memset(&mech, 0, sizeof(mech)); + + if (cx->params && cx->params->data) { + SECKEYRSAPSSParams params; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!arena) { + rv = SECFailure; + goto loser; + } + + PORT_Memset(¶ms, 0, sizeof(params)); + rv = SEC_QuickDERDecodeItem(arena, ¶ms, + SECKEY_RSAPSSParamsTemplate, + cx->params); + if (rv != SECSuccess) { + goto loser; + } + rv = sec_RSAPSSParamsToMechanism(&mech, ¶ms); + if (rv != SECSuccess) { + goto loser; + } + } else { + mech.hashAlg = CKM_SHA_1; + mech.mgf = CKG_MGF1_SHA1; + mech.sLen = digder.len; + } + rv = PK11_SignWithMechanism(privKey, CKM_RSA_PKCS_PSS, &mechItem, + &sigitem, &digder); + if (rv != SECSuccess) { + goto loser; + } + } else { + rv = PK11_Sign(privKey, &sigitem, &digder); + if (rv != SECSuccess) { + goto loser; + } } if ((cx->signalg == SEC_OID_ANSIX9_DSA_SIGNATURE) || (cx->signalg == SEC_OID_ANSIX962_EC_PUBLIC_KEY)) { /* DSAU_EncodeDerSigWithLen works for DSA and ECDSA */ rv = DSAU_EncodeDerSigWithLen(result, &sigitem, sigitem.len); - PORT_Free(sigitem.data); if (rv != SECSuccess) goto loser; + SECITEM_FreeItem(&sigitem, PR_FALSE); } else { result->len = sigitem.len; result->data = sigitem.data; } loser: + if (rv != SECSuccess) { + SECITEM_FreeItem(&sigitem, PR_FALSE); + } SGN_DestroyDigestInfo(di); if (arena != NULL) { PORT_FreeArena(arena, PR_FALSE); @@ -229,18 +286,14 @@ SGN_End(SGNContext *cx, SECItem *result) /************************************************************************/ -/* -** Sign a block of data returning in result a bunch of bytes that are the -** signature. Returns zero on success, an error code on failure. -*/ -SECStatus -SEC_SignData(SECItem *res, const unsigned char *buf, int len, - SECKEYPrivateKey *pk, SECOidTag algid) +static SECStatus +sec_SignData(SECItem *res, const unsigned char *buf, int len, + SECKEYPrivateKey *pk, SECOidTag algid, SECItem *params) { SECStatus rv; SGNContext *sgn; - sgn = SGN_NewContext(algid, pk); + sgn = sgn_NewContext(algid, params, pk); if (sgn == NULL) return SECFailure; @@ -260,6 +313,25 @@ SEC_SignData(SECItem *res, const unsigned char *buf, int len, return rv; } +/* +** Sign a block of data returning in result a bunch of bytes that are the +** signature. Returns zero on success, an error code on failure. +*/ +SECStatus +SEC_SignData(SECItem *res, const unsigned char *buf, int len, + SECKEYPrivateKey *pk, SECOidTag algid) +{ + return sec_SignData(res, buf, len, pk, algid, NULL); +} + +SECStatus +SEC_SignDataWithAlgorithmID(SECItem *res, const unsigned char *buf, int len, + SECKEYPrivateKey *pk, SECAlgorithmID *algid) +{ + SECOidTag tag = SECOID_GetAlgorithmTag(algid); + return sec_SignData(res, buf, len, pk, tag, &algid->parameters); +} + /************************************************************************/ DERTemplate CERTSignedDataTemplate[] = @@ -294,10 +366,10 @@ const SEC_ASN1Template CERT_SignedDataTemplate[] = SEC_ASN1_CHOOSER_IMPLEMENT(CERT_SignedDataTemplate) -SECStatus -SEC_DerSignData(PLArenaPool *arena, SECItem *result, +static SECStatus +sec_DerSignData(PLArenaPool *arena, SECItem *result, const unsigned char *buf, int len, SECKEYPrivateKey *pk, - SECOidTag algID) + SECOidTag algID, SECItem *params) { SECItem it; CERTSignedData sd; @@ -339,7 +411,7 @@ SEC_DerSignData(PLArenaPool *arena, SECItem *result, } /* Sign input buffer */ - rv = SEC_SignData(&it, buf, len, pk, algID); + rv = sec_SignData(&it, buf, len, pk, algID, params); if (rv) goto loser; @@ -349,7 +421,7 @@ SEC_DerSignData(PLArenaPool *arena, SECItem *result, sd.data.len = len; sd.signature.data = it.data; sd.signature.len = it.len << 3; /* convert to bit string */ - rv = SECOID_SetAlgorithmID(arena, &sd.signatureAlgorithm, algID, 0); + rv = SECOID_SetAlgorithmID(arena, &sd.signatureAlgorithm, algID, params); if (rv) goto loser; @@ -362,6 +434,24 @@ SEC_DerSignData(PLArenaPool *arena, SECItem *result, return rv; } +SECStatus +SEC_DerSignData(PLArenaPool *arena, SECItem *result, + const unsigned char *buf, int len, SECKEYPrivateKey *pk, + SECOidTag algID) +{ + return sec_DerSignData(arena, result, buf, len, pk, algID, NULL); +} + +SECStatus +SEC_DerSignDataWithAlgorithmID(PLArenaPool *arena, SECItem *result, + const unsigned char *buf, int len, + SECKEYPrivateKey *pk, + SECAlgorithmID *algID) +{ + SECOidTag tag = SECOID_GetAlgorithmTag(algID); + return sec_DerSignData(arena, result, buf, len, pk, tag, &algID->parameters); +} + SECStatus SGN_Digest(SECKEYPrivateKey *privKey, SECOidTag algtag, SECItem *result, SECItem *digest) @@ -509,3 +599,226 @@ SEC_GetSignatureAlgorithmOidTag(KeyType keyType, SECOidTag hashAlgTag) } return sigTag; } + +static SECItem * +sec_CreateRSAPSSParameters(PLArenaPool *arena, + SECItem *result, + SECOidTag hashAlgTag, + const SECItem *params, + const SECKEYPrivateKey *key) +{ + SECKEYRSAPSSParams pssParams; + int modBytes, hashLength; + unsigned long saltLength; + SECStatus rv; + + if (key->keyType != rsaKey && key->keyType != rsaPssKey) { + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + return NULL; + } + + PORT_Memset(&pssParams, 0, sizeof(pssParams)); + + if (params && params->data) { + /* The parameters field should either be empty or contain + * valid RSA-PSS parameters */ + PORT_Assert(!(params->len == 2 && + params->data[0] == SEC_ASN1_NULL && + params->data[1] == 0)); + rv = SEC_QuickDERDecodeItem(arena, &pssParams, + SECKEY_RSAPSSParamsTemplate, + params); + if (rv != SECSuccess) { + return NULL; + } + } + + if (pssParams.trailerField.data) { + unsigned long trailerField; + + rv = SEC_ASN1DecodeInteger((SECItem *)&pssParams.trailerField, + &trailerField); + if (rv != SECSuccess) { + return NULL; + } + if (trailerField != 1) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + } + + modBytes = PK11_GetPrivateModulusLen((SECKEYPrivateKey *)key); + + /* Determine the hash algorithm to use, based on hashAlgTag and + * pssParams.hashAlg; there are four cases */ + if (hashAlgTag != SEC_OID_UNKNOWN) { + if (pssParams.hashAlg) { + if (SECOID_GetAlgorithmTag(pssParams.hashAlg) != hashAlgTag) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + } + } else if (hashAlgTag == SEC_OID_UNKNOWN) { + if (pssParams.hashAlg) { + hashAlgTag = SECOID_GetAlgorithmTag(pssParams.hashAlg); + } else { + /* Find a suitable hash algorithm based on the NIST recommendation */ + if (modBytes <= 384) { /* 128, in NIST 800-57, Part 1 */ + hashAlgTag = SEC_OID_SHA256; + } else if (modBytes <= 960) { /* 192, NIST 800-57, Part 1 */ + hashAlgTag = SEC_OID_SHA384; + } else { + hashAlgTag = SEC_OID_SHA512; + } + } + } + + if (hashAlgTag != SEC_OID_SHA1 && hashAlgTag != SEC_OID_SHA224 && + hashAlgTag != SEC_OID_SHA256 && hashAlgTag != SEC_OID_SHA384 && + hashAlgTag != SEC_OID_SHA512) { + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + return NULL; + } + + /* Now that the hash algorithm is decided, check if it matches the + * existing parameters if any */ + if (pssParams.maskAlg) { + SECAlgorithmID maskHashAlg; + + if (SECOID_GetAlgorithmTag(pssParams.maskAlg) != SEC_OID_PKCS1_MGF1) { + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + return NULL; + } + + if (pssParams.maskAlg->parameters.data == NULL) { + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + return NULL; + } + + PORT_Memset(&maskHashAlg, 0, sizeof(maskHashAlg)); + rv = SEC_QuickDERDecodeItem(arena, &maskHashAlg, + SEC_ASN1_GET(SECOID_AlgorithmIDTemplate), + &pssParams.maskAlg->parameters); + if (rv != SECSuccess) { + return NULL; + } + + /* Following the recommendation in RFC 4055, assume the hash + * algorithm identical to pssParam.hashAlg */ + if (SECOID_GetAlgorithmTag(&maskHashAlg) != hashAlgTag) { + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + return NULL; + } + } + + hashLength = HASH_ResultLenByOidTag(hashAlgTag); + + if (pssParams.saltLength.data) { + rv = SEC_ASN1DecodeInteger((SECItem *)&pssParams.saltLength, + &saltLength); + if (rv != SECSuccess) { + return NULL; + } + + /* The specified salt length is too long */ + if (saltLength > modBytes - hashLength - 2) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + } + + /* Fill in the parameters */ + if (pssParams.hashAlg) { + if (hashAlgTag == SEC_OID_SHA1) { + /* Omit hashAlg if the the algorithm is SHA-1 (default) */ + pssParams.hashAlg = NULL; + } + } else { + if (hashAlgTag != SEC_OID_SHA1) { + pssParams.hashAlg = PORT_ArenaZAlloc(arena, sizeof(SECAlgorithmID)); + if (!pssParams.hashAlg) { + return NULL; + } + rv = SECOID_SetAlgorithmID(arena, pssParams.hashAlg, hashAlgTag, + NULL); + if (rv != SECSuccess) { + return NULL; + } + } + } + + if (pssParams.maskAlg) { + if (hashAlgTag == SEC_OID_SHA1) { + /* Omit maskAlg if the the algorithm is SHA-1 (default) */ + pssParams.maskAlg = NULL; + } + } else { + if (hashAlgTag != SEC_OID_SHA1) { + SECItem *hashAlgItem; + + PORT_Assert(pssParams.hashAlg != NULL); + + hashAlgItem = SEC_ASN1EncodeItem(arena, NULL, pssParams.hashAlg, + SEC_ASN1_GET(SECOID_AlgorithmIDTemplate)); + if (!hashAlgItem) { + return NULL; + } + pssParams.maskAlg = PORT_ArenaZAlloc(arena, sizeof(SECAlgorithmID)); + if (!pssParams.maskAlg) { + return NULL; + } + rv = SECOID_SetAlgorithmID(arena, pssParams.maskAlg, + SEC_OID_PKCS1_MGF1, hashAlgItem); + if (rv != SECSuccess) { + return NULL; + } + } + } + + if (pssParams.saltLength.data) { + if (saltLength == 20) { + /* Omit the salt length if it is the default */ + pssParams.saltLength.data = NULL; + } + } else { + /* Find a suitable length from the hash algorithm and modulus bits */ + saltLength = PR_MIN(hashLength, modBytes - hashLength - 2); + + if (saltLength != 20 && + !SEC_ASN1EncodeInteger(arena, &pssParams.saltLength, saltLength)) { + return NULL; + } + } + + if (pssParams.trailerField.data) { + /* Omit trailerField if the value is 1 (default) */ + pssParams.trailerField.data = NULL; + } + + return SEC_ASN1EncodeItem(arena, result, + &pssParams, SECKEY_RSAPSSParamsTemplate); +} + +SECItem * +SEC_CreateSignatureAlgorithmParameters(PLArenaPool *arena, + SECItem *result, + SECOidTag signAlgTag, + SECOidTag hashAlgTag, + const SECItem *params, + const SECKEYPrivateKey *key) +{ + switch (signAlgTag) { + case SEC_OID_PKCS1_RSA_PSS_SIGNATURE: + return sec_CreateRSAPSSParameters(arena, result, + hashAlgTag, params, key); + + default: + if (params == NULL) + return NULL; + if (result == NULL) + result = SECITEM_AllocItem(arena, NULL, 0); + if (SECITEM_CopyItem(arena, result, params) != SECSuccess) + return NULL; + return result; + } +} diff --git a/lib/cryptohi/secvfy.c b/lib/cryptohi/secvfy.c index 2ac21abd4d..83c9c579da 100644 --- a/lib/cryptohi/secvfy.c +++ b/lib/cryptohi/secvfy.c @@ -136,6 +136,8 @@ struct VFYContextStr { unsigned char dsasig[DSA_MAX_SIGNATURE_LEN]; /* the full ECDSA signature */ unsigned char ecdsasig[2 * MAX_ECKEY_LEN]; + /* the full RSA signature, only used in RSA-PSS */ + unsigned char rsasig[(RSA_MAX_MODULUS_BITS + 7) / 8]; } u; unsigned int pkcs1RSADigestInfoLen; /* the encoded DigestInfo from a RSA PKCS#1 signature */ @@ -148,6 +150,7 @@ struct VFYContextStr { * VFY_CreateContext call. If false, the * signature must be provided with a * VFY_EndWithSignature call. */ + SECItem *params; }; static SECStatus @@ -250,9 +253,38 @@ sec_DecodeSigAlg(const SECKEYPublicKey *key, SECOidTag sigAlg, *hashalg = SEC_OID_SHA1; break; case SEC_OID_PKCS1_RSA_ENCRYPTION: - case SEC_OID_PKCS1_RSA_PSS_SIGNATURE: *hashalg = SEC_OID_UNKNOWN; /* get it from the RSA signature */ break; + case SEC_OID_PKCS1_RSA_PSS_SIGNATURE: + if (param && param->data) { + SECKEYRSAPSSParams pssParam; + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + return SECFailure; + } + PORT_Memset(&pssParam, 0, sizeof pssParam); + rv = SEC_QuickDERDecodeItem(arena, &pssParam, + SECKEY_RSAPSSParamsTemplate, + param); + if (rv != SECSuccess) { + PORT_FreeArena(arena, PR_FALSE); + return rv; + } + if (pssParam.hashAlg) { + *hashalg = SECOID_GetAlgorithmTag(pssParam.hashAlg); + } else { + *hashalg = SEC_OID_SHA1; /* default, SHA-1 */ + } + PORT_FreeArena(arena, PR_FALSE); + /* only accept hash algorithms */ + if (HASH_GetHashTypeByOidTag(*hashalg) == HASH_AlgNULL) { + /* error set by HASH_GetHashTypeByOidTag */ + return SECFailure; + } + } else { + *hashalg = SEC_OID_SHA1; /* default, SHA-1 */ + } + break; case SEC_OID_ANSIX962_ECDSA_SHA224_SIGNATURE: case SEC_OID_PKCS1_SHA224_WITH_RSA_ENCRYPTION: @@ -434,6 +466,20 @@ vfy_CreateContext(const SECKEYPublicKey *key, const SECItem *sig, cx->key, sig, wincx); break; + case rsaPssKey: + sigLen = SECKEY_SignatureLen(key); + if (sigLen == 0) { + /* error set by SECKEY_SignatureLen */ + rv = SECFailure; + break; + } + if (sig->len != sigLen) { + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + rv = SECFailure; + break; + } + PORT_Memcpy(cx->u.buffer, sig->data, sigLen); + break; case dsaKey: case ecKey: sigLen = SECKEY_SignatureLen(key); @@ -496,6 +542,7 @@ VFYContext * VFY_CreateContextWithAlgorithmID(const SECKEYPublicKey *key, const SECItem *sig, const SECAlgorithmID *sigAlgorithm, SECOidTag *hash, void *wincx) { + VFYContext *cx; SECOidTag encAlg, hashAlg; SECStatus rv = sec_DecodeSigAlg(key, SECOID_GetAlgorithmTag((SECAlgorithmID *)sigAlgorithm), @@ -503,7 +550,13 @@ VFY_CreateContextWithAlgorithmID(const SECKEYPublicKey *key, const SECItem *sig, if (rv != SECSuccess) { return NULL; } - return vfy_CreateContext(key, sig, encAlg, hashAlg, hash, wincx); + + cx = vfy_CreateContext(key, sig, encAlg, hashAlg, hash, wincx); + if (sigAlgorithm->parameters.data) { + cx->params = SECITEM_DupItem(&sigAlgorithm->parameters); + } + + return cx; } void @@ -520,6 +573,9 @@ VFY_DestroyContext(VFYContext *cx, PRBool freeit) if (cx->pkcs1RSADigestInfo) { PORT_Free(cx->pkcs1RSADigestInfo); } + if (cx->params) { + SECITEM_FreeItem(cx->params, PR_TRUE); + } if (freeit) { PORT_ZFree(cx, sizeof(VFYContext)); } @@ -562,7 +618,7 @@ VFY_EndWithSignature(VFYContext *cx, SECItem *sig) { unsigned char final[HASH_LENGTH_MAX]; unsigned part; - SECItem hash, dsasig; /* dsasig is also used for ECDSA */ + SECItem hash, rsasig, dsasig; /* dsasig is also used for ECDSA */ SECStatus rv; if ((cx->hasSignature == PR_FALSE) && (sig == NULL)) { @@ -598,25 +654,70 @@ VFY_EndWithSignature(VFYContext *cx, SECItem *sig) return SECFailure; } break; - case rsaKey: { - SECItem digest; - digest.data = final; - digest.len = part; - if (sig) { - SECOidTag hashid; - PORT_Assert(cx->hashAlg != SEC_OID_UNKNOWN); - rv = recoverPKCS1DigestInfo(cx->hashAlg, &hashid, - &cx->pkcs1RSADigestInfo, - &cx->pkcs1RSADigestInfoLen, - cx->key, - sig, cx->wincx); - PORT_Assert(cx->hashAlg == hashid); + case rsaKey: + if (cx->encAlg == SEC_OID_PKCS1_RSA_PSS_SIGNATURE) { + CK_RSA_PKCS_PSS_PARAMS mech; + SECItem mechItem = { siBuffer, (unsigned char *)&mech, sizeof(mech) }; + SECKEYRSAPSSParams params; + PLArenaPool *arena; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + return SECFailure; + } + + PORT_Memset(¶ms, 0, sizeof(params)); + rv = SEC_QuickDERDecodeItem(arena, ¶ms, + SECKEY_RSAPSSParamsTemplate, + cx->params); + if (rv != SECSuccess) { + PORT_FreeArena(arena, PR_FALSE); + return SECFailure; + } + rv = sec_RSAPSSParamsToMechanism(&mech, ¶ms); + PORT_FreeArena(arena, PR_FALSE); if (rv != SECSuccess) { return SECFailure; } + rsasig.data = cx->u.buffer; + rsasig.len = SECKEY_SignatureLen(cx->key); + if (rsasig.len == 0) { + return SECFailure; + } + if (sig) { + if (sig->len != rsasig.len) { + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + return SECFailure; + } + PORT_Memcpy(rsasig.data, sig->data, rsasig.len); + } + hash.data = final; + hash.len = part; + if (PK11_VerifyWithMechanism(cx->key, CKM_RSA_PKCS_PSS, &mechItem, + &rsasig, &hash, cx->wincx) != SECSuccess) { + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + return SECFailure; + } + } else { + SECItem digest; + digest.data = final; + digest.len = part; + if (sig) { + SECOidTag hashid; + PORT_Assert(cx->hashAlg != SEC_OID_UNKNOWN); + rv = recoverPKCS1DigestInfo(cx->hashAlg, &hashid, + &cx->pkcs1RSADigestInfo, + &cx->pkcs1RSADigestInfoLen, + cx->key, + sig, cx->wincx); + PORT_Assert(cx->hashAlg == hashid); + if (rv != SECSuccess) { + return SECFailure; + } + } + return verifyPKCS1DigestInfo(cx, &digest); } - return verifyPKCS1DigestInfo(cx, &digest); - } + break; default: PORT_SetError(SEC_ERROR_BAD_SIGNATURE); return SECFailure; /* shouldn't happen */ @@ -722,7 +823,7 @@ VFY_VerifyDigestWithAlgorithmID(const SECItem *digest, static SECStatus vfy_VerifyData(const unsigned char *buf, int len, const SECKEYPublicKey *key, const SECItem *sig, SECOidTag encAlg, SECOidTag hashAlg, - SECOidTag *hash, void *wincx) + const SECItem *params, SECOidTag *hash, void *wincx) { SECStatus rv; VFYContext *cx; @@ -730,6 +831,9 @@ vfy_VerifyData(const unsigned char *buf, int len, const SECKEYPublicKey *key, cx = vfy_CreateContext(key, sig, encAlg, hashAlg, hash, wincx); if (cx == NULL) return SECFailure; + if (params) { + cx->params = SECITEM_DupItem(params); + } rv = VFY_Begin(cx); if (rv == SECSuccess) { @@ -748,7 +852,7 @@ VFY_VerifyDataDirect(const unsigned char *buf, int len, SECOidTag encAlg, SECOidTag hashAlg, SECOidTag *hash, void *wincx) { - return vfy_VerifyData(buf, len, key, sig, encAlg, hashAlg, hash, wincx); + return vfy_VerifyData(buf, len, key, sig, encAlg, hashAlg, NULL, hash, wincx); } SECStatus @@ -760,7 +864,7 @@ VFY_VerifyData(const unsigned char *buf, int len, const SECKEYPublicKey *key, if (rv != SECSuccess) { return rv; } - return vfy_VerifyData(buf, len, key, sig, encAlg, hashAlg, NULL, wincx); + return vfy_VerifyData(buf, len, key, sig, encAlg, hashAlg, NULL, NULL, wincx); } SECStatus @@ -777,5 +881,6 @@ VFY_VerifyDataWithAlgorithmID(const unsigned char *buf, int len, if (rv != SECSuccess) { return rv; } - return vfy_VerifyData(buf, len, key, sig, encAlg, hashAlg, hash, wincx); + return vfy_VerifyData(buf, len, key, sig, encAlg, hashAlg, + &sigAlgorithm->parameters, hash, wincx); } diff --git a/lib/nss/nss.def b/lib/nss/nss.def index b61ce023ae..c1d2b31040 100644 --- a/lib/nss/nss.def +++ b/lib/nss/nss.def @@ -1123,3 +1123,12 @@ CERT_FindCertByNicknameOrEmailAddrForUsageCX; ;+ local: ;+ *; ;+}; +;+NSS_3.34 { # NSS 3.34 release +;+ global: +SGN_NewContextWithAlgorithmID; +SEC_SignDataWithAlgorithmID; +SEC_DerSignDataWithAlgorithmID; +SEC_CreateSignatureAlgorithmParameters; +;+ local: +;+ *; +;+}; diff --git a/nss.gyp b/nss.gyp index 76302d7765..a52243c1f9 100644 --- a/nss.gyp +++ b/nss.gyp @@ -163,6 +163,7 @@ 'cmd/vfychain/vfychain.gyp:vfychain', 'cmd/vfyserv/vfyserv.gyp:vfyserv', 'gtests/certhigh_gtest/certhigh_gtest.gyp:certhigh_gtest', + 'gtests/cryptohi_gtest/cryptohi_gtest.gyp:cryptohi_gtest', 'gtests/der_gtest/der_gtest.gyp:der_gtest', 'gtests/certdb_gtest/certdb_gtest.gyp:certdb_gtest', 'gtests/freebl_gtest/freebl_gtest.gyp:prng_gtest', diff --git a/tests/cert/cert.sh b/tests/cert/cert.sh index 6077d5eea3..9fb29c6458 100755 --- a/tests/cert/cert.sh +++ b/tests/cert/cert.sh @@ -510,7 +510,11 @@ cert_all_CA() # dsaroot.cert in $CLIENT_CADIR and in $SERVER_CADIR is one of the last # in the chain - +# +# Create RSA-PSS version of TestCA + ALL_CU_SUBJECT="CN=NSS Test CA (RSA-PSS), O=BOGUS NSS, L=Mountain View, ST=California, C=US" + cert_rsa_pss_CA $CADIR TestCA-rsa-pss -x "CTu,CTu,CTu" ${D_CA} "1" SHA256 + rm $CLIENT_CADIR/rsapssroot.cert $SERVER_CADIR/rsapssroot.cert # @@ -627,7 +631,7 @@ CERTSCRIPT ################################ cert_dsa_CA ############################# # local shell function to build the Temp. Certificate Authority (CA) # used for testing purposes, creating a CA Certificate and a root cert -# This is the ECC version of cert_CA. +# This is the DSA version of cert_CA. ########################################################################## cert_dsa_CA() { @@ -638,7 +642,7 @@ cert_dsa_CA() DOMAIN=$5 CERTSERIAL=$6 - echo "$SCRIPTNAME: Creating an DSA CA Certificate $NICKNAME ==========================" + echo "$SCRIPTNAME: Creating a DSA CA Certificate $NICKNAME ==========================" if [ ! -d "${CUR_CADIR}" ]; then mkdir -p "${CUR_CADIR}" @@ -651,7 +655,7 @@ cert_dsa_CA() LPROFILE="multiaccess:${DOMAIN}" fi - ################# Creating an DSA CA Cert ############################### + ################# Creating a DSA CA Cert ############################### # CU_ACTION="Creating DSA CA Cert $NICKNAME " CU_SUBJECT=$ALL_CU_SUBJECT @@ -690,6 +694,79 @@ CERTSCRIPT + +################################ cert_rsa_pss_CA ############################# +# local shell function to build the Temp. Certificate Authority (CA) +# used for testing purposes, creating a CA Certificate and a root cert +# This is the RSA-PSS version of cert_CA. +########################################################################## +cert_rsa_pss_CA() +{ + CUR_CADIR=$1 + NICKNAME=$2 + SIGNER=$3 + TRUSTARG=$4 + DOMAIN=$5 + CERTSERIAL=$6 + HASHALG=$7 + + echo "$SCRIPTNAME: Creating an RSA-PSS CA Certificate $NICKNAME ==========================" + + if [ ! -d "${CUR_CADIR}" ]; then + mkdir -p "${CUR_CADIR}" + fi + cd ${CUR_CADIR} + pwd + + LPROFILE=. + if [ -n "${MULTIACCESS_DBM}" ]; then + LPROFILE="multiaccess:${DOMAIN}" + fi + + HASHOPT= + if [ -n "$HASHALG" ]; then + HASHOPT="-Z $HASHALG" + fi + + ################# Creating an RSA-PSS CA Cert ############################### + # + CU_ACTION="Creating RSA-PSS CA Cert $NICKNAME " + CU_SUBJECT=$ALL_CU_SUBJECT + certu -S -n $NICKNAME -k rsa --pss $HASHOPT -t $TRUSTARG -v 600 $SIGNER \ + -d ${LPROFILE} -1 -2 -5 -f ${R_PWFILE} -z ${R_NOISE_FILE} \ + -m $CERTSERIAL 2>&1 < ${TMP}/signalgo.txt + + diff ${TMP}/signalgo.exp ${TMP}/signalgo.txt + RET=$? + if [ "$RET" -ne 0 ]; then + CERTFAILED=$RET + html_failed "${CU_ACTION} ($RET) " + cert_log "ERROR: ${CU_ACTION} failed $RET" + else + html_passed "${CU_ACTION}" + fi +} + +cert_test_rsapss() +{ + TEMPFILES="$TEMPFILES ${TMP}/signalgo.exp ${TMP}/signalgo.txt" + + cert_init_cert "${RSAPSSDIR}" "RSA-PSS Test Cert" 1000 "${D_RSAPSS}" + + CU_ACTION="Initialize Cert DB" + certu -N -d "${PROFILEDIR}" -f "${R_PWFILE}" 2>&1 + + CU_ACTION="Import RSA CA Cert" + certu -A -n "TestCA" -t "C,," -d "${PROFILEDIR}" -f "${R_PWFILE}" \ + -i "${R_CADIR}/TestCA.ca.cert" 2>&1 + + CU_ACTION="Import RSA-PSS CA Cert" + certu -A -n "TestCA-rsa-pss" -t "C,," -d "${PROFILEDIR}" -f "${R_PWFILE}" \ + -i "${R_CADIR}/TestCA-rsa-pss.ca.cert" 2>&1 + + CU_ACTION="Verify RSA-PSS CA Cert" + certu -V -u L -e -n "TestCA-rsa-pss" -d "${PROFILEDIR}" -f "${R_PWFILE}" + + # Subject certificate: RSA + # Issuer certificate: RSA + # Signature: RSA-PSS (explicit, with --pss-sign) + CERTNAME="TestUser-rsa-pss1" + + CU_ACTION="Generate Cert Request for $CERTNAME" + CU_SUBJECT="CN=$CERTNAME, E=${CERTNAME}@bogus.com, O=BOGUS NSS, L=Mountain View, ST=California, C=US" + certu -R -d "${PROFILEDIR}" -f "${R_PWFILE}" -z "${R_NOISE_FILE}" -o req 2>&1 + + CU_ACTION="Sign ${CERTNAME}'s Request" + certu -C -c "TestCA" --pss-sign -m 200 -v 60 -d "${P_R_CADIR}" \ + -i req -o "${CERTNAME}.cert" -f "${R_PWFILE}" "$1" 2>&1 + + CU_ACTION="Import $CERTNAME's Cert" + certu -A -n "$CERTNAME" -t ",," -d "${PROFILEDIR}" -f "${R_PWFILE}" \ + -i "${CERTNAME}.cert" 2>&1 + + CU_ACTION="Verify $CERTNAME's Cert" + certu -V -u V -e -n "$CERTNAME" -d "${PROFILEDIR}" -f "${R_PWFILE}" + cat > ${TMP}/signalgo.exp <&1 + + CU_ACTION="Sign ${CERTNAME}'s Request" + certu -C -c "TestCA" --pss-sign -Z SHA512 -m 201 -v 60 -d "${P_R_CADIR}" \ + -i req -o "${CERTNAME}.cert" -f "${R_PWFILE}" "$1" 2>&1 + + CU_ACTION="Import $CERTNAME's Cert" + certu -A -n "$CERTNAME" -t ",," -d "${PROFILEDIR}" -f "${R_PWFILE}" \ + -i "${CERTNAME}.cert" 2>&1 + + CU_ACTION="Verify $CERTNAME's Cert" + certu -V -u V -e -n "$CERTNAME" -d "${PROFILEDIR}" -f "${R_PWFILE}" + cat > ${TMP}/signalgo.exp <&1 + + CU_ACTION="Sign ${CERTNAME}'s Request" + certu -C -c "TestCA-rsa-pss" -m 202 -v 60 -d "${P_R_CADIR}" \ + -i req -o "${CERTNAME}.cert" -f "${R_PWFILE}" "$1" 2>&1 + + CU_ACTION="Import $CERTNAME's Cert" + certu -A -n "$CERTNAME" -t ",," -d "${PROFILEDIR}" -f "${R_PWFILE}" \ + -i "${CERTNAME}.cert" 2>&1 + + CU_ACTION="Verify $CERTNAME's Cert" + certu -V -u V -e -n "$CERTNAME" -d "${PROFILEDIR}" -f "${R_PWFILE}" + cat > ${TMP}/signalgo.exp <&1 + + CU_ACTION="Sign ${CERTNAME}'s Request" + certu -C -c "TestCA" --pss-sign -m 203 -v 60 -d "${P_R_CADIR}" \ + -i req -o "${CERTNAME}.cert" -f "${R_PWFILE}" "$1" 2>&1 + + CU_ACTION="Import $CERTNAME's Cert" + certu -A -n "$CERTNAME" -t ",," -d "${PROFILEDIR}" -f "${R_PWFILE}" \ + -i "${CERTNAME}.cert" 2>&1 + + CU_ACTION="Verify $CERTNAME's Cert" + certu -V -u V -e -n "$CERTNAME" -d "${PROFILEDIR}" -f "${R_PWFILE}" + cat > ${TMP}/signalgo.exp <&1 + + CU_ACTION="Sign ${CERTNAME}'s Request" + certu -C -c "TestCA-rsa-pss" --pss-sign -m 204 -v 60 -d "${P_R_CADIR}" \ + -i req -o "${CERTNAME}.cert" -f "${R_PWFILE}" "$1" 2>&1 + + CU_ACTION="Import $CERTNAME's Cert" + certu -A -n "$CERTNAME" -t ",," -d "${PROFILEDIR}" -f "${R_PWFILE}" \ + -i "${CERTNAME}.cert" 2>&1 + + CU_ACTION="Verify $CERTNAME's Cert" + certu -V -u V -e -n "$CERTNAME" -d "${PROFILEDIR}" -f "${R_PWFILE}" + cat > ${TMP}/signalgo.exp <&1 + + CU_ACTION="Sign ${CERTNAME}'s Request" + certu -C -c "TestCA-rsa-pss" -m 205 -v 60 -d "${P_R_CADIR}" \ + -i req -o "${CERTNAME}.cert" -f "${R_PWFILE}" "$1" 2>&1 + + CU_ACTION="Import $CERTNAME's Cert" + certu -A -n "$CERTNAME" -t ",," -d "${PROFILEDIR}" -f "${R_PWFILE}" \ + -i "${CERTNAME}.cert" 2>&1 + + CU_ACTION="Verify $CERTNAME's Cert" + certu -V -u V -e -n "$CERTNAME" -d "${PROFILEDIR}" -f "${R_PWFILE}" + cat > ${TMP}/signalgo.exp <&1 + + CU_ACTION="Sign ${CERTNAME}'s Request" + RETEXPECTED=255 + certu -C -c "TestCA-rsa-pss" --pss-sign -Z SHA512 -m 206 -v 60 -d "${P_R_CADIR}" \ + -i req -o "${CERTNAME}.cert" -f "${R_PWFILE}" "$1" 2>&1 + RETEXPECTED=0 + + # Subject certificate: RSA-PSS + # Issuer certificate: RSA-PSS + # Signature: RSA-PSS (with compatible hash algorithm) + CERTNAME="TestUser-rsa-pss8" + + CU_ACTION="Generate Cert Request for $CERTNAME" + CU_SUBJECT="CN=$CERTNAME, E=${CERTNAME}@bogus.com, O=BOGUS NSS, L=Mountain View, ST=California, C=US" + certu -R -d "${PROFILEDIR}" -f "${R_PWFILE}" -z "${R_NOISE_FILE}" --pss -o req 2>&1 + + CU_ACTION="Sign ${CERTNAME}'s Request" + certu -C -c "TestCA-rsa-pss" --pss-sign -Z SHA256 -m 207 -v 60 -d "${P_R_CADIR}" \ + -i req -o "${CERTNAME}.cert" -f "${R_PWFILE}" "$1" 2>&1 + + CU_ACTION="Import $CERTNAME's Cert" + certu -A -n "$CERTNAME" -t ",," -d "${PROFILEDIR}" -f "${R_PWFILE}" \ + -i "${CERTNAME}.cert" 2>&1 + + CU_ACTION="Verify $CERTNAME's Cert" + certu -V -u V -e -n "$CERTNAME" -d "${PROFILEDIR}" -f "${R_PWFILE}" + cat > ${TMP}/signalgo.exp <