diff options
Diffstat (limited to '')
-rw-r--r-- | security/nss/lib/cryptohi/Makefile | 46 | ||||
-rw-r--r-- | security/nss/lib/cryptohi/cryptohi.gyp | 27 | ||||
-rw-r--r-- | security/nss/lib/cryptohi/cryptohi.h | 425 | ||||
-rw-r--r-- | security/nss/lib/cryptohi/cryptoht.h | 14 | ||||
-rw-r--r-- | security/nss/lib/cryptohi/dsautil.c | 271 | ||||
-rw-r--r-- | security/nss/lib/cryptohi/exports.gyp | 37 | ||||
-rw-r--r-- | security/nss/lib/cryptohi/key.h | 14 | ||||
-rw-r--r-- | security/nss/lib/cryptohi/keyhi.h | 276 | ||||
-rw-r--r-- | security/nss/lib/cryptohi/keyi.h | 45 | ||||
-rw-r--r-- | security/nss/lib/cryptohi/keyt.h | 14 | ||||
-rw-r--r-- | security/nss/lib/cryptohi/keythi.h | 247 | ||||
-rw-r--r-- | security/nss/lib/cryptohi/manifest.mn | 37 | ||||
-rw-r--r-- | security/nss/lib/cryptohi/sechash.c | 471 | ||||
-rw-r--r-- | security/nss/lib/cryptohi/sechash.h | 60 | ||||
-rw-r--r-- | security/nss/lib/cryptohi/seckey.c | 2320 | ||||
-rw-r--r-- | security/nss/lib/cryptohi/secsign.c | 884 | ||||
-rw-r--r-- | security/nss/lib/cryptohi/secvfy.c | 952 |
17 files changed, 6140 insertions, 0 deletions
diff --git a/security/nss/lib/cryptohi/Makefile b/security/nss/lib/cryptohi/Makefile new file mode 100644 index 0000000000..aae54b1e0f --- /dev/null +++ b/security/nss/lib/cryptohi/Makefile @@ -0,0 +1,46 @@ +#! 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). # +####################################################################### + +####################################################################### +# (5) Execute "global" rules. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/rules.mk + +####################################################################### +# (6) Execute "component" rules. (OPTIONAL) # +####################################################################### + + + +####################################################################### +# (7) Execute "local" rules. (OPTIONAL). # +####################################################################### + + + diff --git a/security/nss/lib/cryptohi/cryptohi.gyp b/security/nss/lib/cryptohi/cryptohi.gyp new file mode 100644 index 0000000000..ef9e63fbf5 --- /dev/null +++ b/security/nss/lib/cryptohi/cryptohi.gyp @@ -0,0 +1,27 @@ +# 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' + ], + 'targets': [ + { + 'target_name': 'cryptohi', + 'type': 'static_library', + 'sources': [ + 'dsautil.c', + 'sechash.c', + 'seckey.c', + 'secsign.c', + 'secvfy.c' + ], + 'dependencies': [ + '<(DEPTH)/exports.gyp:nss_exports' + ] + } + ], + 'variables': { + 'module': 'nss' + } +}
\ No newline at end of file diff --git a/security/nss/lib/cryptohi/cryptohi.h b/security/nss/lib/cryptohi/cryptohi.h new file mode 100644 index 0000000000..7b66f0b0b9 --- /dev/null +++ b/security/nss/lib/cryptohi/cryptohi.h @@ -0,0 +1,425 @@ +/* + * cryptohi.h - public prototypes for the crypto library + * + * 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/. */ + +#ifndef _CRYPTOHI_H_ +#define _CRYPTOHI_H_ + +#include "blapit.h" + +#include "seccomon.h" +#include "secoidt.h" +#include "secdert.h" +#include "cryptoht.h" +#include "keythi.h" +#include "certt.h" + +SEC_BEGIN_PROTOS + +/****************************************/ +/* +** DER encode/decode (EC)DSA signatures +*/ + +/* ANSI X9.57 defines DSA signatures as DER encoded data. Our DSA1 code (and + * most of the rest of the world) just generates 40 bytes of raw data. These + * functions convert between formats. + */ +extern SECStatus DSAU_EncodeDerSig(SECItem *dest, SECItem *src); +extern SECItem *DSAU_DecodeDerSig(const SECItem *item); + +/* + * Unlike DSA1, raw DSA2 and ECDSA signatures do not have a fixed length. + * Rather they contain two integers r and s whose length depends + * on the size of q or the EC key used for signing. + * + * We can reuse the DSAU_EncodeDerSig interface to DER encode + * raw ECDSA signature keeping in mind that the length of r + * is the same as that of s and exactly half of src->len. + * + * For decoding, we need to pass the length of the desired + * raw signature (twice the key size) explicitly. + */ +extern SECStatus DSAU_EncodeDerSigWithLen(SECItem *dest, SECItem *src, + unsigned int len); +extern SECItem *DSAU_DecodeDerSigToLen(const SECItem *item, unsigned int len); + +/****************************************/ +/* +** Signature creation operations +*/ + +/* +** 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 +** another stream of data. +*/ +extern SECStatus SGN_Begin(SGNContext *cx); + +/* +** Update the signing context with more data to sign. +** "cx" the context +** "input" the input data to sign +** "inputLen" the length of the input data +*/ +extern SECStatus SGN_Update(SGNContext *cx, const unsigned char *input, + unsigned int inputLen); + +/* +** Finish the signature process. Use either k0 or k1 to sign the data +** stream that was input using SGN_Update. The resulting signature is +** formatted using PKCS#1 and then encrypted using RSA private or public +** encryption. +** "cx" the context +** "result" the final signature data (memory is allocated) +*/ +extern SECStatus SGN_End(SGNContext *cx, SECItem *result); + +/* +** Sign a single block of data using private key encryption and given +** signature/hash algorithm. +** "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_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, + SECOidTag algtag, SECItem *result, SECItem *digest); + +/* +** DER sign a single block of data using private key encryption and the +** MD5 hashing algorithm. 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 +*/ +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 +** VFY_CreateContextWithAlgorithmID instead. +** "key" the public key to verify with +** "sig" the encrypted signature data if sig is NULL then +** VFY_EndWithSignature must be called with the correct signature at +** the end of the processing. +** "sigAlg" specifies the signing algorithm to use (including the +** hash algorthim). This must match the key type. +** "wincx" void pointer to the window context +*/ +extern VFYContext *VFY_CreateContext(SECKEYPublicKey *key, SECItem *sig, + SECOidTag sigAlg, void *wincx); +/* +** Create a signature verification context. +** "key" the public key to verify with +** "sig" the encrypted signature data if sig is NULL then +** VFY_EndWithSignature must be called with the correct signature at +** the end of the processing. +** "pubkAlg" specifies the cryptographic signing algorithm to use (the +** raw algorithm without any hash specified. This must match the key +** type. +** "hashAlg" specifies the hashing algorithm used. If the key is an +** RSA key, and sig is not NULL, then hashAlg can be SEC_OID_UNKNOWN. +** the hash is selected from data in the sig. +** "hash" optional pointer to return the actual hash algorithm used. +** in practice this should always match the passed in hashAlg (the +** exception is the case where hashAlg is SEC_OID_UNKNOWN above). +** If this value is NULL no, hash oid is returned. +** "wincx" void pointer to the window context +*/ +extern VFYContext *VFY_CreateContextDirect(const SECKEYPublicKey *key, + const SECItem *sig, + SECOidTag pubkAlg, + SECOidTag hashAlg, + SECOidTag *hash, void *wincx); +/* +** Create a signature verification context from a algorithm ID. +** "key" the public key to verify with +** "sig" the encrypted signature data if sig is NULL then +** VFY_EndWithSignature must be called with the correct signature at +** the end of the processing. +** "algid" specifies the signing algorithm and parameters to use. +** This must match the key type. +** "hash" optional pointer to return the oid of the actual hash used in +** the signature. If this value is NULL no, hash oid is returned. +** "wincx" void pointer to the window context +*/ +extern VFYContext *VFY_CreateContextWithAlgorithmID(const SECKEYPublicKey *key, + const SECItem *sig, + const SECAlgorithmID *algid, + SECOidTag *hash, + void *wincx); + +/* +** Destroy a verification-context object. +** "cx" the context to destroy +** "freeit" if PR_TRUE then free the object as well as its sub-objects +*/ +extern void VFY_DestroyContext(VFYContext *cx, PRBool freeit); + +extern SECStatus VFY_Begin(VFYContext *cx); + +/* +** Update a verification context with more input data. The input data +** is fed to a secure hash function (depending on what was in the +** encrypted signature data). +** "cx" the context +** "input" the input data +** "inputLen" the amount of input data +*/ +extern SECStatus VFY_Update(VFYContext *cx, const unsigned char *input, + unsigned int inputLen); + +/* +** Finish the verification process. The return value is a status which +** indicates success or failure. On success, the SECSuccess value is +** returned. Otherwise, SECFailure is returned and the error code found +** using PORT_GetError() indicates what failure occurred. +** "cx" the context +*/ +extern SECStatus VFY_End(VFYContext *cx); + +/* +** Finish the verification process. The return value is a status which +** indicates success or failure. On success, the SECSuccess value is +** returned. Otherwise, SECFailure is returned and the error code found +** using PORT_GetError() indicates what failure occurred. If signature is +** supplied the verification uses this signature to verify, otherwise the +** signature passed in VFY_CreateContext() is used. +** VFY_EndWithSignature(cx,NULL); is identical to VFY_End(cx);. +** "cx" the context +** "sig" the encrypted signature data +*/ +extern SECStatus VFY_EndWithSignature(VFYContext *cx, SECItem *sig); + +/* +** Verify the signature on a block of data for which we already have +** the digest. The signature data is an RSA private key encrypted +** block of data formatted according to PKCS#1. +** This function is deprecated. Use VFY_VerifyDigestDirect or +** VFY_VerifyDigestWithAlgorithmID instead. +** "dig" the digest +** "key" the public key to check the signature with +** "sig" the encrypted signature data +** "sigAlg" specifies the signing algorithm to use. This must match +** the key type. +** "wincx" void pointer to the window context +**/ +extern SECStatus VFY_VerifyDigest(SECItem *dig, SECKEYPublicKey *key, + SECItem *sig, SECOidTag sigAlg, void *wincx); +/* +** Verify the signature on a block of data for which we already have +** the digest. The signature data is an RSA private key encrypted +** block of data formatted according to PKCS#1. +** "dig" the digest +** "key" the public key to check the signature with +** "sig" the encrypted signature data +** "pubkAlg" specifies the cryptographic signing algorithm to use (the +** raw algorithm without any hash specified. This must match the key +** type. +** "hashAlg" specifies the hashing algorithm used. +** "wincx" void pointer to the window context +**/ +extern SECStatus VFY_VerifyDigestDirect(const SECItem *dig, + const SECKEYPublicKey *key, + const SECItem *sig, SECOidTag pubkAlg, + SECOidTag hashAlg, void *wincx); +/* +** Verify the signature on a block of data for which we already have +** the digest. The signature data is an RSA private key encrypted +** block of data formatted according to PKCS#1. +** "key" the public key to verify with +** "sig" the encrypted signature data if sig is NULL then +** VFY_EndWithSignature must be called with the correct signature at +** the end of the processing. +** "algid" specifies the signing algorithm and parameters to use. +** This must match the key type. +** "hash" oid of the actual hash used to create digest. If this value is +** not set to SEC_OID_UNKNOWN, it must match the hash of the signature. +** "wincx" void pointer to the window context +*/ +extern SECStatus VFY_VerifyDigestWithAlgorithmID(const SECItem *dig, + const SECKEYPublicKey *key, const SECItem *sig, + const SECAlgorithmID *algid, SECOidTag hash, + void *wincx); + +/* +** Verify the signature on a block of data. The signature data is an RSA +** private key encrypted block of data formatted according to PKCS#1. +** This function is deprecated. Use VFY_VerifyDataDirect or +** VFY_VerifyDataWithAlgorithmID instead. +** "buf" the input data +** "len" the length of the input data +** "key" the public key to check the signature with +** "sig" the encrypted signature data +** "sigAlg" specifies the signing algorithm to use. This must match +** the key type. +** "wincx" void pointer to the window context +*/ +extern SECStatus VFY_VerifyData(const unsigned char *buf, int len, + const SECKEYPublicKey *key, const SECItem *sig, + SECOidTag sigAlg, void *wincx); +/* +** Verify the signature on a block of data. The signature data is an RSA +** private key encrypted block of data formatted according to PKCS#1. +** "buf" the input data +** "len" the length of the input data +** "key" the public key to check the signature with +** "sig" the encrypted signature data +** "pubkAlg" specifies the cryptographic signing algorithm to use (the +** raw algorithm without any hash specified. This must match the key +** type. +** "hashAlg" specifies the hashing algorithm used. If the key is an +** RSA key, and sig is not NULL, then hashAlg can be SEC_OID_UNKNOWN. +** the hash is selected from data in the sig. +** "hash" optional pointer to return the actual hash algorithm used. +** in practice this should always match the passed in hashAlg (the +** exception is the case where hashAlg is SEC_OID_UNKNOWN above). +** If this value is NULL no, hash oid is returned. +** "wincx" void pointer to the window context +*/ +extern SECStatus VFY_VerifyDataDirect(const unsigned char *buf, int len, + const SECKEYPublicKey *key, + const SECItem *sig, + SECOidTag pubkAlg, SECOidTag hashAlg, + SECOidTag *hash, void *wincx); + +/* +** Verify the signature on a block of data. The signature data is an RSA +** private key encrypted block of data formatted according to PKCS#1. +** "buf" the input data +** "len" the length of the input data +** "key" the public key to check the signature with +** "sig" the encrypted signature data +** "algid" specifies the signing algorithm and parameters to use. +** This must match the key type. +** "hash" optional pointer to return the oid of the actual hash used in +** the signature. If this value is NULL no, hash oid is returned. +** "wincx" void pointer to the window context +*/ +extern SECStatus VFY_VerifyDataWithAlgorithmID(const unsigned char *buf, + int len, const SECKEYPublicKey *key, + const SECItem *sig, + const SECAlgorithmID *algid, SECOidTag *hash, + void *wincx); + +SEC_END_PROTOS + +#endif /* _CRYPTOHI_H_ */ diff --git a/security/nss/lib/cryptohi/cryptoht.h b/security/nss/lib/cryptohi/cryptoht.h new file mode 100644 index 0000000000..5780bf47ae --- /dev/null +++ b/security/nss/lib/cryptohi/cryptoht.h @@ -0,0 +1,14 @@ +/* + * cryptoht.h - public data structures for the crypto library + * + * 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/. */ + +#ifndef _CRYPTOHT_H_ +#define _CRYPTOHT_H_ + +typedef struct SGNContextStr SGNContext; +typedef struct VFYContextStr VFYContext; + +#endif /* _CRYPTOHT_H_ */ diff --git a/security/nss/lib/cryptohi/dsautil.c b/security/nss/lib/cryptohi/dsautil.c new file mode 100644 index 0000000000..cc575878f6 --- /dev/null +++ b/security/nss/lib/cryptohi/dsautil.c @@ -0,0 +1,271 @@ +/* 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 "cryptohi.h" +#include "secasn1.h" +#include "secitem.h" +#include "prerr.h" + +#ifndef DSA1_SUBPRIME_LEN +#define DSA1_SUBPRIME_LEN 20 /* bytes */ +#endif + +typedef struct { + SECItem r; + SECItem s; +} DSA_ASN1Signature; + +const SEC_ASN1Template DSA_SignatureTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(DSA_ASN1Signature) }, + { SEC_ASN1_INTEGER, offsetof(DSA_ASN1Signature, r) }, + { SEC_ASN1_INTEGER, offsetof(DSA_ASN1Signature, s) }, + { 0 } +}; + +/* Input is variable length multi-byte integer, MSB first (big endian). +** Most signficant bit of first byte is NOT treated as a sign bit. +** May be one or more leading bytes of zeros. +** Output is variable length multi-byte integer, MSB first (big endian). +** Most significant bit of first byte will be zero (positive sign bit) +** No more than one leading zero byte. +** Caller supplies dest buffer, and assures that it is long enough, +** e.g. at least one byte longer that src's buffer. +*/ +void +DSAU_ConvertUnsignedToSigned(SECItem *dest, SECItem *src) +{ + unsigned char *pSrc = src->data; + unsigned char *pDst = dest->data; + unsigned int cntSrc = src->len; + + /* skip any leading zeros. */ + while (cntSrc && !(*pSrc)) { + pSrc++; + cntSrc--; + } + if (!cntSrc) { + *pDst = 0; + dest->len = 1; + return; + } + + if (*pSrc & 0x80) + *pDst++ = 0; + + PORT_Memcpy(pDst, pSrc, cntSrc); + dest->len = (pDst - dest->data) + cntSrc; +} + +/* +** src is a buffer holding a signed variable length integer. +** dest is a buffer which will be filled with an unsigned integer, +** MSB first (big endian) with leading zeros, so that the last byte +** of src will be the LSB of the integer. The result will be exactly +** the length specified by the caller in dest->len. +** src can be shorter than dest. src can be longer than dst, but only +** if the extra leading bytes are zeros. +*/ +SECStatus +DSAU_ConvertSignedToFixedUnsigned(SECItem *dest, SECItem *src) +{ + unsigned char *pSrc = src->data; + unsigned char *pDst = dest->data; + unsigned int cntSrc = src->len; + unsigned int cntDst = dest->len; + int zCount = cntDst - cntSrc; + + if (zCount > 0) { + PORT_Memset(pDst, 0, zCount); + PORT_Memcpy(pDst + zCount, pSrc, cntSrc); + return SECSuccess; + } + if (zCount <= 0) { + /* Source is longer than destination. Check for leading zeros. */ + while (zCount++ < 0) { + if (*pSrc++ != 0) + goto loser; + } + } + PORT_Memcpy(pDst, pSrc, cntDst); + return SECSuccess; + +loser: + PORT_SetError(PR_INVALID_ARGUMENT_ERROR); + return SECFailure; +} + +/* src is a "raw" ECDSA or DSA signature, the first half contains r + * and the second half contains s. dest is the DER encoded signature. +*/ +static SECStatus +common_EncodeDerSig(SECItem *dest, SECItem *src) +{ + SECItem *item; + SECItem srcItem; + DSA_ASN1Signature sig; + unsigned char *signedR; + unsigned char *signedS; + unsigned int len; + + /* Allocate memory with room for an extra byte that + * may be required if the top bit in the first byte + * is already set. + */ + len = src->len / 2; + signedR = (unsigned char *)PORT_Alloc(len + 1); + if (!signedR) + return SECFailure; + signedS = (unsigned char *)PORT_ZAlloc(len + 1); + if (!signedS) { + if (signedR) + PORT_Free(signedR); + return SECFailure; + } + + PORT_Memset(&sig, 0, sizeof(sig)); + + /* Must convert r and s from "unsigned" integers to "signed" integers. + ** If the high order bit of the first byte (MSB) is 1, then must + ** prepend with leading zero. + ** Must remove all but one leading zero byte from numbers. + */ + sig.r.type = siUnsignedInteger; + sig.r.data = signedR; + sig.r.len = sizeof signedR; + sig.s.type = siUnsignedInteger; + sig.s.data = signedS; + sig.s.len = sizeof signedR; + + srcItem.data = src->data; + srcItem.len = len; + + DSAU_ConvertUnsignedToSigned(&sig.r, &srcItem); + srcItem.data += len; + DSAU_ConvertUnsignedToSigned(&sig.s, &srcItem); + + item = SEC_ASN1EncodeItem(NULL, dest, &sig, DSA_SignatureTemplate); + if (signedR) + PORT_Free(signedR); + if (signedS) + PORT_Free(signedS); + if (item == NULL) + return SECFailure; + + /* XXX leak item? */ + return SECSuccess; +} + +/* src is a DER-encoded ECDSA or DSA signature. +** Returns a newly-allocated SECItem structure, pointing at a newly allocated +** buffer containing the "raw" signature, which is len bytes of r, +** followed by len bytes of s. For DSA, len is the length of q. +** For ECDSA, len depends on the key size used to create the signature. +*/ +static SECItem * +common_DecodeDerSig(const SECItem *item, unsigned int len) +{ + SECItem *result = NULL; + PORTCheapArenaPool arena; + SECStatus status; + DSA_ASN1Signature sig; + SECItem dst; + + PORT_Memset(&sig, 0, sizeof(sig)); + + /* Make enough room for r + s. */ + PORT_InitCheapArena(&arena, PR_MAX(2 * MAX_ECKEY_LEN, DSA_MAX_SIGNATURE_LEN)); + + result = PORT_ZNew(SECItem); + if (result == NULL) + goto loser; + + result->len = 2 * len; + result->data = (unsigned char *)PORT_Alloc(2 * len); + if (result->data == NULL) + goto loser; + + sig.r.type = siUnsignedInteger; + sig.s.type = siUnsignedInteger; + status = SEC_QuickDERDecodeItem(&arena.arena, &sig, DSA_SignatureTemplate, item); + if (status != SECSuccess) + goto loser; + + /* Convert sig.r and sig.s from variable length signed integers to + ** fixed length unsigned integers. + */ + dst.data = result->data; + dst.len = len; + status = DSAU_ConvertSignedToFixedUnsigned(&dst, &sig.r); + if (status != SECSuccess) + goto loser; + + dst.data += len; + status = DSAU_ConvertSignedToFixedUnsigned(&dst, &sig.s); + if (status != SECSuccess) + goto loser; + +done: + PORT_DestroyCheapArena(&arena); + + return result; + +loser: + if (result != NULL) { + SECITEM_FreeItem(result, PR_TRUE); + result = NULL; + } + goto done; +} + +/* src is a "raw" DSA1 signature, 20 bytes of r followed by 20 bytes of s. +** dest is the signature DER encoded. ? +*/ +SECStatus +DSAU_EncodeDerSig(SECItem *dest, SECItem *src) +{ + PORT_Assert(src->len == 2 * DSA1_SUBPRIME_LEN); + if (src->len != 2 * DSA1_SUBPRIME_LEN) { + PORT_SetError(PR_INVALID_ARGUMENT_ERROR); + return SECFailure; + } + + return common_EncodeDerSig(dest, src); +} + +/* src is a "raw" DSA signature of length len (len/2 bytes of r followed +** by len/2 bytes of s). dest is the signature DER encoded. +*/ +SECStatus +DSAU_EncodeDerSigWithLen(SECItem *dest, SECItem *src, unsigned int len) +{ + + PORT_Assert((src->len == len) && (len % 2 == 0)); + if ((src->len != len) || (src->len % 2 != 0)) { + PORT_SetError(PR_INVALID_ARGUMENT_ERROR); + return SECFailure; + } + + return common_EncodeDerSig(dest, src); +} + +/* src is a DER-encoded DSA signature. +** Returns a newly-allocated SECItem structure, pointing at a newly allocated +** buffer containing the "raw" DSA1 signature, which is 20 bytes of r, +** followed by 20 bytes of s. +*/ +SECItem * +DSAU_DecodeDerSig(const SECItem *item) +{ + return common_DecodeDerSig(item, DSA1_SUBPRIME_LEN); +} + +/* src is a DER-encoded ECDSA signature. +** Returns a newly-allocated SECItem structure, pointing at a newly allocated +** buffer containing the "raw" ECDSA signature of length len containing +** r followed by s (both padded to take up exactly len/2 bytes). +*/ +SECItem * +DSAU_DecodeDerSigToLen(const SECItem *item, unsigned int len) +{ + return common_DecodeDerSig(item, len / 2); +} diff --git a/security/nss/lib/cryptohi/exports.gyp b/security/nss/lib/cryptohi/exports.gyp new file mode 100644 index 0000000000..bb910592ca --- /dev/null +++ b/security/nss/lib/cryptohi/exports.gyp @@ -0,0 +1,37 @@ +# 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' + ], + 'variables': { + 'module': 'nss' + }, + 'targets': [ + { + 'target_name': 'lib_cryptohi_exports', + 'type': 'none', + 'copies': [ + { + 'files': [ + 'cryptohi.h', + 'cryptoht.h', + 'key.h', + 'keyhi.h', + 'keyt.h', + 'keythi.h', + 'sechash.h' + ], + 'destination': '<(nss_public_dist_dir)/<(module)' + }, + { + 'files': [ + 'keyi.h', + ], + 'destination': '<(nss_private_dist_dir)/<(module)' + } + ] + } + ], +} diff --git a/security/nss/lib/cryptohi/key.h b/security/nss/lib/cryptohi/key.h new file mode 100644 index 0000000000..8392031c5a --- /dev/null +++ b/security/nss/lib/cryptohi/key.h @@ -0,0 +1,14 @@ +/* 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/. */ + +#ifndef _KEY_H_ +#define _KEY_H_ + +#if defined(_MSC_VER) || defined(__GNUC__) || defined(__clang__) +#pragma message("key.h is deprecated. Please include keyhi.h instead.") +#endif + +#include "keyhi.h" + +#endif /* _KEY_H_ */ diff --git a/security/nss/lib/cryptohi/keyhi.h b/security/nss/lib/cryptohi/keyhi.h new file mode 100644 index 0000000000..173dbda903 --- /dev/null +++ b/security/nss/lib/cryptohi/keyhi.h @@ -0,0 +1,276 @@ +/* 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/. */ + +#ifndef _KEYHI_H_ +#define _KEYHI_H_ + +#include "plarena.h" + +#include "seccomon.h" +#include "secoidt.h" +#include "secdert.h" +#include "keythi.h" +#include "certt.h" +/*#include "secpkcs5.h" */ + +SEC_BEGIN_PROTOS + +/* +** Destroy a subject-public-key-info object. +*/ +extern void SECKEY_DestroySubjectPublicKeyInfo(CERTSubjectPublicKeyInfo *spki); + +/* +** Copy subject-public-key-info "src" to "dst". "dst" is filled in +** appropriately (memory is allocated for each of the sub objects). +*/ +extern SECStatus SECKEY_CopySubjectPublicKeyInfo(PLArenaPool *arena, + CERTSubjectPublicKeyInfo *dst, + CERTSubjectPublicKeyInfo *src); + +/* +** Update the PQG parameters for a cert's public key. +** Only done for DSA certs +*/ +extern SECStatus +SECKEY_UpdateCertPQG(CERTCertificate *subjectCert); + +/* +** Return the number of bits in the provided big integer. This assumes that the +** SECItem contains a big-endian number and counts from the first non-zero bit. +*/ +extern unsigned SECKEY_BigIntegerBitLength(const SECItem *number); + +/* +** Return the strength of the public key in bytes +*/ +extern unsigned SECKEY_PublicKeyStrength(const SECKEYPublicKey *pubk); + +/* +** Return the strength of the public key in bits +*/ +extern unsigned SECKEY_PublicKeyStrengthInBits(const SECKEYPublicKey *pubk); + +/* +** Return the strength of the private key in bits +*/ +extern unsigned SECKEY_PrivateKeyStrengthInBits(const SECKEYPrivateKey *privk); + +/* +** Return the length of the signature in bytes +*/ +extern unsigned SECKEY_SignatureLen(const SECKEYPublicKey *pubk); + +/* +** Make a copy of the private key "privKey" +*/ +extern SECKEYPrivateKey *SECKEY_CopyPrivateKey(const SECKEYPrivateKey *privKey); + +/* +** Make a copy of the public key "pubKey" +*/ +extern SECKEYPublicKey *SECKEY_CopyPublicKey(const SECKEYPublicKey *pubKey); + +/* +** Convert a private key "privateKey" into a public key +*/ +extern SECKEYPublicKey *SECKEY_ConvertToPublicKey(SECKEYPrivateKey *privateKey); + +/* + * create a new RSA key pair. The private Key is returned... + */ +SECKEYPrivateKey *SECKEY_CreateRSAPrivateKey(int keySizeInBits, + SECKEYPublicKey **pubk, void *cx); + +/* + * create a new DH key pair. The private Key is returned... + */ +SECKEYPrivateKey *SECKEY_CreateDHPrivateKey(SECKEYDHParams *param, + SECKEYPublicKey **pubk, void *cx); + +/* + * create a new EC key pair. The private Key is returned... + */ +SECKEYPrivateKey *SECKEY_CreateECPrivateKey(SECKEYECParams *param, + SECKEYPublicKey **pubk, void *cx); + +/* +** Create a subject-public-key-info based on a public key. +*/ +extern CERTSubjectPublicKeyInfo * +SECKEY_CreateSubjectPublicKeyInfo(const SECKEYPublicKey *k); + +/* +** Convert a base64 ascii encoded DER public key and challenge to spki, +** and verify the signature and challenge data are correct +*/ +extern CERTSubjectPublicKeyInfo * +SECKEY_ConvertAndDecodePublicKeyAndChallenge(char *pkacstr, char *challenge, + void *cx); + +/* +** Encode a CERTSubjectPublicKeyInfo structure. into a +** DER encoded subject public key info. +*/ +SECItem * +SECKEY_EncodeDERSubjectPublicKeyInfo(const SECKEYPublicKey *pubk); + +/* +** Decode a DER encoded subject public key info into a +** CERTSubjectPublicKeyInfo structure. +*/ +extern CERTSubjectPublicKeyInfo * +SECKEY_DecodeDERSubjectPublicKeyInfo(const SECItem *spkider); + +/* +** Convert a base64 ascii encoded DER subject public key info to our +** internal format. +*/ +extern CERTSubjectPublicKeyInfo * +SECKEY_ConvertAndDecodeSubjectPublicKeyInfo(const char *spkistr); + +/* + * extract the public key from a subject Public Key info structure. + * (used by JSS). + */ +extern SECKEYPublicKey * +SECKEY_ExtractPublicKey(const CERTSubjectPublicKeyInfo *); + +/* +** Destroy a private key object. +** "key" the object +*/ +extern void SECKEY_DestroyPrivateKey(SECKEYPrivateKey *key); + +/* +** Destroy a public key object. +** "key" the object +*/ +extern void SECKEY_DestroyPublicKey(SECKEYPublicKey *key); + +/* Destroy and zero out a private key info structure. for now this + * function zero's out memory allocated in an arena for the key + * since PORT_FreeArena does not currently do this. + * + * NOTE -- If a private key info is allocated in an arena, one should + * not call this function with freeit = PR_FALSE. The function should + * destroy the arena. + */ +extern void +SECKEY_DestroyPrivateKeyInfo(SECKEYPrivateKeyInfo *pvk, PRBool freeit); + +/* Destroy and zero out an encrypted private key info. + * + * NOTE -- If a encrypted private key info is allocated in an arena, one should + * not call this function with freeit = PR_FALSE. The function should + * destroy the arena. + */ +extern void +SECKEY_DestroyEncryptedPrivateKeyInfo(SECKEYEncryptedPrivateKeyInfo *epki, + PRBool freeit); + +/* Copy private key info structure. + * poolp is the arena into which the contents of from is to be copied. + * NULL is a valid entry. + * to is the destination private key info + * from is the source private key info + * if either from or to is NULL or an error occurs, SECFailure is + * returned. otherwise, SECSuccess is returned. + */ +extern SECStatus +SECKEY_CopyPrivateKeyInfo(PLArenaPool *poolp, + SECKEYPrivateKeyInfo *to, + const SECKEYPrivateKeyInfo *from); + +extern SECStatus +SECKEY_CacheStaticFlags(SECKEYPrivateKey *key); + +/* Copy encrypted private key info structure. + * poolp is the arena into which the contents of from is to be copied. + * NULL is a valid entry. + * to is the destination encrypted private key info + * from is the source encrypted private key info + * if either from or to is NULL or an error occurs, SECFailure is + * returned. otherwise, SECSuccess is returned. + */ +extern SECStatus +SECKEY_CopyEncryptedPrivateKeyInfo(PLArenaPool *poolp, + SECKEYEncryptedPrivateKeyInfo *to, + const SECKEYEncryptedPrivateKeyInfo *from); +/* + * Accessor functions for key type of public and private keys. + */ +KeyType SECKEY_GetPrivateKeyType(const SECKEYPrivateKey *privKey); +KeyType SECKEY_GetPublicKeyType(const SECKEYPublicKey *pubKey); + +/* + * Creates a PublicKey from its DER encoding. + * Currently only supports RSA, DSA, and DH keys. + */ +SECKEYPublicKey * +SECKEY_ImportDERPublicKey(const SECItem *derKey, CK_KEY_TYPE type); + +SECKEYPrivateKeyList * +SECKEY_NewPrivateKeyList(void); + +void +SECKEY_DestroyPrivateKeyList(SECKEYPrivateKeyList *keys); + +void +SECKEY_RemovePrivateKeyListNode(SECKEYPrivateKeyListNode *node); + +SECStatus +SECKEY_AddPrivateKeyToListTail(SECKEYPrivateKeyList *list, + SECKEYPrivateKey *key); + +#define PRIVKEY_LIST_HEAD(l) ((SECKEYPrivateKeyListNode *)PR_LIST_HEAD(&l->list)) +#define PRIVKEY_LIST_NEXT(n) ((SECKEYPrivateKeyListNode *)n->links.next) +#define PRIVKEY_LIST_END(n, l) (((void *)n) == ((void *)&l->list)) + +SECKEYPublicKeyList * +SECKEY_NewPublicKeyList(void); + +void +SECKEY_DestroyPublicKeyList(SECKEYPublicKeyList *keys); + +void +SECKEY_RemovePublicKeyListNode(SECKEYPublicKeyListNode *node); + +SECStatus +SECKEY_AddPublicKeyToListTail(SECKEYPublicKeyList *list, + SECKEYPublicKey *key); + +#define PUBKEY_LIST_HEAD(l) ((SECKEYPublicKeyListNode *)PR_LIST_HEAD(&l->list)) +#define PUBKEY_LIST_NEXT(n) ((SECKEYPublicKeyListNode *)n->links.next) +#define PUBKEY_LIST_END(n, l) (((void *)n) == ((void *)&l->list)) + +/* + * Length in bits of the EC's field size. This is also the length of + * the x and y coordinates of EC points, such as EC public keys and + * base points. + * + * Return 0 on failure (unknown EC domain parameters). + */ +extern int SECKEY_ECParamsToKeySize(const SECItem *params); + +/* + * Length in bits of the EC base point order, usually denoted n. This + * is also the length of EC private keys and ECDSA signature components + * r and s. + * + * Return 0 on failure (unknown EC domain parameters). + */ +extern int SECKEY_ECParamsToBasePointOrderLen(const SECItem *params); + +/* + * Returns the object identifier of the curve, of the provided + * elliptic curve parameters structures. + * + * Return 0 on failure (unknown EC domain parameters). + */ +SECOidTag SECKEY_GetECCOid(const SECKEYECParams *params); + +SEC_END_PROTOS + +#endif /* _KEYHI_H_ */ diff --git a/security/nss/lib/cryptohi/keyi.h b/security/nss/lib/cryptohi/keyi.h new file mode 100644 index 0000000000..5683afbeb6 --- /dev/null +++ b/security/nss/lib/cryptohi/keyi.h @@ -0,0 +1,45 @@ +/* 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/. */ + +#ifndef _KEYI_H_ +#define _KEYI_H_ +#include "secerr.h" + +SEC_BEGIN_PROTOS +/* NSS private functions */ +/* map an oid to a keytype... actually this function and it's converse + * are good candidates for public functions.. */ +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); + +/* just get the 'encryption' oid from the combined signature oid */ +SECOidTag sec_GetEncAlgFromSigAlg(SECOidTag sigAlg); + +/* extract the RSA-PSS hash algorithms and salt length from + * parameters, taking into account of the default implications. + * + * (parameters is the parameters field of a algorithm ID structure + * (SECAlgorithmID)*/ +SECStatus sec_DecodeRSAPSSParams(PLArenaPool *arena, + const SECItem *params, + SECOidTag *hashAlg, + SECOidTag *maskHashAlg, + unsigned long *saltLength); + +/* convert the encoded RSA-PSS parameters into PKCS #11 mechanism parameters */ +SECStatus sec_DecodeRSAPSSParamsToMechanism(PLArenaPool *arena, + const SECItem *params, + CK_RSA_PKCS_PSS_PARAMS *mech); + +/* make sure the key length matches the policy for keyType */ +SECStatus seckey_EnforceKeySize(KeyType keyType, unsigned keyLength, + SECErrorCodes error); +SEC_END_PROTOS + +#endif /* _KEYHI_H_ */ diff --git a/security/nss/lib/cryptohi/keyt.h b/security/nss/lib/cryptohi/keyt.h new file mode 100644 index 0000000000..5a0d2c2e7c --- /dev/null +++ b/security/nss/lib/cryptohi/keyt.h @@ -0,0 +1,14 @@ +/* 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/. */ + +#ifndef _KEYT_H_ +#define _KEYT_H_ + +#if defined(_MSC_VER) || defined(__GNUC__) || defined(__clang__) +#pragma message("keyt.h is deprecated. Please include keythi.h instead.") +#endif + +#include "keythi.h" + +#endif /* _KEYT_H_ */ diff --git a/security/nss/lib/cryptohi/keythi.h b/security/nss/lib/cryptohi/keythi.h new file mode 100644 index 0000000000..f6170bb787 --- /dev/null +++ b/security/nss/lib/cryptohi/keythi.h @@ -0,0 +1,247 @@ +/* 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/. */ +#ifndef _KEYTHI_H_ +#define _KEYTHI_H_ 1 + +#include "eccutil.h" +#include "plarena.h" +#include "pkcs11t.h" +#include "secmodt.h" +#include "prclist.h" + +/* +** RFC 4055 Section 1.2 specifies three different RSA key types. +** +** rsaKey maps to keys with SEC_OID_PKCS1_RSA_ENCRYPTION and can be used for +** both encryption and signatures with old (PKCS #1 v1.5) and new (PKCS #1 +** v2.1) padding schemes. +** +** rsaPssKey maps to keys with SEC_OID_PKCS1_RSA_PSS_SIGNATURE and may only +** be used for signatures with PSS padding (PKCS #1 v2.1). +** +** rsaOaepKey maps to keys with SEC_OID_PKCS1_RSA_OAEP_ENCRYPTION and may only +** be used for encryption with OAEP padding (PKCS #1 v2.1). +*/ + +typedef enum { + nullKey = 0, + rsaKey = 1, + dsaKey = 2, + fortezzaKey = 3, /* deprecated */ + dhKey = 4, + keaKey = 5, /* deprecated */ + ecKey = 6, + rsaPssKey = 7, + rsaOaepKey = 8 +} KeyType; + +/* +** Template Definitions +**/ + +SEC_BEGIN_PROTOS +extern const SEC_ASN1Template SECKEY_RSAPublicKeyTemplate[]; +extern const SEC_ASN1Template SECKEY_RSAPSSParamsTemplate[]; +extern const SEC_ASN1Template SECKEY_DSAPublicKeyTemplate[]; +extern const SEC_ASN1Template SECKEY_DHPublicKeyTemplate[]; +extern const SEC_ASN1Template SECKEY_DHParamKeyTemplate[]; +extern const SEC_ASN1Template SECKEY_PQGParamsTemplate[]; +extern const SEC_ASN1Template SECKEY_DSAPrivateKeyExportTemplate[]; + +/* Windows DLL accessor functions */ +SEC_ASN1_CHOOSER_DECLARE(SECKEY_DSAPublicKeyTemplate) +SEC_ASN1_CHOOSER_DECLARE(SECKEY_RSAPublicKeyTemplate) +SEC_ASN1_CHOOSER_DECLARE(SECKEY_RSAPSSParamsTemplate) +SEC_END_PROTOS + +/* +** RSA Public Key structures +** member names from PKCS#1, section 7.1 +*/ + +struct SECKEYRSAPublicKeyStr { + PLArenaPool *arena; + SECItem modulus; + SECItem publicExponent; +}; +typedef struct SECKEYRSAPublicKeyStr SECKEYRSAPublicKey; + +/* +** RSA-PSS parameters +*/ +struct SECKEYRSAPSSParamsStr { + SECAlgorithmID *hashAlg; + SECAlgorithmID *maskAlg; + SECItem saltLength; + SECItem trailerField; +}; +typedef struct SECKEYRSAPSSParamsStr SECKEYRSAPSSParams; + +/* +** DSA Public Key and related structures +*/ + +struct SECKEYPQGParamsStr { + PLArenaPool *arena; + SECItem prime; /* p */ + SECItem subPrime; /* q */ + SECItem base; /* g */ + /* XXX chrisk: this needs to be expanded to hold j and validationParms (RFC2459 7.3.2) */ +}; +typedef struct SECKEYPQGParamsStr SECKEYPQGParams; + +struct SECKEYDSAPublicKeyStr { + SECKEYPQGParams params; + SECItem publicValue; +}; +typedef struct SECKEYDSAPublicKeyStr SECKEYDSAPublicKey; + +/* +** Diffie-Hellman Public Key structure +** Structure member names suggested by PKCS#3. +*/ +struct SECKEYDHParamsStr { + PLArenaPool *arena; + SECItem prime; /* p */ + SECItem base; /* g */ +}; +typedef struct SECKEYDHParamsStr SECKEYDHParams; + +struct SECKEYDHPublicKeyStr { + PLArenaPool *arena; + SECItem prime; + SECItem base; + SECItem publicValue; +}; +typedef struct SECKEYDHPublicKeyStr SECKEYDHPublicKey; + +/* +** Elliptic curve Public Key structure +** The PKCS#11 layer needs DER encoding of ANSI X9.62 +** parameters value +*/ +typedef SECItem SECKEYECParams; + +struct SECKEYECPublicKeyStr { + SECKEYECParams DEREncodedParams; + int size; /* size in bits */ + SECItem publicValue; /* encoded point */ + ECPointEncoding encoding; /* deprecated, ignored */ +}; +typedef struct SECKEYECPublicKeyStr SECKEYECPublicKey; + +/* +** FORTEZZA Public Key structures +*/ +struct SECKEYFortezzaPublicKeyStr { + int KEAversion; + int DSSversion; + unsigned char KMID[8]; + SECItem clearance; + SECItem KEApriviledge; + SECItem DSSpriviledge; + SECItem KEAKey; + SECItem DSSKey; + SECKEYPQGParams params; + SECKEYPQGParams keaParams; +}; +typedef struct SECKEYFortezzaPublicKeyStr SECKEYFortezzaPublicKey; +#define KEAprivilege KEApriviledge /* corrected spelling */ +#define DSSprivilege DSSpriviledge /* corrected spelling */ + +struct SECKEYDiffPQGParamsStr { + SECKEYPQGParams DiffKEAParams; + SECKEYPQGParams DiffDSAParams; +}; +typedef struct SECKEYDiffPQGParamsStr SECKEYDiffPQGParams; + +struct SECKEYPQGDualParamsStr { + SECKEYPQGParams CommParams; + SECKEYDiffPQGParams DiffParams; +}; +typedef struct SECKEYPQGDualParamsStr SECKEYPQGDualParams; + +struct SECKEYKEAParamsStr { + PLArenaPool *arena; + SECItem hash; +}; +typedef struct SECKEYKEAParamsStr SECKEYKEAParams; + +struct SECKEYKEAPublicKeyStr { + SECKEYKEAParams params; + SECItem publicValue; +}; +typedef struct SECKEYKEAPublicKeyStr SECKEYKEAPublicKey; + +/* +** A Generic public key object. +*/ +struct SECKEYPublicKeyStr { + PLArenaPool *arena; + KeyType keyType; + PK11SlotInfo *pkcs11Slot; + CK_OBJECT_HANDLE pkcs11ID; + union { + SECKEYRSAPublicKey rsa; + SECKEYDSAPublicKey dsa; + SECKEYDHPublicKey dh; + SECKEYKEAPublicKey kea; + SECKEYFortezzaPublicKey fortezza; + SECKEYECPublicKey ec; + } u; +}; +typedef struct SECKEYPublicKeyStr SECKEYPublicKey; + +/* bit flag definitions for staticflags */ +#define SECKEY_Attributes_Cached 0x1 /* bit 0 states \ + whether attributes are cached */ +#define SECKEY_CKA_PRIVATE (1U << 1) /* bit 1 is the value of CKA_PRIVATE */ +#define SECKEY_CKA_ALWAYS_AUTHENTICATE (1U << 2) + +#define SECKEY_ATTRIBUTES_CACHED(key) \ + (0 != (key->staticflags & SECKEY_Attributes_Cached)) + +#define SECKEY_ATTRIBUTE_VALUE(key, attribute) \ + (0 != (key->staticflags & SECKEY_##attribute)) + +#define SECKEY_HAS_ATTRIBUTE_SET(key, attribute) \ + (0 != (key->staticflags & SECKEY_Attributes_Cached)) ? (0 != (key->staticflags & SECKEY_##attribute)) : PK11_HasAttributeSet(key->pkcs11Slot, key->pkcs11ID, attribute, PR_FALSE) + +#define SECKEY_HAS_ATTRIBUTE_SET_LOCK(key, attribute, haslock) \ + (0 != (key->staticflags & SECKEY_Attributes_Cached)) ? (0 != (key->staticflags & SECKEY_##attribute)) : pk11_HasAttributeSet_Lock(key->pkcs11Slot, key->pkcs11ID, attribute, haslock) + +/* +** A generic key structure +*/ +struct SECKEYPrivateKeyStr { + PLArenaPool *arena; + KeyType keyType; + PK11SlotInfo *pkcs11Slot; /* pkcs11 slot this key lives in */ + CK_OBJECT_HANDLE pkcs11ID; /* ID of pkcs11 object */ + PRBool pkcs11IsTemp; /* temp pkcs11 object, delete it when done */ + void *wincx; /* context for errors and pw prompts */ + PRUint32 staticflags; /* bit flag of cached PKCS#11 attributes */ +}; +typedef struct SECKEYPrivateKeyStr SECKEYPrivateKey; + +typedef struct { + PRCList links; + SECKEYPrivateKey *key; +} SECKEYPrivateKeyListNode; + +typedef struct { + PRCList list; + PLArenaPool *arena; +} SECKEYPrivateKeyList; + +typedef struct { + PRCList links; + SECKEYPublicKey *key; +} SECKEYPublicKeyListNode; + +typedef struct { + PRCList list; + PLArenaPool *arena; +} SECKEYPublicKeyList; +#endif /* _KEYTHI_H_ */ diff --git a/security/nss/lib/cryptohi/manifest.mn b/security/nss/lib/cryptohi/manifest.mn new file mode 100644 index 0000000000..6b1a546dd3 --- /dev/null +++ b/security/nss/lib/cryptohi/manifest.mn @@ -0,0 +1,37 @@ +# +# 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 = ../.. + +MODULE = nss + +REQUIRES = nssutil + +LIBRARY_NAME = cryptohi +SHARED_LIBRARY = $(NULL) + +EXPORTS = \ + cryptohi.h \ + cryptoht.h \ + key.h \ + keyhi.h \ + keyt.h \ + keythi.h \ + sechash.h \ + $(NULL) + +PRIVATE_EXPORTS = \ + keyi.h \ + $(NULL) + +CSRCS = \ + sechash.c \ + seckey.c \ + secsign.c \ + secvfy.c \ + dsautil.c \ + $(NULL) + +# This part of the code, including all sub-dirs, can be optimized for size +export ALLOW_OPT_CODE_SIZE = 1 diff --git a/security/nss/lib/cryptohi/sechash.c b/security/nss/lib/cryptohi/sechash.c new file mode 100644 index 0000000000..474fdfff9e --- /dev/null +++ b/security/nss/lib/cryptohi/sechash.c @@ -0,0 +1,471 @@ +/* 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 "sechash.h" +#include "secoidt.h" +#include "secerr.h" +#include "blapi.h" +#include "pk11func.h" /* for the PK11_ calls below. */ + +static void * +null_hash_new_context(void) +{ + return NULL; +} + +static void * +null_hash_clone_context(void *v) +{ + PORT_Assert(v == NULL); + return NULL; +} + +static void +null_hash_begin(void *v) +{ +} + +static void +null_hash_update(void *v, const unsigned char *input, unsigned int length) +{ +} + +static void +null_hash_end(void *v, unsigned char *output, unsigned int *outLen, + unsigned int maxOut) +{ + *outLen = 0; +} + +static void +null_hash_destroy_context(void *v, PRBool b) +{ + PORT_Assert(v == NULL); +} + +static void * +md2_NewContext(void) +{ + return (void *)PK11_CreateDigestContext(SEC_OID_MD2); +} + +static void * +md5_NewContext(void) +{ + return (void *)PK11_CreateDigestContext(SEC_OID_MD5); +} + +static void * +sha1_NewContext(void) +{ + return (void *)PK11_CreateDigestContext(SEC_OID_SHA1); +} + +static void * +sha224_NewContext(void) +{ + return (void *)PK11_CreateDigestContext(SEC_OID_SHA224); +} + +static void * +sha256_NewContext(void) +{ + return (void *)PK11_CreateDigestContext(SEC_OID_SHA256); +} + +static void * +sha384_NewContext(void) +{ + return (void *)PK11_CreateDigestContext(SEC_OID_SHA384); +} + +static void * +sha512_NewContext(void) +{ + return (void *)PK11_CreateDigestContext(SEC_OID_SHA512); +} + +const SECHashObject SECHashObjects[] = { + { 0, + (void *(*)(void))null_hash_new_context, + (void *(*)(void *))null_hash_clone_context, + (void (*)(void *, PRBool))null_hash_destroy_context, + (void (*)(void *))null_hash_begin, + (void (*)(void *, const unsigned char *, unsigned int))null_hash_update, + (void (*)(void *, unsigned char *, unsigned int *, + unsigned int))null_hash_end, + 0, + HASH_AlgNULL }, + { MD2_LENGTH, + (void *(*)(void))md2_NewContext, + (void *(*)(void *))PK11_CloneContext, + (void (*)(void *, PRBool))PK11_DestroyContext, + (void (*)(void *))PK11_DigestBegin, + (void (*)(void *, const unsigned char *, unsigned int))PK11_DigestOp, + (void (*)(void *, unsigned char *, unsigned int *, unsigned int)) + PK11_DigestFinal, + MD2_BLOCK_LENGTH, + HASH_AlgMD2 }, + { MD5_LENGTH, + (void *(*)(void))md5_NewContext, + (void *(*)(void *))PK11_CloneContext, + (void (*)(void *, PRBool))PK11_DestroyContext, + (void (*)(void *))PK11_DigestBegin, + (void (*)(void *, const unsigned char *, unsigned int))PK11_DigestOp, + (void (*)(void *, unsigned char *, unsigned int *, unsigned int)) + PK11_DigestFinal, + MD5_BLOCK_LENGTH, + HASH_AlgMD5 }, + { SHA1_LENGTH, + (void *(*)(void))sha1_NewContext, + (void *(*)(void *))PK11_CloneContext, + (void (*)(void *, PRBool))PK11_DestroyContext, + (void (*)(void *))PK11_DigestBegin, + (void (*)(void *, const unsigned char *, unsigned int))PK11_DigestOp, + (void (*)(void *, unsigned char *, unsigned int *, unsigned int)) + PK11_DigestFinal, + SHA1_BLOCK_LENGTH, + HASH_AlgSHA1 }, + { SHA256_LENGTH, + (void *(*)(void))sha256_NewContext, + (void *(*)(void *))PK11_CloneContext, + (void (*)(void *, PRBool))PK11_DestroyContext, + (void (*)(void *))PK11_DigestBegin, + (void (*)(void *, const unsigned char *, unsigned int))PK11_DigestOp, + (void (*)(void *, unsigned char *, unsigned int *, unsigned int)) + PK11_DigestFinal, + SHA256_BLOCK_LENGTH, + HASH_AlgSHA256 }, + { SHA384_LENGTH, + (void *(*)(void))sha384_NewContext, + (void *(*)(void *))PK11_CloneContext, + (void (*)(void *, PRBool))PK11_DestroyContext, + (void (*)(void *))PK11_DigestBegin, + (void (*)(void *, const unsigned char *, unsigned int))PK11_DigestOp, + (void (*)(void *, unsigned char *, unsigned int *, unsigned int)) + PK11_DigestFinal, + SHA384_BLOCK_LENGTH, + HASH_AlgSHA384 }, + { SHA512_LENGTH, + (void *(*)(void))sha512_NewContext, + (void *(*)(void *))PK11_CloneContext, + (void (*)(void *, PRBool))PK11_DestroyContext, + (void (*)(void *))PK11_DigestBegin, + (void (*)(void *, const unsigned char *, unsigned int))PK11_DigestOp, + (void (*)(void *, unsigned char *, unsigned int *, unsigned int)) + PK11_DigestFinal, + SHA512_BLOCK_LENGTH, + HASH_AlgSHA512 }, + { SHA224_LENGTH, + (void *(*)(void))sha224_NewContext, + (void *(*)(void *))PK11_CloneContext, + (void (*)(void *, PRBool))PK11_DestroyContext, + (void (*)(void *))PK11_DigestBegin, + (void (*)(void *, const unsigned char *, unsigned int))PK11_DigestOp, + (void (*)(void *, unsigned char *, unsigned int *, unsigned int)) + PK11_DigestFinal, + SHA224_BLOCK_LENGTH, + HASH_AlgSHA224 }, +}; + +const SECHashObject * +HASH_GetHashObject(HASH_HashType type) +{ + return &SECHashObjects[type]; +} + +HASH_HashType +HASH_GetHashTypeByOidTag(SECOidTag hashOid) +{ + HASH_HashType ht = HASH_AlgNULL; + + switch (hashOid) { + case SEC_OID_MD2: + ht = HASH_AlgMD2; + break; + case SEC_OID_MD5: + ht = HASH_AlgMD5; + break; + case SEC_OID_SHA1: + ht = HASH_AlgSHA1; + break; + case SEC_OID_SHA224: + ht = HASH_AlgSHA224; + break; + case SEC_OID_SHA256: + ht = HASH_AlgSHA256; + break; + case SEC_OID_SHA384: + ht = HASH_AlgSHA384; + break; + case SEC_OID_SHA512: + ht = HASH_AlgSHA512; + break; + default: + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + break; + } + return ht; +} + +SECOidTag +HASH_GetHashOidTagByHashType(HASH_HashType type) +{ + SECOidTag oid = SEC_OID_UNKNOWN; + + switch (type) { + case HASH_AlgMD2: + oid = SEC_OID_MD2; + break; + case HASH_AlgMD5: + oid = SEC_OID_MD5; + break; + case HASH_AlgSHA1: + oid = SEC_OID_SHA1; + break; + case HASH_AlgSHA224: + oid = SEC_OID_SHA224; + break; + case HASH_AlgSHA256: + oid = SEC_OID_SHA256; + break; + case HASH_AlgSHA384: + oid = SEC_OID_SHA384; + break; + case HASH_AlgSHA512: + oid = SEC_OID_SHA512; + break; + default: + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + break; + } + return oid; +} + +SECOidTag +HASH_GetHashOidTagByHMACOidTag(SECOidTag hmacOid) +{ + SECOidTag hashOid = SEC_OID_UNKNOWN; + + switch (hmacOid) { + /* no oid exists for HMAC_MD2 */ + /* NSS does not define a oid for HMAC_MD4 */ + case SEC_OID_HMAC_SHA1: + hashOid = SEC_OID_SHA1; + break; + case SEC_OID_HMAC_SHA224: + hashOid = SEC_OID_SHA224; + break; + case SEC_OID_HMAC_SHA256: + hashOid = SEC_OID_SHA256; + break; + case SEC_OID_HMAC_SHA384: + hashOid = SEC_OID_SHA384; + break; + case SEC_OID_HMAC_SHA512: + hashOid = SEC_OID_SHA512; + break; + default: + hashOid = SEC_OID_UNKNOWN; + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + break; + } + return hashOid; +} + +SECOidTag +HASH_GetHMACOidTagByHashOidTag(SECOidTag hashOid) +{ + SECOidTag hmacOid = SEC_OID_UNKNOWN; + + switch (hashOid) { + /* no oid exists for HMAC_MD2 */ + /* NSS does not define a oid for HMAC_MD4 */ + case SEC_OID_SHA1: + hmacOid = SEC_OID_HMAC_SHA1; + break; + case SEC_OID_SHA224: + hmacOid = SEC_OID_HMAC_SHA224; + break; + case SEC_OID_SHA256: + hmacOid = SEC_OID_HMAC_SHA256; + break; + case SEC_OID_SHA384: + hmacOid = SEC_OID_HMAC_SHA384; + break; + case SEC_OID_SHA512: + hmacOid = SEC_OID_HMAC_SHA512; + break; + default: + hmacOid = SEC_OID_UNKNOWN; + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + break; + } + return hmacOid; +} + +const SECHashObject * +HASH_GetHashObjectByOidTag(SECOidTag hashOid) +{ + HASH_HashType ht = HASH_GetHashTypeByOidTag(hashOid); + + return (ht == HASH_AlgNULL) ? NULL : &SECHashObjects[ht]; +} + +/* returns zero for unknown hash OID */ +unsigned int +HASH_ResultLenByOidTag(SECOidTag hashOid) +{ + const SECHashObject *hashObject = HASH_GetHashObjectByOidTag(hashOid); + unsigned int resultLen = 0; + + if (hashObject) + resultLen = hashObject->length; + return resultLen; +} + +/* returns zero if hash type invalid. */ +unsigned int +HASH_ResultLen(HASH_HashType type) +{ + if ((type < HASH_AlgNULL) || (type >= HASH_AlgTOTAL)) { + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + return (0); + } + + return (SECHashObjects[type].length); +} + +unsigned int +HASH_ResultLenContext(HASHContext *context) +{ + return (context->hashobj->length); +} + +SECStatus +HASH_HashBuf(HASH_HashType type, + unsigned char *dest, + const unsigned char *src, + PRUint32 src_len) +{ + HASHContext *cx; + unsigned int part; + + if ((type < HASH_AlgNULL) || (type >= HASH_AlgTOTAL)) { + return (SECFailure); + } + + cx = HASH_Create(type); + if (cx == NULL) { + return (SECFailure); + } + HASH_Begin(cx); + HASH_Update(cx, src, src_len); + HASH_End(cx, dest, &part, HASH_ResultLenContext(cx)); + HASH_Destroy(cx); + + return (SECSuccess); +} + +HASHContext * +HASH_Create(HASH_HashType type) +{ + void *hash_context = NULL; + HASHContext *ret = NULL; + + if ((type < HASH_AlgNULL) || (type >= HASH_AlgTOTAL)) { + return (NULL); + } + + hash_context = (*SECHashObjects[type].create)(); + if (hash_context == NULL) { + goto loser; + } + + ret = (HASHContext *)PORT_Alloc(sizeof(HASHContext)); + if (ret == NULL) { + goto loser; + } + + ret->hash_context = hash_context; + ret->hashobj = &SECHashObjects[type]; + + return (ret); + +loser: + if (hash_context != NULL) { + (*SECHashObjects[type].destroy)(hash_context, PR_TRUE); + } + + return (NULL); +} + +HASHContext * +HASH_Clone(HASHContext *context) +{ + void *hash_context = NULL; + HASHContext *ret = NULL; + + hash_context = (*context->hashobj->clone)(context->hash_context); + if (hash_context == NULL) { + goto loser; + } + + ret = (HASHContext *)PORT_Alloc(sizeof(HASHContext)); + if (ret == NULL) { + goto loser; + } + + ret->hash_context = hash_context; + ret->hashobj = context->hashobj; + + return (ret); + +loser: + if (hash_context != NULL) { + (*context->hashobj->destroy)(hash_context, PR_TRUE); + } + + return (NULL); +} + +void +HASH_Destroy(HASHContext *context) +{ + (*context->hashobj->destroy)(context->hash_context, PR_TRUE); + PORT_Free(context); + return; +} + +void +HASH_Begin(HASHContext *context) +{ + (*context->hashobj->begin)(context->hash_context); + return; +} + +void +HASH_Update(HASHContext *context, + const unsigned char *src, + unsigned int len) +{ + (*context->hashobj->update)(context->hash_context, src, len); + return; +} + +void +HASH_End(HASHContext *context, + unsigned char *result, + unsigned int *result_len, + unsigned int max_result_len) +{ + (*context->hashobj->end)(context->hash_context, result, result_len, + max_result_len); + return; +} + +HASH_HashType +HASH_GetType(HASHContext *context) +{ + return (context->hashobj->type); +} diff --git a/security/nss/lib/cryptohi/sechash.h b/security/nss/lib/cryptohi/sechash.h new file mode 100644 index 0000000000..16e8b67133 --- /dev/null +++ b/security/nss/lib/cryptohi/sechash.h @@ -0,0 +1,60 @@ +/* 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/. */ + +#ifndef _HASH_H_ +#define _HASH_H_ + +#include "seccomon.h" +#include "hasht.h" +#include "secoidt.h" + +SEC_BEGIN_PROTOS + +/* +** Generic hash api. +*/ + +extern unsigned int HASH_ResultLen(HASH_HashType type); + +extern unsigned int HASH_ResultLenContext(HASHContext *context); + +extern unsigned int HASH_ResultLenByOidTag(SECOidTag hashOid); + +extern SECStatus HASH_HashBuf(HASH_HashType type, + unsigned char *dest, + const unsigned char *src, + PRUint32 src_len); + +extern HASHContext *HASH_Create(HASH_HashType type); + +extern HASHContext *HASH_Clone(HASHContext *context); + +extern void HASH_Destroy(HASHContext *context); + +extern void HASH_Begin(HASHContext *context); + +extern void HASH_Update(HASHContext *context, + const unsigned char *src, + unsigned int len); + +extern void HASH_End(HASHContext *context, + unsigned char *result, + unsigned int *result_len, + unsigned int max_result_len); + +extern HASH_HashType HASH_GetType(HASHContext *context); + +extern const SECHashObject *HASH_GetHashObject(HASH_HashType type); + +extern const SECHashObject *HASH_GetHashObjectByOidTag(SECOidTag hashOid); + +extern HASH_HashType HASH_GetHashTypeByOidTag(SECOidTag hashOid); +extern SECOidTag HASH_GetHashOidTagByHMACOidTag(SECOidTag hmacOid); +extern SECOidTag HASH_GetHMACOidTagByHashOidTag(SECOidTag hashOid); + +extern SECOidTag HASH_GetHashOidTagByHashType(HASH_HashType type); + +SEC_END_PROTOS + +#endif /* _HASH_H_ */ diff --git a/security/nss/lib/cryptohi/seckey.c b/security/nss/lib/cryptohi/seckey.c new file mode 100644 index 0000000000..656609e0d2 --- /dev/null +++ b/security/nss/lib/cryptohi/seckey.c @@ -0,0 +1,2320 @@ +/* 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 "cryptohi.h" +#include "keyhi.h" +#include "secoid.h" +#include "secitem.h" +#include "secder.h" +#include "base64.h" +#include "secasn1.h" +#include "cert.h" +#include "pk11func.h" +#include "secerr.h" +#include "secdig.h" +#include "prtime.h" +#include "keyi.h" +#include "nss.h" + +SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate) +SEC_ASN1_MKSUB(SEC_IntegerTemplate) + +const SEC_ASN1Template CERT_SubjectPublicKeyInfoTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(CERTSubjectPublicKeyInfo) }, + { SEC_ASN1_INLINE | SEC_ASN1_XTRN, + offsetof(CERTSubjectPublicKeyInfo, algorithm), + SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, + { SEC_ASN1_BIT_STRING, + offsetof(CERTSubjectPublicKeyInfo, subjectPublicKey) }, + { 0 } +}; + +const SEC_ASN1Template CERT_PublicKeyAndChallengeTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTPublicKeyAndChallenge) }, + { SEC_ASN1_ANY, offsetof(CERTPublicKeyAndChallenge, spki) }, + { SEC_ASN1_IA5_STRING, offsetof(CERTPublicKeyAndChallenge, challenge) }, + { 0 } +}; + +const SEC_ASN1Template SECKEY_RSAPublicKeyTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECKEYPublicKey) }, + { SEC_ASN1_INTEGER, offsetof(SECKEYPublicKey, u.rsa.modulus) }, + { SEC_ASN1_INTEGER, offsetof(SECKEYPublicKey, u.rsa.publicExponent) }, + { 0 } +}; + +static const SEC_ASN1Template seckey_PointerToAlgorithmIDTemplate[] = { + { SEC_ASN1_POINTER | SEC_ASN1_XTRN, 0, + SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) } +}; + +/* Parameters for SEC_OID_PKCS1_RSA_PSS_SIGNATURE */ +const SEC_ASN1Template SECKEY_RSAPSSParamsTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECKEYRSAPSSParams) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | + SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(SECKEYRSAPSSParams, hashAlg), + seckey_PointerToAlgorithmIDTemplate }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | + SEC_ASN1_CONTEXT_SPECIFIC | 1, + offsetof(SECKEYRSAPSSParams, maskAlg), + seckey_PointerToAlgorithmIDTemplate }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | + SEC_ASN1_XTRN | SEC_ASN1_CONTEXT_SPECIFIC | 2, + offsetof(SECKEYRSAPSSParams, saltLength), + SEC_ASN1_SUB(SEC_IntegerTemplate) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | + SEC_ASN1_XTRN | SEC_ASN1_CONTEXT_SPECIFIC | 3, + offsetof(SECKEYRSAPSSParams, trailerField), + SEC_ASN1_SUB(SEC_IntegerTemplate) }, + { 0 } +}; + +const SEC_ASN1Template SECKEY_DSAPublicKeyTemplate[] = { + { SEC_ASN1_INTEGER, offsetof(SECKEYPublicKey, u.dsa.publicValue) }, + { 0 } +}; + +const SEC_ASN1Template SECKEY_PQGParamsTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECKEYPQGParams) }, + { SEC_ASN1_INTEGER, offsetof(SECKEYPQGParams, prime) }, + { SEC_ASN1_INTEGER, offsetof(SECKEYPQGParams, subPrime) }, + { SEC_ASN1_INTEGER, offsetof(SECKEYPQGParams, base) }, + { 0 } +}; + +const SEC_ASN1Template SECKEY_DHPublicKeyTemplate[] = { + { SEC_ASN1_INTEGER, offsetof(SECKEYPublicKey, u.dh.publicValue) }, + { 0 } +}; + +const SEC_ASN1Template SECKEY_DHParamKeyTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECKEYPublicKey) }, + { SEC_ASN1_INTEGER, offsetof(SECKEYPublicKey, u.dh.prime) }, + { SEC_ASN1_INTEGER, offsetof(SECKEYPublicKey, u.dh.base) }, + /* XXX chrisk: this needs to be expanded for decoding of j and validationParms (RFC2459 7.3.2) */ + { SEC_ASN1_SKIP_REST }, + { 0 } +}; + +SEC_ASN1_CHOOSER_IMPLEMENT(SECKEY_DSAPublicKeyTemplate) +SEC_ASN1_CHOOSER_IMPLEMENT(SECKEY_RSAPublicKeyTemplate) +SEC_ASN1_CHOOSER_IMPLEMENT(SECKEY_RSAPSSParamsTemplate) +SEC_ASN1_CHOOSER_IMPLEMENT(CERT_SubjectPublicKeyInfoTemplate) + +/* + * See bugzilla bug 125359 + * Since NSS (via PKCS#11) wants to handle big integers as unsigned ints, + * all of the templates above that en/decode into integers must be converted + * from ASN.1's signed integer type. This is done by marking either the + * source or destination (encoding or decoding, respectively) type as + * siUnsignedInteger. + */ +static void +prepare_rsa_pub_key_for_asn1(SECKEYPublicKey *pubk) +{ + pubk->u.rsa.modulus.type = siUnsignedInteger; + pubk->u.rsa.publicExponent.type = siUnsignedInteger; +} + +static void +prepare_dsa_pub_key_for_asn1(SECKEYPublicKey *pubk) +{ + pubk->u.dsa.publicValue.type = siUnsignedInteger; +} + +static void +prepare_pqg_params_for_asn1(SECKEYPQGParams *params) +{ + params->prime.type = siUnsignedInteger; + params->subPrime.type = siUnsignedInteger; + params->base.type = siUnsignedInteger; +} + +static void +prepare_dh_pub_key_for_asn1(SECKEYPublicKey *pubk) +{ + pubk->u.dh.prime.type = siUnsignedInteger; + pubk->u.dh.base.type = siUnsignedInteger; + pubk->u.dh.publicValue.type = siUnsignedInteger; +} + +/* Create an RSA key pair is any slot able to do so. +** The created keys are "session" (temporary), not "token" (permanent), +** and they are "sensitive", which makes them costly to move to another token. +*/ +SECKEYPrivateKey * +SECKEY_CreateRSAPrivateKey(int keySizeInBits, SECKEYPublicKey **pubk, void *cx) +{ + SECKEYPrivateKey *privk; + PK11RSAGenParams param; + PK11SlotInfo *slot = PK11_GetBestSlot(CKM_RSA_PKCS_KEY_PAIR_GEN, cx); + if (!slot) { + return NULL; + } + + param.keySizeInBits = keySizeInBits; + param.pe = 65537L; + + privk = PK11_GenerateKeyPair(slot, CKM_RSA_PKCS_KEY_PAIR_GEN, ¶m, pubk, + PR_FALSE, PR_TRUE, cx); + PK11_FreeSlot(slot); + return (privk); +} + +/* Create a DH key pair in any slot able to do so, +** This is a "session" (temporary), not "token" (permanent) key. +** Because of the high probability that this key will need to be moved to +** another token, and the high cost of moving "sensitive" keys, we attempt +** to create this key pair without the "sensitive" attribute, but revert to +** creating a "sensitive" key if necessary. +*/ +SECKEYPrivateKey * +SECKEY_CreateDHPrivateKey(SECKEYDHParams *param, SECKEYPublicKey **pubk, void *cx) +{ + SECKEYPrivateKey *privk; + PK11SlotInfo *slot; + + if (!param || !param->base.data || !param->prime.data || + SECKEY_BigIntegerBitLength(¶m->prime) < DH_MIN_P_BITS || + param->base.len == 0 || param->base.len > param->prime.len + 1 || + (param->base.len == 1 && param->base.data[0] == 0)) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + + slot = PK11_GetBestSlot(CKM_DH_PKCS_KEY_PAIR_GEN, cx); + if (!slot) { + return NULL; + } + + privk = PK11_GenerateKeyPair(slot, CKM_DH_PKCS_KEY_PAIR_GEN, param, + pubk, PR_FALSE, PR_FALSE, cx); + if (!privk) + privk = PK11_GenerateKeyPair(slot, CKM_DH_PKCS_KEY_PAIR_GEN, param, + pubk, PR_FALSE, PR_TRUE, cx); + + PK11_FreeSlot(slot); + return (privk); +} + +/* Create an EC key pair in any slot able to do so, +** This is a "session" (temporary), not "token" (permanent) key. +** Because of the high probability that this key will need to be moved to +** another token, and the high cost of moving "sensitive" keys, we attempt +** to create this key pair without the "sensitive" attribute, but revert to +** creating a "sensitive" key if necessary. +*/ +SECKEYPrivateKey * +SECKEY_CreateECPrivateKey(SECKEYECParams *param, SECKEYPublicKey **pubk, void *cx) +{ + SECKEYPrivateKey *privk; + PK11SlotInfo *slot = PK11_GetBestSlot(CKM_EC_KEY_PAIR_GEN, cx); + if (!slot) { + return NULL; + } + + privk = PK11_GenerateKeyPairWithOpFlags(slot, CKM_EC_KEY_PAIR_GEN, + param, pubk, + PK11_ATTR_SESSION | + PK11_ATTR_INSENSITIVE | + PK11_ATTR_PUBLIC, + CKF_DERIVE, CKF_DERIVE | CKF_SIGN, + cx); + if (!privk) + privk = PK11_GenerateKeyPairWithOpFlags(slot, CKM_EC_KEY_PAIR_GEN, + param, pubk, + PK11_ATTR_SESSION | + PK11_ATTR_SENSITIVE | + PK11_ATTR_PRIVATE, + CKF_DERIVE, CKF_DERIVE | CKF_SIGN, + cx); + + PK11_FreeSlot(slot); + return (privk); +} + +void +SECKEY_DestroyPrivateKey(SECKEYPrivateKey *privk) +{ + if (privk) { + if (privk->pkcs11Slot) { + if (privk->pkcs11IsTemp) { + PK11_DestroyObject(privk->pkcs11Slot, privk->pkcs11ID); + } + PK11_FreeSlot(privk->pkcs11Slot); + } + if (privk->arena) { + PORT_FreeArena(privk->arena, PR_TRUE); + } + } +} + +void +SECKEY_DestroyPublicKey(SECKEYPublicKey *pubk) +{ + if (pubk) { + if (pubk->pkcs11Slot) { + if (!PK11_IsPermObject(pubk->pkcs11Slot, pubk->pkcs11ID)) { + PK11_DestroyObject(pubk->pkcs11Slot, pubk->pkcs11ID); + } + PK11_FreeSlot(pubk->pkcs11Slot); + } + if (pubk->arena) { + PORT_FreeArena(pubk->arena, PR_FALSE); + } + } +} + +SECStatus +SECKEY_CopySubjectPublicKeyInfo(PLArenaPool *arena, + CERTSubjectPublicKeyInfo *to, + CERTSubjectPublicKeyInfo *from) +{ + SECStatus rv; + SECItem spk; + + rv = SECOID_CopyAlgorithmID(arena, &to->algorithm, &from->algorithm); + if (rv == SECSuccess) { + /* + * subjectPublicKey is a bit string, whose length is in bits. + * Convert the length from bits to bytes for SECITEM_CopyItem. + */ + spk = from->subjectPublicKey; + DER_ConvertBitString(&spk); + rv = SECITEM_CopyItem(arena, &to->subjectPublicKey, &spk); + /* Set the length back to bits. */ + if (rv == SECSuccess) { + to->subjectPublicKey.len = from->subjectPublicKey.len; + } + } + + return rv; +} + +/* Procedure to update the pqg parameters for a cert's public key. + * pqg parameters only need to be updated for DSA certificates. + * The procedure uses calls to itself recursively to update a certificate + * issuer's pqg parameters. Some important rules are: + * - Do nothing if the cert already has PQG parameters. + * - If the cert does not have PQG parameters, obtain them from the issuer. + * - A valid cert chain cannot have a DSA cert without + * pqg parameters that has a parent that is not a DSA cert. */ + +static SECStatus +seckey_UpdateCertPQGChain(CERTCertificate *subjectCert, int count) +{ + SECStatus rv; + SECOidData *oid = NULL; + int tag; + CERTSubjectPublicKeyInfo *subjectSpki = NULL; + CERTSubjectPublicKeyInfo *issuerSpki = NULL; + CERTCertificate *issuerCert = NULL; + + /* increment cert chain length counter*/ + count++; + + /* check if cert chain length exceeds the maximum length*/ + if (count > CERT_MAX_CERT_CHAIN) { + return SECFailure; + } + + oid = SECOID_FindOID(&subjectCert->subjectPublicKeyInfo.algorithm.algorithm); + if (oid != NULL) { + tag = oid->offset; + + /* Check if cert has a DSA or EC public key. If not, return + * success since no PQG params need to be updated. + * + * Question: do we really need to do this for EC keys. They don't have + * PQG parameters, but they do have parameters. The question is does + * the child cert inherit thost parameters for EC from the parent, or + * do we always include those parameters in each cert. + */ + + if ((tag != SEC_OID_ANSIX9_DSA_SIGNATURE) && + (tag != SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST) && + (tag != SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA224_DIGEST) && + (tag != SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA256_DIGEST) && + (tag != SEC_OID_BOGUS_DSA_SIGNATURE_WITH_SHA1_DIGEST) && + (tag != SEC_OID_SDN702_DSA_SIGNATURE) && + (tag != SEC_OID_ANSIX962_EC_PUBLIC_KEY)) { + + return SECSuccess; + } + } else { + return SECFailure; /* return failure if oid is NULL */ + } + + /* if cert has PQG parameters, return success */ + + subjectSpki = &subjectCert->subjectPublicKeyInfo; + + if (subjectSpki->algorithm.parameters.len != 0) { + return SECSuccess; + } + + /* check if the cert is self-signed */ + if (subjectCert->isRoot) { + /* fail since cert is self-signed and has no pqg params. */ + return SECFailure; + } + + /* get issuer cert */ + issuerCert = CERT_FindCertIssuer(subjectCert, PR_Now(), certUsageAnyCA); + if (!issuerCert) { + return SECFailure; + } + + /* if parent is not DSA, return failure since + we don't allow this case. */ + + oid = SECOID_FindOID(&issuerCert->subjectPublicKeyInfo.algorithm.algorithm); + if (oid != NULL) { + tag = oid->offset; + + /* Check if issuer cert has a DSA public key. If not, + * return failure. */ + + if ((tag != SEC_OID_ANSIX9_DSA_SIGNATURE) && + (tag != SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST) && + (tag != SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA224_DIGEST) && + (tag != SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA256_DIGEST) && + (tag != SEC_OID_BOGUS_DSA_SIGNATURE_WITH_SHA1_DIGEST) && + (tag != SEC_OID_SDN702_DSA_SIGNATURE) && + (tag != SEC_OID_ANSIX962_EC_PUBLIC_KEY)) { + rv = SECFailure; + goto loser; + } + } else { + rv = SECFailure; /* return failure if oid is NULL */ + goto loser; + } + + /* at this point the subject cert has no pqg parameters and the + * issuer cert has a DSA public key. Update the issuer's + * pqg parameters with a recursive call to this same function. */ + + rv = seckey_UpdateCertPQGChain(issuerCert, count); + if (rv != SECSuccess) { + rv = SECFailure; + goto loser; + } + + /* ensure issuer has pqg parameters */ + + issuerSpki = &issuerCert->subjectPublicKeyInfo; + if (issuerSpki->algorithm.parameters.len == 0) { + rv = SECFailure; + } + + /* if update was successful and pqg params present, then copy the + * parameters to the subject cert's key. */ + + if (rv == SECSuccess) { + rv = SECITEM_CopyItem(subjectCert->arena, + &subjectSpki->algorithm.parameters, + &issuerSpki->algorithm.parameters); + } + +loser: + if (issuerCert) { + CERT_DestroyCertificate(issuerCert); + } + return rv; +} + +SECStatus +SECKEY_UpdateCertPQG(CERTCertificate *subjectCert) +{ + if (!subjectCert) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + return seckey_UpdateCertPQGChain(subjectCert, 0); +} + +/* Decode the DSA PQG parameters. The params could be stored in two + * possible formats, the old fortezza-only wrapped format or + * the normal standard format. Store the decoded parameters in + * a V3 certificate data structure. */ + +static SECStatus +seckey_DSADecodePQG(PLArenaPool *arena, SECKEYPublicKey *pubk, + const SECItem *params) +{ + SECStatus rv; + SECItem newparams; + + if (params == NULL) + return SECFailure; + + if (params->data == NULL) + return SECFailure; + + PORT_Assert(arena); + + /* make a copy of the data into the arena so QuickDER output is valid */ + rv = SECITEM_CopyItem(arena, &newparams, params); + + /* Check if params use the standard format. + * The value 0xa1 will appear in the first byte of the parameter data + * if the PQG parameters are not using the standard format. This + * code should be changed to use a better method to detect non-standard + * parameters. */ + + if ((newparams.data[0] != 0xa1) && + (newparams.data[0] != 0xa0)) { + + if (SECSuccess == rv) { + /* PQG params are in the standard format */ + prepare_pqg_params_for_asn1(&pubk->u.dsa.params); + rv = SEC_QuickDERDecodeItem(arena, &pubk->u.dsa.params, + SECKEY_PQGParamsTemplate, + &newparams); + } + } else { + + if (SECSuccess == rv) { + /* else the old fortezza-only wrapped format is used. */ + PORT_SetError(SEC_ERROR_BAD_DER); + rv = SECFailure; + } + } + return rv; +} + +/* Function used to make an oid tag to a key type */ +KeyType +seckey_GetKeyType(SECOidTag tag) +{ + KeyType keyType; + + switch (tag) { + case SEC_OID_X500_RSA_ENCRYPTION: + case SEC_OID_PKCS1_RSA_ENCRYPTION: + keyType = rsaKey; + break; + case SEC_OID_PKCS1_RSA_PSS_SIGNATURE: + keyType = rsaPssKey; + break; + case SEC_OID_PKCS1_RSA_OAEP_ENCRYPTION: + keyType = rsaOaepKey; + break; + case SEC_OID_ANSIX9_DSA_SIGNATURE: + keyType = dsaKey; + break; + case SEC_OID_MISSI_KEA_DSS_OLD: + case SEC_OID_MISSI_KEA_DSS: + case SEC_OID_MISSI_DSS_OLD: + case SEC_OID_MISSI_DSS: + keyType = fortezzaKey; + break; + case SEC_OID_MISSI_KEA: + case SEC_OID_MISSI_ALT_KEA: + keyType = keaKey; + break; + case SEC_OID_X942_DIFFIE_HELMAN_KEY: + keyType = dhKey; + break; + case SEC_OID_ANSIX962_EC_PUBLIC_KEY: + keyType = ecKey; + break; + /* accommodate applications that hand us a signature type when they + * should be handing us a cipher type */ + case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION: + case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION: + case SEC_OID_PKCS1_SHA224_WITH_RSA_ENCRYPTION: + case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION: + case SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION: + case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION: + keyType = rsaKey; + break; + default: + keyType = nullKey; + } + return keyType; +} + +/* Function used to determine what kind of cert we are dealing with. */ +KeyType +CERT_GetCertKeyType(const CERTSubjectPublicKeyInfo *spki) +{ + return seckey_GetKeyType(SECOID_GetAlgorithmTag(&spki->algorithm)); +} + +/* Ensure pubKey contains an OID */ +static SECStatus +seckey_HasCurveOID(const SECKEYPublicKey *pubKey) +{ + SECItem oid; + SECStatus rv; + PORTCheapArenaPool tmpArena; + + PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE); + /* If we can decode it, an OID is available. */ + rv = SEC_QuickDERDecodeItem(&tmpArena.arena, &oid, + SEC_ASN1_GET(SEC_ObjectIDTemplate), + &pubKey->u.ec.DEREncodedParams); + PORT_DestroyCheapArena(&tmpArena); + return rv; +} + +static SECKEYPublicKey * +seckey_ExtractPublicKey(const CERTSubjectPublicKeyInfo *spki) +{ + SECKEYPublicKey *pubk; + SECItem os, newOs, newParms; + SECStatus rv; + PLArenaPool *arena; + SECOidTag tag; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) + return NULL; + + pubk = (SECKEYPublicKey *)PORT_ArenaZAlloc(arena, sizeof(SECKEYPublicKey)); + if (pubk == NULL) { + PORT_FreeArena(arena, PR_FALSE); + return NULL; + } + + pubk->arena = arena; + pubk->pkcs11Slot = 0; + pubk->pkcs11ID = CK_INVALID_HANDLE; + + /* Convert bit string length from bits to bytes */ + os = spki->subjectPublicKey; + DER_ConvertBitString(&os); + + tag = SECOID_GetAlgorithmTag(&spki->algorithm); + + /* copy the DER into the arena, since Quick DER returns data that points + into the DER input, which may get freed by the caller */ + rv = SECITEM_CopyItem(arena, &newOs, &os); + if (rv == SECSuccess) + switch (tag) { + case SEC_OID_X500_RSA_ENCRYPTION: + case SEC_OID_PKCS1_RSA_ENCRYPTION: + case SEC_OID_PKCS1_RSA_PSS_SIGNATURE: + pubk->keyType = rsaKey; + prepare_rsa_pub_key_for_asn1(pubk); + rv = SEC_QuickDERDecodeItem(arena, pubk, SECKEY_RSAPublicKeyTemplate, &newOs); + if (rv == SECSuccess) + return pubk; + break; + case SEC_OID_ANSIX9_DSA_SIGNATURE: + case SEC_OID_SDN702_DSA_SIGNATURE: + pubk->keyType = dsaKey; + prepare_dsa_pub_key_for_asn1(pubk); + rv = SEC_QuickDERDecodeItem(arena, pubk, SECKEY_DSAPublicKeyTemplate, &newOs); + if (rv != SECSuccess) + break; + + rv = seckey_DSADecodePQG(arena, pubk, + &spki->algorithm.parameters); + + if (rv == SECSuccess) + return pubk; + break; + case SEC_OID_X942_DIFFIE_HELMAN_KEY: + pubk->keyType = dhKey; + prepare_dh_pub_key_for_asn1(pubk); + rv = SEC_QuickDERDecodeItem(arena, pubk, SECKEY_DHPublicKeyTemplate, &newOs); + if (rv != SECSuccess) + break; + + /* copy the DER into the arena, since Quick DER returns data that points + into the DER input, which may get freed by the caller */ + rv = SECITEM_CopyItem(arena, &newParms, &spki->algorithm.parameters); + if (rv != SECSuccess) + break; + + rv = SEC_QuickDERDecodeItem(arena, pubk, SECKEY_DHParamKeyTemplate, + &newParms); + + if (rv == SECSuccess) + return pubk; + break; + case SEC_OID_ANSIX962_EC_PUBLIC_KEY: + /* A basic sanity check on inputs. */ + if (spki->algorithm.parameters.len == 0 || newOs.len == 0) { + PORT_SetError(SEC_ERROR_INPUT_LEN); + break; + } + pubk->keyType = ecKey; + pubk->u.ec.size = 0; + + /* Since PKCS#11 directly takes the DER encoding of EC params + * and public value, we don't need any decoding here. + */ + rv = SECITEM_CopyItem(arena, &pubk->u.ec.DEREncodedParams, + &spki->algorithm.parameters); + if (rv != SECSuccess) { + break; + } + rv = SECITEM_CopyItem(arena, &pubk->u.ec.publicValue, &newOs); + if (rv != SECSuccess) { + break; + } + pubk->u.ec.encoding = ECPoint_Undefined; + rv = seckey_HasCurveOID(pubk); + if (rv == SECSuccess) { + return pubk; + } + break; + + default: + PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG); + break; + } + + SECKEY_DestroyPublicKey(pubk); + return NULL; +} + +/* required for JSS */ +SECKEYPublicKey * +SECKEY_ExtractPublicKey(const CERTSubjectPublicKeyInfo *spki) +{ + return seckey_ExtractPublicKey(spki); +} + +SECKEYPublicKey * +CERT_ExtractPublicKey(CERTCertificate *cert) +{ + return seckey_ExtractPublicKey(&cert->subjectPublicKeyInfo); +} + +int +SECKEY_ECParamsToKeySize(const SECItem *encodedParams) +{ + SECOidTag tag; + SECItem oid = { siBuffer, NULL, 0 }; + + /* The encodedParams data contains 0x06 (SEC_ASN1_OBJECT_ID), + * followed by the length of the curve oid and the curve oid. + */ + oid.len = encodedParams->data[1]; + oid.data = encodedParams->data + 2; + if ((tag = SECOID_FindOIDTag(&oid)) == SEC_OID_UNKNOWN) + return 0; + + switch (tag) { + case SEC_OID_SECG_EC_SECP112R1: + case SEC_OID_SECG_EC_SECP112R2: + return 112; + + case SEC_OID_SECG_EC_SECT113R1: + case SEC_OID_SECG_EC_SECT113R2: + return 113; + + case SEC_OID_SECG_EC_SECP128R1: + case SEC_OID_SECG_EC_SECP128R2: + return 128; + + case SEC_OID_SECG_EC_SECT131R1: + case SEC_OID_SECG_EC_SECT131R2: + return 131; + + case SEC_OID_SECG_EC_SECP160K1: + case SEC_OID_SECG_EC_SECP160R1: + case SEC_OID_SECG_EC_SECP160R2: + return 160; + + case SEC_OID_SECG_EC_SECT163K1: + case SEC_OID_SECG_EC_SECT163R1: + case SEC_OID_SECG_EC_SECT163R2: + case SEC_OID_ANSIX962_EC_C2PNB163V1: + case SEC_OID_ANSIX962_EC_C2PNB163V2: + case SEC_OID_ANSIX962_EC_C2PNB163V3: + return 163; + + case SEC_OID_ANSIX962_EC_C2PNB176V1: + return 176; + + case SEC_OID_ANSIX962_EC_C2TNB191V1: + case SEC_OID_ANSIX962_EC_C2TNB191V2: + case SEC_OID_ANSIX962_EC_C2TNB191V3: + case SEC_OID_ANSIX962_EC_C2ONB191V4: + case SEC_OID_ANSIX962_EC_C2ONB191V5: + return 191; + + case SEC_OID_SECG_EC_SECP192K1: + case SEC_OID_ANSIX962_EC_PRIME192V1: + case SEC_OID_ANSIX962_EC_PRIME192V2: + case SEC_OID_ANSIX962_EC_PRIME192V3: + return 192; + + case SEC_OID_SECG_EC_SECT193R1: + case SEC_OID_SECG_EC_SECT193R2: + return 193; + + case SEC_OID_ANSIX962_EC_C2PNB208W1: + return 208; + + case SEC_OID_SECG_EC_SECP224K1: + case SEC_OID_SECG_EC_SECP224R1: + return 224; + + case SEC_OID_SECG_EC_SECT233K1: + case SEC_OID_SECG_EC_SECT233R1: + return 233; + + case SEC_OID_SECG_EC_SECT239K1: + case SEC_OID_ANSIX962_EC_C2TNB239V1: + case SEC_OID_ANSIX962_EC_C2TNB239V2: + case SEC_OID_ANSIX962_EC_C2TNB239V3: + case SEC_OID_ANSIX962_EC_C2ONB239V4: + case SEC_OID_ANSIX962_EC_C2ONB239V5: + case SEC_OID_ANSIX962_EC_PRIME239V1: + case SEC_OID_ANSIX962_EC_PRIME239V2: + case SEC_OID_ANSIX962_EC_PRIME239V3: + return 239; + + case SEC_OID_SECG_EC_SECP256K1: + case SEC_OID_ANSIX962_EC_PRIME256V1: + return 256; + + case SEC_OID_ANSIX962_EC_C2PNB272W1: + return 272; + + case SEC_OID_SECG_EC_SECT283K1: + case SEC_OID_SECG_EC_SECT283R1: + return 283; + + case SEC_OID_ANSIX962_EC_C2PNB304W1: + return 304; + + case SEC_OID_ANSIX962_EC_C2TNB359V1: + return 359; + + case SEC_OID_ANSIX962_EC_C2PNB368W1: + return 368; + + case SEC_OID_SECG_EC_SECP384R1: + return 384; + + case SEC_OID_SECG_EC_SECT409K1: + case SEC_OID_SECG_EC_SECT409R1: + return 409; + + case SEC_OID_ANSIX962_EC_C2TNB431R1: + return 431; + + case SEC_OID_SECG_EC_SECP521R1: + return 521; + + case SEC_OID_SECG_EC_SECT571K1: + case SEC_OID_SECG_EC_SECT571R1: + return 571; + + case SEC_OID_CURVE25519: + return 255; + + default: + PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE); + return 0; + } +} + +int +SECKEY_ECParamsToBasePointOrderLen(const SECItem *encodedParams) +{ + SECOidTag tag; + SECItem oid = { siBuffer, NULL, 0 }; + + /* The encodedParams data contains 0x06 (SEC_ASN1_OBJECT_ID), + * followed by the length of the curve oid and the curve oid. + */ + oid.len = encodedParams->data[1]; + oid.data = encodedParams->data + 2; + if ((tag = SECOID_FindOIDTag(&oid)) == SEC_OID_UNKNOWN) + return 0; + + switch (tag) { + case SEC_OID_SECG_EC_SECP112R1: + return 112; + case SEC_OID_SECG_EC_SECP112R2: + return 110; + + case SEC_OID_SECG_EC_SECT113R1: + case SEC_OID_SECG_EC_SECT113R2: + return 113; + + case SEC_OID_SECG_EC_SECP128R1: + return 128; + case SEC_OID_SECG_EC_SECP128R2: + return 126; + + case SEC_OID_SECG_EC_SECT131R1: + case SEC_OID_SECG_EC_SECT131R2: + return 131; + + case SEC_OID_SECG_EC_SECP160K1: + case SEC_OID_SECG_EC_SECP160R1: + case SEC_OID_SECG_EC_SECP160R2: + return 161; + + case SEC_OID_SECG_EC_SECT163K1: + return 163; + case SEC_OID_SECG_EC_SECT163R1: + return 162; + case SEC_OID_SECG_EC_SECT163R2: + case SEC_OID_ANSIX962_EC_C2PNB163V1: + return 163; + case SEC_OID_ANSIX962_EC_C2PNB163V2: + case SEC_OID_ANSIX962_EC_C2PNB163V3: + return 162; + + case SEC_OID_ANSIX962_EC_C2PNB176V1: + return 161; + + case SEC_OID_ANSIX962_EC_C2TNB191V1: + return 191; + case SEC_OID_ANSIX962_EC_C2TNB191V2: + return 190; + case SEC_OID_ANSIX962_EC_C2TNB191V3: + return 189; + case SEC_OID_ANSIX962_EC_C2ONB191V4: + return 191; + case SEC_OID_ANSIX962_EC_C2ONB191V5: + return 188; + + case SEC_OID_SECG_EC_SECP192K1: + case SEC_OID_ANSIX962_EC_PRIME192V1: + case SEC_OID_ANSIX962_EC_PRIME192V2: + case SEC_OID_ANSIX962_EC_PRIME192V3: + return 192; + + case SEC_OID_SECG_EC_SECT193R1: + case SEC_OID_SECG_EC_SECT193R2: + return 193; + + case SEC_OID_ANSIX962_EC_C2PNB208W1: + return 193; + + case SEC_OID_SECG_EC_SECP224K1: + return 225; + case SEC_OID_SECG_EC_SECP224R1: + return 224; + + case SEC_OID_SECG_EC_SECT233K1: + return 232; + case SEC_OID_SECG_EC_SECT233R1: + return 233; + + case SEC_OID_SECG_EC_SECT239K1: + case SEC_OID_ANSIX962_EC_C2TNB239V1: + return 238; + case SEC_OID_ANSIX962_EC_C2TNB239V2: + return 237; + case SEC_OID_ANSIX962_EC_C2TNB239V3: + return 236; + case SEC_OID_ANSIX962_EC_C2ONB239V4: + return 238; + case SEC_OID_ANSIX962_EC_C2ONB239V5: + return 237; + case SEC_OID_ANSIX962_EC_PRIME239V1: + case SEC_OID_ANSIX962_EC_PRIME239V2: + case SEC_OID_ANSIX962_EC_PRIME239V3: + return 239; + + case SEC_OID_SECG_EC_SECP256K1: + case SEC_OID_ANSIX962_EC_PRIME256V1: + return 256; + + case SEC_OID_ANSIX962_EC_C2PNB272W1: + return 257; + + case SEC_OID_SECG_EC_SECT283K1: + return 281; + case SEC_OID_SECG_EC_SECT283R1: + return 282; + + case SEC_OID_ANSIX962_EC_C2PNB304W1: + return 289; + + case SEC_OID_ANSIX962_EC_C2TNB359V1: + return 353; + + case SEC_OID_ANSIX962_EC_C2PNB368W1: + return 353; + + case SEC_OID_SECG_EC_SECP384R1: + return 384; + + case SEC_OID_SECG_EC_SECT409K1: + return 407; + case SEC_OID_SECG_EC_SECT409R1: + return 409; + + case SEC_OID_ANSIX962_EC_C2TNB431R1: + return 418; + + case SEC_OID_SECG_EC_SECP521R1: + return 521; + + case SEC_OID_SECG_EC_SECT571K1: + case SEC_OID_SECG_EC_SECT571R1: + return 570; + + case SEC_OID_CURVE25519: + return 255; + + default: + PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE); + return 0; + } +} + +/* The number of bits in the number from the first non-zero bit onward. */ +unsigned +SECKEY_BigIntegerBitLength(const SECItem *number) +{ + const unsigned char *p; + unsigned octets; + unsigned bits; + + if (!number || !number->data) { + PORT_SetError(SEC_ERROR_INVALID_KEY); + return 0; + } + + p = number->data; + octets = number->len; + while (octets > 0 && !*p) { + ++p; + --octets; + } + if (octets == 0) { + return 0; + } + /* bits = 7..1 because we know at least one bit is set already */ + /* Note: This could do a binary search, but this is faster for keys if we + * assume that good keys will have the MSB set. */ + for (bits = 7; bits > 0; --bits) { + if (*p & (1 << bits)) { + break; + } + } + return octets * 8 + bits - 7; +} + +/* returns key strength in bytes (not bits) */ +unsigned +SECKEY_PublicKeyStrength(const SECKEYPublicKey *pubk) +{ + return (SECKEY_PublicKeyStrengthInBits(pubk) + 7) / 8; +} + +/* returns key strength in bits */ +unsigned +SECKEY_PublicKeyStrengthInBits(const SECKEYPublicKey *pubk) +{ + unsigned bitSize = 0; + + if (!pubk) { + PORT_SetError(SEC_ERROR_INVALID_KEY); + return 0; + } + + /* interpret modulus length as key strength */ + switch (pubk->keyType) { + case rsaKey: + bitSize = SECKEY_BigIntegerBitLength(&pubk->u.rsa.modulus); + break; + case dsaKey: + bitSize = SECKEY_BigIntegerBitLength(&pubk->u.dsa.params.prime); + break; + case dhKey: + bitSize = SECKEY_BigIntegerBitLength(&pubk->u.dh.prime); + break; + case ecKey: + bitSize = SECKEY_ECParamsToKeySize(&pubk->u.ec.DEREncodedParams); + break; + default: + PORT_SetError(SEC_ERROR_INVALID_KEY); + break; + } + return bitSize; +} + +unsigned +SECKEY_PrivateKeyStrengthInBits(const SECKEYPrivateKey *privk) +{ + unsigned bitSize = 0; + SECItem params = { siBuffer, NULL, 0 }; + SECStatus rv; + + if (!privk) { + PORT_SetError(SEC_ERROR_INVALID_KEY); + return 0; + } + + /* interpret modulus length as key strength */ + switch (privk->keyType) { + case rsaKey: + case rsaPssKey: + case rsaOaepKey: + /* some tokens don't export CKA_MODULUS on the private key, + * PK11_SignatureLen works around this if necessary */ + bitSize = PK11_SignatureLen((SECKEYPrivateKey *)privk) * PR_BITS_PER_BYTE; + if (bitSize == -1) { + bitSize = 0; + } + return bitSize; + case dsaKey: + case fortezzaKey: + case dhKey: + case keaKey: + rv = PK11_ReadAttribute(privk->pkcs11Slot, privk->pkcs11ID, + CKA_PRIME, NULL, ¶ms); + if ((rv != SECSuccess) || (params.data == NULL)) { + PORT_SetError(SEC_ERROR_INVALID_KEY); + return 0; + } + bitSize = SECKEY_BigIntegerBitLength(¶ms); + PORT_Free(params.data); + return bitSize; + case ecKey: + rv = PK11_ReadAttribute(privk->pkcs11Slot, privk->pkcs11ID, + CKA_EC_PARAMS, NULL, ¶ms); + if ((rv != SECSuccess) || (params.data == NULL)) { + return 0; + } + bitSize = SECKEY_ECParamsToKeySize(¶ms); + PORT_Free(params.data); + return bitSize; + default: + break; + } + PORT_SetError(SEC_ERROR_INVALID_KEY); + return 0; +} + +/* returns signature length in bytes (not bits) */ +unsigned +SECKEY_SignatureLen(const SECKEYPublicKey *pubk) +{ + unsigned size; + + switch (pubk->keyType) { + case rsaKey: + case rsaPssKey: + if (pubk->u.rsa.modulus.len == 0) { + return 0; + } + if (pubk->u.rsa.modulus.data[0] == 0) { + return pubk->u.rsa.modulus.len - 1; + } + return pubk->u.rsa.modulus.len; + 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); + return ((size + 7) / 8) * 2; + default: + break; + } + PORT_SetError(SEC_ERROR_INVALID_KEY); + return 0; +} + +SECKEYPrivateKey * +SECKEY_CopyPrivateKey(const SECKEYPrivateKey *privk) +{ + SECKEYPrivateKey *copyk; + PLArenaPool *arena; + + if (!privk || !privk->pkcs11Slot) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + return NULL; + } + + copyk = (SECKEYPrivateKey *)PORT_ArenaZAlloc(arena, sizeof(SECKEYPrivateKey)); + if (copyk) { + copyk->arena = arena; + copyk->keyType = privk->keyType; + + /* copy the PKCS #11 parameters */ + copyk->pkcs11Slot = PK11_ReferenceSlot(privk->pkcs11Slot); + /* if the key we're referencing was a temparary key we have just + * created, that we want to go away when we're through, we need + * to make a copy of it */ + if (privk->pkcs11IsTemp) { + copyk->pkcs11ID = + PK11_CopyKey(privk->pkcs11Slot, privk->pkcs11ID); + if (copyk->pkcs11ID == CK_INVALID_HANDLE) + goto fail; + } else { + copyk->pkcs11ID = privk->pkcs11ID; + } + copyk->pkcs11IsTemp = privk->pkcs11IsTemp; + copyk->wincx = privk->wincx; + copyk->staticflags = privk->staticflags; + return copyk; + } else { + PORT_SetError(SEC_ERROR_NO_MEMORY); + } + +fail: + PORT_FreeArena(arena, PR_FALSE); + return NULL; +} + +SECKEYPublicKey * +SECKEY_CopyPublicKey(const SECKEYPublicKey *pubk) +{ + SECKEYPublicKey *copyk; + PLArenaPool *arena; + SECStatus rv = SECSuccess; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + copyk = (SECKEYPublicKey *)PORT_ArenaZAlloc(arena, sizeof(SECKEYPublicKey)); + if (!copyk) { + PORT_FreeArena(arena, PR_FALSE); + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + copyk->arena = arena; + copyk->keyType = pubk->keyType; + if (pubk->pkcs11Slot && + PK11_IsPermObject(pubk->pkcs11Slot, pubk->pkcs11ID)) { + copyk->pkcs11Slot = PK11_ReferenceSlot(pubk->pkcs11Slot); + copyk->pkcs11ID = pubk->pkcs11ID; + } else { + copyk->pkcs11Slot = NULL; /* go get own reference */ + copyk->pkcs11ID = CK_INVALID_HANDLE; + } + switch (pubk->keyType) { + case rsaKey: + rv = SECITEM_CopyItem(arena, ©k->u.rsa.modulus, + &pubk->u.rsa.modulus); + if (rv == SECSuccess) { + rv = SECITEM_CopyItem(arena, ©k->u.rsa.publicExponent, + &pubk->u.rsa.publicExponent); + if (rv == SECSuccess) + return copyk; + } + break; + case dsaKey: + rv = SECITEM_CopyItem(arena, ©k->u.dsa.publicValue, + &pubk->u.dsa.publicValue); + if (rv != SECSuccess) + break; + rv = SECITEM_CopyItem(arena, ©k->u.dsa.params.prime, + &pubk->u.dsa.params.prime); + if (rv != SECSuccess) + break; + rv = SECITEM_CopyItem(arena, ©k->u.dsa.params.subPrime, + &pubk->u.dsa.params.subPrime); + if (rv != SECSuccess) + break; + rv = SECITEM_CopyItem(arena, ©k->u.dsa.params.base, + &pubk->u.dsa.params.base); + break; + case dhKey: + rv = SECITEM_CopyItem(arena, ©k->u.dh.prime, &pubk->u.dh.prime); + if (rv != SECSuccess) + break; + rv = SECITEM_CopyItem(arena, ©k->u.dh.base, &pubk->u.dh.base); + if (rv != SECSuccess) + break; + rv = SECITEM_CopyItem(arena, ©k->u.dh.publicValue, + &pubk->u.dh.publicValue); + break; + case ecKey: + copyk->u.ec.size = pubk->u.ec.size; + rv = seckey_HasCurveOID(pubk); + if (rv != SECSuccess) { + break; + } + rv = SECITEM_CopyItem(arena, ©k->u.ec.DEREncodedParams, + &pubk->u.ec.DEREncodedParams); + if (rv != SECSuccess) { + break; + } + copyk->u.ec.encoding = ECPoint_Undefined; + rv = SECITEM_CopyItem(arena, ©k->u.ec.publicValue, + &pubk->u.ec.publicValue); + break; + case nullKey: + return copyk; + default: + PORT_SetError(SEC_ERROR_INVALID_KEY); + rv = SECFailure; + break; + } + if (rv == SECSuccess) + return copyk; + + SECKEY_DestroyPublicKey(copyk); + return NULL; +} + +/* + * Check that a given key meets the policy limits for the given key + * size. + */ +SECStatus +seckey_EnforceKeySize(KeyType keyType, unsigned keyLength, SECErrorCodes error) +{ + PRInt32 opt = -1; + PRInt32 optVal; + SECStatus rv; + + switch (keyType) { + case rsaKey: + case rsaPssKey: + case rsaOaepKey: + opt = NSS_RSA_MIN_KEY_SIZE; + break; + case dsaKey: + case fortezzaKey: + opt = NSS_DSA_MIN_KEY_SIZE; + break; + case dhKey: + case keaKey: + opt = NSS_DH_MIN_KEY_SIZE; + break; + case ecKey: + opt = NSS_ECC_MIN_KEY_SIZE; + break; + case nullKey: + default: + PORT_SetError(SEC_ERROR_INVALID_KEY); + return SECFailure; + } + PORT_Assert(opt != -1); + rv = NSS_OptionGet(opt, &optVal); + if (rv != SECSuccess) { + return rv; + } + if (optVal > keyLength) { + PORT_SetError(error); + return SECFailure; + } + return SECSuccess; +} + +/* + * Use the private key to find a public key handle. The handle will be on + * the same slot as the private key. + */ +static CK_OBJECT_HANDLE +seckey_FindPublicKeyHandle(SECKEYPrivateKey *privk, SECKEYPublicKey *pubk) +{ + CK_OBJECT_HANDLE keyID; + + /* this helper function is only used below. If we want to make this more + * general, we would need to free up any already cached handles if the + * slot doesn't match up with the private key slot */ + PORT_Assert(pubk->pkcs11ID == CK_INVALID_HANDLE); + + /* first look for a matching public key */ + keyID = PK11_MatchItem(privk->pkcs11Slot, privk->pkcs11ID, CKO_PUBLIC_KEY); + if (keyID != CK_INVALID_HANDLE) { + return keyID; + } + + /* none found, create a temp one, make the pubk the owner */ + pubk->pkcs11ID = PK11_DerivePubKeyFromPrivKey(privk); + if (pubk->pkcs11ID == CK_INVALID_HANDLE) { + /* end of the road. Token doesn't have matching public key, nor can + * token regenerate a new public key from and existing private key. */ + return CK_INVALID_HANDLE; + } + pubk->pkcs11Slot = PK11_ReferenceSlot(privk->pkcs11Slot); + return pubk->pkcs11ID; +} + +SECKEYPublicKey * +SECKEY_ConvertToPublicKey(SECKEYPrivateKey *privk) +{ + SECKEYPublicKey *pubk; + PLArenaPool *arena; + CERTCertificate *cert; + SECStatus rv; + CK_OBJECT_HANDLE pubKeyHandle; + SECItem decodedPoint; + + /* + * First try to look up the cert. + */ + cert = PK11_GetCertFromPrivateKey(privk); + if (cert) { + pubk = CERT_ExtractPublicKey(cert); + CERT_DestroyCertificate(cert); + return pubk; + } + + /* couldn't find the cert, build pub key by hand */ + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + pubk = (SECKEYPublicKey *)PORT_ArenaZAlloc(arena, + sizeof(SECKEYPublicKey)); + if (pubk == NULL) { + PORT_FreeArena(arena, PR_FALSE); + return NULL; + } + pubk->keyType = privk->keyType; + pubk->pkcs11Slot = NULL; + pubk->pkcs11ID = CK_INVALID_HANDLE; + pubk->arena = arena; + + switch (privk->keyType) { + case nullKey: + /* Nothing to query, if the cert isn't there, we're done -- no way + * to get the public key */ + break; + case dsaKey: + pubKeyHandle = seckey_FindPublicKeyHandle(privk, pubk); + if (pubKeyHandle == CK_INVALID_HANDLE) + break; + rv = PK11_ReadAttribute(privk->pkcs11Slot, pubKeyHandle, + CKA_BASE, arena, &pubk->u.dsa.params.base); + if (rv != SECSuccess) + break; + rv = PK11_ReadAttribute(privk->pkcs11Slot, pubKeyHandle, + CKA_PRIME, arena, &pubk->u.dsa.params.prime); + if (rv != SECSuccess) + break; + rv = PK11_ReadAttribute(privk->pkcs11Slot, pubKeyHandle, + CKA_SUBPRIME, arena, &pubk->u.dsa.params.subPrime); + if (rv != SECSuccess) + break; + rv = PK11_ReadAttribute(privk->pkcs11Slot, pubKeyHandle, + CKA_VALUE, arena, &pubk->u.dsa.publicValue); + if (rv != SECSuccess) + break; + return pubk; + case dhKey: + pubKeyHandle = seckey_FindPublicKeyHandle(privk, pubk); + if (pubKeyHandle == CK_INVALID_HANDLE) + break; + rv = PK11_ReadAttribute(privk->pkcs11Slot, pubKeyHandle, + CKA_BASE, arena, &pubk->u.dh.base); + if (rv != SECSuccess) + break; + rv = PK11_ReadAttribute(privk->pkcs11Slot, pubKeyHandle, + CKA_PRIME, arena, &pubk->u.dh.prime); + if (rv != SECSuccess) + break; + rv = PK11_ReadAttribute(privk->pkcs11Slot, pubKeyHandle, + CKA_VALUE, arena, &pubk->u.dh.publicValue); + if (rv != SECSuccess) + break; + return pubk; + case rsaKey: + rv = PK11_ReadAttribute(privk->pkcs11Slot, privk->pkcs11ID, + CKA_MODULUS, arena, &pubk->u.rsa.modulus); + if (rv != SECSuccess) + break; + rv = PK11_ReadAttribute(privk->pkcs11Slot, privk->pkcs11ID, + CKA_PUBLIC_EXPONENT, arena, &pubk->u.rsa.publicExponent); + if (rv != SECSuccess) + break; + return pubk; + case ecKey: + rv = PK11_ReadAttribute(privk->pkcs11Slot, privk->pkcs11ID, + CKA_EC_PARAMS, arena, &pubk->u.ec.DEREncodedParams); + if (rv != SECSuccess) { + break; + } + rv = PK11_ReadAttribute(privk->pkcs11Slot, privk->pkcs11ID, + CKA_EC_POINT, arena, &pubk->u.ec.publicValue); + if (rv != SECSuccess || pubk->u.ec.publicValue.len == 0) { + pubKeyHandle = seckey_FindPublicKeyHandle(privk, pubk); + if (pubKeyHandle == CK_INVALID_HANDLE) + break; + rv = PK11_ReadAttribute(privk->pkcs11Slot, pubKeyHandle, + CKA_EC_POINT, arena, &pubk->u.ec.publicValue); + if (rv != SECSuccess) + break; + } + /* ec.publicValue should be decoded, PKCS #11 defines CKA_EC_POINT + * as encoded, but it's not always. try do decoded it and if it + * succeeds store the decoded value */ + rv = SEC_QuickDERDecodeItem(arena, &decodedPoint, + SEC_ASN1_GET(SEC_OctetStringTemplate), &pubk->u.ec.publicValue); + if (rv == SECSuccess) { + /* both values are in the public key arena, so it's safe to + * overwrite the old value */ + pubk->u.ec.publicValue = decodedPoint; + } + pubk->u.ec.encoding = ECPoint_Undefined; + return pubk; + default: + break; + } + + /* must use Destroy public key here, because some paths create temporary + * PKCS #11 objects which need to be freed */ + SECKEY_DestroyPublicKey(pubk); + return NULL; +} + +static CERTSubjectPublicKeyInfo * +seckey_CreateSubjectPublicKeyInfo_helper(SECKEYPublicKey *pubk) +{ + CERTSubjectPublicKeyInfo *spki; + PLArenaPool *arena; + SECItem params = { siBuffer, NULL, 0 }; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + spki = (CERTSubjectPublicKeyInfo *)PORT_ArenaZAlloc(arena, sizeof(*spki)); + if (spki != NULL) { + SECStatus rv; + SECItem *rv_item; + + spki->arena = arena; + switch (pubk->keyType) { + case rsaKey: + rv = SECOID_SetAlgorithmID(arena, &spki->algorithm, + SEC_OID_PKCS1_RSA_ENCRYPTION, 0); + if (rv == SECSuccess) { + /* + * DER encode the public key into the subjectPublicKeyInfo. + */ + prepare_rsa_pub_key_for_asn1(pubk); + rv_item = SEC_ASN1EncodeItem(arena, &spki->subjectPublicKey, + pubk, SECKEY_RSAPublicKeyTemplate); + if (rv_item != NULL) { + /* + * The stored value is supposed to be a BIT_STRING, + * so convert the length. + */ + spki->subjectPublicKey.len <<= 3; + /* + * We got a good one; return it. + */ + return spki; + } + } + break; + case dsaKey: + /* DER encode the params. */ + prepare_pqg_params_for_asn1(&pubk->u.dsa.params); + rv_item = SEC_ASN1EncodeItem(arena, ¶ms, &pubk->u.dsa.params, + SECKEY_PQGParamsTemplate); + if (rv_item != NULL) { + rv = SECOID_SetAlgorithmID(arena, &spki->algorithm, + SEC_OID_ANSIX9_DSA_SIGNATURE, + ¶ms); + if (rv == SECSuccess) { + /* + * DER encode the public key into the subjectPublicKeyInfo. + */ + prepare_dsa_pub_key_for_asn1(pubk); + rv_item = SEC_ASN1EncodeItem(arena, &spki->subjectPublicKey, + pubk, + SECKEY_DSAPublicKeyTemplate); + if (rv_item != NULL) { + /* + * The stored value is supposed to be a BIT_STRING, + * so convert the length. + */ + spki->subjectPublicKey.len <<= 3; + /* + * We got a good one; return it. + */ + return spki; + } + } + } + SECITEM_FreeItem(¶ms, PR_FALSE); + break; + case ecKey: + rv = SECITEM_CopyItem(arena, ¶ms, + &pubk->u.ec.DEREncodedParams); + if (rv != SECSuccess) + break; + + rv = SECOID_SetAlgorithmID(arena, &spki->algorithm, + SEC_OID_ANSIX962_EC_PUBLIC_KEY, + ¶ms); + if (rv != SECSuccess) + break; + + rv = SECITEM_CopyItem(arena, &spki->subjectPublicKey, + &pubk->u.ec.publicValue); + + if (rv == SECSuccess) { + /* + * The stored value is supposed to be a BIT_STRING, + * so convert the length. + */ + spki->subjectPublicKey.len <<= 3; + /* + * We got a good one; return it. + */ + return spki; + } + break; + case dhKey: /* later... */ + + break; + default: + break; + } + } else { + PORT_SetError(SEC_ERROR_NO_MEMORY); + } + + PORT_FreeArena(arena, PR_FALSE); + return NULL; +} + +CERTSubjectPublicKeyInfo * +SECKEY_CreateSubjectPublicKeyInfo(const SECKEYPublicKey *pubk) +{ + CERTSubjectPublicKeyInfo *spki; + SECKEYPublicKey *tempKey; + + if (!pubk) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + + tempKey = SECKEY_CopyPublicKey(pubk); + if (!tempKey) { + return NULL; + } + spki = seckey_CreateSubjectPublicKeyInfo_helper(tempKey); + SECKEY_DestroyPublicKey(tempKey); + return spki; +} + +void +SECKEY_DestroySubjectPublicKeyInfo(CERTSubjectPublicKeyInfo *spki) +{ + if (spki && spki->arena) { + PORT_FreeArena(spki->arena, PR_FALSE); + } +} + +SECItem * +SECKEY_EncodeDERSubjectPublicKeyInfo(const SECKEYPublicKey *pubk) +{ + CERTSubjectPublicKeyInfo *spki = NULL; + SECItem *spkiDER = NULL; + + /* get the subjectpublickeyinfo */ + spki = SECKEY_CreateSubjectPublicKeyInfo(pubk); + if (spki == NULL) { + goto finish; + } + + /* DER-encode the subjectpublickeyinfo */ + spkiDER = SEC_ASN1EncodeItem(NULL /*arena*/, NULL /*dest*/, spki, + CERT_SubjectPublicKeyInfoTemplate); + + SECKEY_DestroySubjectPublicKeyInfo(spki); + +finish: + return spkiDER; +} + +CERTSubjectPublicKeyInfo * +SECKEY_DecodeDERSubjectPublicKeyInfo(const SECItem *spkider) +{ + PLArenaPool *arena; + CERTSubjectPublicKeyInfo *spki; + SECStatus rv; + SECItem newSpkider; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + spki = (CERTSubjectPublicKeyInfo *) + PORT_ArenaZAlloc(arena, sizeof(CERTSubjectPublicKeyInfo)); + if (spki != NULL) { + spki->arena = arena; + + /* copy the DER into the arena, since Quick DER returns data that points + into the DER input, which may get freed by the caller */ + rv = SECITEM_CopyItem(arena, &newSpkider, spkider); + if (rv == SECSuccess) { + rv = SEC_QuickDERDecodeItem(arena, spki, + CERT_SubjectPublicKeyInfoTemplate, &newSpkider); + } + if (rv == SECSuccess) + return spki; + } else { + PORT_SetError(SEC_ERROR_NO_MEMORY); + } + + PORT_FreeArena(arena, PR_FALSE); + return NULL; +} + +/* + * Decode a base64 ascii encoded DER encoded subject public key info. + */ +CERTSubjectPublicKeyInfo * +SECKEY_ConvertAndDecodeSubjectPublicKeyInfo(const char *spkistr) +{ + CERTSubjectPublicKeyInfo *spki; + SECStatus rv; + SECItem der; + + rv = ATOB_ConvertAsciiToItem(&der, spkistr); + if (rv != SECSuccess) + return NULL; + + spki = SECKEY_DecodeDERSubjectPublicKeyInfo(&der); + + PORT_Free(der.data); + return spki; +} + +/* + * Decode a base64 ascii encoded DER encoded public key and challenge + * Verify digital signature and make sure challenge matches + */ +CERTSubjectPublicKeyInfo * +SECKEY_ConvertAndDecodePublicKeyAndChallenge(char *pkacstr, char *challenge, + void *wincx) +{ + CERTSubjectPublicKeyInfo *spki = NULL; + CERTPublicKeyAndChallenge pkac; + SECStatus rv; + SECItem signedItem; + PLArenaPool *arena = NULL; + CERTSignedData sd; + SECItem sig; + SECKEYPublicKey *pubKey = NULL; + unsigned int len; + + signedItem.data = NULL; + + /* convert the base64 encoded data to binary */ + rv = ATOB_ConvertAsciiToItem(&signedItem, pkacstr); + if (rv != SECSuccess) { + goto loser; + } + + /* create an arena */ + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + goto loser; + } + + /* decode the outer wrapping of signed data */ + PORT_Memset(&sd, 0, sizeof(CERTSignedData)); + rv = SEC_QuickDERDecodeItem(arena, &sd, CERT_SignedDataTemplate, &signedItem); + if (rv) { + goto loser; + } + + /* decode the public key and challenge wrapper */ + PORT_Memset(&pkac, 0, sizeof(CERTPublicKeyAndChallenge)); + rv = SEC_QuickDERDecodeItem(arena, &pkac, CERT_PublicKeyAndChallengeTemplate, + &sd.data); + if (rv) { + goto loser; + } + + /* decode the subject public key info */ + spki = SECKEY_DecodeDERSubjectPublicKeyInfo(&pkac.spki); + if (spki == NULL) { + goto loser; + } + + /* get the public key */ + pubKey = seckey_ExtractPublicKey(spki); + if (pubKey == NULL) { + goto loser; + } + + /* check the signature */ + sig = sd.signature; + DER_ConvertBitString(&sig); + rv = VFY_VerifyDataWithAlgorithmID(sd.data.data, sd.data.len, pubKey, &sig, + &(sd.signatureAlgorithm), NULL, wincx); + if (rv != SECSuccess) { + goto loser; + } + + /* check the challenge */ + if (challenge) { + len = PORT_Strlen(challenge); + /* length is right */ + if (len != pkac.challenge.len) { + goto loser; + } + /* actual data is right */ + if (PORT_Memcmp(challenge, pkac.challenge.data, len) != 0) { + goto loser; + } + } + goto done; + +loser: + /* make sure that we return null if we got an error */ + if (spki) { + SECKEY_DestroySubjectPublicKeyInfo(spki); + } + spki = NULL; + +done: + if (signedItem.data) { + PORT_Free(signedItem.data); + } + if (arena) { + PORT_FreeArena(arena, PR_FALSE); + } + if (pubKey) { + SECKEY_DestroyPublicKey(pubKey); + } + + return spki; +} + +void +SECKEY_DestroyPrivateKeyInfo(SECKEYPrivateKeyInfo *pvk, + PRBool freeit) +{ + PLArenaPool *poolp; + + if (pvk != NULL) { + if (pvk->arena) { + poolp = pvk->arena; + /* zero structure since PORT_FreeArena does not support + * this yet. + */ + PORT_Memset(pvk->privateKey.data, 0, pvk->privateKey.len); + PORT_Memset(pvk, 0, sizeof(*pvk)); + if (freeit == PR_TRUE) { + PORT_FreeArena(poolp, PR_TRUE); + } else { + pvk->arena = poolp; + } + } else { + SECITEM_ZfreeItem(&pvk->version, PR_FALSE); + SECITEM_ZfreeItem(&pvk->privateKey, PR_FALSE); + SECOID_DestroyAlgorithmID(&pvk->algorithm, PR_FALSE); + PORT_Memset(pvk, 0, sizeof(*pvk)); + if (freeit == PR_TRUE) { + PORT_Free(pvk); + } + } + } +} + +void +SECKEY_DestroyEncryptedPrivateKeyInfo(SECKEYEncryptedPrivateKeyInfo *epki, + PRBool freeit) +{ + PLArenaPool *poolp; + + if (epki != NULL) { + if (epki->arena) { + poolp = epki->arena; + /* zero structure since PORT_FreeArena does not support + * this yet. + */ + PORT_Memset(epki->encryptedData.data, 0, epki->encryptedData.len); + PORT_Memset(epki, 0, sizeof(*epki)); + if (freeit == PR_TRUE) { + PORT_FreeArena(poolp, PR_TRUE); + } else { + epki->arena = poolp; + } + } else { + SECITEM_ZfreeItem(&epki->encryptedData, PR_FALSE); + SECOID_DestroyAlgorithmID(&epki->algorithm, PR_FALSE); + PORT_Memset(epki, 0, sizeof(*epki)); + if (freeit == PR_TRUE) { + PORT_Free(epki); + } + } + } +} + +SECStatus +SECKEY_CopyPrivateKeyInfo(PLArenaPool *poolp, + SECKEYPrivateKeyInfo *to, + const SECKEYPrivateKeyInfo *from) +{ + SECStatus rv = SECFailure; + + if ((to == NULL) || (from == NULL)) { + return SECFailure; + } + + rv = SECOID_CopyAlgorithmID(poolp, &to->algorithm, &from->algorithm); + if (rv != SECSuccess) { + return SECFailure; + } + rv = SECITEM_CopyItem(poolp, &to->privateKey, &from->privateKey); + if (rv != SECSuccess) { + return SECFailure; + } + rv = SECITEM_CopyItem(poolp, &to->version, &from->version); + + return rv; +} + +SECStatus +SECKEY_CopyEncryptedPrivateKeyInfo(PLArenaPool *poolp, + SECKEYEncryptedPrivateKeyInfo *to, + const SECKEYEncryptedPrivateKeyInfo *from) +{ + SECStatus rv = SECFailure; + + if ((to == NULL) || (from == NULL)) { + return SECFailure; + } + + rv = SECOID_CopyAlgorithmID(poolp, &to->algorithm, &from->algorithm); + if (rv != SECSuccess) { + return SECFailure; + } + rv = SECITEM_CopyItem(poolp, &to->encryptedData, &from->encryptedData); + + return rv; +} + +KeyType +SECKEY_GetPrivateKeyType(const SECKEYPrivateKey *privKey) +{ + return privKey->keyType; +} + +KeyType +SECKEY_GetPublicKeyType(const SECKEYPublicKey *pubKey) +{ + return pubKey->keyType; +} + +SECKEYPublicKey * +SECKEY_ImportDERPublicKey(const SECItem *derKey, CK_KEY_TYPE type) +{ + SECKEYPublicKey *pubk = NULL; + SECStatus rv = SECFailure; + SECItem newDerKey; + PLArenaPool *arena = NULL; + + if (!derKey) { + return NULL; + } + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto finish; + } + + pubk = PORT_ArenaZNew(arena, SECKEYPublicKey); + if (pubk == NULL) { + goto finish; + } + pubk->arena = arena; + + rv = SECITEM_CopyItem(pubk->arena, &newDerKey, derKey); + if (SECSuccess != rv) { + goto finish; + } + + pubk->pkcs11Slot = NULL; + pubk->pkcs11ID = CK_INVALID_HANDLE; + + switch (type) { + case CKK_RSA: + prepare_rsa_pub_key_for_asn1(pubk); + rv = SEC_QuickDERDecodeItem(pubk->arena, pubk, SECKEY_RSAPublicKeyTemplate, &newDerKey); + pubk->keyType = rsaKey; + break; + case CKK_DSA: + prepare_dsa_pub_key_for_asn1(pubk); + rv = SEC_QuickDERDecodeItem(pubk->arena, pubk, SECKEY_DSAPublicKeyTemplate, &newDerKey); + pubk->keyType = dsaKey; + break; + case CKK_DH: + prepare_dh_pub_key_for_asn1(pubk); + rv = SEC_QuickDERDecodeItem(pubk->arena, pubk, SECKEY_DHPublicKeyTemplate, &newDerKey); + pubk->keyType = dhKey; + break; + default: + rv = SECFailure; + break; + } + +finish: + if (rv != SECSuccess) { + if (arena != NULL) { + PORT_FreeArena(arena, PR_FALSE); + } + pubk = NULL; + } + return pubk; +} + +SECKEYPrivateKeyList * +SECKEY_NewPrivateKeyList(void) +{ + PLArenaPool *arena = NULL; + SECKEYPrivateKeyList *ret = NULL; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + goto loser; + } + + ret = (SECKEYPrivateKeyList *)PORT_ArenaZAlloc(arena, + sizeof(SECKEYPrivateKeyList)); + if (ret == NULL) { + goto loser; + } + + ret->arena = arena; + + PR_INIT_CLIST(&ret->list); + + return (ret); + +loser: + if (arena != NULL) { + PORT_FreeArena(arena, PR_FALSE); + } + + return (NULL); +} + +void +SECKEY_DestroyPrivateKeyList(SECKEYPrivateKeyList *keys) +{ + while (!PR_CLIST_IS_EMPTY(&keys->list)) { + SECKEY_RemovePrivateKeyListNode( + (SECKEYPrivateKeyListNode *)(PR_LIST_HEAD(&keys->list))); + } + + PORT_FreeArena(keys->arena, PR_FALSE); + + return; +} + +void +SECKEY_RemovePrivateKeyListNode(SECKEYPrivateKeyListNode *node) +{ + PR_ASSERT(node->key); + SECKEY_DestroyPrivateKey(node->key); + node->key = NULL; + PR_REMOVE_LINK(&node->links); + return; +} + +SECStatus +SECKEY_AddPrivateKeyToListTail(SECKEYPrivateKeyList *list, + SECKEYPrivateKey *key) +{ + SECKEYPrivateKeyListNode *node; + + node = (SECKEYPrivateKeyListNode *)PORT_ArenaZAlloc(list->arena, + sizeof(SECKEYPrivateKeyListNode)); + if (node == NULL) { + goto loser; + } + + PR_INSERT_BEFORE(&node->links, &list->list); + node->key = key; + return (SECSuccess); + +loser: + return (SECFailure); +} + +SECKEYPublicKeyList * +SECKEY_NewPublicKeyList(void) +{ + PLArenaPool *arena = NULL; + SECKEYPublicKeyList *ret = NULL; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + goto loser; + } + + ret = (SECKEYPublicKeyList *)PORT_ArenaZAlloc(arena, + sizeof(SECKEYPublicKeyList)); + if (ret == NULL) { + goto loser; + } + + ret->arena = arena; + + PR_INIT_CLIST(&ret->list); + + return (ret); + +loser: + if (arena != NULL) { + PORT_FreeArena(arena, PR_FALSE); + } + + return (NULL); +} + +void +SECKEY_DestroyPublicKeyList(SECKEYPublicKeyList *keys) +{ + while (!PR_CLIST_IS_EMPTY(&keys->list)) { + SECKEY_RemovePublicKeyListNode( + (SECKEYPublicKeyListNode *)(PR_LIST_HEAD(&keys->list))); + } + + PORT_FreeArena(keys->arena, PR_FALSE); + + return; +} + +void +SECKEY_RemovePublicKeyListNode(SECKEYPublicKeyListNode *node) +{ + PR_ASSERT(node->key); + SECKEY_DestroyPublicKey(node->key); + node->key = NULL; + PR_REMOVE_LINK(&node->links); + return; +} + +SECStatus +SECKEY_AddPublicKeyToListTail(SECKEYPublicKeyList *list, + SECKEYPublicKey *key) +{ + SECKEYPublicKeyListNode *node; + + node = (SECKEYPublicKeyListNode *)PORT_ArenaZAlloc(list->arena, + sizeof(SECKEYPublicKeyListNode)); + if (node == NULL) { + goto loser; + } + + PR_INSERT_BEFORE(&node->links, &list->list); + node->key = key; + return (SECSuccess); + +loser: + return (SECFailure); +} + +#define SECKEY_CacheAttribute(key, attribute) \ + if (CK_TRUE == PK11_HasAttributeSet(key->pkcs11Slot, key->pkcs11ID, attribute, PR_FALSE)) { \ + key->staticflags |= SECKEY_##attribute; \ + } else { \ + key->staticflags &= (~SECKEY_##attribute); \ + } + +SECStatus +SECKEY_CacheStaticFlags(SECKEYPrivateKey *key) +{ + SECStatus rv = SECFailure; + if (key && key->pkcs11Slot && key->pkcs11ID) { + key->staticflags |= SECKEY_Attributes_Cached; + SECKEY_CacheAttribute(key, CKA_PRIVATE); + SECKEY_CacheAttribute(key, CKA_ALWAYS_AUTHENTICATE); + rv = SECSuccess; + } + return rv; +} + +SECOidTag +SECKEY_GetECCOid(const SECKEYECParams *params) +{ + SECItem oid = { siBuffer, NULL, 0 }; + SECOidData *oidData = NULL; + + /* + * params->data needs to contain the ASN encoding of an object ID (OID) + * representing a named curve. Here, we strip away everything + * before the actual OID and use the OID to look up a named curve. + */ + if (params->data[0] != SEC_ASN1_OBJECT_ID) + 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; + 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_DecodeRSAPSSParams(PLArenaPool *arena, + const SECItem *params, + SECOidTag *retHashAlg, SECOidTag *retMaskHashAlg, + unsigned long *retSaltLength) +{ + SECKEYRSAPSSParams pssParams; + SECOidTag hashAlg; + SECOidTag maskHashAlg; + unsigned long saltLength; + unsigned long trailerField; + SECStatus rv; + + PORT_Memset(&pssParams, 0, sizeof(pssParams)); + rv = SEC_QuickDERDecodeItem(arena, &pssParams, + SECKEY_RSAPSSParamsTemplate, + params); + if (rv != SECSuccess) { + return rv; + } + + if (pssParams.hashAlg) { + hashAlg = SECOID_GetAlgorithmTag(pssParams.hashAlg); + } else { + hashAlg = SEC_OID_SHA1; /* default, SHA-1 */ + } + + if (pssParams.maskAlg) { + SECAlgorithmID algId; + + if (SECOID_GetAlgorithmTag(pssParams.maskAlg) != SEC_OID_PKCS1_MGF1) { + /* only MGF1 is known to PKCS#11 */ + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + return SECFailure; + } + + rv = SEC_QuickDERDecodeItem(arena, &algId, + SEC_ASN1_GET(SECOID_AlgorithmIDTemplate), + &pssParams.maskAlg->parameters); + if (rv != SECSuccess) { + return rv; + } + maskHashAlg = SECOID_GetAlgorithmTag(&algId); + } else { + maskHashAlg = SEC_OID_SHA1; /* default, MGF1 with SHA-1 */ + } + + if (pssParams.saltLength.data) { + rv = SEC_ASN1DecodeInteger((SECItem *)&pssParams.saltLength, &saltLength); + if (rv != SECSuccess) { + return rv; + } + } else { + saltLength = 20; /* default, 20 */ + } + + if (pssParams.trailerField.data) { + rv = SEC_ASN1DecodeInteger((SECItem *)&pssParams.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; + } + } + + if (retHashAlg) { + *retHashAlg = hashAlg; + } + if (retMaskHashAlg) { + *retMaskHashAlg = maskHashAlg; + } + if (retSaltLength) { + *retSaltLength = saltLength; + } + + return SECSuccess; +} + +SECStatus +sec_DecodeRSAPSSParamsToMechanism(PLArenaPool *arena, + const SECItem *params, + CK_RSA_PKCS_PSS_PARAMS *mech) +{ + SECOidTag hashAlg; + SECOidTag maskHashAlg; + unsigned long saltLength; + SECStatus rv; + + rv = sec_DecodeRSAPSSParams(arena, params, + &hashAlg, &maskHashAlg, &saltLength); + if (rv != SECSuccess) { + return SECFailure; + } + + mech->hashAlg = sec_GetHashMechanismByOidTag(hashAlg); + if (mech->hashAlg == CKM_INVALID_MECHANISM) { + return SECFailure; + } + + mech->mgf = sec_GetMgfTypeByOidTag(maskHashAlg); + if (mech->mgf == 0) { + return SECFailure; + } + + mech->sLen = saltLength; + + return SECSuccess; +} diff --git a/security/nss/lib/cryptohi/secsign.c b/security/nss/lib/cryptohi/secsign.c new file mode 100644 index 0000000000..8779904d35 --- /dev/null +++ b/security/nss/lib/cryptohi/secsign.c @@ -0,0 +1,884 @@ +/* + * Signature stuff. + * + * 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 <stdio.h> +#include "cryptohi.h" +#include "sechash.h" +#include "secder.h" +#include "keyhi.h" +#include "secoid.h" +#include "secdig.h" +#include "pk11func.h" +#include "secerr.h" +#include "keyi.h" +#include "nss.h" + +struct SGNContextStr { + SECOidTag signalg; + SECOidTag hashalg; + void *hashcx; + const SECHashObject *hashobj; + SECKEYPrivateKey *key; + SECItem *params; +}; + +static SGNContext * +sgn_NewContext(SECOidTag alg, SECItem *params, SECKEYPrivateKey *key) +{ + SGNContext *cx; + SECOidTag hashalg, signalg; + KeyType keyType; + PRUint32 policyFlags; + PRInt32 optFlags; + 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, params, &signalg, &hashalg); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + return NULL; + } + keyType = seckey_GetKeyType(signalg); + + /* verify our key type */ + if (key->keyType != keyType && + !((key->keyType == dsaKey) && (keyType == fortezzaKey)) && + !((key->keyType == rsaKey) && (keyType == rsaPssKey))) { + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + return NULL; + } + if (NSS_OptionGet(NSS_KEY_SIZE_POLICY_FLAGS, &optFlags) != SECFailure) { + if (optFlags & NSS_KEY_SIZE_POLICY_SIGN_FLAG) { + rv = seckey_EnforceKeySize(key->keyType, + SECKEY_PrivateKeyStrengthInBits(key), + SEC_ERROR_SIGNATURE_ALGORITHM_DISABLED); + if (rv != SECSuccess) { + return NULL; + } + } + } + /* check the policy on the hash algorithm */ + if ((NSS_GetAlgorithmPolicy(hashalg, &policyFlags) == SECFailure) || + !(policyFlags & NSS_USE_ALG_IN_ANY_SIGNATURE)) { + PORT_SetError(SEC_ERROR_SIGNATURE_ALGORITHM_DISABLED); + return NULL; + } + /* check the policy on the encryption algorithm */ + if ((NSS_GetAlgorithmPolicy(signalg, &policyFlags) == SECFailure) || + !(policyFlags & NSS_USE_ALG_IN_ANY_SIGNATURE)) { + PORT_SetError(SEC_ERROR_SIGNATURE_ALGORITHM_DISABLED); + return NULL; + } + + 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; + } + if (freeit) { + PORT_ZFree(cx, sizeof(SGNContext)); + } + } +} + +SECStatus +SGN_Begin(SGNContext *cx) +{ + if (cx->hashcx != NULL) { + (*cx->hashobj->destroy)(cx->hashcx, PR_TRUE); + cx->hashcx = NULL; + } + + cx->hashobj = HASH_GetHashObjectByOidTag(cx->hashalg); + if (!cx->hashobj) + return SECFailure; /* error code is already set */ + + cx->hashcx = (*cx->hashobj->create)(); + if (cx->hashcx == NULL) + return SECFailure; + + (*cx->hashobj->begin)(cx->hashcx); + return SECSuccess; +} + +SECStatus +SGN_Update(SGNContext *cx, const unsigned char *input, unsigned int inputLen) +{ + if (cx->hashcx == NULL) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + (*cx->hashobj->update)(cx->hashcx, input, inputLen); + return SECSuccess; +} + +/* XXX Old template; want to expunge it eventually. */ +static DERTemplate SECAlgorithmIDTemplate[] = { + { DER_SEQUENCE, + 0, NULL, sizeof(SECAlgorithmID) }, + { DER_OBJECT_ID, + offsetof(SECAlgorithmID, algorithm) }, + { DER_OPTIONAL | DER_ANY, + offsetof(SECAlgorithmID, parameters) }, + { 0 } +}; + +/* + * XXX OLD Template. Once all uses have been switched over to new one, + * remove this. + */ +static DERTemplate SGNDigestInfoTemplate[] = { + { DER_SEQUENCE, + 0, NULL, sizeof(SGNDigestInfo) }, + { DER_INLINE, + offsetof(SGNDigestInfo, digestAlgorithm), + SECAlgorithmIDTemplate }, + { DER_OCTET_STRING, + offsetof(SGNDigestInfo, digest) }, + { 0 } +}; + +SECStatus +SGN_End(SGNContext *cx, SECItem *result) +{ + unsigned char digest[HASH_LENGTH_MAX]; + unsigned part1; + int signatureLen; + 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 && + cx->signalg != SEC_OID_PKCS1_RSA_PSS_SIGNATURE) { + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!arena) { + rv = SECFailure; + goto loser; + } + + /* Construct digest info */ + di = SGN_CreateDigestInfo(cx->hashalg, digest, part1); + if (!di) { + rv = SECFailure; + goto loser; + } + + /* Der encode the digest as a DigestInfo */ + rv = DER_Encode(arena, &digder, SGNDigestInfoTemplate, + di); + if (rv != SECSuccess) { + goto loser; + } + } else { + digder.data = digest; + digder.len = part1; + } + + /* + ** Encrypt signature after constructing appropriate PKCS#1 signature + ** block + */ + signatureLen = PK11_SignatureLen(privKey); + if (signatureLen <= 0) { + PORT_SetError(SEC_ERROR_INVALID_KEY); + rv = SECFailure; + goto loser; + } + sigitem.len = signatureLen; + sigitem.data = (unsigned char *)PORT_Alloc(signatureLen); + + if (sigitem.data == NULL) { + rv = SECFailure; + 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) { + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!arena) { + rv = SECFailure; + goto loser; + } + + rv = sec_DecodeRSAPSSParamsToMechanism(arena, cx->params, &mech); + 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); + 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; +} + +/************************************************************************/ + +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, params, pk); + + if (sgn == NULL) + return SECFailure; + + rv = SGN_Begin(sgn); + if (rv != SECSuccess) + goto loser; + + rv = SGN_Update(sgn, buf, len); + if (rv != SECSuccess) + goto loser; + + 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) }, + { DER_INLINE, + offsetof(CERTSignedData, signatureAlgorithm), + SECAlgorithmIDTemplate }, + { DER_BIT_STRING, + offsetof(CERTSignedData, signature) }, + { 0 } +}; + +SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate) + +const SEC_ASN1Template CERT_SignedDataTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(CERTSignedData) }, + { SEC_ASN1_ANY, + offsetof(CERTSignedData, data) }, + { SEC_ASN1_INLINE | SEC_ASN1_XTRN, + offsetof(CERTSignedData, signatureAlgorithm), + SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, + { SEC_ASN1_BIT_STRING, + offsetof(CERTSignedData, signature) }, + { 0 } +}; + +SEC_ASN1_CHOOSER_IMPLEMENT(CERT_SignedDataTemplate) + +static SECStatus +sec_DerSignData(PLArenaPool *arena, SECItem *result, + const unsigned char *buf, int len, SECKEYPrivateKey *pk, + 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 + * and algID match + */ + + if (algID == SEC_OID_UNKNOWN) { + switch (pk->keyType) { + case rsaKey: + algID = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION; + break; + case dsaKey: + /* get Signature length (= q_len*2) and work from there */ + switch (PK11_SignatureLen(pk)) { + case 320: + algID = SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST; + break; + case 448: + algID = SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA224_DIGEST; + break; + case 512: + default: + algID = SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA256_DIGEST; + break; + } + break; + case ecKey: + algID = SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE; + break; + default: + PORT_SetError(SEC_ERROR_INVALID_KEY); + return SECFailure; + } + } + + /* Sign input buffer */ + 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, 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; + SECOidTag enctag; + PRUint32 policyFlags; + PRInt32 optFlags; + + result->data = 0; + + if (NSS_OptionGet(NSS_KEY_SIZE_POLICY_FLAGS, &optFlags) != SECFailure) { + if (optFlags & NSS_KEY_SIZE_POLICY_SIGN_FLAG) { + rv = seckey_EnforceKeySize(privKey->keyType, + SECKEY_PrivateKeyStrengthInBits(privKey), + SEC_ERROR_SIGNATURE_ALGORITHM_DISABLED); + if (rv != SECSuccess) { + return SECFailure; + } + } + } + /* check the policy on the hash algorithm */ + if ((NSS_GetAlgorithmPolicy(algtag, &policyFlags) == SECFailure) || + !(policyFlags & NSS_USE_ALG_IN_ANY_SIGNATURE)) { + PORT_SetError(SEC_ERROR_SIGNATURE_ALGORITHM_DISABLED); + return SECFailure; + } + /* check the policy on the encryption algorithm */ + enctag = sec_GetEncAlgFromSigAlg( + SEC_GetSignatureAlgorithmOidTag(privKey->keyType, algtag)); + if ((enctag == SEC_OID_UNKNOWN) || + (NSS_GetAlgorithmPolicy(enctag, &policyFlags) == SECFailure) || + !(policyFlags & NSS_USE_ALG_IN_ANY_SIGNATURE)) { + PORT_SetError(SEC_ERROR_SIGNATURE_ALGORITHM_DISABLED); + return SECFailure; + } + + if (privKey->keyType == rsaKey) { + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!arena) { + rv = SECFailure; + goto loser; + } + + /* Construct digest info */ + di = SGN_CreateDigestInfo(algtag, digest->data, digest->len); + if (!di) { + rv = SECFailure; + goto loser; + } + + /* Der encode the digest as a DigestInfo */ + rv = DER_Encode(arena, &digder, SGNDigestInfoTemplate, + di); + if (rv != SECSuccess) { + goto loser; + } + } else { + digder.data = digest->data; + digder.len = digest->len; + } + + /* + ** Encrypt signature after constructing appropriate PKCS#1 signature + ** block + */ + modulusLen = PK11_SignatureLen(privKey); + if (modulusLen <= 0) { + PORT_SetError(SEC_ERROR_INVALID_KEY); + rv = SECFailure; + goto loser; + } + result->len = modulusLen; + result->data = (unsigned char *)PORT_Alloc(modulusLen); + result->type = siBuffer; + + if (result->data == NULL) { + rv = SECFailure; + goto loser; + } + + rv = PK11_Sign(privKey, result, &digder); + if (rv != SECSuccess) { + PORT_Free(result->data); + result->data = NULL; + } + +loser: + SGN_DestroyDigestInfo(di); + if (arena != NULL) { + PORT_FreeArena(arena, PR_FALSE); + } + return rv; +} + +SECOidTag +SEC_GetSignatureAlgorithmOidTag(KeyType keyType, SECOidTag hashAlgTag) +{ + SECOidTag sigTag = SEC_OID_UNKNOWN; + + switch (keyType) { + case rsaKey: + switch (hashAlgTag) { + case SEC_OID_MD2: + sigTag = SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION; + break; + case SEC_OID_MD5: + sigTag = SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION; + break; + case SEC_OID_SHA1: + sigTag = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION; + break; + case SEC_OID_SHA224: + sigTag = SEC_OID_PKCS1_SHA224_WITH_RSA_ENCRYPTION; + break; + case SEC_OID_UNKNOWN: /* default for RSA if not specified */ + case SEC_OID_SHA256: + sigTag = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION; + break; + case SEC_OID_SHA384: + sigTag = SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION; + break; + case SEC_OID_SHA512: + sigTag = SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION; + break; + default: + break; + } + break; + case dsaKey: + switch (hashAlgTag) { + case SEC_OID_SHA1: + sigTag = SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST; + break; + case SEC_OID_SHA224: + sigTag = SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA224_DIGEST; + break; + case SEC_OID_UNKNOWN: /* default for DSA if not specified */ + case SEC_OID_SHA256: + sigTag = SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA256_DIGEST; + break; + default: + break; + } + break; + case ecKey: + switch (hashAlgTag) { + case SEC_OID_SHA1: + sigTag = SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE; + break; + case SEC_OID_SHA224: + sigTag = SEC_OID_ANSIX962_ECDSA_SHA224_SIGNATURE; + break; + case SEC_OID_UNKNOWN: /* default for ECDSA if not specified */ + case SEC_OID_SHA256: + sigTag = SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE; + break; + case SEC_OID_SHA384: + sigTag = SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE; + break; + case SEC_OID_SHA512: + sigTag = SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE; + break; + 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; + 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 > (unsigned long)(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 new file mode 100644 index 0000000000..8c9dc2d87d --- /dev/null +++ b/security/nss/lib/cryptohi/secvfy.c @@ -0,0 +1,952 @@ +/* + * Verification stuff. + * + * 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 <stdio.h> +#include "cryptohi.h" +#include "sechash.h" +#include "keyhi.h" +#include "secasn1.h" +#include "secoid.h" +#include "pk11func.h" +#include "pkcs1sig.h" +#include "secdig.h" +#include "secerr.h" +#include "keyi.h" +#include "nss.h" + +/* +** Recover the DigestInfo from an RSA PKCS#1 signature. +** +** If givenDigestAlg != SEC_OID_UNKNOWN, copy givenDigestAlg to digestAlgOut. +** Otherwise, parse the DigestInfo structure and store the decoded digest +** algorithm into digestAlgOut. +** +** Store the encoded DigestInfo into digestInfo. +** Store the DigestInfo length into digestInfoLen. +** +** This function does *not* verify that the AlgorithmIdentifier in the +** DigestInfo identifies givenDigestAlg or that the DigestInfo is encoded +** correctly; verifyPKCS1DigestInfo does that. +** +** XXX this is assuming that the signature algorithm has WITH_RSA_ENCRYPTION +*/ +static SECStatus +recoverPKCS1DigestInfo(SECOidTag givenDigestAlg, + /*out*/ SECOidTag *digestAlgOut, + /*out*/ unsigned char **digestInfo, + /*out*/ unsigned int *digestInfoLen, + SECKEYPublicKey *key, + const SECItem *sig, void *wincx) +{ + SGNDigestInfo *di = NULL; + SECItem it; + PRBool rv = SECSuccess; + + PORT_Assert(digestAlgOut); + PORT_Assert(digestInfo); + PORT_Assert(digestInfoLen); + PORT_Assert(key); + PORT_Assert(key->keyType == rsaKey); + PORT_Assert(sig); + + it.data = NULL; + it.len = SECKEY_PublicKeyStrength(key); + if (it.len != 0) { + it.data = (unsigned char *)PORT_Alloc(it.len); + } + if (it.len == 0 || it.data == NULL) { + rv = SECFailure; + } + + if (rv == SECSuccess) { + /* decrypt the block */ + rv = PK11_VerifyRecover(key, sig, &it, wincx); + } + + if (rv == SECSuccess) { + if (givenDigestAlg != SEC_OID_UNKNOWN) { + /* We don't need to parse the DigestInfo if the caller gave us the + * digest algorithm to use. Later verifyPKCS1DigestInfo will verify + * that the DigestInfo identifies the given digest algorithm and + * that the DigestInfo is encoded absolutely correctly. + */ + *digestInfoLen = it.len; + *digestInfo = (unsigned char *)it.data; + *digestAlgOut = givenDigestAlg; + return SECSuccess; + } + } + + if (rv == SECSuccess) { + /* The caller didn't specify a digest algorithm to use, so choose the + * digest algorithm by parsing the AlgorithmIdentifier within the + * DigestInfo. + */ + di = SGN_DecodeDigestInfo(&it); + if (!di) { + rv = SECFailure; + } + } + + if (rv == SECSuccess) { + *digestAlgOut = SECOID_GetAlgorithmTag(&di->digestAlgorithm); + if (*digestAlgOut == SEC_OID_UNKNOWN) { + rv = SECFailure; + } + } + + if (di) { + SGN_DestroyDigestInfo(di); + } + + if (rv == SECSuccess) { + *digestInfoLen = it.len; + *digestInfo = (unsigned char *)it.data; + } else { + if (it.data) { + PORT_Free(it.data); + } + *digestInfo = NULL; + *digestInfoLen = 0; + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + } + + return rv; +} + +struct VFYContextStr { + SECOidTag hashAlg; /* the hash algorithm */ + SECKEYPublicKey *key; + /* + * This buffer holds either the digest or the full signature + * depending on the type of the signature (key->keyType). It is + * defined as a union to make sure it always has enough space. + * + * Use the "buffer" union member to reference the buffer. + * Note: do not take the size of the "buffer" union member. Take + * the size of the union or some other union member instead. + */ + 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; + return _SGN_VerifyPKCS1DigestInfo( + cx->hashAlg, digest, &pkcs1DigestInfo, + PR_FALSE /*XXX: unsafeAllowMissingParameters*/); +} + +static unsigned int +checkedSignatureLen(const SECKEYPublicKey *pubk) +{ + unsigned int sigLen = SECKEY_SignatureLen(pubk); + if (sigLen == 0) { + /* Error set by SECKEY_SignatureLen */ + return sigLen; + } + unsigned int maxSigLen; + switch (pubk->keyType) { + case rsaKey: + case rsaPssKey: + maxSigLen = (RSA_MAX_MODULUS_BITS + 7) / 8; + break; + case dsaKey: + maxSigLen = DSA_MAX_SIGNATURE_LEN; + break; + case ecKey: + maxSigLen = 2 * MAX_ECKEY_LEN; + break; + default: + PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG); + return 0; + } + if (sigLen > maxSigLen) { + PORT_SetError(SEC_ERROR_INVALID_KEY); + return 0; + } + return sigLen; +} + +/* + * decode the ECDSA or DSA signature from it's DER wrapping. + * The unwrapped/raw signature is placed in the buffer pointed + * to by dsig and has enough room for len bytes. + */ +static SECStatus +decodeECorDSASignature(SECOidTag algid, const SECItem *sig, unsigned char *dsig, + unsigned int len) +{ + SECItem *dsasig = NULL; /* also used for ECDSA */ + + /* Safety: Ensure algId is as expected and that signature size is within maxmimums */ + if (algid == SEC_OID_ANSIX9_DSA_SIGNATURE) { + if (len > DSA_MAX_SIGNATURE_LEN) { + goto loser; + } + } else if (algid == SEC_OID_ANSIX962_EC_PUBLIC_KEY) { + if (len > MAX_ECKEY_LEN * 2) { + goto loser; + } + } else { + goto loser; + } + + /* Decode and pad to length */ + dsasig = DSAU_DecodeDerSigToLen((SECItem *)sig, len); + if (dsasig == NULL) { + goto loser; + } + if (dsasig->len != len) { + SECITEM_FreeItem(dsasig, PR_TRUE); + goto loser; + } + + PORT_Memcpy(dsig, dsasig->data, len); + SECITEM_FreeItem(dsasig, PR_TRUE); + + return SECSuccess; + +loser: + PORT_SetError(SEC_ERROR_BAD_DER); + return SECFailure; +} + +const SEC_ASN1Template hashParameterTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECItem) }, + { SEC_ASN1_OBJECT_ID, 0 }, + { SEC_ASN1_SKIP_REST }, + { 0 } +}; + +/* + * Get just the encryption algorithm from the signature algorithm + */ +SECOidTag +sec_GetEncAlgFromSigAlg(SECOidTag sigAlg) +{ + /* get the "encryption" algorithm */ + switch (sigAlg) { + case SEC_OID_PKCS1_RSA_ENCRYPTION: + case SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION: + case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION: + case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION: + case SEC_OID_ISO_SHA_WITH_RSA_SIGNATURE: + case SEC_OID_ISO_SHA1_WITH_RSA_SIGNATURE: + case SEC_OID_PKCS1_SHA224_WITH_RSA_ENCRYPTION: + case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION: + case SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION: + case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION: + return SEC_OID_PKCS1_RSA_ENCRYPTION; + case SEC_OID_PKCS1_RSA_PSS_SIGNATURE: + return SEC_OID_PKCS1_RSA_PSS_SIGNATURE; + + /* what about normal DSA? */ + case SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST: + case SEC_OID_BOGUS_DSA_SIGNATURE_WITH_SHA1_DIGEST: + case SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA224_DIGEST: + case SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA256_DIGEST: + return SEC_OID_ANSIX9_DSA_SIGNATURE; + case SEC_OID_MISSI_DSS: + case SEC_OID_MISSI_KEA_DSS: + case SEC_OID_MISSI_KEA_DSS_OLD: + case SEC_OID_MISSI_DSS_OLD: + return SEC_OID_MISSI_DSS; + case SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE: + case SEC_OID_ANSIX962_ECDSA_SHA224_SIGNATURE: + case SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE: + case SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE: + case SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE: + case SEC_OID_ANSIX962_ECDSA_SIGNATURE_RECOMMENDED_DIGEST: + case SEC_OID_ANSIX962_ECDSA_SIGNATURE_SPECIFIED_DIGEST: + return SEC_OID_ANSIX962_EC_PUBLIC_KEY; + /* we don't implement MD4 hashes */ + case SEC_OID_PKCS1_MD4_WITH_RSA_ENCRYPTION: + default: + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + break; + } + return SEC_OID_UNKNOWN; +} + +/* + * Pulls the hash algorithm, signing algorithm, and key type out of a + * composite algorithm. + * + * sigAlg: the composite algorithm to dissect. + * hashalg: address of a SECOidTag which will be set with the hash algorithm. + * encalg: address of a SECOidTag which will be set with the signing alg. + * + * Returns: SECSuccess if the algorithm was acceptable, SECFailure if the + * algorithm was not found or was not a signing algorithm. + */ +SECStatus +sec_DecodeSigAlg(const SECKEYPublicKey *key, SECOidTag sigAlg, + const SECItem *param, SECOidTag *encalgp, SECOidTag *hashalg) +{ + unsigned int len; + PLArenaPool *arena; + SECStatus rv; + SECItem oid; + SECOidTag encalg; + + PR_ASSERT(hashalg != NULL); + PR_ASSERT(encalgp != NULL); + + switch (sigAlg) { + /* We probably shouldn't be generating MD2 signatures either */ + case SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION: + *hashalg = SEC_OID_MD2; + break; + case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION: + *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: + if (param && param->data) { + PORTCheapArenaPool tmpArena; + + PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE); + rv = sec_DecodeRSAPSSParams(&tmpArena.arena, param, + hashalg, NULL, NULL); + PORT_DestroyCheapArena(&tmpArena); + + /* only accept hash algorithms */ + if (rv != SECSuccess || HASH_GetHashTypeByOidTag(*hashalg) == HASH_AlgNULL) { + /* error set by sec_DecodeRSAPSSParams or 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: + case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION: + case SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA256_DIGEST: + *hashalg = SEC_OID_SHA256; + break; + case SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE: + case SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION: + *hashalg = SEC_OID_SHA384; + break; + case SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE: + case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION: + *hashalg = SEC_OID_SHA512; + break; + + /* what about normal DSA? */ + case SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST: + case SEC_OID_BOGUS_DSA_SIGNATURE_WITH_SHA1_DIGEST: + case SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE: + *hashalg = SEC_OID_SHA1; + break; + case SEC_OID_MISSI_DSS: + case SEC_OID_MISSI_KEA_DSS: + case SEC_OID_MISSI_KEA_DSS_OLD: + case SEC_OID_MISSI_DSS_OLD: + *hashalg = SEC_OID_SHA1; + break; + case SEC_OID_ANSIX962_ECDSA_SIGNATURE_RECOMMENDED_DIGEST: + /* This is an EC algorithm. Recommended means the largest + * hash algorithm that is not reduced by the keysize of + * the EC algorithm. Note that key strength is in bytes and + * algorithms are specified in bits. Never use an algorithm + * weaker than sha1. */ + len = SECKEY_PublicKeyStrength(key); + if (len < 28) { /* 28 bytes == 224 bits */ + *hashalg = SEC_OID_SHA1; + } else if (len < 32) { /* 32 bytes == 256 bits */ + *hashalg = SEC_OID_SHA224; + } else if (len < 48) { /* 48 bytes == 384 bits */ + *hashalg = SEC_OID_SHA256; + } else if (len < 64) { /* 48 bytes == 512 bits */ + *hashalg = SEC_OID_SHA384; + } else { + /* use the largest in this case */ + *hashalg = SEC_OID_SHA512; + } + break; + case SEC_OID_ANSIX962_ECDSA_SIGNATURE_SPECIFIED_DIGEST: + if (param == NULL) { + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + return SECFailure; + } + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + return SECFailure; + } + rv = SEC_QuickDERDecodeItem(arena, &oid, hashParameterTemplate, param); + if (rv == SECSuccess) { + *hashalg = SECOID_FindOIDTag(&oid); + } + PORT_FreeArena(arena, PR_FALSE); + if (rv != SECSuccess) { + return rv; + } + /* only accept hash algorithms */ + if (HASH_GetHashTypeByOidTag(*hashalg) == HASH_AlgNULL) { + /* error set by HASH_GetHashTypeByOidTag */ + return SECFailure; + } + break; + /* we don't implement MD4 hashes */ + case SEC_OID_PKCS1_MD4_WITH_RSA_ENCRYPTION: + default: + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + return SECFailure; + } + + encalg = sec_GetEncAlgFromSigAlg(sigAlg); + if (encalg == SEC_OID_UNKNOWN) { + return SECFailure; + } + *encalgp = encalg; + + return SECSuccess; +} + +/* + * we can verify signatures that come from 2 different sources: + * one in with the signature contains a signature oid, and the other + * in which the signature is managed by a Public key (encAlg) oid + * and a hash oid. The latter is the more basic, so that's what + * our base vfyCreate function takes. + * + * There is one noteworthy corner case, if we are using an RSA key, and the + * signature block is provided, then the hashAlg can be specified as + * SEC_OID_UNKNOWN. In this case, verify will use the hash oid supplied + * in the RSA signature block. + */ +static VFYContext * +vfy_CreateContext(const SECKEYPublicKey *key, const SECItem *sig, + SECOidTag encAlg, SECOidTag hashAlg, SECOidTag *hash, void *wincx) +{ + VFYContext *cx; + SECStatus rv; + unsigned int sigLen; + KeyType type; + PRUint32 policyFlags; + PRInt32 optFlags; + + /* make sure the encryption algorithm matches the key type */ + /* RSA-PSS algorithm can be used with both rsaKey and rsaPssKey */ + type = seckey_GetKeyType(encAlg); + if ((key->keyType != type) && + ((key->keyType != rsaKey) || (type != rsaPssKey))) { + PORT_SetError(SEC_ERROR_PKCS7_KEYALG_MISMATCH); + return NULL; + } + if (NSS_OptionGet(NSS_KEY_SIZE_POLICY_FLAGS, &optFlags) != SECFailure) { + if (optFlags & NSS_KEY_SIZE_POLICY_VERIFY_FLAG) { + rv = seckey_EnforceKeySize(key->keyType, + SECKEY_PublicKeyStrengthInBits(key), + SEC_ERROR_SIGNATURE_ALGORITHM_DISABLED); + if (rv != SECSuccess) { + return NULL; + } + } + } + /* check the policy on the encryption algorithm */ + if ((NSS_GetAlgorithmPolicy(encAlg, &policyFlags) == SECFailure) || + !(policyFlags & NSS_USE_ALG_IN_ANY_SIGNATURE)) { + PORT_SetError(SEC_ERROR_SIGNATURE_ALGORITHM_DISABLED); + return NULL; + } + + cx = (VFYContext *)PORT_ZAlloc(sizeof(VFYContext)); + if (cx == NULL) { + goto loser; + } + + cx->wincx = wincx; + cx->hasSignature = (sig != NULL); + cx->encAlg = encAlg; + cx->hashAlg = hashAlg; + cx->key = SECKEY_CopyPublicKey(key); + cx->pkcs1RSADigestInfo = NULL; + rv = SECSuccess; + if (sig) { + rv = SECFailure; + if (type == rsaKey) { + rv = recoverPKCS1DigestInfo(hashAlg, &cx->hashAlg, + &cx->pkcs1RSADigestInfo, + &cx->pkcs1RSADigestInfoLen, + cx->key, + sig, wincx); + } else { + sigLen = checkedSignatureLen(key); + /* Check signature length is within limits */ + if (sigLen == 0) { + /* error set by checkedSignatureLen */ + rv = SECFailure; + goto loser; + } + if (sigLen > sizeof(cx->u)) { + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + rv = SECFailure; + goto loser; + } + switch (type) { + case rsaPssKey: + if (sig->len != sigLen) { + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + rv = SECFailure; + goto loser; + } + PORT_Memcpy(cx->u.buffer, sig->data, sigLen); + rv = SECSuccess; + break; + case ecKey: + case dsaKey: + /* decodeECorDSASignature will check sigLen == sig->len after padding */ + rv = decodeECorDSASignature(encAlg, sig, cx->u.buffer, sigLen); + break; + default: + /* Unreachable */ + rv = SECFailure; + goto loser; + } + } + if (rv != SECSuccess) { + goto loser; + } + } + + /* check hash alg again, RSA may have changed it.*/ + if (HASH_GetHashTypeByOidTag(cx->hashAlg) == HASH_AlgNULL) { + /* error set by HASH_GetHashTypeByOidTag */ + goto loser; + } + /* check the policy on the hash algorithm. Do this after + * the rsa decode because some uses of this function get hash implicitly + * from the RSA signature itself. */ + if ((NSS_GetAlgorithmPolicy(cx->hashAlg, &policyFlags) == SECFailure) || + !(policyFlags & NSS_USE_ALG_IN_ANY_SIGNATURE)) { + PORT_SetError(SEC_ERROR_SIGNATURE_ALGORITHM_DISABLED); + goto loser; + } + + if (hash) { + *hash = cx->hashAlg; + } + return cx; + +loser: + if (cx) { + VFY_DestroyContext(cx, PR_TRUE); + } + return 0; +} + +VFYContext * +VFY_CreateContext(SECKEYPublicKey *key, SECItem *sig, SECOidTag sigAlg, + void *wincx) +{ + SECOidTag encAlg, hashAlg; + SECStatus rv = sec_DecodeSigAlg(key, sigAlg, NULL, &encAlg, &hashAlg); + if (rv != SECSuccess) { + return NULL; + } + return vfy_CreateContext(key, sig, encAlg, hashAlg, NULL, wincx); +} + +VFYContext * +VFY_CreateContextDirect(const SECKEYPublicKey *key, const SECItem *sig, + SECOidTag encAlg, SECOidTag hashAlg, + SECOidTag *hash, void *wincx) +{ + 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; + } + + 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) +{ + if (cx->hashcx != NULL) { + (*cx->hashobj->destroy)(cx->hashcx, PR_TRUE); + cx->hashcx = NULL; + } + + cx->hashobj = HASH_GetHashObjectByOidTag(cx->hashAlg); + if (!cx->hashobj) + return SECFailure; /* error code is set */ + + cx->hashcx = (*cx->hashobj->create)(); + if (cx->hashcx == NULL) + return SECFailure; + + (*cx->hashobj->begin)(cx->hashcx); + return SECSuccess; +} + +SECStatus +VFY_Update(VFYContext *cx, const unsigned char *input, unsigned inputLen) +{ + if (cx->hashcx == NULL) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + (*cx->hashobj->update)(cx->hashcx, input, inputLen); + return SECSuccess; +} + +SECStatus +VFY_EndWithSignature(VFYContext *cx, SECItem *sig) +{ + unsigned char final[HASH_LENGTH_MAX]; + unsigned part; + 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) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + (*cx->hashobj->end)(cx->hashcx, final, &part, sizeof(final)); + switch (cx->key->keyType) { + case ecKey: + case dsaKey: + dsasig.len = checkedSignatureLen(cx->key); + if (dsasig.len == 0) { + return SECFailure; + } + if (dsasig.len > sizeof(cx->u)) { + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + return SECFailure; + } + dsasig.data = cx->u.buffer; + + if (sig) { + rv = decodeECorDSASignature(cx->encAlg, sig, dsasig.data, + dsasig.len); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + return SECFailure; + } + } + 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: + if (cx->encAlg == SEC_OID_PKCS1_RSA_PSS_SIGNATURE) { + CK_RSA_PKCS_PSS_PARAMS mech; + SECItem mechItem = { siBuffer, (unsigned char *)&mech, sizeof(mech) }; + PORTCheapArenaPool tmpArena; + + PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE); + rv = sec_DecodeRSAPSSParamsToMechanism(&tmpArena.arena, + cx->params, + &mech); + PORT_DestroyCheapArena(&tmpArena); + if (rv != SECSuccess) { + return SECFailure; + } + + rsasig.data = cx->u.buffer; + rsasig.len = checkedSignatureLen(cx->key); + if (rsasig.len == 0) { + /* Error set by checkedSignatureLen */ + return SECFailure; + } + if (rsasig.len > sizeof(cx->u)) { + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + 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); + if (rv != SECSuccess) { + return SECFailure; + } + PORT_Assert(cx->hashAlg == hashid); + } + return verifyPKCS1DigestInfo(cx, &digest); + } + break; + default: + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + return SECFailure; /* shouldn't happen */ + } + return SECSuccess; +} + +SECStatus +VFY_End(VFYContext *cx) +{ + return VFY_EndWithSignature(cx, NULL); +} + +/************************************************************************/ +/* + * Verify that a previously-computed digest matches a signature. + */ +static SECStatus +vfy_VerifyDigest(const SECItem *digest, const SECKEYPublicKey *key, + const SECItem *sig, SECOidTag encAlg, SECOidTag hashAlg, + void *wincx) +{ + SECStatus rv; + VFYContext *cx; + SECItem dsasig; /* also used for ECDSA */ + rv = SECFailure; + + cx = vfy_CreateContext(key, sig, encAlg, hashAlg, NULL, wincx); + if (cx != NULL) { + switch (key->keyType) { + case rsaKey: + rv = verifyPKCS1DigestInfo(cx, digest); + /* Error (if any) set by verifyPKCS1DigestInfo */ + break; + case ecKey: + case dsaKey: + dsasig.data = cx->u.buffer; + dsasig.len = checkedSignatureLen(cx->key); + if (dsasig.len == 0) { + /* Error set by checkedSignatureLen */ + rv = SECFailure; + break; + } + if (dsasig.len > sizeof(cx->u)) { + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + rv = SECFailure; + break; + } + rv = PK11_Verify(cx->key, &dsasig, (SECItem *)digest, cx->wincx); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + } + break; + default: + break; + } + VFY_DestroyContext(cx, PR_TRUE); + } + return rv; +} + +SECStatus +VFY_VerifyDigestDirect(const SECItem *digest, const SECKEYPublicKey *key, + const SECItem *sig, SECOidTag encAlg, + SECOidTag hashAlg, void *wincx) +{ + return vfy_VerifyDigest(digest, key, sig, encAlg, hashAlg, wincx); +} + +SECStatus +VFY_VerifyDigest(SECItem *digest, SECKEYPublicKey *key, SECItem *sig, + SECOidTag algid, void *wincx) +{ + SECOidTag encAlg, hashAlg; + SECStatus rv = sec_DecodeSigAlg(key, algid, NULL, &encAlg, &hashAlg); + if (rv != SECSuccess) { + return SECFailure; + } + return vfy_VerifyDigest(digest, key, sig, encAlg, hashAlg, wincx); +} + +/* + * this function takes an optional hash oid, which the digest function + * will be compared with our target hash value. + */ +SECStatus +VFY_VerifyDigestWithAlgorithmID(const SECItem *digest, + const SECKEYPublicKey *key, const SECItem *sig, + const SECAlgorithmID *sigAlgorithm, + SECOidTag hashCmp, void *wincx) +{ + SECOidTag encAlg, hashAlg; + SECStatus rv = sec_DecodeSigAlg(key, + SECOID_GetAlgorithmTag((SECAlgorithmID *)sigAlgorithm), + &sigAlgorithm->parameters, &encAlg, &hashAlg); + if (rv != SECSuccess) { + return rv; + } + if (hashCmp != SEC_OID_UNKNOWN && + hashAlg != SEC_OID_UNKNOWN && + hashCmp != hashAlg) { + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + 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, + 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); + } + + VFY_DestroyContext(cx, PR_TRUE); + return rv; +} + +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, 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, 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, + &sigAlgorithm->parameters, hash, wincx); +} |