| |
| /* 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 "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_NETSCAPE_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_NETSCAPE_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_NETSCAPE_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++; |
| 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++; |
| signedattr = attrs; |
| /* 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)); |
| PORT_Assert(signedattr != NULL); |
| signedcount = attrs - signedattr; |
| |
| for (ap = signedattr; signedcount; ap++, signedcount--) { |
| pk11_SignedToUnsigned(ap); |
| } |
| |
| rv = PK11_CreateNewObject(slot, CK_INVALID_SESSION, |
| 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; |
| PLArenaPool *arena = NULL; |
| SECKEYRawPrivateKey rawKey; |
| SECKEYPrivateKeyInfo *pki; |
| SECItem *encoded; |
| SECStatus rv; |
| |
| if (pk->keyType != rsaKey) { |
| PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); |
| goto loser; |
| } |
| |
| arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
| if (!arena) { |
| goto loser; |
| } |
| memset(&rawKey, 0, sizeof(rawKey)); |
| rawKey.keyType = pk->keyType; |
| 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; |
| } |
| |
| pki = PORT_ArenaZNew(arena, SECKEYPrivateKeyInfo); |
| if (!pki) { |
| goto loser; |
| } |
| encoded = SEC_ASN1EncodeItem(arena, &pki->privateKey, &rawKey, |
| SECKEY_RSAPrivateKeyExportTemplate); |
| if (!encoded) { |
| goto loser; |
| } |
| rv = SECOID_SetAlgorithmID(arena, &pki->algorithm, |
| SEC_OID_PKCS1_RSA_ENCRYPTION, NULL); |
| if (rv != SECSuccess) { |
| 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; |
| } |