summaryrefslogtreecommitdiff
path: root/usr/src/lib/pkcs11/pkcs11_softtoken/common/softEC.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/pkcs11/pkcs11_softtoken/common/softEC.c')
-rw-r--r--usr/src/lib/pkcs11/pkcs11_softtoken/common/softEC.c635
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(&params->fieldID.u.prime, B_FALSE);
+ SECITEM_FreeItem(&params->curve.a, B_FALSE);
+ SECITEM_FreeItem(&params->curve.b, B_FALSE);
+ SECITEM_FreeItem(&params->curve.seed, B_FALSE);
+ SECITEM_FreeItem(&params->base, B_FALSE);
+ SECITEM_FreeItem(&params->order, B_FALSE);
+ SECITEM_FreeItem(&params->DEREncoding, B_FALSE);
+ SECITEM_FreeItem(&params->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(&params_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(&params_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(&params_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);
+}