/* 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 #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; } /* Random numbers */ CK_RV ncr_get_random_bytes (void *dest, size_t size) { CK_RV res; g_return_val_if_fail (dest != NULL, CKR_ARGUMENTS_BAD); g_return_val_if_fail (size <= INT_MAX, CKR_ARGUMENTS_BAD); res = ensure_ncr_is_open (); if (res != CKR_OK) return res; if (PK11_GenerateRandom (dest, size) != SECSuccess) return CKR_GENERAL_ERROR; return CKR_OK; } /* 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, CERTSubjectPublicKeyInfo *spki) { struct ncr_public_key *k; k = malloc (sizeof (*k)); if (k == NULL) return CKR_HOST_MEMORY; k->key = SECKEY_ExtractPublicKey (spki); if (k->key == NULL) { free (k); return CKR_GENERAL_ERROR; } *key = k; return CKR_OK; } CK_RV ncr_public_key_create (struct ncr_public_key **key, const void *der, size_t der_size) { CERTSubjectPublicKeyInfo *spki; SECItem der_key; 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; spki = SECKEY_DecodeDERSubjectPublicKeyInfo (&der_key); if (spki == NULL) return CKR_GENERAL_ERROR; res = public_key_create (key, spki); SECKEY_DestroySubjectPublicKeyInfo (spki); return res; } CK_RV ncr_public_key_export (struct ncr_public_key *key, void *dest, size_t *dest_size_ptr) { SECItem *der; 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 (dest_size_ptr != NULL, CKR_ARGUMENTS_BAD); der = SECKEY_EncodeDERSubjectPublicKeyInfo (key->key); if (der == NULL) return CKR_GENERAL_ERROR; if (dest == NULL) { *dest_size_ptr = der->len; res = CKR_OK; goto end_der; } if (*dest_size_ptr < der->len) { *dest_size_ptr = der->len; res = CKR_BUFFER_TOO_SMALL; goto end_der; } *dest_size_ptr = der->len; memcpy (dest, der->data, der->len); res = CKR_OK; end_der: SECITEM_FreeItem (der, 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; } /* Create a private key based on a PKCS#8 privateKeyInfo DER_PKI. */ static CK_RV private_key_create (struct ncr_private_key **key, CK_KEY_TYPE type, bool sensitive, const SECItem *der_pki, const SECItem *public_value) { static const uint8_t wrap_key[32]; /* = { 0, }; */ static const uint8_t wrap_iv[16]; /* = { 0, }; */ struct ncr_private_key *k; PK11SlotInfo *slot; SECItem key_item, iv_item, wrapped_item; SECStatus ss; PK11SymKey *wrapping_key; PK11Context *ctx; int wrapped_len; k = malloc (sizeof (*k)); if (k == NULL) return CKR_HOST_MEMORY; 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) 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_pki->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_pki->data, der_pki->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 */, sensitive, type, NULL, 0, NULL); SECITEM_ZfreeItem (&wrapped_item, PR_FALSE); PK11_FreeSymKey (wrapping_key); PK11_FreeSlot (slot); 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_k: free (k); return CKR_GENERAL_ERROR; } /* Create a private key based on INPUT_PKI. PKI->version is ignored. */ static CK_RV private_key_create_components (struct ncr_private_key **key, CK_KEY_TYPE type, bool sensitive, const struct private_key_info *input_pki, const SECItem *public_value) { static const uint8_t zero; /* = 0; */ struct private_key_info pki; SECItem der_pki; CK_RV res; pki.version.type = siUnsignedInteger; pki.version.data = (void *)&zero; pki.version.len = sizeof (zero); pki.algorithm = input_pki->algorithm; pki.private_key = input_pki->private_key; der_pki.data = NULL; der_pki.len = 0; if (SEC_ASN1EncodeItem (NULL, &der_pki, &pki, private_key_info_asn1_template) == NULL) return CKR_HOST_MEMORY; res = private_key_create (key, type, sensitive, &der_pki, public_value); SECITEM_ZfreeItem (&der_pki, PR_FALSE); return res; } /* Set DER_PKI to a PKCS#8 privateKeyInfo describing KEY. */ static CK_RV private_key_export (struct ncr_private_key *key, PRArenaPool *arena, SECItem *der_pki) { 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; SECStatus ss; CK_RV res; g_return_val_if_fail (key != NULL, CKR_KEY_HANDLE_INVALID); 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 err_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 err_wrapping_key; } if (SECITEM_AllocItem (NULL, &wrapped_item, wrapped_item.len) == NULL) { res = CKR_HOST_MEMORY; goto err_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 err_wrapped_item; } ctx = PK11_CreateContextBySymKey (CKM_AES_CBC_PAD, CKA_DECRYPT, wrapping_key, &iv_item); if (ctx == NULL) { res = CKR_GENERAL_ERROR; goto err_wrapped_item; } memset (&der_info, 0, sizeof (der_info)); if (SECITEM_AllocItem (arena, &der_info, wrapped_item.len) == NULL) { PK11_DestroyContext (ctx, PR_TRUE); res = CKR_HOST_MEMORY; goto err_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 err_wrapped_item; } /* 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 err_wrapped_item; } der_info.len += der_info_len; *der_pki = der_info; res = CKR_OK; err_wrapped_item: SECITEM_ZfreeItem (&wrapped_item, PR_FALSE); err_wrapping_key: PK11_FreeSymKey (wrapping_key); err_slot: PK11_FreeSlot (slot); return res; } /* Fill PKI with info from KEY. */ static CK_RV private_key_export_components (struct ncr_private_key *key, PRArenaPool *arena, struct private_key_info *pki) { SECItem der_pki; CK_RV res; res = private_key_export (key, arena, &der_pki); if (res != CKR_OK) return res; /* "type" is accessed by the decoder for ASN1_INTEGER */ pki->version.type = siUnsignedInteger; if (SEC_QuickDERDecodeItem (arena, pki, private_key_info_asn1_template, &der_pki) != SECSuccess) return CKR_GENERAL_ERROR; if (pki->version.len != 1 || pki->version.data[0] != 0) return CKR_GENERAL_ERROR; return CKR_OK; } CK_RV ncr_private_key_set_sensitive (struct ncr_private_key *key) { static const CK_BBOOL true_value = CK_TRUE; CK_RV res; SECStatus ss; SECItem item; res = ensure_ncr_is_open (); if (res != CKR_OK) return res; g_return_val_if_fail (key != NULL, CKR_KEY_HANDLE_INVALID); item.data = (void *)&true_value; item.len = sizeof (true_value); ss = PK11_WriteRawAttribute (PK11_TypePrivKey, key->key, CKA_SENSITIVE, &item); if (ss != SECSuccess) return CKR_GENERAL_ERROR; key->sensitive = true; return CKR_OK; } CK_RV ncr_private_key_export (struct ncr_private_key *key, void *dest, size_t *dest_size_ptr) { PRArenaPool *arena; SECItem der_pki; 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); /* 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) return CKR_HOST_MEMORY; res = private_key_export (key, arena, &der_pki); if (res != CKR_OK) goto end; if (dest == NULL) { *dest_size_ptr = der_pki.len; res = CKR_OK; goto end; } if (*dest_size_ptr < der_pki.len) { *dest_size_ptr = der_pki.len; res = CKR_BUFFER_TOO_SMALL; goto end; } *dest_size_ptr = der_pki.len; memcpy (dest, der_pki.data, der_pki.len); res = CKR_OK; end: PORT_FreeArena (arena, PR_TRUE); 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; } static CK_RV key_pair_generate (struct ncr_public_key **public_key, struct ncr_private_key **private_key, CK_MECHANISM_TYPE mech, bool sensitive, void *params) { struct ncr_public_key *pub; struct ncr_private_key *priv; PK11SlotInfo *slot; g_return_val_if_fail (public_key != NULL, CKR_ARGUMENTS_BAD); g_return_val_if_fail (private_key != NULL, CKR_ARGUMENTS_BAD); pub = malloc (sizeof (*pub)); if (pub == NULL) return CKR_HOST_MEMORY; priv = malloc (sizeof (*priv)); if (priv == NULL) { free (pub); return CKR_HOST_MEMORY; } slot = PK11_GetBestSlot (mech, NULL); if (slot == NULL) goto err_priv; priv->key = PK11_GenerateKeyPair(slot, mech, params, &pub->key, PR_FALSE /* isPerm */, sensitive, NULL); PK11_FreeSlot (slot); if (priv->key == NULL) goto err_priv; priv->sensitive = sensitive; *public_key = pub; *private_key = priv; return CKR_OK; err_priv: free (priv); free (pub); return CKR_GENERAL_ERROR; } /* Multi-precision integers */ /* Validate SRC and use it to set up DEST for ASN.1 encoding */ static CK_RV mpi_create_SECItems_for_encoding (SECItem *dest, const struct ncr_mpi *src, size_t num) { size_t i; g_return_val_if_fail (src != NULL, CKR_ARGUMENTS_BAD); for (i = 0; i < num; i++) g_return_val_if_fail (src[i].data != NULL, CKR_ARGUMENTS_BAD); for (i = 0; i < num; i++) { dest[i].type = siUnsignedInteger; dest[i].data = src[i].data; dest[i].len = src[i].size; } return CKR_OK; } /* Handle of decoded SRC to DEST */ static CK_RV mpi_output_decoded_SECItems (struct ncr_mpi *dest, const SECItem *src, size_t num) { size_t i; CK_RV res; for (i = 0; i < num; i++) { if (dest[i].data == NULL) goto sizes_only; } res = CKR_OK; for (i = 0; i < num; i++) { if (dest[i].size < src[i].len) res = CKR_BUFFER_TOO_SMALL; dest[i].size = src[i].len; } if (res != CKR_OK) return res; for (i = 0; i < num; i++) memcpy (dest[i].data, src[i].data, src[i].len); return CKR_OK; sizes_only: for (i = 0; i < num; i++) dest[i].size = src[i].len; return CKR_OK; } /* RSA keys */ struct rsa_public_key { SECItem items[NCR_RSA_PUBLIC_NUM_MPIS]; }; 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, items) + (X) * sizeof (SECItem), NULL, \ 0 \ } INT (NCR_RSA_PUBLIC_MPI_MODULUS), INT (NCR_RSA_PUBLIC_MPI_PUBLIC_EXPONENT), #undef INT { 0, 0, NULL, 0 } }; struct rsa_private_key { SECItem version; SECItem items[NCR_RSA_PRIVATE_NUM_MPIS]; }; static const SEC_ASN1Template rsa_private_key_asn1_template[] = { { SEC_ASN1_SEQUENCE, 0, NULL, sizeof (struct rsa_private_key) }, { SEC_ASN1_INTEGER, offsetof (struct rsa_private_key, version), NULL, 0 }, #define INT(X) \ { \ SEC_ASN1_INTEGER, \ offsetof (struct rsa_private_key, items) + (X) * sizeof (SECItem), NULL, \ 0 \ } INT (NCR_RSA_PRIVATE_MPI_MODULUS), INT (NCR_RSA_PRIVATE_MPI_PUBLIC_EXPONENT), INT (NCR_RSA_PRIVATE_MPI_PRIVATE_EXPONENT), INT (NCR_RSA_PRIVATE_MPI_PRIME_1), INT (NCR_RSA_PRIVATE_MPI_PRIME_2), INT (NCR_RSA_PRIVATE_MPI_EXPONENT_1), INT (NCR_RSA_PRIVATE_MPI_EXPONENT_2), INT (NCR_RSA_PRIVATE_MPI_COEFFICIENT), #undef INT { 0, 0, NULL, 0 } }; static CK_RV rsa_validate_algorithm_id (const SECAlgorithmID *id) { static const uint8_t asn1_null[] = { SEC_ASN1_NULL, 0 }; static const SECItem asn1_null_item = { 0, (void *)&asn1_null, sizeof (asn1_null) }; const SECOidData *oid; oid = SECOID_FindOIDByTag (SEC_OID_PKCS1_RSA_ENCRYPTION); if (oid == NULL || !SECITEM_ItemsAreEqual(&id->algorithm, &oid->oid) || !SECITEM_ItemsAreEqual(&id->parameters, &asn1_null_item)) return CKR_GENERAL_ERROR; return CKR_OK; } CK_RV ncr_public_key_create_rsa (struct ncr_public_key **key, const struct ncr_mpi mpis[static NCR_RSA_PUBLIC_NUM_MPIS]) { struct rsa_public_key der_input; CERTSubjectPublicKeyInfo spki; CK_RV res; res = ensure_ncr_is_open (); if (res != CKR_OK) return res; res = mpi_create_SECItems_for_encoding (der_input.items, mpis, NCR_RSA_PUBLIC_NUM_MPIS); if (res != CKR_OK) return res; spki.subjectPublicKey.data = NULL; spki.subjectPublicKey.len = 0; if (SEC_ASN1EncodeItem (NULL, &spki.subjectPublicKey, &der_input, rsa_public_key_asn1_template) == NULL) return CKR_HOST_MEMORY; /* spki->subjectPublicKey is encoded as BIT_STRING, so "len" needs to be a number of _bits_. */ if (spki.subjectPublicKey.len > UINT_MAX / 8) { res = CKR_GENERAL_ERROR; goto end_subjectPublicKey; } spki.subjectPublicKey.len *= 8; memset (&spki.algorithm, 0, sizeof (spki.algorithm)); if (SECOID_SetAlgorithmID (NULL, &spki.algorithm, SEC_OID_PKCS1_RSA_ENCRYPTION, NULL) != SECSuccess) { res = CKR_GENERAL_ERROR; goto end_subjectPublicKey; } res = public_key_create (key, &spki); SECOID_DestroyAlgorithmID (&spki.algorithm, PR_FALSE); end_subjectPublicKey: PORT_Free (spki.subjectPublicKey.data); return res; } CK_RV ncr_public_key_export_rsa (struct ncr_public_key *key, struct ncr_mpi mpis[static NCR_RSA_PUBLIC_NUM_MPIS]) { struct rsa_public_key der_output; CERTSubjectPublicKeyInfo *spki; PRArenaPool *arena; SECItem key_item; CK_RV res; size_t i; 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 (mpis != NULL, CKR_ARGUMENTS_BAD); spki = SECKEY_CreateSubjectPublicKeyInfo(key->key); if (spki == NULL) return CKR_GENERAL_ERROR; res = rsa_validate_algorithm_id (&spki->algorithm); if (res != CKR_OK) goto end_spki; /* 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_spki; } /* spki->subjectPublicKey is encoded as BIT_STRING, so "len" is a number of _bits_. */ key_item.data = spki->subjectPublicKey.data; key_item.len = (spki->subjectPublicKey.len / 8 + (spki->subjectPublicKey.len % 8 != 0)); /* Setting type to siUnsignedInteger requests removal of leading zeroes. */ for (i = 0; i < NCR_RSA_PUBLIC_NUM_MPIS; i++) der_output.items[i].type = siUnsignedInteger; if (SEC_QuickDERDecodeItem (arena, &der_output, rsa_public_key_asn1_template, &key_item) != SECSuccess) { res = CKR_GENERAL_ERROR; goto end_arena; } res = mpi_output_decoded_SECItems(mpis, der_output.items, NCR_RSA_PUBLIC_NUM_MPIS); end_arena: PORT_FreeArena (arena, PR_FALSE); end_spki: SECKEY_DestroySubjectPublicKeyInfo (spki); return res; } CK_RV ncr_private_key_create_rsa (struct ncr_private_key **key, _Bool sensitive, const struct ncr_mpi mpis[static NCR_RSA_PRIVATE_NUM_MPIS]) { static const uint8_t zero; /* = 0; */ struct rsa_private_key der_input; struct private_key_info pki; CK_RV res; res = ensure_ncr_is_open (); if (res != CKR_OK) return res; res = mpi_create_SECItems_for_encoding (der_input.items, mpis, NCR_RSA_PRIVATE_NUM_MPIS); if (res != CKR_OK) return res; memset (&pki.algorithm, 0, sizeof (pki.algorithm)); if (SECOID_SetAlgorithmID (NULL, &pki.algorithm, SEC_OID_PKCS1_RSA_ENCRYPTION, NULL) != SECSuccess) return CKR_GENERAL_ERROR; der_input.version.type = siUnsignedInteger; der_input.version.data = (void *)&zero; der_input.version.len = sizeof (zero); pki.private_key.data = NULL; pki.private_key.len = 0; if (SEC_ASN1EncodeItem (NULL, &pki.private_key, &der_input, rsa_private_key_asn1_template) == NULL) { res = CKR_HOST_MEMORY; goto end_algorithm; } res = private_key_create_components (key, CKK_RSA, sensitive, &pki, &der_input.items[NCR_RSA_PRIVATE_MPI_MODULUS]); SECITEM_ZfreeItem (&pki.private_key, PR_FALSE); end_algorithm: SECOID_DestroyAlgorithmID (&pki.algorithm, PR_FALSE); return res; } CK_RV ncr_private_key_export_rsa (struct ncr_private_key *key, struct ncr_mpi mpis[static NCR_RSA_PRIVATE_NUM_MPIS]) { struct private_key_info pki; struct rsa_private_key der_output; PRArenaPool *arena; CK_RV res; size_t i; res = ensure_ncr_is_open (); if (res != CKR_OK) return res; g_return_val_if_fail (mpis != NULL, CKR_ARGUMENTS_BAD); /* 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) return CKR_HOST_MEMORY; res = private_key_export_components (key, arena, &pki); if (res != CKR_OK) goto end; res = rsa_validate_algorithm_id (&pki.algorithm); if (res != CKR_OK) goto end; /* Setting type to siUnsignedInteger requests removal of leading zeroes. */ der_output.version.type = siUnsignedInteger; for (i = 0; i < NCR_RSA_PRIVATE_NUM_MPIS; i++) der_output.items[i].type = siUnsignedInteger; if (SEC_QuickDERDecodeItem (arena, &der_output, rsa_private_key_asn1_template, &pki.private_key) != SECSuccess) { res = CKR_GENERAL_ERROR; goto end; } if (der_output.version.len != 1 || der_output.version.data[0] != 0) { res = CKR_GENERAL_ERROR; goto end; } res = mpi_output_decoded_SECItems(mpis, der_output.items, NCR_RSA_PRIVATE_NUM_MPIS); end: PORT_FreeArena (arena, PR_TRUE); return res; } CK_RV ncr_key_pair_generate_rsa (struct ncr_public_key **public_key, struct ncr_private_key **private_key, CK_MECHANISM_TYPE mech, _Bool sensitive, CK_ULONG modulus_bits, const struct ncr_mpi *public_exponent) { PK11RSAGenParams params; CK_RV res; res = ensure_ncr_is_open (); if (res != CKR_OK) return res; switch (mech) { case CKM_RSA_PKCS_KEY_PAIR_GEN: case CKM_RSA_X9_31_KEY_PAIR_GEN: break; default: g_return_val_if_reached (CKR_MECHANISM_INVALID); } g_return_val_if_fail (modulus_bits <= INT_MAX, CKR_ARGUMENTS_BAD); params.keySizeInBits = modulus_bits; if (public_exponent == NULL) params.pe = 65537; else { unsigned long val; const uint8_t *p, *end; end = (const uint8_t *)public_exponent->data + public_exponent->size; for (p = public_exponent->data; p < end && *p == 0; p++) ; g_return_val_if_fail ((size_t)(end - p) <= sizeof (val), CKR_ARGUMENTS_BAD); val = 0; while (p < end) { val = (val << 8) | *p; p++; } params.pe = val; } return key_pair_generate (public_key, private_key, mech, sensitive, ¶ms); } /* DSA keys */ /* Contains both the "key" and domain parameters */ struct dsa_public_values { SECItem items[NCR_DSA_PUBLIC_NUM_MPIS]; }; static const SEC_ASN1Template dsa_public_values_params_asn1_template[] = { { SEC_ASN1_SEQUENCE, 0, NULL, sizeof (struct dsa_public_values) }, #define INT(X) \ { \ SEC_ASN1_INTEGER, \ offsetof (struct dsa_public_values, items) + (X) * sizeof (SECItem), \ NULL, 0 \ } INT (NCR_DSA_PUBLIC_MPI_PRIME), INT (NCR_DSA_PUBLIC_MPI_SUBPRIME), INT (NCR_DSA_PUBLIC_MPI_BASE), #undef INT { 0, 0, NULL, 0 } }; static const SEC_ASN1Template dsa_public_values_key_asn1_template[] = { { SEC_ASN1_INTEGER, offsetof (struct dsa_public_values, items) + NCR_DSA_PUBLIC_MPI_VALUE * sizeof (SECItem), NULL, 0 } }; /* Contains both the "key" and domain parameters */ struct dsa_private_values { SECItem items[NCR_DSA_PRIVATE_NUM_MPIS]; }; static const SEC_ASN1Template dsa_private_values_params_asn1_template[] = { { SEC_ASN1_SEQUENCE, 0, NULL, sizeof (struct dsa_private_values) }, #define INT(X) \ { \ SEC_ASN1_INTEGER, \ offsetof (struct dsa_private_values, items) + (X) * sizeof (SECItem), \ NULL, 0 \ } INT (NCR_DSA_PRIVATE_MPI_PRIME), INT (NCR_DSA_PRIVATE_MPI_SUBPRIME), INT (NCR_DSA_PRIVATE_MPI_BASE), #undef INT { 0, 0, NULL, 0 } }; static const SEC_ASN1Template dsa_private_values_key_asn1_template[] = { { SEC_ASN1_INTEGER, offsetof (struct dsa_private_values, items) + NCR_DSA_PRIVATE_MPI_VALUE * sizeof (SECItem), NULL, 0 } }; static CK_RV dsa_validate_algorithm_id (const SECAlgorithmID *id) { const SECOidData *oid; oid = SECOID_FindOIDByTag (SEC_OID_ANSIX9_DSA_SIGNATURE); if (oid == NULL || !SECITEM_ItemsAreEqual(&id->algorithm, &oid->oid)) return CKR_GENERAL_ERROR; return CKR_OK; } CK_RV ncr_public_key_create_dsa (struct ncr_public_key **key, const struct ncr_mpi mpis[static NCR_DSA_PUBLIC_NUM_MPIS]) { struct dsa_public_values der_input; CERTSubjectPublicKeyInfo spki; SECItem params; SECStatus ss; CK_RV res; res = ensure_ncr_is_open (); if (res != CKR_OK) return res; res = mpi_create_SECItems_for_encoding (der_input.items, mpis, NCR_DSA_PUBLIC_NUM_MPIS); if (res != CKR_OK) return res; spki.subjectPublicKey.data = NULL; spki.subjectPublicKey.len = 0; if (SEC_ASN1EncodeItem (NULL, &spki.subjectPublicKey, &der_input, dsa_public_values_key_asn1_template) == NULL) return CKR_HOST_MEMORY; /* spki->subjectPublicKey is encoded as BIT_STRING, so "len" needs to be a number of _bits_. */ if (spki.subjectPublicKey.len > UINT_MAX / 8) { res = CKR_GENERAL_ERROR; goto end_subjectPublicKey; } spki.subjectPublicKey.len *= 8; params.data = NULL; params.len = 0; if (SEC_ASN1EncodeItem (NULL, ¶ms, &der_input, dsa_public_values_params_asn1_template) == NULL) { res = CKR_HOST_MEMORY; goto end_subjectPublicKey; } memset (&spki.algorithm, 0, sizeof (spki.algorithm)); ss = SECOID_SetAlgorithmID (NULL, &spki.algorithm, SEC_OID_ANSIX9_DSA_SIGNATURE, ¶ms); PORT_Free (params.data); if (ss != SECSuccess) { res = CKR_GENERAL_ERROR; goto end_subjectPublicKey; } res = public_key_create (key, &spki); SECOID_DestroyAlgorithmID (&spki.algorithm, PR_FALSE); end_subjectPublicKey: PORT_Free (spki.subjectPublicKey.data); return res; } CK_RV ncr_public_key_export_dsa (struct ncr_public_key *key, struct ncr_mpi mpis [static NCR_DSA_PUBLIC_NUM_MPIS]) { struct dsa_public_values der_output; CERTSubjectPublicKeyInfo *spki; PRArenaPool *arena; SECItem key_item; CK_RV res; size_t i; 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 (mpis != NULL, CKR_ARGUMENTS_BAD); spki = SECKEY_CreateSubjectPublicKeyInfo(key->key); if (spki == NULL) return CKR_GENERAL_ERROR; res = dsa_validate_algorithm_id (&spki->algorithm); if (res != CKR_OK) goto end_spki; /* 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_spki; } /* Setting type to siUnsignedInteger requests removal of leading zeroes. */ for (i = 0; i < NCR_DSA_PUBLIC_NUM_MPIS; i++) der_output.items[i].type = siUnsignedInteger; if (SEC_QuickDERDecodeItem (arena, &der_output, dsa_public_values_params_asn1_template, &spki->algorithm.parameters) != SECSuccess) { res = CKR_GENERAL_ERROR; goto end_arena; } /* spki->subjectPublicKey is encoded as BIT_STRING, so "len" is a number of _bits_. */ key_item.data = spki->subjectPublicKey.data; key_item.len = (spki->subjectPublicKey.len / 8 + (spki->subjectPublicKey.len % 8 != 0)); if (SEC_QuickDERDecodeItem (arena, &der_output, dsa_public_values_key_asn1_template, &key_item) != SECSuccess) { res = CKR_GENERAL_ERROR; goto end_arena; } res = mpi_output_decoded_SECItems(mpis, der_output.items, NCR_DSA_PUBLIC_NUM_MPIS); end_arena: PORT_FreeArena (arena, PR_FALSE); end_spki: SECKEY_DestroySubjectPublicKeyInfo (spki); return res; } CK_RV ncr_private_key_create_dsa (struct ncr_private_key **key, _Bool sensitive, const struct ncr_mpi mpis[static NCR_DSA_PRIVATE_NUM_MPIS], const struct ncr_mpi *public_value) { struct dsa_private_values der_input; SECItem public_item, params; struct private_key_info pki; SECStatus ss; CK_RV res; res = ensure_ncr_is_open (); if (res != CKR_OK) return res; res = mpi_create_SECItems_for_encoding (der_input.items, mpis, NCR_DSA_PRIVATE_NUM_MPIS); if (res != CKR_OK) return res; res = mpi_create_SECItems_for_encoding (&public_item, public_value, 1); if (res != CKR_OK) return res; params.data = NULL; params.len = 0; if (SEC_ASN1EncodeItem (NULL, ¶ms, &der_input, dsa_private_values_params_asn1_template) == NULL) return CKR_HOST_MEMORY; memset (&pki.algorithm, 0, sizeof (pki.algorithm)); ss = SECOID_SetAlgorithmID (NULL, &pki.algorithm, SEC_OID_ANSIX9_DSA_SIGNATURE, ¶ms); PORT_Free (params.data); if (ss != SECSuccess) return CKR_GENERAL_ERROR; pki.private_key.data = NULL; pki.private_key.len = 0; if (SEC_ASN1EncodeItem (NULL, &pki.private_key, &der_input, dsa_private_values_key_asn1_template) == NULL) { res = CKR_HOST_MEMORY; goto end_algorithm; } res = private_key_create_components (key, CKK_DSA, sensitive, &pki, &public_item); SECITEM_ZfreeItem (&pki.private_key, PR_FALSE); end_algorithm: SECOID_DestroyAlgorithmID (&pki.algorithm, PR_FALSE); return res; } CK_RV ncr_private_key_export_dsa (struct ncr_private_key *key, struct ncr_mpi mpis[static NCR_DSA_PRIVATE_NUM_MPIS]) { struct private_key_info pki; struct dsa_private_values der_output; PRArenaPool *arena; CK_RV res; size_t i; res = ensure_ncr_is_open (); if (res != CKR_OK) return res; g_return_val_if_fail (mpis != NULL, CKR_ARGUMENTS_BAD); /* 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) return CKR_HOST_MEMORY; res = private_key_export_components (key, arena, &pki); if (res != CKR_OK) goto end; res = dsa_validate_algorithm_id (&pki.algorithm); if (res != CKR_OK) goto end; /* Setting type to siUnsignedInteger requests removal of leading zeroes. */ for (i = 0; i < NCR_DSA_PRIVATE_NUM_MPIS; i++) der_output.items[i].type = siUnsignedInteger; if (SEC_QuickDERDecodeItem (arena, &der_output, dsa_private_values_params_asn1_template, &pki.algorithm.parameters) != SECSuccess) { res = CKR_GENERAL_ERROR; goto end; } if (SEC_QuickDERDecodeItem (arena, &der_output, dsa_private_values_key_asn1_template, &pki.private_key) != SECSuccess) { res = CKR_GENERAL_ERROR; goto end; } res = mpi_output_decoded_SECItems(mpis, der_output.items, NCR_DSA_PRIVATE_NUM_MPIS); end: PORT_FreeArena (arena, PR_TRUE); return res; } CK_RV ncr_key_pair_generate_dsa (struct ncr_public_key **public_key, struct ncr_private_key **private_key, CK_MECHANISM_TYPE mech, _Bool sensitive, const struct ncr_mpi mpis[static NCR_DSA_GEN_NUM_MPIS]) { SECKEYPQGParams params; size_t i; CK_RV res; res = ensure_ncr_is_open (); if (res != CKR_OK) return res; g_return_val_if_fail (mech == CKM_DSA_KEY_PAIR_GEN, CKR_MECHANISM_INVALID); g_return_val_if_fail (mpis != NULL, CKR_ARGUMENTS_BAD); for (i = 0; i < NCR_DSA_GEN_NUM_MPIS; i++) g_return_val_if_fail (mpis[i].data != NULL, CKR_ARGUMENTS_BAD); params.prime.data = mpis[NCR_DSA_GEN_MPI_PRIME].data; params.prime.len = mpis[NCR_DSA_GEN_MPI_PRIME].size; params.subPrime.data = mpis[NCR_DSA_GEN_MPI_SUBPRIME].data; params.subPrime.len = mpis[NCR_DSA_GEN_MPI_SUBPRIME].size; params.base.data = mpis[NCR_DSA_GEN_MPI_BASE].data; params.base.len = mpis[NCR_DSA_GEN_MPI_BASE].size; return key_pair_generate (public_key, private_key, mech, sensitive, ¶ms); } /* 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; }