diff options
Diffstat (limited to 'security/nss/lib/cryptohi/seckey.c')
-rw-r--r-- | security/nss/lib/cryptohi/seckey.c | 96 |
1 files changed, 91 insertions, 5 deletions
diff --git a/security/nss/lib/cryptohi/seckey.c b/security/nss/lib/cryptohi/seckey.c index 173096050..fa13bc33e 100644 --- a/security/nss/lib/cryptohi/seckey.c +++ b/security/nss/lib/cryptohi/seckey.c @@ -1211,6 +1211,37 @@ SECKEY_CopyPublicKey(const SECKEYPublicKey *pubk) return NULL; } +/* + * 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) { @@ -1218,6 +1249,8 @@ SECKEY_ConvertToPublicKey(SECKEYPrivateKey *privk) PLArenaPool *arena; CERTCertificate *cert; SECStatus rv; + CK_OBJECT_HANDLE pubKeyHandle; + SECItem decodedPoint; /* * First try to look up the cert. @@ -1248,11 +1281,47 @@ SECKEY_ConvertToPublicKey(SECKEYPrivateKey *privk) switch (privk->keyType) { case nullKey: - case dhKey: - case dsaKey: /* 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); @@ -1263,7 +1332,6 @@ SECKEY_ConvertToPublicKey(SECKEYPrivateKey *privk) if (rv != SECSuccess) break; return pubk; - break; case ecKey: rv = PK11_ReadAttribute(privk->pkcs11Slot, privk->pkcs11ID, CKA_EC_PARAMS, arena, &pubk->u.ec.DEREncodedParams); @@ -1273,7 +1341,23 @@ SECKEY_ConvertToPublicKey(SECKEYPrivateKey *privk) rv = PK11_ReadAttribute(privk->pkcs11Slot, privk->pkcs11ID, CKA_EC_POINT, arena, &pubk->u.ec.publicValue); if (rv != SECSuccess || pubk->u.ec.publicValue.len == 0) { - break; + 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; @@ -1281,7 +1365,9 @@ SECKEY_ConvertToPublicKey(SECKEYPrivateKey *privk) break; } - PORT_FreeArena(arena, PR_FALSE); + /* must use Destroy public key here, because some paths create temporary + * PKCS #11 objects which need to be freed */ + SECKEY_DestroyPublicKey(pubk); return NULL; } |