--- a/cmd/certutil/certutil.c
+++ b/cmd/certutil/certutil.c
@@ -189,16 +189,18 @@ CertReq(SECKEYPrivateKey *privk, SECKEYP
CERTSubjectPublicKeyInfo *spki;
CERTCertificateRequest *cr;
SECItem *encoding;
SECOidTag signAlgTag;
SECStatus rv;
PLArenaPool *arena;
void *extHandle;
SECItem signedReq = { siBuffer, NULL, 0 };
+ SECAlgorithmID signAlg;
+ SECItem *params = NULL;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (!arena) {
SECU_PrintError(progName, "out of memory");
return SECFailure;
}
/* Create info about public key */
@@ -206,21 +208,35 @@ CertReq(SECKEYPrivateKey *privk, SECKEYP
if (!spki) {
PORT_FreeArena(arena, PR_FALSE);
SECU_PrintError(progName, "unable to create subject public key");
return SECFailure;
}
/* 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;
}
}
/* Generate certificate request */
cr = CERT_CreateCertificateRequest(subject, spki, NULL);
SECKEY_DestroySubjectPublicKeyInfo(spki);
@@ -251,26 +267,44 @@ CertReq(SECKEYPrivateKey *privk, SECKEYP
SEC_ASN1_GET(CERT_CertificateRequestTemplate));
CERT_DestroyCertificateRequest(cr);
if (encoding == NULL) {
PORT_FreeArena(arena, PR_FALSE);
SECU_PrintError(progName, "der encoding of request failed");
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;
+ }
+ }
+
/* 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;
- }
-
- rv = SEC_DerSignData(arena, &signedReq, encoding->data, encoding->len,
- privk, signAlgTag);
+ 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");
return SECFailure;
}
/* Encode request in specified format */
if (ascii) {
@@ -1860,56 +1894,129 @@ MakeV1Cert(CERTCertDBHandle *handle,
if (issuerCert) {
CERT_DestroyCertificate(issuerCert);
}
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;
}
switch (certVersion) {
case (SEC_CERTIFICATE_VERSION_1):
/* The initial version for x509 certificates is version one
* and this default value must be an implicit DER encoding. */
cert->version.data = NULL;
@@ -1918,30 +2025,32 @@ SignCert(CERTCertDBHandle *handle, CERTC
case (SEC_CERTIFICATE_VERSION_2):
case (SEC_CERTIFICATE_VERSION_3):
case 3: /* unspecified format (would be version 4 certificate). */
*(cert->version.data) = certVersion;
cert->version.len = 1;
break;
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
- return SECFailure;
+ rv = SECFailure;
+ goto done;
}
der.len = 0;
der.data = NULL;
dummy = SEC_ASN1EncodeItem(arena, &der, cert,
SEC_ASN1_GET(CERT_CertificateTemplate));
if (!dummy) {
fprintf(stderr, "Could not encode certificate.\n");
rv = SECFailure;
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
* when the arena is freed */
goto done;
}
done:
if (caPrivateKey) {
@@ -1964,16 +2073,17 @@ CreateCert(
int validityMonths,
const char *emailAddrs,
const char *dnsNames,
PRBool ascii,
PRBool selfsign,
certutilExtnList extnList,
const char *extGeneric,
int certVersion,
+ PRBool pssSign,
SECItem *certDER)
{
void *extHandle = NULL;
CERTCertificate *subjectCert = NULL;
CERTCertificateRequest *certReq = NULL;
SECStatus rv = SECSuccess;
CERTCertExtension **CRexts;
@@ -2024,17 +2134,17 @@ CreateCert(
fprintf(stderr, "Failed to locate private key.\n");
rv = SECFailure;
break;
}
}
rv = SignCert(handle, subjectCert, selfsign, hashAlgTag,
*selfsignprivkey, issuerNickName,
- certVersion, pwarg);
+ certVersion, pssSign, pwarg);
if (rv != SECSuccess)
break;
rv = SECFailure;
if (ascii) {
char *asciiDER = BTOA_DataToAscii(subjectCert->derCert.data,
subjectCert->derCert.len);
if (asciiDER) {
@@ -2347,16 +2457,17 @@ enum certutilOpts {
opt_KeyAttrFlags,
opt_EmptyPassword,
opt_CertVersion,
opt_AddSubjectAltNameExt,
opt_DumpExtensionValue,
opt_GenericExtensions,
opt_NewNickname,
opt_Pss,
+ opt_PssSign,
opt_Help
};
static const secuCommandFlag commands_init[] =
{
{ /* cmd_AddCert */ 'A', PR_FALSE, 0, PR_FALSE },
{ /* cmd_CreateNewCert */ 'C', PR_FALSE, 0, PR_FALSE },
{ /* cmd_DeleteCert */ 'D', PR_FALSE, 0, PR_FALSE },
@@ -2467,16 +2578,18 @@ static const secuCommandFlag options_ini
{ /* opt_DumpExtensionValue */ 0, PR_TRUE, 0, PR_FALSE,
"dump-ext-val" },
{ /* opt_GenericExtensions */ 0, PR_TRUE, 0, PR_FALSE,
"extGeneric" },
{ /* opt_NewNickname */ 0, PR_TRUE, 0, PR_FALSE,
"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]))
static secuCommandFlag certutil_commands[NUM_COMMANDS];
static secuCommandFlag certutil_options[NUM_OPTIONS];
static const secuCommand certutil = {
NUM_COMMANDS,
@@ -3358,16 +3471,35 @@ certutil_main(int argc, char **argv, PRB
if (keytype != rsaKey) {
PR_fprintf(PR_STDERR,
"%s -%c: --pss only works with RSA keys.\n",
progName, commandToRun);
return 255;
}
}
+ /* --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 ||
certutil.commands[cmd_CreateNewCert].activated) {
certutil_extns[ext_keyUsage].activated =
certutil.options[opt_AddCmdKeyUsageExt].activated;
if (!certutil_extns[ext_keyUsage].activated) {
certutil_extns[ext_keyUsage].activated =
@@ -3495,16 +3627,17 @@ certutil_main(int argc, char **argv, PRB
certutil.options[opt_ExtendedDNSNames].arg,
certutil.options[opt_ASCIIForIO].activated &&
certutil.commands[cmd_CreateNewCert].activated,
certutil.options[opt_SelfSign].activated,
certutil_extns,
(certutil.options[opt_GenericExtensions].activated ? certutil.options[opt_GenericExtensions].arg
: NULL),
certVersion,
+ certutil.options[opt_PssSign].activated,
&certDER);
if (rv)
goto shutdown;
}
/*
* Adding a cert to the database (or slot)
*/
--- a/cmd/lib/secutil.c
+++ b/cmd/lib/secutil.c
@@ -1307,25 +1307,22 @@ SECU_PrintAlgorithmID(FILE *out, SECAlgo
break;
default:
secu_PrintPBEParams(out, &a->parameters, "Parameters", level + 1);
break;
}
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);
}
}
static void
secu_PrintAttribute(FILE *out, SEC_PKCS7Attribute *attr, char *m, int level)
new file mode 100644
--- /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). #
+#######################################################################
new file mode 100644
--- /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'
+ }
+}
new file mode 100644
--- /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 <string>
+
+#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<std::tuple<SECOidTag, SECOidTag>> {};
+
+class SignParamsSourceTest : public SignParamsTestF,
+ public ::testing::WithParamInterface<SECOidTag> {};
+
+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
new file mode 100644
--- /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)
--- a/gtests/manifest.mn
+++ b/gtests/manifest.mn
@@ -16,16 +16,17 @@ UTIL_SRCDIRS = \
$(NULL)
endif
ifneq ($(NSS_BUILD_SOFTOKEN_ONLY),1)
ifneq ($(NSS_BUILD_UTIL_ONLY),1)
NSS_SRCDIRS = \
certdb_gtest \
certhigh_gtest \
+ cryptohi_gtest \
der_gtest \
pk11_gtest \
softoken_gtest \
ssl_gtest \
nss_bogo_shim \
$(NULL)
endif
endif
--- a/gtests/ssl_gtest/ssl_auth_unittest.cc
+++ b/gtests/ssl_gtest/ssl_auth_unittest.cc
@@ -24,17 +24,35 @@ namespace nss_test {
TEST_P(TlsConnectGeneric, ServerAuthBigRsa) {
Reset(TlsAgent::kRsa2048);
Connect();
CheckKeys();
}
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;
EXPECT_TRUE(client_->GetPeerChainLength(&chain_length));
EXPECT_EQ(2UL, chain_length);
}
TEST_P(TlsConnectGeneric, ClientAuth) {
--- a/gtests/ssl_gtest/tls_agent.cc
+++ b/gtests/ssl_gtest/tls_agent.cc
@@ -31,17 +31,16 @@ namespace nss_test {
const char* TlsAgent::states[] = {"INIT", "CONNECTING", "CONNECTED", "ERROR"};
const std::string TlsAgent::kClient = "client"; // both sign and encrypt
const std::string TlsAgent::kRsa2048 = "rsa2048"; // bigger
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";
const std::string TlsAgent::kServerEcdhRsa = "ecdh_rsa";
const std::string TlsAgent::kServerEcdhEcdsa = "ecdh_ecdsa";
const std::string TlsAgent::kServerDsa = "dsa";
TlsAgent::TlsAgent(const std::string& name, Role role,
--- a/gtests/ssl_gtest/tls_agent.h
+++ b/gtests/ssl_gtest/tls_agent.h
@@ -61,17 +61,16 @@ class TlsAgent : public PollTarget {
enum State { STATE_INIT, STATE_CONNECTING, STATE_CONNECTED, STATE_ERROR };
static const std::string kClient; // the client key is sign only
static const std::string kRsa2048; // bigger sign and encrypt for either
static const std::string kServerRsa; // both sign and encrypt
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;
static const std::string kServerEcdhEcdsa;
static const std::string kServerEcdhRsa;
static const std::string kServerDsa;
TlsAgent(const std::string& name, Role role, SSLProtocolVariant variant);
--- a/lib/certdb/certdb.c
+++ b/lib/certdb/certdb.c
@@ -1187,16 +1187,17 @@ CERT_CheckKeyUsage(CERTCertificate *cert
KeyType keyType = CERT_GetCertKeyType(&cert->subjectPublicKeyInfo);
/* turn off the special bit */
requiredUsage &= (~KU_KEY_AGREEMENT_OR_ENCIPHERMENT);
switch (keyType) {
case rsaKey:
requiredUsage |= KU_KEY_ENCIPHERMENT;
break;
+ case rsaPssKey:
case dsaKey:
requiredUsage |= KU_DIGITAL_SIGNATURE;
break;
case dhKey:
requiredUsage |= KU_KEY_AGREEMENT;
break;
case ecKey:
/* Accept either signature or agreement. */
--- a/lib/cryptohi/cryptohi.h
+++ b/lib/cryptohi/cryptohi.h
@@ -55,16 +55,24 @@ extern SECItem *DSAU_DecodeDerSigToLen(c
/*
** Create a new signature context used for signing a data stream.
** "alg" the signature algorithm to use (e.g. SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION)
** "privKey" the private key to use
*/
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
** "freeit" if PR_TRUE then free the object as well as its sub-objects
*/
extern void SGN_DestroyContext(SGNContext *cx, PRBool freeit);
/*
** Reset the signing context "cx" to its initial state, preparing it for
@@ -101,16 +109,31 @@ extern SECStatus SGN_End(SGNContext *cx,
** "algid" the signature/hash algorithm to sign with
** (must be compatible with the key type).
*/
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.
** "result" the final signature data (memory is allocated)
** "digest" the digest to sign
** "privKey" the private key to encrypt with
** "algtag" The algorithm tag to encode (need for RSA only)
*/
extern SECStatus SGN_Digest(SECKEYPrivateKey *privKey,
@@ -127,30 +150,68 @@ extern SECStatus SGN_Digest(SECKEYPrivat
** "len" the amount of data to sign
** "pk" the private key to encrypt with
*/
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
** "freeit" if PR_TRUE then free the object as well as its sub-objects
*/
extern void SEC_DestroySignedData(CERTSignedData *sd, PRBool freeit);
/*
** Get the signature algorithm tag number for the given key type and hash
** algorithm tag. Returns SEC_OID_UNKNOWN if key type and hash algorithm
** do not match or are not supported.
*/
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
*/
/*
** Create a signature verification context. This version is deprecated,
** This function is deprecated. Use VFY_CreateContextDirect or
--- a/lib/cryptohi/keyi.h
+++ b/lib/cryptohi/keyi.h
@@ -12,11 +12,14 @@ SEC_BEGIN_PROTOS
KeyType seckey_GetKeyType(SECOidTag pubKeyOid);
/* extract the 'encryption' (could be signing) and hash oids from and
* algorithm, key and parameters (parameters is the parameters field
* of a algorithm ID structure (SECAlgorithmID)*/
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_ */
--- a/lib/cryptohi/seckey.c
+++ b/lib/cryptohi/seckey.c
@@ -1043,16 +1043,17 @@ SECKEY_PublicKeyStrengthInBits(const SEC
unsigned
SECKEY_SignatureLen(const SECKEYPublicKey *pubk)
{
unsigned char b0;
unsigned size;
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:
return pubk->u.dsa.params.subPrime.len * 2;
case ecKey:
/* Get the base point order length in bits and adjust */
size = SECKEY_ECParamsToBasePointOrderLen(
&pubk->u.ec.DEREncodedParams);
@@ -1969,8 +1970,97 @@ SECKEY_GetECCOid(const SECKEYECParams *p
return 0;
oid.len = params->len - 2;
oid.data = params->data + 2;
if ((oidData = SECOID_FindOID(&oid)) == NULL)
return 0;
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;
+}
--- a/lib/cryptohi/secsign.c
+++ b/lib/cryptohi/secsign.c
@@ -17,57 +17,73 @@
#include "keyi.h"
struct SGNContextStr {
SECOidTag signalg;
SECOidTag hashalg;
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;
KeyType keyType;
SECStatus rv;
/* OK, map a PKCS #7 hash and encrypt algorithm into
* a standard hashing algorithm. Why did we pass in the whole
* PKCS #7 algTag if we were just going to change here you might
* ask. Well the answer is for some cards we may have to do the
* hashing on card. It may not support CKM_RSA_PKCS sign algorithm,
* 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;
}
keyType = seckey_GetKeyType(signalg);
/* 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;
}
cx = (SGNContext *)PORT_ZAlloc(sizeof(SGNContext));
if (cx) {
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)
{
if (cx) {
if (cx->hashcx != NULL) {
(*cx->hashobj->destroy)(cx->hashcx, PR_TRUE);
cx->hashcx = NULL;
}
@@ -143,25 +159,27 @@ SGN_End(SGNContext *cx, SECItem *result)
SECStatus rv;
SECItem digder, sigitem;
PLArenaPool *arena = 0;
SECKEYPrivateKey *privKey = cx->key;
SGNDigestInfo *di = 0;
result->data = 0;
digder.data = 0;
+ sigitem.data = 0;
/* Finish up digest function */
if (cx->hashcx == NULL) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
(*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) {
rv = SECFailure;
goto loser;
}
/* Construct digest info */
@@ -195,57 +213,92 @@ SGN_End(SGNContext *cx, SECItem *result)
sigitem.len = signatureLen;
sigitem.data = (unsigned char *)PORT_Alloc(signatureLen);
if (sigitem.data == NULL) {
rv = SECFailure;
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);
}
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)
+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;
rv = SGN_Begin(sgn);
if (rv != SECSuccess)
goto loser;
@@ -255,16 +308,35 @@ SEC_SignData(SECItem *res, const unsigne
rv = SGN_End(sgn, res);
loser:
SGN_DestroyContext(sgn, PR_TRUE);
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[] =
{
{ DER_SEQUENCE,
0, NULL, sizeof(CERTSignedData) },
{ DER_ANY,
offsetof(CERTSignedData, data) },
@@ -289,20 +361,20 @@ const SEC_ASN1Template CERT_SignedDataTe
SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
{ SEC_ASN1_BIT_STRING,
offsetof(CERTSignedData, signature) },
{ 0 }
};
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;
SECStatus rv;
it.data = 0;
/* XXX We should probably have some asserts here to make sure the key type
@@ -334,40 +406,58 @@ SEC_DerSignData(PLArenaPool *arena, SECI
break;
default:
PORT_SetError(SEC_ERROR_INVALID_KEY);
return SECFailure;
}
}
/* Sign input buffer */
- rv = SEC_SignData(&it, buf, len, pk, algID);
+ rv = sec_SignData(&it, buf, len, pk, algID, params);
if (rv)
goto loser;
/* Fill out SignedData object */
PORT_Memset(&sd, 0, sizeof(sd));
sd.data.data = (unsigned char *)buf;
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;
/* DER encode the signed data object */
rv = DER_Encode(arena, result, CERTSignedDataTemplate, &sd);
/* FALL THROUGH */
loser:
PORT_Free(it.data);
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)
{
int modulusLen;
SECStatus rv;
SECItem digder;
PLArenaPool *arena = 0;
SGNDigestInfo *di = 0;
@@ -504,8 +594,231 @@ SEC_GetSignatureAlgorithmOidTag(KeyType
default:
break;
}
default:
break;
}
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;
+ }
+}
--- a/lib/cryptohi/secvfy.c
+++ b/lib/cryptohi/secvfy.c
@@ -131,28 +131,31 @@ struct VFYContextStr {
*/
union {
unsigned char buffer[1];
/* the full DSA signature... 40 bytes */
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 */
unsigned char *pkcs1RSADigestInfo;
void *wincx;
void *hashcx;
const SECHashObject *hashobj;
SECOidTag encAlg; /* enc alg */
PRBool hasSignature; /* true if the signature was provided in the
* VFY_CreateContext call. If false, the
* signature must be provided with a
* VFY_EndWithSignature call. */
+ SECItem *params;
};
static SECStatus
verifyPKCS1DigestInfo(const VFYContext *cx, const SECItem *digest)
{
SECItem pkcs1DigestInfo;
pkcs1DigestInfo.data = cx->pkcs1RSADigestInfo;
pkcs1DigestInfo.len = cx->pkcs1RSADigestInfoLen;
@@ -245,18 +248,47 @@ sec_DecodeSigAlg(const SECKEYPublicKey *
*hashalg = SEC_OID_MD5;
break;
case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION:
case SEC_OID_ISO_SHA_WITH_RSA_SIGNATURE:
case SEC_OID_ISO_SHA1_WITH_RSA_SIGNATURE:
*hashalg = SEC_OID_SHA1;
break;
case SEC_OID_PKCS1_RSA_ENCRYPTION:
+ *hashalg = SEC_OID_UNKNOWN; /* get it from the RSA signature */
+ break;
case SEC_OID_PKCS1_RSA_PSS_SIGNATURE:
- *hashalg = SEC_OID_UNKNOWN; /* get it from the RSA 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:
case SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA224_DIGEST:
*hashalg = SEC_OID_SHA224;
break;
case SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE:
@@ -429,16 +461,30 @@ vfy_CreateContext(const SECKEYPublicKey
switch (type) {
case rsaKey:
rv = recoverPKCS1DigestInfo(hashAlg, &cx->hashAlg,
&cx->pkcs1RSADigestInfo,
&cx->pkcs1RSADigestInfoLen,
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);
if (sigLen == 0) {
/* error set by SECKEY_SignatureLen */
rv = SECFailure;
break;
}
@@ -491,40 +537,50 @@ VFY_CreateContextDirect(const SECKEYPubl
{
return vfy_CreateContext(key, sig, encAlg, hashAlg, hash, wincx);
}
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),
&sigAlgorithm->parameters, &encAlg, &hashAlg);
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
VFY_DestroyContext(VFYContext *cx, PRBool freeit)
{
if (cx) {
if (cx->hashcx != NULL) {
(*cx->hashobj->destroy)(cx->hashcx, PR_TRUE);
cx->hashcx = NULL;
}
if (cx->key) {
SECKEY_DestroyPublicKey(cx->key);
}
if (cx->pkcs1RSADigestInfo) {
PORT_Free(cx->pkcs1RSADigestInfo);
}
+ if (cx->params) {
+ SECITEM_FreeItem(cx->params, PR_TRUE);
+ }
if (freeit) {
PORT_ZFree(cx, sizeof(VFYContext));
}
}
}
SECStatus
VFY_Begin(VFYContext *cx)
@@ -557,17 +613,17 @@ VFY_Update(VFYContext *cx, const unsigne
return SECSuccess;
}
SECStatus
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)) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
if (cx->hashcx == NULL) {
@@ -593,35 +649,80 @@ VFY_EndWithSignature(VFYContext *cx, SEC
}
hash.data = final;
hash.len = part;
if (PK11_Verify(cx->key, &dsasig, &hash, cx->wincx) != SECSuccess) {
PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
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 */
}
return SECSuccess;
}
SECStatus
@@ -717,24 +818,27 @@ VFY_VerifyDigestWithAlgorithmID(const SE
return SECFailure;
}
return vfy_VerifyDigest(digest, key, sig, encAlg, hashAlg, wincx);
}
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;
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) {
rv = VFY_Update(cx, (unsigned char *)buf, len);
if (rv == SECSuccess)
rv = VFY_End(cx);
}
@@ -743,39 +847,40 @@ vfy_VerifyData(const unsigned char *buf,
}
SECStatus
VFY_VerifyDataDirect(const unsigned char *buf, int len,
const SECKEYPublicKey *key, const SECItem *sig,
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
VFY_VerifyData(const unsigned char *buf, int len, const SECKEYPublicKey *key,
const SECItem *sig, SECOidTag algid, void *wincx)
{
SECOidTag encAlg, hashAlg;
SECStatus rv = sec_DecodeSigAlg(key, algid, NULL, &encAlg, &hashAlg);
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
VFY_VerifyDataWithAlgorithmID(const unsigned char *buf, int len,
const SECKEYPublicKey *key,
const SECItem *sig,
const SECAlgorithmID *sigAlgorithm,
SECOidTag *hash, void *wincx)
{
SECOidTag encAlg, hashAlg;
SECOidTag sigAlg = SECOID_GetAlgorithmTag((SECAlgorithmID *)sigAlgorithm);
SECStatus rv = sec_DecodeSigAlg(key, sigAlg,
&sigAlgorithm->parameters, &encAlg, &hashAlg);
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);
}
--- a/lib/nss/nss.def
+++ b/lib/nss/nss.def
@@ -1118,8 +1118,17 @@ PK11_GetTokenURI;
;+NSS_3.33 { # NSS 3.33 release
;+ global:
CERT_FindCertByIssuerAndSNCX;
CERT_FindCertByNicknameOrEmailAddrCX;
CERT_FindCertByNicknameOrEmailAddrForUsageCX;
;+ local:
;+ *;
;+};
+;+NSS_3.34 { # NSS 3.34 release
+;+ global:
+SGN_NewContextWithAlgorithmID;
+SEC_SignDataWithAlgorithmID;
+SEC_DerSignDataWithAlgorithmID;
+SEC_CreateSignatureAlgorithmParameters;
+;+ local:
+;+ *;
+;+};
--- a/nss.gyp
+++ b/nss.gyp
@@ -158,16 +158,17 @@
'cmd/tests/tests.gyp:encodeinttest',
'cmd/tests/tests.gyp:nonspr10',
'cmd/tests/tests.gyp:remtest',
'cmd/tests/tests.gyp:secmodtest',
'cmd/tstclnt/tstclnt.gyp:tstclnt',
'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',
'gtests/freebl_gtest/freebl_gtest.gyp:blake2b_gtest',
'gtests/pk11_gtest/pk11_gtest.gyp:pk11_gtest',
'gtests/softoken_gtest/softoken_gtest.gyp:softoken_gtest',
'gtests/ssl_gtest/ssl_gtest.gyp:ssl_gtest',
'gtests/util_gtest/util_gtest.gyp:util_gtest',
--- a/tests/cert/cert.sh
+++ b/tests/cert/cert.sh
@@ -505,17 +505,21 @@ cert_all_CA()
cert_dsa_CA $CLIENT_CADIR chain-1-clientCA-dsa "-c clientCA-dsa" "u,u,u" ${D_CLIENT_CA} "6"
ALL_CU_SUBJECT="CN=NSS Chain2 Client Test CA (DSA), O=BOGUS NSS, L=Santa Clara, ST=California, C=US"
cert_dsa_CA $CLIENT_CADIR chain-2-clientCA-dsa "-c chain-1-clientCA-dsa" "u,u,u" ${D_CLIENT_CA} "7"
rm $CLIENT_CADIR/dsaroot.cert $SERVER_CADIR/dsaroot.cert
# 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
#
# Create EC version of TestCA
CA_CURVE="secp521r1"
ALL_CU_SUBJECT="CN=NSS Test CA (ECC), O=BOGUS NSS, L=Mountain View, ST=California, C=US"
cert_ec_CA $CADIR TestCA-ec -x "CTu,CTu,CTu" ${D_CA} "1" ${CA_CURVE}
#
@@ -622,41 +626,41 @@ 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()
{
CUR_CADIR=$1
NICKNAME=$2
SIGNER=$3
TRUSTARG=$4
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}"
fi
cd ${CUR_CADIR}
pwd
LPROFILE=.
if [ -n "${MULTIACCESS_DBM}" ]; then
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
certu -S -n $NICKNAME -k dsa -t $TRUSTARG -v 600 $SIGNER \
-d ${LPROFILE} -1 -2 -5 -f ${R_PWFILE} -z ${R_NOISE_FILE} \
-m $CERTSERIAL 2>&1 <<CERTSCRIPT
5
6
@@ -685,16 +689,89 @@ CERTSCRIPT
Exit 7 "Fatal - failed to export dsa root cert"
fi
cp dsaroot.cert ${NICKNAME}.ca.cert
}
+
+################################ 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 <<CERTSCRIPT
+5
+6
+9
+n
+y
+-1
+n
+5
+6
+7
+9
+n
+CERTSCRIPT
+
+ if [ "$RET" -ne 0 ]; then
+ echo "return value is $RET"
+ Exit 6 "Fatal - failed to create RSA-PSS CA cert"
+ fi
+
+ ################# Exporting RSA-PSS Root Cert ###############################
+ #
+ CU_ACTION="Exporting RSA-PSS Root Cert"
+ certu -L -n $NICKNAME -r -d ${LPROFILE} -o rsapssroot.cert
+ if [ "$RET" -ne 0 ]; then
+ Exit 7 "Fatal - failed to export RSA-PSS root cert"
+ fi
+ cp rsapssroot.cert ${NICKNAME}.ca.cert
+}
+
+
+
+
################################ cert_ec_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.
##########################################################################
cert_ec_CA()
{
CUR_CADIR=$1
@@ -1968,16 +2045,273 @@ cert_test_implicit_db_init()
{
echo "$SCRIPTNAME: test implicit database init"
CU_ACTION="Add cert with trust flags to db with implicit init"
mkdir ${IMPLICIT_INIT_DIR}
certu -A -n ca -t 'C,C,C' -d ${P_R_IMPLICIT_INIT_DIR} -i "${SERVER_CADIR}/serverCA.ca.cert"
}
+check_sign_algo()
+{
+ certu -L -n "$CERTNAME" -d "${PROFILEDIR}" -f "${R_PWFILE}" | \
+ sed -n '/^ *Data:/,/^$/{
+/^ Signature Algorithm/,/^ *Salt Length/s/^ //p
+}' > ${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 <<EOF
+Signature Algorithm: PKCS #1 RSA-PSS Signature
+ Parameters:
+ Hash algorithm: SHA-256
+ Mask algorithm: PKCS #1 MGF1 Mask Generation Function
+ Mask hash algorithm: SHA-256
+ Salt Length: 32 (0x20)
+EOF
+ check_sign_algo
+
+ # Subject certificate: RSA
+ # Issuer certificate: RSA
+ # Signature: RSA-PSS (explict, with --pss-sign -Z SHA512)
+ CERTNAME="TestUser-rsa-pss2"
+
+ 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 -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 <<EOF
+Signature Algorithm: PKCS #1 RSA-PSS Signature
+ Parameters:
+ Hash algorithm: SHA-512
+ Mask algorithm: PKCS #1 MGF1 Mask Generation Function
+ Mask hash algorithm: SHA-512
+ Salt Length: 64 (0x40)
+EOF
+ check_sign_algo
+
+ # Subject certificate: RSA
+ # Issuer certificate: RSA-PSS
+ # Signature: RSA-PSS
+ CERTNAME="TestUser-rsa-pss3"
+
+ 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-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 <<EOF
+Signature Algorithm: PKCS #1 RSA-PSS Signature
+ Parameters:
+ Hash algorithm: SHA-256
+ Mask algorithm: PKCS #1 MGF1 Mask Generation Function
+ Mask hash algorithm: SHA-256
+ Salt Length: 32 (0x20)
+EOF
+ check_sign_algo
+
+ # Subject certificate: RSA-PSS
+ # Issuer certificate: RSA
+ # Signature: RSA-PSS (explicit, with --pss-sign)
+ CERTNAME="TestUser-rsa-pss4"
+
+ 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" --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 <<EOF
+Signature Algorithm: PKCS #1 RSA-PSS Signature
+ Parameters:
+ Hash algorithm: SHA-256
+ Mask algorithm: PKCS #1 MGF1 Mask Generation Function
+ Mask hash algorithm: SHA-256
+ Salt Length: 32 (0x20)
+EOF
+ check_sign_algo
+
+ # Subject certificate: RSA-PSS
+ # Issuer certificate: RSA-PSS
+ # Signature: RSA-PSS (explicit, with --pss-sign)
+ CERTNAME="TestUser-rsa-pss5"
+
+ 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 -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 <<EOF
+Signature Algorithm: PKCS #1 RSA-PSS Signature
+ Parameters:
+ Hash algorithm: SHA-256
+ Mask algorithm: PKCS #1 MGF1 Mask Generation Function
+ Mask hash algorithm: SHA-256
+ Salt Length: 32 (0x20)
+EOF
+ check_sign_algo
+
+ # Subject certificate: RSA-PSS
+ # Issuer certificate: RSA-PSS
+ # Signature: RSA-PSS (implicit, without --pss-sign)
+ CERTNAME="TestUser-rsa-pss6"
+
+ 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" -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 <<EOF
+Signature Algorithm: PKCS #1 RSA-PSS Signature
+ Parameters:
+ Hash algorithm: SHA-256
+ Mask algorithm: PKCS #1 MGF1 Mask Generation Function
+ Mask hash algorithm: SHA-256
+ Salt Length: 32 (0x20)
+EOF
+ check_sign_algo
+
+ # Subject certificate: RSA-PSS
+ # Issuer certificate: RSA-PSS
+ # Signature: RSA-PSS (with conflicting hash algorithm)
+ CERTNAME="TestUser-rsa-pss7"
+
+ 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"
+ 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 <<EOF
+Signature Algorithm: PKCS #1 RSA-PSS Signature
+ Parameters:
+ Hash algorithm: SHA-256
+ Mask algorithm: PKCS #1 MGF1 Mask Generation Function
+ Mask hash algorithm: SHA-256
+ Salt Length: 32 (0x20)
+EOF
+ check_sign_algo
+}
+
############################## cert_cleanup ############################
# local shell function to finish this script (no exit since it might be
# sourced)
########################################################################
cert_cleanup()
{
cert_log "$SCRIPTNAME: finished $SCRIPTNAME"
html "</TABLE><BR>"
@@ -2004,16 +2338,17 @@ fi
# cert_rsa_exponent
# fi
cert_eccurves
cert_extensions
cert_san_and_generic_extensions
cert_test_password
cert_test_distrust
cert_test_ocspresp
+cert_test_rsapss
if [ -z "$NSS_TEST_DISABLE_CRL" ] ; then
cert_crl_ssl
else
echo "$SCRIPTNAME: Skipping CRL Tests"
fi
if [ -n "$DO_DIST_ST" -a "$DO_DIST_ST" = "TRUE" ] ; then
--- a/tests/common/init.sh
+++ b/tests/common/init.sh
@@ -58,16 +58,17 @@ if [ -z "${INIT_SOURCED}" -o "${INIT_SOU
ALICEDIR=${HOSTDIR}/alicedir
BOBDIR=${HOSTDIR}/bobdir
DAVEDIR=${HOSTDIR}/dave
EVEDIR=${HOSTDIR}/eve
FIPSDIR=${HOSTDIR}/fips
DBPASSDIR=${HOSTDIR}/dbpass
ECCURVES_DIR=${HOSTDIR}/eccurves
DISTRUSTDIR=${HOSTDIR}/distrust
+ RSAPSSDIR=${HOSTDIR}/rsapss
SERVER_CADIR=${HOSTDIR}/serverCA
CLIENT_CADIR=${HOSTDIR}/clientCA
EXT_SERVERDIR=${HOSTDIR}/ext_server
EXT_CLIENTDIR=${HOSTDIR}/ext_client
IMPLICIT_INIT_DIR=${HOSTDIR}/implicit_init
IOPR_CADIR=${HOSTDIR}/CA_iopr
@@ -535,16 +536,17 @@ if [ -z "${INIT_SOURCED}" -o "${INIT_SOU
D_FIPS="FIPS.$version"
D_DBPASS="DBPASS.$version"
D_ECCURVES="ECCURVES.$version"
D_EXT_SERVER="ExtendedServer.$version"
D_EXT_CLIENT="ExtendedClient.$version"
D_IMPLICIT_INIT="ImplicitInit.$version"
D_CERT_EXTENSTIONS="CertExtensions.$version"
D_DISTRUST="Distrust.$version"
+ D_RSAPSS="RSAPSS.$version"
# we need relative pathnames of these files abd directories, since our
# tools can't handle the unix style absolut pathnames on cygnus
R_CADIR=../CA
R_SERVERDIR=../server
R_CLIENTDIR=../client
R_IOPR_CADIR=../CA_iopr
--- a/tests/ssl_gtests/ssl_gtests.sh
+++ b/tests/ssl_gtests/ssl_gtests.sh
@@ -36,26 +36,30 @@ certscript() {
}
# $1: name
# $2: type
# $3+: usages: sign or kex
make_cert() {
name=$1
type=$2
+ unset type_args trust sign
case $type in
dsa) type_args='-g 1024' ;;
rsa) type_args='-g 1024' ;;
rsa2048) type_args='-g 2048';type=rsa ;;
rsapss) type_args='-g 1024 --pss';type=rsa ;;
p256) type_args='-q nistp256';type=ec ;;
p384) type_args='-q secp384r1';type=ec ;;
p521) type_args='-q secp521r1';type=ec ;;
rsa_ca) type_args='-g 1024';trust='CT,CT,CT';ca=y;type=rsa ;;
rsa_chain) type_args='-g 1024';sign='-c rsa_ca';type=rsa;;
+ rsapss_ca) type_args='-g 1024 --pss';trust='CT,CT,CT';ca=y;type=rsa ;;
+ rsapss_chain) type_args='-g 1024';sign='-c rsa_pss_ca';type=rsa;;
+ rsa_ca_rsapss_chain) type_args='-g 1024 --pss-sign';sign='-c rsa_ca';type=rsa;;
ecdh_rsa) type_args='-q nistp256';sign='-c rsa_ca';type=ec ;;
esac
shift 2
counter=$(($counter + 1))
certscript $@ | ${BINDIR}/certutil -S \
-z ${R_NOISE_FILE} -d "${PROFILEDIR}" \
-n $name -s "CN=$name" -t ${trust:-,,} ${sign:--x} -m $counter \
-w -2 -v 120 -k $type $type_args -Z SHA256 -1 -2
@@ -82,16 +86,19 @@ ssl_gtest_certs() {
make_cert rsa_pss rsapss sign
make_cert rsa_decrypt rsa kex
make_cert ecdsa256 p256 sign
make_cert ecdsa384 p384 sign
make_cert ecdsa521 p521 sign
make_cert ecdh_ecdsa p256 kex
make_cert rsa_ca rsa_ca ca
make_cert rsa_chain rsa_chain sign
+ make_cert rsa_pss_ca rsapss_ca ca
+ make_cert rsa_pss_chain rsapss_chain sign
+ make_cert rsa_ca_rsa_pss_chain rsa_ca_rsapss_chain sign
make_cert ecdh_rsa ecdh_rsa kex
make_cert dsa dsa sign
}
############################## ssl_gtest_init ##########################
# local shell function to initialize this script
########################################################################
ssl_gtest_init()