/* * 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 #include #include #include #include #include #include #include #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); }