diff options
Diffstat (limited to 'usr/src/lib/pkcs11/pkcs11_softtoken/common/softEC.c')
-rw-r--r-- | usr/src/lib/pkcs11/pkcs11_softtoken/common/softEC.c | 635 |
1 files changed, 635 insertions, 0 deletions
diff --git a/usr/src/lib/pkcs11/pkcs11_softtoken/common/softEC.c b/usr/src/lib/pkcs11/pkcs11_softtoken/common/softEC.c new file mode 100644 index 0000000000..e57fb014dd --- /dev/null +++ b/usr/src/lib/pkcs11/pkcs11_softtoken/common/softEC.c @@ -0,0 +1,635 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <sys/types.h> +#include <sys/crypto/common.h> +#include <security/cryptoki.h> +#include <bignum.h> +#include <des_impl.h> +#include "softGlobal.h" +#include "softSession.h" +#include "softObject.h" +#include "softEC.h" +#include "softRandom.h" +#include "softCrypt.h" +#include "softOps.h" +#include "softMAC.h" + +void +soft_free_ecparams(ECParams *params, boolean_t freeit) +{ + SECITEM_FreeItem(¶ms->fieldID.u.prime, B_FALSE); + SECITEM_FreeItem(¶ms->curve.a, B_FALSE); + SECITEM_FreeItem(¶ms->curve.b, B_FALSE); + SECITEM_FreeItem(¶ms->curve.seed, B_FALSE); + SECITEM_FreeItem(¶ms->base, B_FALSE); + SECITEM_FreeItem(¶ms->order, B_FALSE); + SECITEM_FreeItem(¶ms->DEREncoding, B_FALSE); + SECITEM_FreeItem(¶ms->curveOID, B_FALSE); + if (freeit) + free(params); +} + +static void +soft_free_ecc_context(soft_ecc_ctx_t *ecc_ctx) +{ + if (ecc_ctx != NULL) { + if (ecc_ctx->key != NULL) { + soft_cleanup_object(ecc_ctx->key); + free(ecc_ctx->key); + } + + soft_free_ecparams(&ecc_ctx->ecparams, B_FALSE); + free(ecc_ctx); + } +} + +void +soft_free_ecprivkey(ECPrivateKey *key) +{ + soft_free_ecparams(&key->ecParams, B_FALSE); + /* + * Don't free publicValue or privateValue + * as these values are copied into objects. + */ + SECITEM_FreeItem(&key->version, B_FALSE); + free(key); +} + +/* + * Called from init routines to do basic sanity checks. Init routines, + * e.g. sign_init should fail rather than subsequent operations. + */ +static int +check_key(soft_object_t *key_p, boolean_t sign) +{ + biginteger_t *p; + ulong_t len; + + if (sign) { + if ((key_p->class != CKO_PRIVATE_KEY) || + (key_p->key_type != CKK_EC)) + return (CKR_KEY_TYPE_INCONSISTENT); + + p = OBJ_PRI_EC_VALUE(key_p); + len = p->big_value_len; + if (p->big_value == NULL) + len = 0; + + if (len < CRYPTO_BITS2BYTES(EC_MIN_KEY_LEN) || + len > CRYPTO_BITS2BYTES(EC_MAX_KEY_LEN)) + return (CKR_KEY_SIZE_RANGE); + } else { + if ((key_p->class != CKO_PUBLIC_KEY) || + (key_p->key_type != CKK_EC)) + return (CKR_KEY_TYPE_INCONSISTENT); + + p = OBJ_PUB_EC_POINT(key_p); + len = p->big_value_len; + if (p->big_value == NULL) + len = 0; + + if (len < CRYPTO_BITS2BYTES(EC_MIN_KEY_LEN) * 2 + 1 || + len > CRYPTO_BITS2BYTES(EC_MAX_KEY_LEN) * 2 + 1) + return (CKR_KEY_SIZE_RANGE); + } + + return (CKR_OK); +} + +/* + * This function places the octet string of the specified attribute + * into the corresponding key object. + */ +static void +soft_genECkey_set_attribute(soft_object_t *key, biginteger_t *bi, + CK_ATTRIBUTE_TYPE type) +{ + biginteger_t *dst; + + switch (type) { + case CKA_VALUE: + dst = OBJ_PRI_EC_VALUE(key); + break; + + case CKA_EC_POINT: + dst = OBJ_PUB_EC_POINT(key); + break; + } + copy_bigint_attr(bi, dst); +} + +CK_RV +soft_ec_genkey_pair(soft_object_t *pubkey, soft_object_t *prikey) +{ + CK_RV rv; + CK_ATTRIBUTE template; + ECPrivateKey *privKey; /* contains both public and private values */ + ECParams *ecparams; + SECKEYECParams params_item; + biginteger_t bi; + uchar_t param_buffer[EC_MAX_OID_LEN]; + uint_t paramlen; + + if ((pubkey->class != CKO_PUBLIC_KEY) || + (pubkey->key_type != CKK_EC)) + return (CKR_KEY_TYPE_INCONSISTENT); + + if ((prikey->class != CKO_PRIVATE_KEY) || + (prikey->key_type != CKK_EC)) + return (CKR_KEY_TYPE_INCONSISTENT); + + template.type = CKA_EC_PARAMS; + template.pValue = param_buffer; + template.ulValueLen = sizeof (param_buffer); + rv = soft_get_public_key_attribute(pubkey, &template); + if (rv != CKR_OK) { + return (rv); + } + paramlen = template.ulValueLen; + + /* private key also has CKA_EC_PARAMS attribute */ + rv = set_extra_attr_to_object(prikey, CKA_EC_PARAMS, &template); + if (rv != CKR_OK) { + return (rv); + } + + /* ASN1 check */ + if (param_buffer[0] != 0x06 || + param_buffer[1] != paramlen - 2) { + return (CKR_ATTRIBUTE_VALUE_INVALID); + } + params_item.len = paramlen; + params_item.data = param_buffer; + if (EC_DecodeParams(¶ms_item, &ecparams, 0) != SECSuccess) { + /* bad curve OID */ + return (CKR_ARGUMENTS_BAD); + } + + if (EC_NewKey(ecparams, &privKey, 0) != SECSuccess) { + soft_free_ecparams(ecparams, B_TRUE); + return (CKR_FUNCTION_FAILED); + } + + bi.big_value = privKey->privateValue.data; + bi.big_value_len = privKey->privateValue.len; + soft_genECkey_set_attribute(prikey, &bi, CKA_VALUE); + + bi.big_value = privKey->publicValue.data; + bi.big_value_len = privKey->publicValue.len; + soft_genECkey_set_attribute(pubkey, &bi, CKA_EC_POINT); + + soft_free_ecprivkey(privKey); + soft_free_ecparams(ecparams, B_TRUE); + + return (CKR_OK); +} + +CK_RV +soft_ec_key_derive(soft_object_t *basekey, soft_object_t *secretkey, + void *mech_params, size_t mech_params_len) +{ + CK_RV rv; + CK_ATTRIBUTE template; + CK_ECDH1_DERIVE_PARAMS *ecdh1_derive_params = mech_params; + uchar_t value[EC_MAX_VALUE_LEN]; + uint32_t value_len = sizeof (value); + uchar_t params[EC_MAX_OID_LEN]; + uint32_t params_len = sizeof (params); + uint32_t keylen; + ECParams *ecparams; + SECKEYECParams params_item; + SECItem public_value_item, private_value_item, secret_item; + uchar_t *buf; + + if (mech_params_len != sizeof (CK_ECDH1_DERIVE_PARAMS) || + ecdh1_derive_params->kdf != CKD_NULL) { + return (CKR_MECHANISM_PARAM_INVALID); + } + + template.type = CKA_VALUE; + template.pValue = value; + template.ulValueLen = value_len; + rv = soft_get_private_key_attribute(basekey, &template); + if (rv != CKR_OK) { + return (rv); + } + value_len = template.ulValueLen; + private_value_item.data = value; + private_value_item.len = value_len; + + template.type = CKA_EC_PARAMS; + template.pValue = params; + template.ulValueLen = params_len; + rv = soft_get_private_key_attribute(basekey, &template); + if (rv != CKR_OK) { + return (rv); + } + params_len = template.ulValueLen; + + switch (secretkey->key_type) { + case CKK_DES: + keylen = DES_KEYSIZE; + break; + case CKK_DES2: + keylen = DES2_KEYSIZE; + break; + case CKK_DES3: + keylen = DES3_KEYSIZE; + break; + case CKK_RC4: + case CKK_AES: + case CKK_GENERIC_SECRET: +#ifdef __sparcv9 + /* LINTED */ + keylen = (uint32_t)OBJ_SEC_VALUE_LEN(secretkey); +#else /* !__sparcv9 */ + keylen = OBJ_SEC_VALUE_LEN(secretkey); +#endif /* __sparcv9 */ + break; + } + + /* ASN1 check */ + if (params[0] != 0x06 || + params[1] != params_len - 2) { + return (CKR_ATTRIBUTE_VALUE_INVALID); + } + params_item.data = params; + params_item.len = params_len; + if (EC_DecodeParams(¶ms_item, &ecparams, 0) != SECSuccess) { + /* bad curve OID */ + return (CKR_ARGUMENTS_BAD); + } + + public_value_item.data = ecdh1_derive_params->pPublicData; + public_value_item.len = ecdh1_derive_params->ulPublicDataLen; + + secret_item.data = NULL; + secret_item.len = 0; + + if (ECDH_Derive(&public_value_item, ecparams, &private_value_item, + B_FALSE, &secret_item, 0) != SECSuccess) { + soft_free_ecparams(ecparams, B_TRUE); + return (CKR_FUNCTION_FAILED); + } else { + rv = CKR_OK; + } + + if (keylen == 0) + keylen = secret_item.len; + + if (keylen > secret_item.len) { + rv = CKR_ATTRIBUTE_VALUE_INVALID; + goto out; + } + buf = malloc(keylen); + if (buf == NULL) { + rv = CKR_HOST_MEMORY; + goto out; + } + bcopy(secret_item.data + secret_item.len - keylen, buf, keylen); + OBJ_SEC_VALUE_LEN(secretkey) = keylen; + OBJ_SEC_VALUE(secretkey) = buf; + +out: + soft_free_ecparams(ecparams, B_TRUE); + SECITEM_FreeItem(&secret_item, B_FALSE); + + return (rv); +} + +/* + * Allocate a ECC context for the active sign or verify operation. + * This function is called without the session lock held. + */ +CK_RV +soft_ecc_sign_verify_init_common(soft_session_t *session_p, + CK_MECHANISM_PTR pMechanism, soft_object_t *key_p, + boolean_t sign) +{ + CK_RV rv; + CK_ATTRIBUTE template; + CK_MECHANISM digest_mech; + soft_ecc_ctx_t *ecc_ctx; + soft_object_t *tmp_key = NULL; + uchar_t params[EC_MAX_OID_LEN]; + ECParams *ecparams; + SECKEYECParams params_item; + + if ((rv = check_key(key_p, sign)) != CKR_OK) + return (rv); + + if (pMechanism->mechanism == CKM_ECDSA_SHA1) { + digest_mech.mechanism = CKM_SHA_1; + rv = soft_digest_init_internal(session_p, &digest_mech); + if (rv != CKR_OK) + return (rv); + } + + ecc_ctx = malloc(sizeof (soft_ecc_ctx_t)); + if (ecc_ctx == NULL) { + return (CKR_HOST_MEMORY); + } + + /* + * Make a copy of the signature or verification key, and save it + * in the ECC crypto context since it will be used later for + * signing/verification. We don't want to hold any object reference + * on this original key while doing signing/verification. + */ + (void) pthread_mutex_lock(&key_p->object_mutex); + rv = soft_copy_object(key_p, &tmp_key, SOFT_COPY_OBJ_ORIG_SH, NULL); + if ((rv != CKR_OK) || (tmp_key == NULL)) { + /* Most likely we ran out of space. */ + (void) pthread_mutex_unlock(&key_p->object_mutex); + free(ecc_ctx); + return (rv); + } + + + template.type = CKA_EC_PARAMS; + template.pValue = params; + template.ulValueLen = sizeof (params); + rv = soft_get_private_key_attribute(key_p, &template); + (void) pthread_mutex_unlock(&key_p->object_mutex); + if (rv != CKR_OK) { + goto out; + } + + /* ASN1 check */ + if (params[0] != 0x06 || + params[1] != template.ulValueLen - 2) { + rv = CKR_ATTRIBUTE_VALUE_INVALID; + goto out; + } + params_item.data = params; + params_item.len = template.ulValueLen; + + ecc_ctx->key = tmp_key; + + if (EC_DecodeParams(¶ms_item, &ecparams, 0) != SECSuccess) { + /* bad curve OID */ + rv = CKR_ARGUMENTS_BAD; + goto out; + } + ecc_ctx->ecparams = *ecparams; + free(ecparams); + + (void) pthread_mutex_lock(&session_p->session_mutex); + + if (sign) { + session_p->sign.context = ecc_ctx; + session_p->sign.mech.mechanism = pMechanism->mechanism; + } else { + session_p->verify.context = ecc_ctx; + session_p->verify.mech.mechanism = pMechanism->mechanism; + } + + (void) pthread_mutex_unlock(&session_p->session_mutex); + return (CKR_OK); + +out: + soft_cleanup_object(tmp_key); + free(tmp_key); + free(ecc_ctx); + + return (rv); +} + +CK_RV +soft_ecc_digest_sign_common(soft_session_t *session_p, CK_BYTE_PTR pData, + CK_ULONG ulDataLen, CK_BYTE_PTR pSigned, + CK_ULONG_PTR pulSignedLen, boolean_t Final) +{ + CK_RV rv = CKR_OK; + CK_BYTE hash[SHA1_HASH_SIZE]; + CK_ULONG hash_len = SHA1_HASH_SIZE; + + if (pSigned != NULL) { + if (Final) { + rv = soft_digest_final(session_p, hash, &hash_len); + } else { + rv = soft_digest(session_p, pData, ulDataLen, hash, + &hash_len); + } + + if (rv != CKR_OK) { + (void) pthread_mutex_lock(&session_p->session_mutex); + soft_free_ecc_context(session_p->sign.context); + session_p->sign.context = NULL; + session_p->digest.flags = 0; + (void) pthread_mutex_unlock(&session_p->session_mutex); + return (rv); + } + } + + rv = soft_ecc_sign(session_p, hash, hash_len, pSigned, pulSignedLen); + +clean_exit: + (void) pthread_mutex_lock(&session_p->session_mutex); + /* soft_digest_common() has freed the digest context */ + session_p->digest.flags = 0; + (void) pthread_mutex_unlock(&session_p->session_mutex); + +clean1: + return (rv); +} + +CK_RV +soft_ecc_sign(soft_session_t *session_p, CK_BYTE_PTR pData, + CK_ULONG ulDataLen, CK_BYTE_PTR pSigned, + CK_ULONG_PTR pulSignedLen) +{ + CK_RV rv = CKR_OK; + SECStatus ss; + soft_ecc_ctx_t *ecc_ctx = session_p->sign.context; + soft_object_t *key = ecc_ctx->key; + uchar_t value[EC_MAX_VALUE_LEN]; + CK_ATTRIBUTE template; + ECPrivateKey ECkey; + SECItem signature_item; + SECItem digest_item; + + if ((key->class != CKO_PRIVATE_KEY) || (key->key_type != CKK_EC)) { + rv = CKR_KEY_TYPE_INCONSISTENT; + goto clean_exit; + } + + if (ulDataLen > EC_MAX_DIGEST_LEN) { + rv = CKR_DATA_LEN_RANGE; + goto clean_exit; + } + + /* structure assignment */ + ECkey.ecParams = ecc_ctx->ecparams; + + template.type = CKA_VALUE; + template.pValue = value; + template.ulValueLen = sizeof (value); + rv = soft_get_private_key_attribute(key, &template); + if (rv != CKR_OK) { + goto clean_exit; + } + + ECkey.privateValue.data = value; + ECkey.privateValue.len = template.ulValueLen; + + signature_item.data = pSigned; + signature_item.len = *pulSignedLen; + + digest_item.data = pData; + digest_item.len = ulDataLen; + + if ((ss = ECDSA_SignDigest(&ECkey, &signature_item, &digest_item, 0)) + != SECSuccess) { + if (ss == SECBufferTooSmall) + return (CKR_BUFFER_TOO_SMALL); + + rv = CKR_FUNCTION_FAILED; + goto clean_exit; + } + + if (rv == CKR_OK) { + *pulSignedLen = signature_item.len; + if (pSigned == NULL) + return (rv); + } + +clean_exit: + (void) pthread_mutex_lock(&session_p->session_mutex); + soft_free_ecc_context(session_p->sign.context); + session_p->sign.context = NULL; + (void) pthread_mutex_unlock(&session_p->session_mutex); + return (rv); +} + +CK_RV +soft_ecc_verify(soft_session_t *session_p, CK_BYTE_PTR pData, + CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, + CK_ULONG ulSignatureLen) +{ + CK_RV rv = CKR_OK; + soft_ecc_ctx_t *ecc_ctx = session_p->verify.context; + soft_object_t *key = ecc_ctx->key; + uchar_t point[EC_MAX_POINT_LEN]; + CK_ATTRIBUTE template; + ECPublicKey ECkey; + SECItem signature_item; + SECItem digest_item; + + if ((key->class != CKO_PUBLIC_KEY) ||(key->key_type != CKK_EC)) { + rv = CKR_KEY_TYPE_INCONSISTENT; + goto clean_exit; + } + + if (ulSignatureLen > EC_MAX_SIG_LEN) { + rv = CKR_SIGNATURE_LEN_RANGE; + goto clean_exit; + } + + if (ulDataLen > EC_MAX_DIGEST_LEN) { + rv = CKR_DATA_LEN_RANGE; + goto clean_exit; + } + + /* structure assignment */ + ECkey.ecParams = ecc_ctx->ecparams; + + template.type = CKA_EC_POINT; + template.pValue = point; + template.ulValueLen = sizeof (point); + rv = soft_get_public_key_attribute(key, &template); + if (rv != CKR_OK) { + goto clean_exit; + } + + ECkey.publicValue.data = point; + ECkey.publicValue.len = template.ulValueLen; + + signature_item.data = pSignature; + signature_item.len = ulSignatureLen; + + digest_item.data = pData; + digest_item.len = ulDataLen; + + if (ECDSA_VerifyDigest(&ECkey, &signature_item, &digest_item, 0) + != SECSuccess) { + rv = CKR_SIGNATURE_INVALID; + } else { + rv = CKR_OK; + } + +clean_exit: + (void) pthread_mutex_lock(&session_p->session_mutex); + soft_free_ecc_context(session_p->verify.context); + session_p->verify.context = NULL; + (void) pthread_mutex_unlock(&session_p->session_mutex); + return (rv); +} + + +CK_RV +soft_ecc_digest_verify_common(soft_session_t *session_p, CK_BYTE_PTR pData, + CK_ULONG ulDataLen, CK_BYTE_PTR pSigned, + CK_ULONG ulSignedLen, boolean_t Final) +{ + CK_RV rv; + CK_BYTE hash[SHA1_HASH_SIZE]; + CK_ULONG hash_len = SHA1_HASH_SIZE; + + if (Final) { + rv = soft_digest_final(session_p, hash, &hash_len); + } else { + rv = soft_digest(session_p, pData, ulDataLen, hash, &hash_len); + } + + if (rv != CKR_OK) { + (void) pthread_mutex_lock(&session_p->session_mutex); + soft_free_ecc_context(session_p->verify.context); + session_p->verify.context = NULL; + session_p->digest.flags = 0; + (void) pthread_mutex_unlock(&session_p->session_mutex); + return (rv); + } + + /* + * Now, we are ready to verify the data using signature. + * soft_ecc_verify() will free the verification key. + */ + rv = soft_ecc_verify(session_p, hash, hash_len, + pSigned, ulSignedLen); + +clean_exit: + (void) pthread_mutex_lock(&session_p->session_mutex); + /* soft_digest_common() has freed the digest context */ + session_p->digest.flags = 0; + (void) pthread_mutex_unlock(&session_p->session_mutex); + return (rv); +} |