/* NSS libncrypto implementation. Copyright 2010 Red Hat, Inc. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Red Hat author: Miloslav Trmač */ #include #include #include #include #include #include #include #include /* Global state */ /* FIXME: Only init NSS/connect to the daemon when actually necessary. */ static pthread_mutex_t refcount_mutex = PTHREAD_MUTEX_INITIALIZER; static size_t refcount; static CK_RV ncr_open_locked (void) { if (refcount == 0) { if (NSS_NoDB_Init (NULL) != SECSuccess) return CKR_GENERAL_ERROR; } refcount++; return CKR_OK; } CK_RV ncr_open (void) { CK_RV res; if (pthread_mutex_lock (&refcount_mutex) != 0) return CKR_GENERAL_ERROR; res = ncr_open_locked (); if (pthread_mutex_unlock (&refcount_mutex) != 0) return CKR_GENERAL_ERROR; return res; } static CK_RV ensure_ncr_is_open (void) { CK_RV res; if (pthread_mutex_lock (&refcount_mutex) != 0) return CKR_GENERAL_ERROR; if (refcount != 0) res = CKR_OK; else res = ncr_open_locked (); if (pthread_mutex_unlock (&refcount_mutex) != 0) return CKR_GENERAL_ERROR; return res; } CK_RV ncr_close (void) { CK_RV res; if (pthread_mutex_lock (&refcount_mutex) != 0) return CKR_GENERAL_ERROR; if (refcount == 0) res = CKR_GENERAL_ERROR; else { refcount--; res = CKR_OK; if (refcount == 0) { if (NSS_Shutdown () != SECSuccess) res = CKR_GENERAL_ERROR; } } if (pthread_mutex_unlock (&refcount_mutex) != 0) return CKR_GENERAL_ERROR; return res; } /* Asymmetric keys */ struct ncr_public_key { SECKEYPublicKey *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) { struct ncr_public_key *k; k = malloc (sizeof (*k)); if (k == NULL) return CKR_HOST_MEMORY; k->key = SECKEY_ImportDERPublicKey((SECItem *)der_key, type); if (k->key == NULL) { free (k); return CKR_GENERAL_ERROR; } *key = k; return CKR_OK; } /* The caller is responsible for freeing the arena and der_spki. */ static CK_RV public_key_export (struct ncr_public_key *key, void **der_key, size_t *der_key_size, PRArenaPool **arena_ptr, SECItem **der_spki_ptr) { struct subject_pub_key_info { SECAlgorithmID algorithm; SECItem pub_key; }; static const SEC_ASN1Template asn1_template[] = { { SEC_ASN1_SEQUENCE, 0, NULL, sizeof (struct subject_pub_key_info) }, { SEC_ASN1_INLINE, offsetof (struct subject_pub_key_info, algorithm), SECOID_AlgorithmIDTemplate, 0, }, { SEC_ASN1_BIT_STRING, offsetof (struct subject_pub_key_info, pub_key), NULL, 0, }, { 0, 0, NULL, 0 } }; struct subject_pub_key_info der_output; PRArenaPool *arena; SECItem *der_spki; CK_RV res; g_return_val_if_fail (key != NULL, CKR_ARGUMENTS_BAD); der_spki = SECKEY_EncodeDERSubjectPublicKeyInfo (key->key); if (der_spki == NULL) return CKR_GENERAL_ERROR; /* 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 err_der_spki; } if (SEC_QuickDERDecodeItem (arena, &der_output, asn1_template, der_spki) != SECSuccess) { res = CKR_GENERAL_ERROR; goto err_arena; } *der_key = der_output.pub_key.data; /* der_output.pub_key is encoded as BIT_STRING, so pub_key.len is a number of _bits_. */ *der_key_size = (der_output.pub_key.len / 8 + (der_output.pub_key.len % 8 != 0)); *arena_ptr = arena; *der_spki_ptr = der_spki; return CKR_OK; err_arena: PORT_FreeArena (arena, PR_FALSE); err_der_spki: SECITEM_FreeItem (der_spki, PR_TRUE); return res; } CK_RV ncr_public_key_create (struct ncr_public_key **key, CK_KEY_TYPE type, const void *der, size_t der_size) { CK_RV res; SECItem der_key; res = ensure_ncr_is_open (); if (res != CKR_OK) return res; g_return_val_if_fail (key != NULL, CKR_ARGUMENTS_BAD); g_return_val_if_fail (der != NULL, CKR_ARGUMENTS_BAD); der_key.data = (void *)der; der_key.len = der_size; return public_key_create (key, type, &der_key); } CK_RV ncr_public_key_export (struct ncr_public_key *key, void *dest, size_t *dest_size_ptr) { PRArenaPool *arena; SECItem *der_spki; void *der_key; size_t der_key_size; CK_RV res; res = ensure_ncr_is_open (); if (res != CKR_OK) return res; g_return_val_if_fail (dest_size_ptr != NULL, CKR_ARGUMENTS_BAD); res = public_key_export (key, &der_key, &der_key_size, &arena, &der_spki); if (res != CKR_OK) return res; if (dest == NULL) { *dest_size_ptr = der_key_size; res = CKR_OK; goto end; } if (*dest_size_ptr < der_key_size) { *dest_size_ptr = der_key_size; res = CKR_BUFFER_TOO_SMALL; goto end; } *dest_size_ptr = der_key_size; memcpy (dest, der_key, der_key_size); res = CKR_OK; end: PORT_FreeArena (arena, PR_FALSE); SECITEM_FreeItem (der_spki, PR_TRUE); return res; } CK_RV ncr_public_key_destroy (struct ncr_public_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); /* Note: this leaks "key->key", which the function does not free (642767). */ SECKEY_DestroyPublicKey (key->key); free (key); return CKR_OK; } static CK_RV private_key_create (struct ncr_private_key **key, CK_KEY_TYPE type, bool sensitive, const SECItem *der_key, const SECItem *public_value) { 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 private_key_info der_input; PK11SlotInfo *slot; SECItem der_info, *der_res, key_item, iv_item, wrapped_item; SECOidTag alg_tag; SECStatus ss; PK11SymKey *wrapping_key; PK11Context *ctx; int wrapped_len; switch (type) { case CKK_RSA: alg_tag = SEC_OID_PKCS1_RSA_ENCRYPTION; break; default: g_return_val_if_reached (CKR_ARGUMENTS_BAD); } k = malloc (sizeof (*k)); if (k == NULL) return CKR_HOST_MEMORY; der_input.version.type = siUnsignedInteger; der_input.version.data = (void *)&zero; der_input.version.len = sizeof (zero); memset (&der_input.algorithm, 0, sizeof (der_input.algorithm)); if (SECOID_SetAlgorithmID (NULL, &der_input.algorithm, alg_tag, NULL) != SECSuccess) { free (k); return CKR_GENERAL_ERROR; } der_input.private_key = *der_key; der_info.data = NULL; der_info.len = 0; der_res = SEC_ASN1EncodeItem (NULL, &der_info, &der_input, private_key_info_asn1_template); SECOID_DestroyAlgorithmID (&der_input.algorithm, PR_FALSE); if (der_res == NULL) { free (k); return CKR_HOST_MEMORY; } slot = PK11_GetBestSlot (CKM_AES_CBC_PAD, NULL); if (slot == NULL) goto err_der_info; 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) goto err_slot; iv_item.data = (void *)wrap_iv; iv_item.len = sizeof (wrap_iv); ctx = PK11_CreateContextBySymKey (CKM_AES_CBC_PAD, CKA_ENCRYPT, wrapping_key, &iv_item); if (ctx == NULL) goto err_wrapping_key; memset (&wrapped_item, 0, sizeof (wrapped_item)); if (SECITEM_AllocItem (NULL, &wrapped_item, der_info.len + 16) == NULL) { PK11_DestroyContext (ctx, PR_TRUE); goto err_wrapping_key; } if (PK11_CipherOp (ctx, wrapped_item.data, &wrapped_len, wrapped_item.len, der_info.data, der_info.len) != SECSuccess) { PK11_DestroyContext (ctx, PR_TRUE); goto err_wrapped_item; } /* C_EncryptFinal is only available through this function. "Nice.". */ ss = PK11_DigestFinal (ctx, wrapped_item.data + wrapped_len, &wrapped_item.len, wrapped_item.len - wrapped_len); PK11_DestroyContext (ctx, PR_TRUE); if (ss != SECSuccess) goto err_wrapped_item; wrapped_item.len += wrapped_len; k->key = PK11_UnwrapPrivKey (slot, wrapping_key, CKM_AES_CBC_PAD, &iv_item, &wrapped_item, NULL, (SECItem *)public_value, PR_FALSE /* token */, PR_FALSE /* sensitive */, type, NULL, 0, NULL); SECITEM_ZfreeItem (&wrapped_item, PR_FALSE); PK11_FreeSymKey (wrapping_key); PK11_FreeSlot (slot); PORT_Free (der_info.data); if (k->key == NULL) goto err_k; k->sensitive = sensitive; *key = k; return CKR_OK; err_wrapped_item: SECITEM_ZfreeItem (&wrapped_item, PR_FALSE); err_wrapping_key: PK11_FreeSymKey (wrapping_key); err_slot: PK11_FreeSlot (slot); err_der_info: PORT_Free (der_info.data); err_k: free (k); return CKR_GENERAL_ERROR; } /* 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, _Bool sensitive, const void *der, size_t der_size, const void *public_value, size_t public_value_size) { SECItem der_key; SECItem public; CK_RV res; res = ensure_ncr_is_open (); if (res != CKR_OK) return res; g_return_val_if_fail (key != NULL, CKR_ARGUMENTS_BAD); g_return_val_if_fail (der != NULL, CKR_ARGUMENTS_BAD); der_key.data = (void *)der; der_key.len = der_size; public.data = (void *)public_value; public.len = public_value_size; 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 ncr_private_key_destroy (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); SECKEY_DestroyPrivateKey (key->key); free (key); return CKR_OK; } /* RSA keys */ struct rsa_public_key { SECItem modulus, public_exponent; }; static const SEC_ASN1Template rsa_public_key_asn1_template[] = { { SEC_ASN1_SEQUENCE, 0, NULL, sizeof (struct rsa_public_key) }, #define INT(X) { \ SEC_ASN1_INTEGER, offsetof (struct rsa_public_key, X), NULL, 0, \ } INT (modulus), INT (public_exponent), #undef INT { 0, 0, NULL, 0 } }; CK_RV ncr_public_key_create_rsa (struct ncr_public_key **key, const void *modulus, size_t modulus_size, const void *public_exponent, size_t public_exponent_size) { struct rsa_public_key der_input; SECItem der_key; CK_RV res; res = ensure_ncr_is_open (); if (res != CKR_OK) return res; g_return_val_if_fail (modulus != NULL, CKR_ARGUMENTS_BAD); g_return_val_if_fail (public_exponent != NULL, CKR_ARGUMENTS_BAD); #define INT(X) \ der_input.X.type = siUnsignedInteger; \ der_input.X.data = (void *)X; \ der_input.X.len = X##_size; INT (modulus); INT (public_exponent); #undef INT der_key.data = NULL; der_key.len = 0; if (SEC_ASN1EncodeItem(NULL, &der_key, &der_input, rsa_public_key_asn1_template) == NULL) return CKR_HOST_MEMORY; res = public_key_create (key, CKK_RSA, &der_key); PORT_Free (der_key.data); return res; } 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) { struct rsa_public_key der_output; PRArenaPool *arena; SECItem *der_spki, der_key_item; void *der_key; size_t der_key_size; CK_RV res; res = ensure_ncr_is_open (); if (res != CKR_OK) return res; g_return_val_if_fail (modulus_size_ptr != NULL, CKR_ARGUMENTS_BAD); g_return_val_if_fail (public_exponent_size_ptr != NULL, CKR_ARGUMENTS_BAD); res = public_key_export (key, &der_key, &der_key_size, &arena, &der_spki); if (res != CKR_OK) return res; der_key_item.data = der_key; der_key_item.len = der_key_size; if (der_key_item.len != der_key_size) { res = CKR_GENERAL_ERROR; goto end; } /* Setting type to siUnsignedInteger requests removal of leading zeroes. */ der_output.modulus.type = siUnsignedInteger; der_output.public_exponent.type = siUnsignedInteger; if (SEC_QuickDERDecodeItem (arena, &der_output, rsa_public_key_asn1_template, &der_key_item) != SECSuccess) { res = CKR_GENERAL_ERROR; goto end; } if (modulus == NULL || public_exponent == NULL) { *modulus_size_ptr = der_output.modulus.len; *public_exponent_size_ptr = der_output.public_exponent.len; res = CKR_OK; goto end; } res = CKR_OK; if (*modulus_size_ptr < der_output.modulus.len) { *modulus_size_ptr = der_output.modulus.len; res = CKR_BUFFER_TOO_SMALL; } if (*public_exponent_size_ptr < der_output.public_exponent.len) { *public_exponent_size_ptr = der_output.public_exponent.len; res = CKR_BUFFER_TOO_SMALL; } if (res != CKR_OK) goto end; *modulus_size_ptr = der_output.modulus.len; *public_exponent_size_ptr = der_output.public_exponent.len; memcpy (modulus, der_output.modulus.data, der_output.modulus.len); memcpy (public_exponent, der_output.public_exponent.data, der_output.public_exponent.len); end: PORT_FreeArena (arena, PR_FALSE); SECITEM_FreeItem (der_spki, PR_TRUE); return res; } 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, const void *private_exponent, size_t private_exponent_size, const void *prime_1, size_t prime_1_size, const void *prime_2, size_t prime_2_size, const void *exponent_1, size_t exponent_1_size, const void *exponent_2, size_t exponent_2_size, const void *coefficient, size_t coefficient_size) { struct rsa_pub_key { SECItem version; SECItem modulus, public_exponent, private_exponent, prime_1, prime_2; SECItem exponent_1, exponent_2, coefficient; }; static const SEC_ASN1Template asn1_template[] = { { SEC_ASN1_SEQUENCE, 0, NULL, sizeof (struct rsa_pub_key) }, #define INT(X) { SEC_ASN1_INTEGER, offsetof (struct rsa_pub_key, X), NULL, 0, } INT (version), INT (modulus), INT (public_exponent), INT (private_exponent), INT (prime_1), INT (prime_2), INT (exponent_1), INT (exponent_2), INT (coefficient), #undef INT { 0, 0, NULL, 0 } }; static const uint8_t zero; /* = 0; */ struct rsa_pub_key der_input; SECItem der_key; CK_RV res; res = ensure_ncr_is_open (); if (res != CKR_OK) return res; g_return_val_if_fail (modulus != NULL, CKR_ARGUMENTS_BAD); g_return_val_if_fail (public_exponent != NULL, CKR_ARGUMENTS_BAD); der_input.version.type = siUnsignedInteger; der_input.version.data = (void *)&zero; der_input.version.len = sizeof (zero); #define INT(X) \ der_input.X.type = siUnsignedInteger; \ der_input.X.data = (void *)X; \ der_input.X.len = X##_size; INT (modulus); INT (public_exponent); INT (private_exponent); INT (prime_1); INT (prime_2); INT (exponent_1); INT (exponent_2); INT (coefficient); #undef INT der_key.data = NULL; der_key.len = 0; if (SEC_ASN1EncodeItem (NULL, &der_key, &der_input, asn1_template) == NULL) return CKR_HOST_MEMORY; res = private_key_create (key, CKK_RSA, sensitive, &der_key, &der_input.modulus); PORT_Free (der_key.data); return res; } /* Asymmetric operations */ CK_RV ncr_public_key_encrypt (CK_MECHANISM_TYPE mech, struct ncr_public_key *key, void *dest, size_t *dest_size_ptr, const void *src, size_t src_size) { size_t dest_size; SECStatus ss; 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); g_return_val_if_fail (dest_size_ptr != NULL, CKR_ARGUMENTS_BAD); /* This is correct for RSA, at least. */ dest_size = SECKEY_SignatureLen (key->key); if (dest_size == 0) return CKR_GENERAL_ERROR; if (dest == NULL) { *dest_size_ptr = dest_size; return CKR_OK; } if (*dest_size_ptr < dest_size) { *dest_size_ptr = dest_size; return CKR_BUFFER_TOO_SMALL; } *dest_size_ptr = dest_size; g_return_val_if_fail (src != NULL || src_size == 0, CKR_ARGUMENTS_BAD); switch (mech) { case CKM_RSA_PKCS: ss = PK11_PubEncryptPKCS1 (key->key, dest, (void *)src, src_size, NULL); break; case CKM_RSA_X_509: ss = PK11_PubEncryptRaw (key->key, dest, (void *)src, src_size, NULL); break; default: g_return_val_if_reached (CKR_MECHANISM_INVALID); } if (ss != SECSuccess) return CKR_GENERAL_ERROR; return CKR_OK; } CK_RV ncr_private_key_decrypt (CK_MECHANISM_TYPE mech, struct ncr_private_key *key, void *dest, size_t *dest_size_ptr, const void *src, size_t src_size) { unsigned out_len; SECStatus ss; 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); g_return_val_if_fail (dest_size_ptr != NULL, CKR_ARGUMENTS_BAD); g_return_val_if_fail (src != NULL || src_size == 0, CKR_ARGUMENTS_BAD); /* Don't change the value if the PrivDecrypt function doesn't touch it. */ out_len = *dest_size_ptr; switch (mech) { case CKM_RSA_PKCS: ss = PK11_PrivDecryptPKCS1 (key->key, dest, &out_len, *dest_size_ptr, (void *)src, src_size); break; case CKM_RSA_X_509: ss = PK11_PrivDecryptRaw (key->key, dest, &out_len, *dest_size_ptr, (void *)src, src_size); break; default: g_return_val_if_reached (CKR_MECHANISM_INVALID); } *dest_size_ptr = out_len; if (ss != SECSuccess) return CKR_GENERAL_ERROR; return CKR_OK; } CK_RV ncr_private_key_sign (CK_MECHANISM_TYPE mech, struct ncr_private_key *key, void *dest, size_t *dest_size_ptr, const void *src, size_t src_size) { size_t dest_size; SECItem src_item, dest_item; 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); g_return_val_if_fail (dest_size_ptr != NULL, CKR_ARGUMENTS_BAD); g_return_val_if_fail (mech == PK11_MapSignKeyType (key->key->keyType), CKR_MECHANISM_INVALID); dest_size = PK11_SignatureLen (key->key); if (dest_size == 0) return CKR_GENERAL_ERROR; if (dest == NULL) { *dest_size_ptr = dest_size; return CKR_OK; } if (*dest_size_ptr < dest_size) { *dest_size_ptr = dest_size; return CKR_BUFFER_TOO_SMALL; } g_return_val_if_fail (src != NULL || src_size == 0, CKR_ARGUMENTS_BAD); src_item.data = (void *)src; src_item.len = src_size; dest_item.data = dest; dest_item.len = *dest_size_ptr; if (PK11_Sign (key->key, &dest_item, &src_item) != SECSuccess) return CKR_GENERAL_ERROR; *dest_size_ptr = dest_item.len; return CKR_OK; } CK_RV ncr_public_key_verify (CK_MECHANISM_TYPE mech, struct ncr_public_key *key, const void *signature, size_t signature_size, const void *src, size_t src_size) { SECItem sig_item, src_item; 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); g_return_val_if_fail (signature != NULL || signature_size == 0, CKR_ARGUMENTS_BAD); g_return_val_if_fail (src != NULL || src_size == 0, CKR_ARGUMENTS_BAD); g_return_val_if_fail (mech == PK11_MapSignKeyType (key->key->keyType), CKR_MECHANISM_INVALID); sig_item.data = (void *)signature; sig_item.len = signature_size; src_item.data = (void *)src; src_item.len = src_size; return PK11_Verify (key->key, &sig_item, &src_item, NULL) == SECSuccess ? CKR_OK : CKR_GENERAL_ERROR; }