diff options
Diffstat (limited to 'security/nss/lib/pk11wrap/pk11pk12.c')
-rw-r--r-- | security/nss/lib/pk11wrap/pk11pk12.c | 829 |
1 files changed, 829 insertions, 0 deletions
diff --git a/security/nss/lib/pk11wrap/pk11pk12.c b/security/nss/lib/pk11wrap/pk11pk12.c new file mode 100644 index 0000000000..917b7f0f67 --- /dev/null +++ b/security/nss/lib/pk11wrap/pk11pk12.c @@ -0,0 +1,829 @@ + +/* 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/. */ +/* + * This file PKCS #12 fuctions that should really be moved to the + * PKCS #12 directory, however we can't do that in a point release + * because that will break binary compatibility, so we keep them here for now. + */ + +#include "seccomon.h" +#include "secmod.h" +#include "secmodi.h" +#include "secmodti.h" +#include "secmodt.h" +#include "pkcs11.h" +#include "pk11func.h" +#include "secitem.h" +#include "keyhi.h" +#include "secoid.h" +#include "secasn1.h" +#include "secerr.h" +#include "prerror.h" + +/* These data structures should move to a common .h file shared between the + * wrappers and the pkcs 12 code. */ + +/* +** RSA Raw Private Key structures +*/ + +/* member names from PKCS#1, section 7.2 */ +struct SECKEYRSAPrivateKeyStr { + PLArenaPool *arena; + SECItem version; + SECItem modulus; + SECItem publicExponent; + SECItem privateExponent; + SECItem prime1; + SECItem prime2; + SECItem exponent1; + SECItem exponent2; + SECItem coefficient; +}; +typedef struct SECKEYRSAPrivateKeyStr SECKEYRSAPrivateKey; + +/* +** DSA Raw Private Key structures +*/ + +struct SECKEYDSAPrivateKeyStr { + SECKEYPQGParams params; + SECItem privateValue; +}; +typedef struct SECKEYDSAPrivateKeyStr SECKEYDSAPrivateKey; + +/* +** Diffie-Hellman Raw Private Key structures +** Structure member names suggested by PKCS#3. +*/ +struct SECKEYDHPrivateKeyStr { + PLArenaPool *arena; + SECItem prime; + SECItem base; + SECItem privateValue; +}; +typedef struct SECKEYDHPrivateKeyStr SECKEYDHPrivateKey; + +/* +** Elliptic Curve Private Key structures +** <https://tools.ietf.org/html/rfc5915#section-3> +*/ +struct SECKEYECPrivateKeyStr { + PLArenaPool *arena; + SECItem version; + SECItem curveOID; /* optional/ignored */ + SECItem publicValue; /* required (for now) */ + SECItem privateValue; +}; +typedef struct SECKEYECPrivateKeyStr SECKEYECPrivateKey; + +/* +** raw private key object +*/ +struct SECKEYRawPrivateKeyStr { + PLArenaPool *arena; + KeyType keyType; + union { + SECKEYRSAPrivateKey rsa; + SECKEYDSAPrivateKey dsa; + SECKEYDHPrivateKey dh; + SECKEYECPrivateKey ec; + } u; +}; +typedef struct SECKEYRawPrivateKeyStr SECKEYRawPrivateKey; + +SEC_ASN1_MKSUB(SEC_AnyTemplate) +SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate) + +/* ASN1 Templates for new decoder/encoder */ +/* + * Attribute value for PKCS8 entries (static?) + */ +const SEC_ASN1Template SECKEY_AttributeTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(SECKEYAttribute) }, + { SEC_ASN1_OBJECT_ID, offsetof(SECKEYAttribute, attrType) }, + { SEC_ASN1_SET_OF | SEC_ASN1_XTRN, offsetof(SECKEYAttribute, attrValue), + SEC_ASN1_SUB(SEC_AnyTemplate) }, + { 0 } +}; + +const SEC_ASN1Template SECKEY_SetOfAttributeTemplate[] = { + { SEC_ASN1_SET_OF, 0, SECKEY_AttributeTemplate }, +}; + +const SEC_ASN1Template SECKEY_PrivateKeyInfoTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECKEYPrivateKeyInfo) }, + { SEC_ASN1_INTEGER, offsetof(SECKEYPrivateKeyInfo, version) }, + { SEC_ASN1_INLINE | SEC_ASN1_XTRN, + offsetof(SECKEYPrivateKeyInfo, algorithm), + SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, + { SEC_ASN1_OCTET_STRING, offsetof(SECKEYPrivateKeyInfo, privateKey) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(SECKEYPrivateKeyInfo, attributes), + SECKEY_SetOfAttributeTemplate }, + { 0 } +}; + +const SEC_ASN1Template SECKEY_PointerToPrivateKeyInfoTemplate[] = { + { SEC_ASN1_POINTER, 0, SECKEY_PrivateKeyInfoTemplate } +}; + +const SEC_ASN1Template SECKEY_RSAPrivateKeyExportTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECKEYRawPrivateKey) }, + { SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.rsa.version) }, + { SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.rsa.modulus) }, + { SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.rsa.publicExponent) }, + { SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.rsa.privateExponent) }, + { SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.rsa.prime1) }, + { SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.rsa.prime2) }, + { SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.rsa.exponent1) }, + { SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.rsa.exponent2) }, + { SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.rsa.coefficient) }, + { 0 } +}; + +const SEC_ASN1Template SECKEY_DSAPrivateKeyExportTemplate[] = { + { SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.dsa.privateValue) }, +}; + +const SEC_ASN1Template SECKEY_DHPrivateKeyExportTemplate[] = { + { SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.dh.privateValue) }, + { SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.dh.base) }, + { SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.dh.prime) }, +}; + +SEC_ASN1_MKSUB(SEC_BitStringTemplate) +SEC_ASN1_MKSUB(SEC_ObjectIDTemplate) + +const SEC_ASN1Template SECKEY_ECPrivateKeyExportTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECKEYRawPrivateKey) }, + { SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.ec.version) }, + { SEC_ASN1_OCTET_STRING, + offsetof(SECKEYRawPrivateKey, u.ec.privateValue) }, + /* This value will always be ignored. u.ec.curveOID will always be + * overriden with the outer AlgorithmID.parameters. */ + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | + SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC | + SEC_ASN1_XTRN | 0, + offsetof(SECKEYRawPrivateKey, u.ec.curveOID), + SEC_ASN1_SUB(SEC_ObjectIDTemplate) }, + /* The public value is optional per RFC, but required in NSS. We + * can't do scalar mult on ECs to get a raw point with PK11 APIs. */ + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | + SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC | + SEC_ASN1_XTRN | 1, + offsetof(SECKEYRawPrivateKey, u.ec.publicValue), + SEC_ASN1_SUB(SEC_BitStringTemplate) }, + { 0 } +}; + +const SEC_ASN1Template SECKEY_EncryptedPrivateKeyInfoTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(SECKEYEncryptedPrivateKeyInfo) }, + { SEC_ASN1_INLINE | SEC_ASN1_XTRN, + offsetof(SECKEYEncryptedPrivateKeyInfo, algorithm), + SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, + { SEC_ASN1_OCTET_STRING, + offsetof(SECKEYEncryptedPrivateKeyInfo, encryptedData) }, + { 0 } +}; + +const SEC_ASN1Template SECKEY_PointerToEncryptedPrivateKeyInfoTemplate[] = { + { SEC_ASN1_POINTER, 0, SECKEY_EncryptedPrivateKeyInfoTemplate } +}; + +SEC_ASN1_CHOOSER_IMPLEMENT(SECKEY_EncryptedPrivateKeyInfoTemplate) +SEC_ASN1_CHOOSER_IMPLEMENT(SECKEY_PointerToEncryptedPrivateKeyInfoTemplate) +SEC_ASN1_CHOOSER_IMPLEMENT(SECKEY_PrivateKeyInfoTemplate) +SEC_ASN1_CHOOSER_IMPLEMENT(SECKEY_PointerToPrivateKeyInfoTemplate) + +/* + * 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_priv_key_export_for_asn1(SECKEYRawPrivateKey *key) +{ + key->u.rsa.modulus.type = siUnsignedInteger; + key->u.rsa.publicExponent.type = siUnsignedInteger; + key->u.rsa.privateExponent.type = siUnsignedInteger; + key->u.rsa.prime1.type = siUnsignedInteger; + key->u.rsa.prime2.type = siUnsignedInteger; + key->u.rsa.exponent1.type = siUnsignedInteger; + key->u.rsa.exponent2.type = siUnsignedInteger; + key->u.rsa.coefficient.type = siUnsignedInteger; +} + +static void +prepare_dsa_priv_key_export_for_asn1(SECKEYRawPrivateKey *key) +{ + key->u.dsa.privateValue.type = siUnsignedInteger; + key->u.dsa.params.prime.type = siUnsignedInteger; + key->u.dsa.params.subPrime.type = siUnsignedInteger; + key->u.dsa.params.base.type = siUnsignedInteger; +} + +static void +prepare_dh_priv_key_export_for_asn1(SECKEYRawPrivateKey *key) +{ + key->u.dh.privateValue.type = siUnsignedInteger; + key->u.dh.prime.type = siUnsignedInteger; + key->u.dh.base.type = siUnsignedInteger; +} + +static void +prepare_ec_priv_key_export_for_asn1(SECKEYRawPrivateKey *key) +{ + key->u.ec.version.type = siUnsignedInteger; + key->u.ec.curveOID.type = siUnsignedInteger; + key->u.ec.privateValue.type = siUnsignedInteger; + key->u.ec.publicValue.type = siUnsignedInteger; +} + +SECStatus +PK11_ImportDERPrivateKeyInfo(PK11SlotInfo *slot, SECItem *derPKI, + SECItem *nickname, SECItem *publicValue, PRBool isPerm, + PRBool isPrivate, unsigned int keyUsage, void *wincx) +{ + return PK11_ImportDERPrivateKeyInfoAndReturnKey(slot, derPKI, + nickname, publicValue, + isPerm, isPrivate, keyUsage, + NULL, wincx); +} + +SECStatus +PK11_ImportDERPrivateKeyInfoAndReturnKey(PK11SlotInfo *slot, SECItem *derPKI, + SECItem *nickname, SECItem *publicValue, + PRBool isPerm, PRBool isPrivate, unsigned int keyUsage, + SECKEYPrivateKey **privk, void *wincx) +{ + SECKEYPrivateKeyInfo *pki = NULL; + PLArenaPool *temparena = NULL; + SECStatus rv = SECFailure; + + temparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!temparena) + return rv; + pki = PORT_ArenaZNew(temparena, SECKEYPrivateKeyInfo); + if (!pki) { + PORT_FreeArena(temparena, PR_FALSE); + return rv; + } + pki->arena = temparena; + + rv = SEC_ASN1DecodeItem(pki->arena, pki, SECKEY_PrivateKeyInfoTemplate, + derPKI); + if (rv != SECSuccess) { + /* If SEC_ASN1DecodeItem fails, we cannot assume anything about the + * validity of the data in pki. The best we can do is free the arena + * and return. */ + PORT_FreeArena(temparena, PR_TRUE); + return rv; + } + if (pki->privateKey.data == NULL) { + /* If SEC_ASN1DecodeItems succeeds but SECKEYPrivateKeyInfo.privateKey + * is a zero-length octet string, free the arena and return a failure + * to avoid trying to zero the corresponding SECItem in + * SECKEY_DestroyPrivateKeyInfo(). */ + PORT_FreeArena(temparena, PR_TRUE); + PORT_SetError(SEC_ERROR_BAD_KEY); + return SECFailure; + } + + rv = PK11_ImportPrivateKeyInfoAndReturnKey(slot, pki, nickname, + publicValue, isPerm, isPrivate, + keyUsage, privk, wincx); + + /* this zeroes the key and frees the arena */ + SECKEY_DestroyPrivateKeyInfo(pki, PR_TRUE /*freeit*/); + return rv; +} + +SECStatus +PK11_ImportAndReturnPrivateKey(PK11SlotInfo *slot, SECKEYRawPrivateKey *lpk, + SECItem *nickname, SECItem *publicValue, PRBool isPerm, + PRBool isPrivate, unsigned int keyUsage, SECKEYPrivateKey **privk, + void *wincx) +{ + CK_BBOOL cktrue = CK_TRUE; + CK_BBOOL ckfalse = CK_FALSE; + CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY; + CK_KEY_TYPE keyType = CKK_RSA; + CK_OBJECT_HANDLE objectID; + CK_ATTRIBUTE theTemplate[20]; + int templateCount = 0; + SECStatus rv = SECFailure; + CK_ATTRIBUTE *attrs; + CK_ATTRIBUTE *signedattr = NULL; + int signedcount = 0; + CK_ATTRIBUTE *ap; + SECItem *ck_id = NULL; + + attrs = theTemplate; + + PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof(keyClass)); + attrs++; + PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(keyType)); + attrs++; + PK11_SETATTRS(attrs, CKA_TOKEN, isPerm ? &cktrue : &ckfalse, + sizeof(CK_BBOOL)); + attrs++; + PK11_SETATTRS(attrs, CKA_SENSITIVE, isPrivate ? &cktrue : &ckfalse, + sizeof(CK_BBOOL)); + attrs++; + PK11_SETATTRS(attrs, CKA_PRIVATE, isPrivate ? &cktrue : &ckfalse, + sizeof(CK_BBOOL)); + attrs++; + + switch (lpk->keyType) { + case rsaKey: + keyType = CKK_RSA; + PK11_SETATTRS(attrs, CKA_UNWRAP, (keyUsage & KU_KEY_ENCIPHERMENT) ? &cktrue : &ckfalse, + sizeof(CK_BBOOL)); + attrs++; + PK11_SETATTRS(attrs, CKA_DECRYPT, (keyUsage & KU_DATA_ENCIPHERMENT) ? &cktrue : &ckfalse, + sizeof(CK_BBOOL)); + attrs++; + PK11_SETATTRS(attrs, CKA_SIGN, (keyUsage & KU_DIGITAL_SIGNATURE) ? &cktrue : &ckfalse, + sizeof(CK_BBOOL)); + attrs++; + PK11_SETATTRS(attrs, CKA_SIGN_RECOVER, + (keyUsage & KU_DIGITAL_SIGNATURE) ? &cktrue + : &ckfalse, + sizeof(CK_BBOOL)); + attrs++; + ck_id = PK11_MakeIDFromPubKey(&lpk->u.rsa.modulus); + if (ck_id == NULL) { + goto loser; + } + PK11_SETATTRS(attrs, CKA_ID, ck_id->data, ck_id->len); + attrs++; + if (nickname) { + PK11_SETATTRS(attrs, CKA_LABEL, nickname->data, nickname->len); + attrs++; + } + signedattr = attrs; + PK11_SETATTRS(attrs, CKA_MODULUS, lpk->u.rsa.modulus.data, + lpk->u.rsa.modulus.len); + attrs++; + PK11_SETATTRS(attrs, CKA_PUBLIC_EXPONENT, + lpk->u.rsa.publicExponent.data, + lpk->u.rsa.publicExponent.len); + attrs++; + PK11_SETATTRS(attrs, CKA_PRIVATE_EXPONENT, + lpk->u.rsa.privateExponent.data, + lpk->u.rsa.privateExponent.len); + attrs++; + PK11_SETATTRS(attrs, CKA_PRIME_1, + lpk->u.rsa.prime1.data, + lpk->u.rsa.prime1.len); + attrs++; + PK11_SETATTRS(attrs, CKA_PRIME_2, + lpk->u.rsa.prime2.data, + lpk->u.rsa.prime2.len); + attrs++; + PK11_SETATTRS(attrs, CKA_EXPONENT_1, + lpk->u.rsa.exponent1.data, + lpk->u.rsa.exponent1.len); + attrs++; + PK11_SETATTRS(attrs, CKA_EXPONENT_2, + lpk->u.rsa.exponent2.data, + lpk->u.rsa.exponent2.len); + attrs++; + PK11_SETATTRS(attrs, CKA_COEFFICIENT, + lpk->u.rsa.coefficient.data, + lpk->u.rsa.coefficient.len); + attrs++; + break; + case dsaKey: + keyType = CKK_DSA; + /* To make our intenal PKCS #11 module work correctly with + * our database, we need to pass in the public key value for + * this dsa key. We have a netscape only CKA_ value to do this. + * Only send it to internal slots */ + if (publicValue == NULL) { + goto loser; + } + if (PK11_IsInternal(slot)) { + PK11_SETATTRS(attrs, CKA_NSS_DB, + publicValue->data, publicValue->len); + attrs++; + } + PK11_SETATTRS(attrs, CKA_SIGN, &cktrue, sizeof(CK_BBOOL)); + attrs++; + PK11_SETATTRS(attrs, CKA_SIGN_RECOVER, &cktrue, sizeof(CK_BBOOL)); + attrs++; + if (nickname) { + PK11_SETATTRS(attrs, CKA_LABEL, nickname->data, nickname->len); + attrs++; + } + ck_id = PK11_MakeIDFromPubKey(publicValue); + if (ck_id == NULL) { + goto loser; + } + PK11_SETATTRS(attrs, CKA_ID, ck_id->data, ck_id->len); + attrs++; + signedattr = attrs; + PK11_SETATTRS(attrs, CKA_PRIME, lpk->u.dsa.params.prime.data, + lpk->u.dsa.params.prime.len); + attrs++; + PK11_SETATTRS(attrs, CKA_SUBPRIME, lpk->u.dsa.params.subPrime.data, + lpk->u.dsa.params.subPrime.len); + attrs++; + PK11_SETATTRS(attrs, CKA_BASE, lpk->u.dsa.params.base.data, + lpk->u.dsa.params.base.len); + attrs++; + PK11_SETATTRS(attrs, CKA_VALUE, lpk->u.dsa.privateValue.data, + lpk->u.dsa.privateValue.len); + attrs++; + break; + case dhKey: + keyType = CKK_DH; + /* To make our intenal PKCS #11 module work correctly with + * our database, we need to pass in the public key value for + * this dh key. We have a netscape only CKA_ value to do this. + * Only send it to internal slots */ + if (PK11_IsInternal(slot)) { + PK11_SETATTRS(attrs, CKA_NSS_DB, + publicValue->data, publicValue->len); + attrs++; + } + PK11_SETATTRS(attrs, CKA_DERIVE, &cktrue, sizeof(CK_BBOOL)); + attrs++; + if (nickname) { + PK11_SETATTRS(attrs, CKA_LABEL, nickname->data, nickname->len); + attrs++; + } + ck_id = PK11_MakeIDFromPubKey(publicValue); + if (ck_id == NULL) { + goto loser; + } + PK11_SETATTRS(attrs, CKA_ID, ck_id->data, ck_id->len); + attrs++; + signedattr = attrs; + PK11_SETATTRS(attrs, CKA_PRIME, lpk->u.dh.prime.data, + lpk->u.dh.prime.len); + attrs++; + PK11_SETATTRS(attrs, CKA_BASE, lpk->u.dh.base.data, + lpk->u.dh.base.len); + attrs++; + PK11_SETATTRS(attrs, CKA_VALUE, lpk->u.dh.privateValue.data, + lpk->u.dh.privateValue.len); + attrs++; + break; + case ecKey: + keyType = CKK_EC; + if (lpk->u.ec.publicValue.len == 0) { + goto loser; + } + if (PK11_IsInternal(slot)) { + PK11_SETATTRS(attrs, CKA_NSS_DB, + lpk->u.ec.publicValue.data, + lpk->u.ec.publicValue.len); + attrs++; + } + PK11_SETATTRS(attrs, CKA_SIGN, (keyUsage & KU_DIGITAL_SIGNATURE) ? &cktrue : &ckfalse, + sizeof(CK_BBOOL)); + attrs++; + PK11_SETATTRS(attrs, CKA_SIGN_RECOVER, + (keyUsage & KU_DIGITAL_SIGNATURE) ? &cktrue + : &ckfalse, + sizeof(CK_BBOOL)); + attrs++; + PK11_SETATTRS(attrs, CKA_DERIVE, (keyUsage & KU_KEY_AGREEMENT) ? &cktrue : &ckfalse, + sizeof(CK_BBOOL)); + attrs++; + if (nickname) { + PK11_SETATTRS(attrs, CKA_LABEL, nickname->data, nickname->len); + attrs++; + } + ck_id = PK11_MakeIDFromPubKey(&lpk->u.ec.publicValue); + if (ck_id == NULL) { + goto loser; + } + PK11_SETATTRS(attrs, CKA_ID, ck_id->data, ck_id->len); + attrs++; + /* No signed attrs for EC */ + /* curveOID always is a copy of AlgorithmID.parameters. */ + PK11_SETATTRS(attrs, CKA_EC_PARAMS, lpk->u.ec.curveOID.data, + lpk->u.ec.curveOID.len); + attrs++; + PK11_SETATTRS(attrs, CKA_VALUE, lpk->u.ec.privateValue.data, + lpk->u.ec.privateValue.len); + attrs++; + PK11_SETATTRS(attrs, CKA_EC_POINT, lpk->u.ec.publicValue.data, + lpk->u.ec.publicValue.len); + attrs++; + break; + default: + PORT_SetError(SEC_ERROR_BAD_KEY); + goto loser; + } + templateCount = attrs - theTemplate; + PORT_Assert(templateCount <= sizeof(theTemplate) / sizeof(CK_ATTRIBUTE)); + if (lpk->keyType != ecKey) { + PORT_Assert(signedattr); + signedcount = attrs - signedattr; + for (ap = signedattr; signedcount; ap++, signedcount--) { + pk11_SignedToUnsigned(ap); + } + } + + rv = PK11_CreateNewObject(slot, CK_INVALID_HANDLE, + theTemplate, templateCount, isPerm, &objectID); + + /* create and return a SECKEYPrivateKey */ + if (rv == SECSuccess && privk != NULL) { + *privk = PK11_MakePrivKey(slot, lpk->keyType, !isPerm, objectID, wincx); + if (*privk == NULL) { + rv = SECFailure; + } + } +loser: + if (ck_id) { + SECITEM_ZfreeItem(ck_id, PR_TRUE); + } + return rv; +} + +SECStatus +PK11_ImportPrivateKeyInfoAndReturnKey(PK11SlotInfo *slot, + SECKEYPrivateKeyInfo *pki, SECItem *nickname, SECItem *publicValue, + PRBool isPerm, PRBool isPrivate, unsigned int keyUsage, + SECKEYPrivateKey **privk, void *wincx) +{ + SECStatus rv = SECFailure; + SECKEYRawPrivateKey *lpk = NULL; + const SEC_ASN1Template *keyTemplate, *paramTemplate; + void *paramDest = NULL; + PLArenaPool *arena = NULL; + + arena = PORT_NewArena(2048); + if (!arena) { + return SECFailure; + } + + /* need to change this to use RSA/DSA keys */ + lpk = (SECKEYRawPrivateKey *)PORT_ArenaZAlloc(arena, + sizeof(SECKEYRawPrivateKey)); + if (lpk == NULL) { + goto loser; + } + lpk->arena = arena; + + switch (SECOID_GetAlgorithmTag(&pki->algorithm)) { + case SEC_OID_PKCS1_RSA_ENCRYPTION: + prepare_rsa_priv_key_export_for_asn1(lpk); + keyTemplate = SECKEY_RSAPrivateKeyExportTemplate; + paramTemplate = NULL; + paramDest = NULL; + lpk->keyType = rsaKey; + break; + case SEC_OID_ANSIX9_DSA_SIGNATURE: + prepare_dsa_priv_key_export_for_asn1(lpk); + keyTemplate = SECKEY_DSAPrivateKeyExportTemplate; + paramTemplate = SECKEY_PQGParamsTemplate; + paramDest = &(lpk->u.dsa.params); + lpk->keyType = dsaKey; + break; + case SEC_OID_X942_DIFFIE_HELMAN_KEY: + if (!publicValue) { + goto loser; + } + prepare_dh_priv_key_export_for_asn1(lpk); + keyTemplate = SECKEY_DHPrivateKeyExportTemplate; + paramTemplate = NULL; + paramDest = NULL; + lpk->keyType = dhKey; + break; + case SEC_OID_ANSIX962_EC_PUBLIC_KEY: + prepare_ec_priv_key_export_for_asn1(lpk); + keyTemplate = SECKEY_ECPrivateKeyExportTemplate; + paramTemplate = NULL; + paramDest = NULL; + lpk->keyType = ecKey; + break; + + default: + keyTemplate = NULL; + paramTemplate = NULL; + paramDest = NULL; + break; + } + + if (!keyTemplate) { + goto loser; + } + + /* decode the private key and any algorithm parameters */ + rv = SEC_QuickDERDecodeItem(arena, lpk, keyTemplate, &pki->privateKey); + if (rv != SECSuccess) { + goto loser; + } + + if (lpk->keyType == ecKey) { + /* Convert length in bits to length in bytes. */ + lpk->u.ec.publicValue.len >>= 3; + + /* Always override curveOID, we're ignoring any given value. */ + rv = SECITEM_CopyItem(arena, &lpk->u.ec.curveOID, + &pki->algorithm.parameters); + if (rv != SECSuccess) { + goto loser; + } + } + + if (paramDest && paramTemplate) { + rv = SEC_ASN1DecodeItem(arena, paramDest, paramTemplate, + &(pki->algorithm.parameters)); + if (rv != SECSuccess) { + goto loser; + } + } + + rv = PK11_ImportAndReturnPrivateKey(slot, lpk, nickname, publicValue, isPerm, + isPrivate, keyUsage, privk, wincx); + +loser: + if (arena != NULL) { + PORT_FreeArena(arena, PR_TRUE); + } + + return rv; +} + +SECStatus +PK11_ImportPrivateKeyInfo(PK11SlotInfo *slot, SECKEYPrivateKeyInfo *pki, + SECItem *nickname, SECItem *publicValue, PRBool isPerm, + PRBool isPrivate, unsigned int keyUsage, void *wincx) +{ + return PK11_ImportPrivateKeyInfoAndReturnKey(slot, pki, nickname, + publicValue, isPerm, isPrivate, keyUsage, NULL, wincx); +} + +SECItem * +PK11_ExportDERPrivateKeyInfo(SECKEYPrivateKey *pk, void *wincx) +{ + SECKEYPrivateKeyInfo *pki = PK11_ExportPrivKeyInfo(pk, wincx); + SECItem *derPKI; + + if (!pki) { + return NULL; + } + derPKI = SEC_ASN1EncodeItem(NULL, NULL, pki, + SECKEY_PrivateKeyInfoTemplate); + SECKEY_DestroyPrivateKeyInfo(pki, PR_TRUE); + return derPKI; +} + +static PRBool +ReadAttribute(SECKEYPrivateKey *key, CK_ATTRIBUTE_TYPE type, + PLArenaPool *arena, SECItem *output) +{ + SECStatus rv = PK11_ReadAttribute(key->pkcs11Slot, key->pkcs11ID, type, + arena, output); + return rv == SECSuccess; +} + +/* + * The caller is responsible for freeing the return value by passing it to + * SECKEY_DestroyPrivateKeyInfo(..., PR_TRUE). + */ +SECKEYPrivateKeyInfo * +PK11_ExportPrivKeyInfo(SECKEYPrivateKey *pk, void *wincx) +{ + /* PrivateKeyInfo version (always zero) */ + const unsigned char pkiVersion = 0; + /* RSAPrivateKey version (always zero) */ + const unsigned char rsaVersion = 0; + /* ECPrivateKey version (always one) */ + const unsigned char ecVersion = 1; + PLArenaPool *arena = NULL; + SECKEYRawPrivateKey rawKey; + SECKEYPrivateKeyInfo *pki; + SECItem *encoded; + const SEC_ASN1Template *keyTemplate; + SECStatus rv; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!arena) { + goto loser; + } + memset(&rawKey, 0, sizeof(rawKey)); + rawKey.keyType = pk->keyType; + pki = PORT_ArenaZNew(arena, SECKEYPrivateKeyInfo); + if (!pki) { + goto loser; + } + + switch (pk->keyType) { + case rsaKey: { + rawKey.u.rsa.version.type = siUnsignedInteger; + rawKey.u.rsa.version.data = (unsigned char *)PORT_ArenaAlloc(arena, 1); + if (!rawKey.u.rsa.version.data) { + goto loser; + } + + rawKey.u.rsa.version.data[0] = rsaVersion; + rawKey.u.rsa.version.len = 1; + + /* Read the component attributes of the private key */ + prepare_rsa_priv_key_export_for_asn1(&rawKey); + if (!ReadAttribute(pk, CKA_MODULUS, arena, &rawKey.u.rsa.modulus) || + !ReadAttribute(pk, CKA_PUBLIC_EXPONENT, arena, + &rawKey.u.rsa.publicExponent) || + !ReadAttribute(pk, CKA_PRIVATE_EXPONENT, arena, + &rawKey.u.rsa.privateExponent) || + !ReadAttribute(pk, CKA_PRIME_1, arena, &rawKey.u.rsa.prime1) || + !ReadAttribute(pk, CKA_PRIME_2, arena, &rawKey.u.rsa.prime2) || + !ReadAttribute(pk, CKA_EXPONENT_1, arena, + &rawKey.u.rsa.exponent1) || + !ReadAttribute(pk, CKA_EXPONENT_2, arena, + &rawKey.u.rsa.exponent2) || + !ReadAttribute(pk, CKA_COEFFICIENT, arena, + &rawKey.u.rsa.coefficient)) { + goto loser; + } + + keyTemplate = SECKEY_RSAPrivateKeyExportTemplate; + + rv = SECOID_SetAlgorithmID(arena, &pki->algorithm, SEC_OID_PKCS1_RSA_ENCRYPTION, NULL); + if (rv != SECSuccess) { + goto loser; + } + + } break; + case ecKey: { + rawKey.u.ec.version.type = siUnsignedInteger; + rawKey.u.ec.version.data = (unsigned char *)PORT_ArenaAlloc(arena, 1); + if (!rawKey.u.ec.version.data) { + goto loser; + } + rawKey.u.ec.version.data[0] = ecVersion; + rawKey.u.ec.version.len = 1; + + SECItem curveOID; + /* Read the component attributes of the private key */ + prepare_ec_priv_key_export_for_asn1(&rawKey); + if (!ReadAttribute(pk, CKA_VALUE, arena, + &rawKey.u.ec.privateValue) || + !ReadAttribute(pk, CKA_EC_PARAMS, arena, &curveOID)) { + goto loser; + } + if (!ReadAttribute(pk, CKA_EC_POINT, arena, + &rawKey.u.ec.publicValue)) { + SECKEYPublicKey *pubk = SECKEY_ConvertToPublicKey(pk); + if (pubk == NULL) + goto loser; + rv = SECITEM_CopyItem(arena, &rawKey.u.ec.publicValue, &pubk->u.ec.publicValue); + SECKEY_DestroyPublicKey(pubk); + if (rv != SECSuccess) { + goto loser; + } + } + + keyTemplate = SECKEY_ECPrivateKeyExportTemplate; + /* Convert length in bytes to length in bits. */ + rawKey.u.ec.publicValue.len <<= 3; + + rv = SECOID_SetAlgorithmID(arena, &pki->algorithm, SEC_OID_ANSIX962_EC_PUBLIC_KEY, &curveOID); + if (rv != SECSuccess) { + goto loser; + } + + } break; + default: { + PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); + goto loser; + } + } + + encoded = SEC_ASN1EncodeItem(arena, &pki->privateKey, &rawKey, keyTemplate); + if (!encoded) { + goto loser; + } + pki->version.type = siUnsignedInteger; + pki->version.data = (unsigned char *)PORT_ArenaAlloc(arena, 1); + if (!pki->version.data) { + goto loser; + } + pki->version.data[0] = pkiVersion; + pki->version.len = 1; + pki->arena = arena; + + return pki; + +loser: + if (arena) { + PORT_FreeArena(arena, PR_TRUE); + } + return NULL; +} |