summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/cryptohi
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /security/nss/lib/cryptohi
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'security/nss/lib/cryptohi')
-rw-r--r--security/nss/lib/cryptohi/Makefile46
-rw-r--r--security/nss/lib/cryptohi/cryptohi.gyp27
-rw-r--r--security/nss/lib/cryptohi/cryptohi.h425
-rw-r--r--security/nss/lib/cryptohi/cryptoht.h14
-rw-r--r--security/nss/lib/cryptohi/dsautil.c271
-rw-r--r--security/nss/lib/cryptohi/exports.gyp37
-rw-r--r--security/nss/lib/cryptohi/key.h14
-rw-r--r--security/nss/lib/cryptohi/keyhi.h276
-rw-r--r--security/nss/lib/cryptohi/keyi.h45
-rw-r--r--security/nss/lib/cryptohi/keyt.h14
-rw-r--r--security/nss/lib/cryptohi/keythi.h247
-rw-r--r--security/nss/lib/cryptohi/manifest.mn37
-rw-r--r--security/nss/lib/cryptohi/sechash.c471
-rw-r--r--security/nss/lib/cryptohi/sechash.h60
-rw-r--r--security/nss/lib/cryptohi/seckey.c2320
-rw-r--r--security/nss/lib/cryptohi/secsign.c884
-rw-r--r--security/nss/lib/cryptohi/secvfy.c952
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, &param, 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(&param->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, &params);
+ if ((rv != SECSuccess) || (params.data == NULL)) {
+ PORT_SetError(SEC_ERROR_INVALID_KEY);
+ return 0;
+ }
+ bitSize = SECKEY_BigIntegerBitLength(&params);
+ PORT_Free(params.data);
+ return bitSize;
+ case ecKey:
+ rv = PK11_ReadAttribute(privk->pkcs11Slot, privk->pkcs11ID,
+ CKA_EC_PARAMS, NULL, &params);
+ if ((rv != SECSuccess) || (params.data == NULL)) {
+ return 0;
+ }
+ bitSize = SECKEY_ECParamsToKeySize(&params);
+ 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, &copyk->u.rsa.modulus,
+ &pubk->u.rsa.modulus);
+ if (rv == SECSuccess) {
+ rv = SECITEM_CopyItem(arena, &copyk->u.rsa.publicExponent,
+ &pubk->u.rsa.publicExponent);
+ if (rv == SECSuccess)
+ return copyk;
+ }
+ break;
+ case dsaKey:
+ rv = SECITEM_CopyItem(arena, &copyk->u.dsa.publicValue,
+ &pubk->u.dsa.publicValue);
+ if (rv != SECSuccess)
+ break;
+ rv = SECITEM_CopyItem(arena, &copyk->u.dsa.params.prime,
+ &pubk->u.dsa.params.prime);
+ if (rv != SECSuccess)
+ break;
+ rv = SECITEM_CopyItem(arena, &copyk->u.dsa.params.subPrime,
+ &pubk->u.dsa.params.subPrime);
+ if (rv != SECSuccess)
+ break;
+ rv = SECITEM_CopyItem(arena, &copyk->u.dsa.params.base,
+ &pubk->u.dsa.params.base);
+ break;
+ case dhKey:
+ rv = SECITEM_CopyItem(arena, &copyk->u.dh.prime, &pubk->u.dh.prime);
+ if (rv != SECSuccess)
+ break;
+ rv = SECITEM_CopyItem(arena, &copyk->u.dh.base, &pubk->u.dh.base);
+ if (rv != SECSuccess)
+ break;
+ rv = SECITEM_CopyItem(arena, &copyk->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, &copyk->u.ec.DEREncodedParams,
+ &pubk->u.ec.DEREncodedParams);
+ if (rv != SECSuccess) {
+ break;
+ }
+ copyk->u.ec.encoding = ECPoint_Undefined;
+ rv = SECITEM_CopyItem(arena, &copyk->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, &params, &pubk->u.dsa.params,
+ SECKEY_PQGParamsTemplate);
+ if (rv_item != NULL) {
+ rv = SECOID_SetAlgorithmID(arena, &spki->algorithm,
+ SEC_OID_ANSIX9_DSA_SIGNATURE,
+ &params);
+ 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(&params, PR_FALSE);
+ break;
+ case ecKey:
+ rv = SECITEM_CopyItem(arena, &params,
+ &pubk->u.ec.DEREncodedParams);
+ if (rv != SECSuccess)
+ break;
+
+ rv = SECOID_SetAlgorithmID(arena, &spki->algorithm,
+ SEC_OID_ANSIX962_EC_PUBLIC_KEY,
+ &params);
+ 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);
+}