diff options
author | wolfbeast <mcwerewolf@gmail.com> | 2018-06-06 21:27:04 +0200 |
---|---|---|
committer | wolfbeast <mcwerewolf@gmail.com> | 2018-06-06 21:27:04 +0200 |
commit | 4a71b30364a4b6d1eaf16fcfdc8e873e6697f293 (patch) | |
tree | a47014077c14579249859ad34afcc5a8f2f0730a /security/nss/lib/cryptohi | |
parent | d7da72799521386c110dbba73b1e483b00a0a56a (diff) | |
parent | 2dad0ec41d0b69c0a815012e6ea4bdde81b2875b (diff) | |
download | UXP-4a71b30364a4b6d1eaf16fcfdc8e873e6697f293.tar UXP-4a71b30364a4b6d1eaf16fcfdc8e873e6697f293.tar.gz UXP-4a71b30364a4b6d1eaf16fcfdc8e873e6697f293.tar.lz UXP-4a71b30364a4b6d1eaf16fcfdc8e873e6697f293.tar.xz UXP-4a71b30364a4b6d1eaf16fcfdc8e873e6697f293.zip |
Merge branch 'NSS-335'
Diffstat (limited to 'security/nss/lib/cryptohi')
-rw-r--r-- | security/nss/lib/cryptohi/cryptohi.h | 61 | ||||
-rw-r--r-- | security/nss/lib/cryptohi/keyi.h | 3 | ||||
-rw-r--r-- | security/nss/lib/cryptohi/seckey.c | 122 | ||||
-rw-r--r-- | security/nss/lib/cryptohi/secsign.c | 378 | ||||
-rw-r--r-- | security/nss/lib/cryptohi/secvfy.c | 149 |
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), + ¶ms->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 *)¶ms->saltLength, &saltLength); + if (rv != SECSuccess) { + return rv; + } + } else { + saltLength = 20; /* default, 20 */ + } + mech->sLen = saltLength; + + if (params->trailerField.data) { + rv = SEC_ASN1DecodeInteger((SECItem *)¶ms->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(¶ms, 0, sizeof(params)); + rv = SEC_QuickDERDecodeItem(arena, ¶ms, + SECKEY_RSAPSSParamsTemplate, + cx->params); + if (rv != SECSuccess) { + goto loser; + } + rv = sec_RSAPSSParamsToMechanism(&mech, ¶ms); + if (rv != SECSuccess) { + goto loser; + } + } else { + mech.hashAlg = CKM_SHA_1; + mech.mgf = CKG_MGF1_SHA1; + mech.sLen = digder.len; + } + rv = PK11_SignWithMechanism(privKey, CKM_RSA_PKCS_PSS, &mechItem, + &sigitem, &digder); + if (rv != SECSuccess) { + goto loser; + } + } else { + rv = PK11_Sign(privKey, &sigitem, &digder); + if (rv != SECSuccess) { + goto loser; + } } if ((cx->signalg == SEC_OID_ANSIX9_DSA_SIGNATURE) || (cx->signalg == SEC_OID_ANSIX962_EC_PUBLIC_KEY)) { /* DSAU_EncodeDerSigWithLen works for DSA and ECDSA */ rv = DSAU_EncodeDerSigWithLen(result, &sigitem, sigitem.len); - PORT_Free(sigitem.data); if (rv != SECSuccess) goto loser; + SECITEM_FreeItem(&sigitem, PR_FALSE); } else { result->len = sigitem.len; result->data = sigitem.data; } loser: + if (rv != SECSuccess) { + SECITEM_FreeItem(&sigitem, PR_FALSE); + } SGN_DestroyDigestInfo(di); if (arena != NULL) { PORT_FreeArena(arena, PR_FALSE); @@ -229,18 +286,14 @@ 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(¶ms, 0, sizeof(params)); + rv = SEC_QuickDERDecodeItem(arena, ¶ms, + SECKEY_RSAPSSParamsTemplate, + cx->params); + if (rv != SECSuccess) { + PORT_FreeArena(arena, PR_FALSE); + return SECFailure; + } + rv = sec_RSAPSSParamsToMechanism(&mech, ¶ms); + PORT_FreeArena(arena, PR_FALSE); if (rv != SECSuccess) { return SECFailure; } + rsasig.data = cx->u.buffer; + rsasig.len = SECKEY_SignatureLen(cx->key); + if (rsasig.len == 0) { + return SECFailure; + } + if (sig) { + if (sig->len != rsasig.len) { + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + return SECFailure; + } + PORT_Memcpy(rsasig.data, sig->data, rsasig.len); + } + hash.data = final; + hash.len = part; + if (PK11_VerifyWithMechanism(cx->key, CKM_RSA_PKCS_PSS, &mechItem, + &rsasig, &hash, cx->wincx) != SECSuccess) { + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + return SECFailure; + } + } else { + SECItem digest; + digest.data = final; + digest.len = part; + if (sig) { + SECOidTag hashid; + PORT_Assert(cx->hashAlg != SEC_OID_UNKNOWN); + rv = recoverPKCS1DigestInfo(cx->hashAlg, &hashid, + &cx->pkcs1RSADigestInfo, + &cx->pkcs1RSADigestInfoLen, + cx->key, + sig, cx->wincx); + PORT_Assert(cx->hashAlg == hashid); + if (rv != SECSuccess) { + return SECFailure; + } + } + return verifyPKCS1DigestInfo(cx, &digest); } - return verifyPKCS1DigestInfo(cx, &digest); - } + break; default: PORT_SetError(SEC_ERROR_BAD_SIGNATURE); return SECFailure; /* shouldn't happen */ @@ -722,7 +823,7 @@ VFY_VerifyDigestWithAlgorithmID(const SECItem *digest, static SECStatus vfy_VerifyData(const unsigned char *buf, int len, const SECKEYPublicKey *key, const SECItem *sig, SECOidTag encAlg, SECOidTag hashAlg, - SECOidTag *hash, void *wincx) + const SECItem *params, SECOidTag *hash, void *wincx) { SECStatus rv; VFYContext *cx; @@ -730,6 +831,9 @@ vfy_VerifyData(const unsigned char *buf, int len, const SECKEYPublicKey *key, cx = vfy_CreateContext(key, sig, encAlg, hashAlg, hash, wincx); if (cx == NULL) return SECFailure; + if (params) { + cx->params = SECITEM_DupItem(params); + } rv = VFY_Begin(cx); if (rv == SECSuccess) { @@ -748,7 +852,7 @@ VFY_VerifyDataDirect(const unsigned char *buf, int len, SECOidTag encAlg, SECOidTag hashAlg, SECOidTag *hash, void *wincx) { - return vfy_VerifyData(buf, len, key, sig, encAlg, hashAlg, hash, wincx); + return vfy_VerifyData(buf, len, key, sig, encAlg, hashAlg, NULL, hash, wincx); } SECStatus @@ -760,7 +864,7 @@ VFY_VerifyData(const unsigned char *buf, int len, const SECKEYPublicKey *key, if (rv != SECSuccess) { return rv; } - return vfy_VerifyData(buf, len, key, sig, encAlg, hashAlg, NULL, wincx); + return vfy_VerifyData(buf, len, key, sig, encAlg, hashAlg, NULL, NULL, wincx); } SECStatus @@ -777,5 +881,6 @@ VFY_VerifyDataWithAlgorithmID(const unsigned char *buf, int len, if (rv != SECSuccess) { return rv; } - return vfy_VerifyData(buf, len, key, sig, encAlg, hashAlg, hash, wincx); + return vfy_VerifyData(buf, len, key, sig, encAlg, hashAlg, + &sigAlgorithm->parameters, hash, wincx); } |