summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/cryptohi/seckey.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--security/nss/lib/cryptohi/seckey.c2320
1 files changed, 2320 insertions, 0 deletions
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;
+}