From 6e919f9347656ba0ae390038679ac942ad6a9964 Mon Sep 17 00:00:00 2001 From: Miloslav Trmač Date: Thu, 25 Nov 2010 23:11:03 +0100 Subject: Add private key extraction support --- include/ncrypto/ncrypto.h | 8 +- lib/ncrypto_nss.c | 223 +++++++++++++++++++++++++++++++++++++++------- 2 files changed, 199 insertions(+), 32 deletions(-) diff --git a/include/ncrypto/ncrypto.h b/include/ncrypto/ncrypto.h index 9f5a3f4..47ca5df 100644 --- a/include/ncrypto/ncrypto.h +++ b/include/ncrypto/ncrypto.h @@ -76,10 +76,14 @@ CK_RV ncr_public_key_create (struct ncr_public_key **key, CK_KEY_TYPE type, CK_RV ncr_public_key_export (struct ncr_public_key *key, void *dest, size_t *dest_size_ptr); CK_RV ncr_public_key_destroy (struct ncr_public_key *key); +/* "Sensitive" corresponds to CKA_SENSITIVE. */ CK_RV ncr_private_key_create (struct ncr_private_key **key, CK_KEY_TYPE type, - const void *der, size_t der_size, + _Bool sensitive, const void *der, size_t der_size, const void *public_value, size_t public_value_size); +CK_RV ncr_private_key_set_sensitive (struct ncr_private_key *key); +CK_RV ncr_private_key_export (struct ncr_private_key *key, void *dest, + size_t *dest_size_ptr); CK_RV ncr_private_key_destroy (struct ncr_private_key *key); /* RSA keys */ @@ -92,7 +96,7 @@ CK_RV ncr_public_key_export_rsa (struct ncr_public_key *key, void *modulus, size_t *modulus_size_ptr, void *public_exponent, size_t *public_exponent_size_ptr); -CK_RV ncr_private_key_create_rsa (struct ncr_private_key **key, +CK_RV ncr_private_key_create_rsa (struct ncr_private_key **key, _Bool sensitive, const void *modulus, size_t modulus_size, const void *public_exponent, size_t public_exponent_size, diff --git a/lib/ncrypto_nss.c b/lib/ncrypto_nss.c index d6ded4f..48d2e47 100644 --- a/lib/ncrypto_nss.c +++ b/lib/ncrypto_nss.c @@ -27,6 +27,7 @@ Red Hat author: Miloslav Trmač */ #include #include +#include #include #include @@ -122,8 +123,32 @@ struct ncr_public_key struct ncr_private_key { SECKEYPrivateKey *key; + bool sensitive; }; +struct private_key_info +{ + SECItem version; + SECAlgorithmID algorithm; + SECItem private_key; +}; + +static const SEC_ASN1Template private_key_info_asn1_template[] = + { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof (struct private_key_info) }, + { SEC_ASN1_INTEGER, offsetof (struct private_key_info, version), NULL, 0 }, + { + SEC_ASN1_INLINE, offsetof (struct private_key_info, algorithm), + SECOID_AlgorithmIDTemplate, 0 + }, + { + SEC_ASN1_OCTET_STRING, offsetof (struct private_key_info, private_key), + NULL, 0 + }, + /* Attributes are optional and we never use them. */ + { 0, 0, NULL, 0 } + }; + static CK_RV public_key_create (struct ncr_public_key **key, CK_KEY_TYPE type, const SECItem *der_key) @@ -295,36 +320,15 @@ ncr_public_key_destroy (struct ncr_public_key *key) static CK_RV private_key_create (struct ncr_private_key **key, CK_KEY_TYPE type, - const SECItem *der_key, const SECItem *public_value) + bool sensitive, const SECItem *der_key, + const SECItem *public_value) { - struct priv_key_info - { - SECItem version; - SECAlgorithmID algorithm; - SECItem private_key; - }; - - static const SEC_ASN1Template asn1_template[] = - { - { SEC_ASN1_SEQUENCE, 0, NULL, sizeof (struct priv_key_info) }, - { SEC_ASN1_INTEGER, offsetof (struct priv_key_info, version), NULL, 0, }, - { - SEC_ASN1_INLINE, offsetof (struct priv_key_info, algorithm), - SECOID_AlgorithmIDTemplate, 0, - }, - { - SEC_ASN1_OCTET_STRING, offsetof (struct priv_key_info, private_key), - NULL, 0, - }, - /* Attributes are optional and we never use them. */ - { 0, 0, NULL, 0 } - }; static const uint8_t wrap_key[32]; /* = { 0, }; */ static const uint8_t wrap_iv[16]; /* = { 0, }; */ static const uint8_t zero; /* = 0; */ struct ncr_private_key *k; - struct priv_key_info der_input; + struct private_key_info der_input; PK11SlotInfo *slot; SECItem der_info, *der_res, key_item, iv_item, wrapped_item; SECOidTag alg_tag; @@ -361,7 +365,8 @@ private_key_create (struct ncr_private_key **key, CK_KEY_TYPE type, der_info.data = NULL; der_info.len = 0; - der_res = SEC_ASN1EncodeItem (NULL, &der_info, &der_input, asn1_template); + der_res = SEC_ASN1EncodeItem (NULL, &der_info, &der_input, + private_key_info_asn1_template); SECOID_DestroyAlgorithmID (&der_input.algorithm, PR_FALSE); @@ -423,6 +428,7 @@ private_key_create (struct ncr_private_key **key, CK_KEY_TYPE type, if (k->key == NULL) goto err_k; + k->sensitive = sensitive; *key = k; return CKR_OK; @@ -442,7 +448,7 @@ private_key_create (struct ncr_private_key **key, CK_KEY_TYPE type, /* FIXME: public_value should not be necessary, it is somewhere inside "der". */ CK_RV ncr_private_key_create (struct ncr_private_key **key, CK_KEY_TYPE type, - const void *der, size_t der_size, + _Bool sensitive, const void *der, size_t der_size, const void *public_value, size_t public_value_size) { SECItem der_key; @@ -460,7 +466,162 @@ ncr_private_key_create (struct ncr_private_key **key, CK_KEY_TYPE type, der_key.len = der_size; public.data = (void *)public_value; public.len = public_value_size; - return private_key_create (key, type, &der_key, &public); + return private_key_create (key, type, sensitive, &der_key, &public); +} + +CK_RV +ncr_private_key_set_sensitive (struct ncr_private_key *key) +{ + CK_RV res; + + res = ensure_ncr_is_open (); + if (res != CKR_OK) + return res; + + g_return_val_if_fail (key != NULL, CKR_KEY_HANDLE_INVALID); + key->sensitive = true; + return CKR_OK; +} + +CK_RV +ncr_private_key_export (struct ncr_private_key *key, void *dest, + size_t *dest_size_ptr) +{ + static const uint8_t wrap_key[32]; /* = { 0, }; */ + static const uint8_t wrap_iv[16]; /* = { 0, }; */ + + PK11SlotInfo *slot; + SECItem key_item, iv_item, wrapped_item, der_info; + PK11SymKey *wrapping_key; + PK11Context *ctx; + int der_info_len; + PRArenaPool *arena; + struct private_key_info der_output; + SECStatus ss; + CK_RV res; + + g_return_val_if_fail (key != NULL, CKR_KEY_HANDLE_INVALID); + g_return_val_if_fail (dest_size_ptr != NULL, CKR_ARGUMENTS_BAD); + + g_return_val_if_fail (!key->sensitive, CKR_ATTRIBUTE_SENSITIVE); + + slot = PK11_GetBestSlot (CKM_AES_CBC_PAD, NULL); + if (slot == NULL) + return CKR_GENERAL_ERROR; + + key_item.data = (void *)wrap_key; + key_item.len = sizeof (wrap_key); + wrapping_key = PK11_ImportSymKeyWithFlags (slot, CKM_AES_CBC_PAD, + PK11_OriginUnwrap, CKA_FLAGS_ONLY, + &key_item, + CKF_ENCRYPT | CKF_UNWRAP, + PR_FALSE /* isPerm */, NULL); + if (wrapping_key == NULL) + { + res = CKR_GENERAL_ERROR; + goto end_slot; + } + iv_item.data = (void *)wrap_iv; + iv_item.len = sizeof (wrap_iv); + + memset (&wrapped_item, 0, sizeof (wrapped_item)); + if (PK11_WrapPrivKey(slot, wrapping_key, key->key, CKM_AES_CBC_PAD, &iv_item, + &wrapped_item, NULL) != SECSuccess) + { + res = CKR_GENERAL_ERROR; + goto end_wrapping_key; + } + if (SECITEM_AllocItem (NULL, &wrapped_item, wrapped_item.len) == NULL) + { + res = CKR_HOST_MEMORY; + goto end_wrapping_key; + } + if (PK11_WrapPrivKey(slot, wrapping_key, key->key, CKM_AES_CBC_PAD, &iv_item, + &wrapped_item, NULL) != SECSuccess) + { + res = CKR_GENERAL_ERROR; + goto end_wrapped_item; + } + + ctx = PK11_CreateContextBySymKey (CKM_AES_CBC_PAD, CKA_DECRYPT, wrapping_key, + &iv_item); + if (ctx == NULL) + { + res = CKR_GENERAL_ERROR; + goto end_wrapped_item; + } + memset (&der_info, 0, sizeof (der_info)); + if (SECITEM_AllocItem (NULL, &der_info, wrapped_item.len) == NULL) + { + PK11_DestroyContext (ctx, PR_TRUE); + res = CKR_HOST_MEMORY; + goto end_wrapped_item; + } + if (PK11_CipherOp (ctx, der_info.data, &der_info_len, der_info.len, + wrapped_item.data, wrapped_item.len) != SECSuccess) + { + PK11_DestroyContext (ctx, PR_TRUE); + res = CKR_GENERAL_ERROR; + goto end_der_info; + } + /* C_DecryptFinal is only available through this function. "Nice.". */ + ss = PK11_DigestFinal (ctx, der_info.data + der_info_len, + &der_info.len, der_info.len - der_info_len); + PK11_DestroyContext (ctx, PR_TRUE); + if (ss != SECSuccess) + { + res = CKR_GENERAL_ERROR; + goto end_der_info; + } + der_info.len += der_info_len; + + /* Ugly... the PLArenaPool type is from NSPR, but NSS implementation accesses + memory only initialized through NSS's PORT_* */ + arena = PORT_NewArena (SEC_ASN1_DEFAULT_ARENA_SIZE); + if (arena == NULL) + { + res = CKR_HOST_MEMORY; + goto end_der_info; + } + + /* "type" is accessed by the decoder for ASN1_INTEGER */ + der_output.version.type = siUnsignedInteger; + if (SEC_QuickDERDecodeItem (arena, &der_output, + private_key_info_asn1_template, &der_info) + != SECSuccess) + { + res = CKR_GENERAL_ERROR; + goto end_arena; + } + + /* Should we validate the version and algorithm ID here? */ + + if (dest == NULL) + { + *dest_size_ptr = der_output.private_key.len; + return CKR_OK; + } + if (*dest_size_ptr < der_output.private_key.len) + { + *dest_size_ptr = der_output.private_key.len; + return CKR_BUFFER_TOO_SMALL; + } + *dest_size_ptr = der_output.private_key.len; + + memcpy (dest, der_output.private_key.data, der_output.private_key.len); + res = CKR_OK; + + end_arena: + PORT_FreeArena (arena, PR_TRUE); + end_der_info: + SECITEM_ZfreeItem (&der_info, PR_FALSE); + end_wrapped_item: + SECITEM_ZfreeItem (&wrapped_item, PR_FALSE); + end_wrapping_key: + PK11_FreeSymKey (wrapping_key); + end_slot: + PK11_FreeSlot (slot); + return res; } CK_RV @@ -608,8 +769,9 @@ ncr_public_key_export_rsa (struct ncr_public_key *key, void *modulus, } CK_RV -ncr_private_key_create_rsa (struct ncr_private_key **key, const void *modulus, - size_t modulus_size, const void *public_exponent, +ncr_private_key_create_rsa (struct ncr_private_key **key, _Bool sensitive, + const void *modulus, size_t modulus_size, + const void *public_exponent, size_t public_exponent_size, const void *private_exponent, size_t private_exponent_size, const void *prime_1, @@ -673,7 +835,8 @@ ncr_private_key_create_rsa (struct ncr_private_key **key, const void *modulus, if (SEC_ASN1EncodeItem (NULL, &der_key, &der_input, asn1_template) == NULL) return CKR_HOST_MEMORY; - res = private_key_create (key, CKK_RSA, &der_key, &der_input.modulus); + res = private_key_create (key, CKK_RSA, sensitive, &der_key, + &der_input.modulus); PORT_Free (der_key.data); return res; -- cgit