summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/cryptohi
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/lib/cryptohi')
-rw-r--r--security/nss/lib/cryptohi/cryptohi.h61
-rw-r--r--security/nss/lib/cryptohi/keyi.h3
-rw-r--r--security/nss/lib/cryptohi/seckey.c122
-rw-r--r--security/nss/lib/cryptohi/secsign.c378
-rw-r--r--security/nss/lib/cryptohi/secvfy.c149
5 files changed, 663 insertions, 50 deletions
diff --git a/security/nss/lib/cryptohi/cryptohi.h b/security/nss/lib/cryptohi/cryptohi.h
index f658daa9e..e529fa34f 100644
--- a/security/nss/lib/cryptohi/cryptohi.h
+++ b/security/nss/lib/cryptohi/cryptohi.h
@@ -60,6 +60,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
** "freeit" if PR_TRUE then free the object as well as its sub-objects
@@ -106,6 +114,21 @@ extern SECStatus SEC_SignData(SECItem *result,
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)
@@ -132,6 +155,27 @@ extern SECStatus SEC_DerSignData(PLArenaPool *arena, SECItem *result,
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
@@ -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/security/nss/lib/cryptohi/keyi.h b/security/nss/lib/cryptohi/keyi.h
index f8f5f7f7d..ee11fc905 100644
--- a/security/nss/lib/cryptohi/keyi.h
+++ b/security/nss/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/security/nss/lib/cryptohi/seckey.c b/security/nss/lib/cryptohi/seckey.c
index 9ea48b767..0f9353f3b 100644
--- a/security/nss/lib/cryptohi/seckey.c
+++ b/security/nss/lib/cryptohi/seckey.c
@@ -221,8 +221,7 @@ SECKEY_CreateECPrivateKey(SECKEYECParams *param, SECKEYPublicKey **pubk, void *c
PK11_ATTR_SESSION |
PK11_ATTR_INSENSITIVE |
PK11_ATTR_PUBLIC,
- CKF_DERIVE, CKF_DERIVE |
- CKF_SIGN,
+ CKF_DERIVE, CKF_DERIVE | CKF_SIGN,
cx);
if (!privk)
privk = PK11_GenerateKeyPairWithOpFlags(slot, CKM_EC_KEY_PAIR_GEN,
@@ -230,8 +229,7 @@ SECKEY_CreateECPrivateKey(SECKEYECParams *param, SECKEYPublicKey **pubk, void *c
PK11_ATTR_SESSION |
PK11_ATTR_SENSITIVE |
PK11_ATTR_PRIVATE,
- CKF_DERIVE, CKF_DERIVE |
- CKF_SIGN,
+ CKF_DERIVE, CKF_DERIVE | CKF_SIGN,
cx);
PK11_FreeSlot(slot);
@@ -1048,6 +1046,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 +1973,118 @@ 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;
+ case SEC_OID_SHA224:
+ return CKM_SHA224;
+ case SEC_OID_SHA1:
+ return CKM_SHA_1;
+ default:
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ return CKM_INVALID_MECHANISM;
+ }
+}
+
+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;
+ case SEC_OID_SHA224:
+ return CKG_MGF1_SHA224;
+ case SEC_OID_SHA1:
+ return CKG_MGF1_SHA1;
+ default:
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ return 0;
+ }
+}
+
+SECStatus
+sec_RSAPSSParamsToMechanism(CK_RSA_PKCS_PSS_PARAMS *mech,
+ const SECKEYRSAPSSParams *params)
+{
+ SECStatus rv = SECSuccess;
+ SECOidTag hashAlgTag;
+ unsigned long saltLength;
+ unsigned long trailerField;
+
+ 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 (mech->hashAlg == CKM_INVALID_MECHANISM) {
+ return SECFailure;
+ }
+
+ 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),
+ &params->maskAlg->parameters);
+ PORT_DestroyCheapArena(&tmpArena);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ maskHashAlgTag = SECOID_GetAlgorithmTag(&maskHashAlg);
+ mech->mgf = sec_GetMgfTypeByOidTag(maskHashAlgTag);
+ if (mech->mgf == 0) {
+ return SECFailure;
+ }
+ } else {
+ mech->mgf = CKG_MGF1_SHA1; /* default, MGF1 with SHA-1 */
+ }
+
+ if (params->saltLength.data) {
+ rv = SEC_ASN1DecodeInteger((SECItem *)&params->saltLength, &saltLength);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ } else {
+ saltLength = 20; /* default, 20 */
+ }
+ mech->sLen = saltLength;
+
+ if (params->trailerField.data) {
+ rv = SEC_ASN1DecodeInteger((SECItem *)&params->trailerField, &trailerField);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ if (trailerField != 1) {
+ /* the value must be 1, which represents the trailer field
+ * with hexadecimal value 0xBC */
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ }
+
+ return rv;
+}
diff --git a/security/nss/lib/cryptohi/secsign.c b/security/nss/lib/cryptohi/secsign.c
index d06cb2e85..dc10f2fa6 100644
--- a/security/nss/lib/cryptohi/secsign.c
+++ b/security/nss/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(&params, 0, sizeof(params));
+ rv = SEC_QuickDERDecodeItem(arena, &params,
+ SECKEY_RSAPSSParamsTemplate,
+ cx->params);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ rv = sec_RSAPSSParamsToMechanism(&mech, &params);
+ 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 @@ loser:
/************************************************************************/
-/*
-** 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 @@ loser:
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;
@@ -363,6 +435,24 @@ loser:
}
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,243 @@ 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;
+ PRBool defaultSHA1 = PR_FALSE;
+ 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;
+ }
+ defaultSHA1 = PR_TRUE;
+ }
+
+ 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) {
+ SECOidTag tag = SEC_OID_UNKNOWN;
+
+ if (pssParams.hashAlg) {
+ tag = SECOID_GetAlgorithmTag(pssParams.hashAlg);
+ } else if (defaultSHA1) {
+ tag = SEC_OID_SHA1;
+ }
+
+ if (tag != SEC_OID_UNKNOWN && tag != hashAlgTag) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+ } else if (hashAlgTag == SEC_OID_UNKNOWN) {
+ if (pssParams.hashAlg) {
+ hashAlgTag = SECOID_GetAlgorithmTag(pssParams.hashAlg);
+ } else if (defaultSHA1) {
+ hashAlgTag = SEC_OID_SHA1;
+ } 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;
+ }
+ } else if (defaultSHA1) {
+ if (hashAlgTag != SEC_OID_SHA1) {
+ 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;
+ }
+ } else if (defaultSHA1) {
+ saltLength = 20;
+ }
+
+ /* 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/security/nss/lib/cryptohi/secvfy.c b/security/nss/lib/cryptohi/secvfy.c
index 2ac21abd4..83c9c579d 100644
--- a/security/nss/lib/cryptohi/secvfy.c
+++ b/security/nss/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(&params, 0, sizeof(params));
+ rv = SEC_QuickDERDecodeItem(arena, &params,
+ SECKEY_RSAPSSParamsTemplate,
+ cx->params);
+ if (rv != SECSuccess) {
+ PORT_FreeArena(arena, PR_FALSE);
+ return SECFailure;
+ }
+ rv = sec_RSAPSSParamsToMechanism(&mech, &params);
+ 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);
}