/* * 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 "softGlobal.h" #include "softSession.h" #include "softObject.h" #include "softDSA.h" #include "softRandom.h" #include "softOps.h" #include "softMAC.h" #include "softCrypt.h" /* * Allocate a DSA context for the active sign or verify operation. * This function is called without the session lock held. */ CK_RV soft_dsa_sign_verify_init_common(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism, soft_object_t *key_p, boolean_t sign) { soft_dsa_ctx_t *dsa_ctx; CK_MECHANISM digest_mech; soft_object_t *tmp_key = NULL; CK_RV rv; if (sign) { if ((key_p->class != CKO_PRIVATE_KEY) || (key_p->key_type != CKK_DSA)) return (CKR_KEY_TYPE_INCONSISTENT); } else { if ((key_p->class != CKO_PUBLIC_KEY) || (key_p->key_type != CKK_DSA)) return (CKR_KEY_TYPE_INCONSISTENT); } if (pMechanism->mechanism == CKM_DSA_SHA1) { digest_mech.mechanism = CKM_SHA_1; rv = soft_digest_init_internal(session_p, &digest_mech); if (rv != CKR_OK) return (rv); } dsa_ctx = malloc(sizeof (soft_dsa_ctx_t)); if (dsa_ctx == NULL) { return (CKR_HOST_MEMORY); } /* * Make a copy of the signature or verification key, and save it * in the DSA 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(dsa_ctx); return (rv); } /* No need to hold the lock on the old object. */ (void) pthread_mutex_unlock(&key_p->object_mutex); dsa_ctx->key = tmp_key; (void) pthread_mutex_lock(&session_p->session_mutex); if (sign) { session_p->sign.context = dsa_ctx; session_p->sign.mech.mechanism = pMechanism->mechanism; } else { session_p->verify.context = dsa_ctx; session_p->verify.mech.mechanism = pMechanism->mechanism; } (void) pthread_mutex_unlock(&session_p->session_mutex); return (CKR_OK); } BIG_ERR_CODE DSA_key_init(DSAkey *key, int size) { BIG_ERR_CODE err; int len; len = size / 32; key->size = size; if ((err = big_init1(&(key->q), 5, NULL, 0)) != BIG_OK) return (err); if ((err = big_init1(&(key->p), len, NULL, 0)) != BIG_OK) goto ret1; if ((err = big_init1(&(key->g), len, NULL, 0)) != BIG_OK) goto ret2; if ((err = big_init1(&(key->x), 5, NULL, 0)) != BIG_OK) goto ret3; if ((err = big_init1(&(key->y), len, NULL, 0)) != BIG_OK) goto ret4; if ((err = big_init1(&(key->k), 5, NULL, 0)) != BIG_OK) goto ret5; if ((err = big_init1(&(key->r), 5, NULL, 0)) != BIG_OK) goto ret6; if ((err = big_init1(&(key->s), 5, NULL, 0)) != BIG_OK) goto ret7; if ((err = big_init1(&(key->v), 5, NULL, 0)) != BIG_OK) goto ret8; return (BIG_OK); ret8: big_finish(&(key->s)); ret7: big_finish(&(key->r)); ret6: big_finish(&(key->k)); ret5: big_finish(&(key->y)); ret4: big_finish(&(key->x)); ret3: big_finish(&(key->g)); ret2: big_finish(&(key->p)); ret1: big_finish(&(key->q)); return (err); } void DSA_key_finish(DSAkey *key) { big_finish(&(key->v)); big_finish(&(key->s)); big_finish(&(key->r)); big_finish(&(key->k)); big_finish(&(key->y)); big_finish(&(key->x)); big_finish(&(key->g)); big_finish(&(key->p)); big_finish(&(key->q)); } CK_RV dsa_sign(soft_object_t *key, CK_BYTE_PTR in, CK_ULONG inlen, CK_BYTE_PTR out) { uchar_t q[MAX_KEY_ATTR_BUFLEN]; uchar_t p[MAX_KEY_ATTR_BUFLEN]; uchar_t g[MAX_KEY_ATTR_BUFLEN]; uchar_t x[MAX_KEY_ATTR_BUFLEN]; uint_t qlen = sizeof (q); uint_t plen = sizeof (p); uint_t glen = sizeof (g); uint_t xlen = sizeof (x); DSAkey dsakey; BIGNUM msg, tmp, tmp1, tmp2; BIG_ERR_CODE err; CK_RV rv; rv = soft_get_private_attr(key, CKA_SUBPRIME, q, &qlen); if (rv != CKR_OK) { goto clean1; } if (20 != qlen) { rv = CKR_KEY_SIZE_RANGE; goto clean1; } rv = soft_get_private_attr(key, CKA_PRIME, p, &plen); if (rv != CKR_OK) { goto clean1; } rv = soft_get_private_attr(key, CKA_BASE, g, &glen); if (rv != CKR_OK) { goto clean1; } if (glen != plen) { rv = CKR_KEY_SIZE_RANGE; goto clean1; } rv = soft_get_private_attr(key, CKA_VALUE, x, &xlen); if (rv != CKR_OK) { goto clean1; } if (20 < xlen) { rv = CKR_KEY_SIZE_RANGE; goto clean1; } if ((err = DSA_key_init(&dsakey, plen * 8)) != BIG_OK) { rv = CKR_HOST_MEMORY; goto clean1; } if ((err = big_init(&msg, 5)) != BIG_OK) { goto clean6; } if ((err = big_init(&tmp, plen / 4 + 11)) != BIG_OK) { goto clean7; } if ((err = big_init(&tmp1, 11)) != BIG_OK) { goto clean8; } if ((err = big_init(&tmp2, 5)) != BIG_OK) { goto clean9; } bytestring2bignum(&(dsakey.g), g, plen); bytestring2bignum(&(dsakey.x), x, 20); bytestring2bignum(&(dsakey.p), p, plen); bytestring2bignum(&(dsakey.q), q, 20); bytestring2bignum(&msg, (uchar_t *)in, inlen); if ((err = random_bignum(&(dsakey.k), DSA_SUBPRIME_BITS, B_FALSE)) != BIG_OK) goto clean10; if ((err = big_div_pos(NULL, &(dsakey.k), &(dsakey.k), &(dsakey.q))) != BIG_OK) goto clean10; if ((err = big_modexp(&tmp, &(dsakey.g), &(dsakey.k), &(dsakey.p), NULL)) != BIG_OK) goto clean10; if ((err = big_div_pos(NULL, &(dsakey.r), &tmp, &(dsakey.q))) != BIG_OK) goto clean10; if ((err = big_ext_gcd_pos(NULL, NULL, &tmp, &(dsakey.q), &(dsakey.k))) != BIG_OK) goto clean10; if (tmp.sign == -1) if ((err = big_add(&tmp, &tmp, &(dsakey.q))) != BIG_OK) goto clean10; /* tmp <- k^-1 */ if ((err = big_mul(&tmp1, &(dsakey.x), &(dsakey.r))) != BIG_OK) goto clean10; if ((err = big_add(&tmp1, &tmp1, &msg)) != BIG_OK) goto clean10; if ((err = big_mul(&tmp, &tmp1, &tmp)) != BIG_OK) goto clean10; if ((err = big_div_pos(NULL, &(dsakey.s), &tmp, &(dsakey.q))) != BIG_OK) goto clean10; bignum2bytestring((uchar_t *)out, &(dsakey.r), 20); bignum2bytestring((uchar_t *)out + 20, &(dsakey.s), 20); err = BIG_OK; clean10: big_finish(&tmp2); clean9: big_finish(&tmp1); clean8: big_finish(&tmp); clean7: big_finish(&msg); clean6: DSA_key_finish(&dsakey); if (err == BIG_OK) rv = CKR_OK; else if (err == BIG_NO_MEM) rv = CKR_HOST_MEMORY; else rv = CKR_FUNCTION_FAILED; clean1: return (rv); } CK_RV dsa_verify(soft_object_t *key, CK_BYTE_PTR data, CK_BYTE_PTR sig) { uchar_t g[MAX_KEY_ATTR_BUFLEN]; uchar_t y[MAX_KEY_ATTR_BUFLEN]; uchar_t p[MAX_KEY_ATTR_BUFLEN]; uchar_t q[MAX_KEY_ATTR_BUFLEN]; uint_t glen = sizeof (g); uint_t ylen = sizeof (y); uint_t plen = sizeof (p); uint_t qlen = sizeof (q); DSAkey dsakey; BIGNUM msg, tmp1, tmp2, tmp3; CK_RV rv; rv = soft_get_public_attr(key, CKA_SUBPRIME, q, &qlen); if (rv != CKR_OK) { goto clean1; } if (20 != qlen) { rv = CKR_KEY_SIZE_RANGE; goto clean1; } rv = soft_get_public_attr(key, CKA_PRIME, p, &plen); if (rv != CKR_OK) { goto clean1; } rv = soft_get_public_attr(key, CKA_BASE, g, &glen); if (rv != CKR_OK) { goto clean1; } if (plen < glen) { rv = CKR_KEY_SIZE_RANGE; goto clean1; } rv = soft_get_public_attr(key, CKA_VALUE, y, &ylen); if (rv != CKR_OK) { goto clean1; } if (plen < ylen) { rv = CKR_KEY_SIZE_RANGE; goto clean1; } if (DSA_key_init(&dsakey, plen * 8) != BIG_OK) { rv = CKR_HOST_MEMORY; goto clean1; } rv = CKR_HOST_MEMORY; if (big_init(&msg, 5) != BIG_OK) { goto clean6; } if (big_init(&tmp1, plen / 2) != BIG_OK) { goto clean7; } if (big_init(&tmp2, plen / 4) != BIG_OK) { goto clean8; } if (big_init(&tmp3, 10) != BIG_OK) { goto clean9; } bytestring2bignum(&(dsakey.g), g, glen); bytestring2bignum(&(dsakey.y), y, ylen); bytestring2bignum(&(dsakey.p), p, plen); bytestring2bignum(&(dsakey.q), q, 20); bytestring2bignum(&(dsakey.r), (uchar_t *)sig, 20); bytestring2bignum(&(dsakey.s), ((uchar_t *)sig) + 20, 20); bytestring2bignum(&msg, (uchar_t *)data, 20); if (big_ext_gcd_pos(NULL, &tmp2, NULL, &(dsakey.s), &(dsakey.q)) != BIG_OK) goto clean10; if (tmp2.sign == -1) if (big_add(&tmp2, &tmp2, &(dsakey.q)) != BIG_OK) goto clean10; /* tmp2 <- w */ if (big_mul(&tmp1, &msg, &tmp2) != BIG_OK) goto clean10; if (big_div_pos(NULL, &tmp1, &tmp1, &(dsakey.q)) != BIG_OK) goto clean10; /* tmp1 <- u_1 */ if (big_mul(&tmp2, &tmp2, &(dsakey.r)) != BIG_OK) goto clean10; if (big_div_pos(NULL, &tmp2, &tmp2, &(dsakey.q)) != BIG_OK) goto clean10; /* tmp2 <- u_2 */ if (big_modexp(&tmp1, &(dsakey.g), &tmp1, &(dsakey.p), NULL) != BIG_OK) goto clean10; if (big_modexp(&tmp2, &(dsakey.y), &tmp2, &(dsakey.p), NULL) != BIG_OK) goto clean10; if (big_mul(&tmp1, &tmp1, &tmp2) != BIG_OK) goto clean10; if (big_div_pos(NULL, &tmp1, &tmp1, &(dsakey.p)) != BIG_OK) goto clean10; if (big_div_pos(NULL, &tmp1, &tmp1, &(dsakey.q)) != BIG_OK) goto clean10; if (big_cmp_abs(&tmp1, &(dsakey.r)) == 0) rv = CKR_OK; else rv = CKR_SIGNATURE_INVALID; clean10: big_finish(&tmp3); clean9: big_finish(&tmp2); clean8: big_finish(&tmp1); clean7: big_finish(&msg); clean6: DSA_key_finish(&dsakey); clean1: return (rv); } CK_RV soft_dsa_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]; /* space enough for SHA1 and MD5 */ CK_ULONG hash_len = SHA1_HASH_SIZE; soft_dsa_ctx_t *dsa_ctx = session_p->sign.context; soft_object_t *key = dsa_ctx->key; /* Check arguments before performing message digest. */ if (pSigned == NULL) { /* Application asks for the length of the output buffer. */ *pulSignedLen = DSA_SIGNATURE_LENGTH; goto clean1; } /* Is the application-supplied buffer large enough? */ if (*pulSignedLen < DSA_SIGNATURE_LENGTH) { *pulSignedLen = DSA_SIGNATURE_LENGTH; rv = CKR_BUFFER_TOO_SMALL; goto clean1; } 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) { /* free the signature key */ soft_cleanup_object(key); free(key); goto clean_exit; } /* * Now, we are ready to sign the data * soft_dsa_sign() will free the signature key. */ rv = soft_dsa_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_dsa_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; soft_dsa_ctx_t *dsa_ctx = session_p->sign.context; soft_object_t *key = dsa_ctx->key; if ((key->class != CKO_PRIVATE_KEY) || (key->key_type != CKK_DSA)) { rv = CKR_KEY_TYPE_INCONSISTENT; goto clean_exit; } /* Output length is always 40 bytes. */ if (pSigned == NULL) { /* Application asks for the length of the output buffer. */ *pulSignedLen = DSA_SIGNATURE_LENGTH; return (CKR_OK); } /* Input data length needs to be 20 bytes. */ if (ulDataLen != 20) { rv = CKR_DATA_LEN_RANGE; goto clean_exit; } if (*pulSignedLen < DSA_SIGNATURE_LENGTH) { *pulSignedLen = DSA_SIGNATURE_LENGTH; return (CKR_BUFFER_TOO_SMALL); } rv = dsa_sign(key, pData, ulDataLen, pSigned); if (rv == CKR_OK) { *pulSignedLen = DSA_SIGNATURE_LENGTH; } clean_exit: (void) pthread_mutex_lock(&session_p->session_mutex); free(session_p->sign.context); session_p->sign.context = NULL; (void) pthread_mutex_unlock(&session_p->session_mutex); soft_cleanup_object(key); free(key); return (rv); } CK_RV soft_dsa_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_dsa_ctx_t *dsa_ctx = session_p->verify.context; soft_object_t *key = dsa_ctx->key; if ((key->class != CKO_PUBLIC_KEY) ||(key->key_type != CKK_DSA)) { rv = CKR_KEY_TYPE_INCONSISTENT; goto clean_exit; } /* The signature length is always 40 bytes. */ if (ulSignatureLen != DSA_SIGNATURE_LENGTH) { rv = CKR_SIGNATURE_LEN_RANGE; goto clean_exit; } /* Input data length needs to be 20 bytes. */ if (ulDataLen != 20) { rv = CKR_DATA_LEN_RANGE; goto clean_exit; } rv = dsa_verify(key, pData, pSignature); clean_exit: (void) pthread_mutex_lock(&session_p->session_mutex); free(session_p->verify.context); session_p->verify.context = NULL; (void) pthread_mutex_unlock(&session_p->session_mutex); soft_cleanup_object(key); free(key); return (rv); } CK_RV soft_dsa_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]; /* space enough for SHA1 and MD5 */ CK_ULONG hash_len = SHA1_HASH_SIZE; soft_dsa_ctx_t *dsa_ctx = session_p->verify.context; soft_object_t *key = dsa_ctx->key; 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) { /* free the verification key */ soft_cleanup_object(key); free(key); goto clean_exit; } /* * Now, we are ready to verify the data using signature. * soft_dsa_verify() will free the verification key. */ rv = soft_dsa_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); } CK_RV soft_genDSAkey_set_attribute(soft_object_t *key, CK_ATTRIBUTE_TYPE type, uchar_t *value, uint32_t value_len, boolean_t public) { CK_RV rv = CKR_OK; biginteger_t *dst = NULL; biginteger_t src; switch (type) { case CKA_VALUE: if (public) dst = OBJ_PUB_DSA_VALUE(key); else dst = OBJ_PRI_DSA_VALUE(key); break; case CKA_PRIME: if (public) dst = OBJ_PUB_DSA_PRIME(key); else dst = OBJ_PRI_DSA_PRIME(key); break; case CKA_SUBPRIME: if (public) dst = OBJ_PUB_DSA_SUBPRIME(key); else dst = OBJ_PRI_DSA_SUBPRIME(key); break; case CKA_BASE: if (public) dst = OBJ_PUB_DSA_BASE(key); else dst = OBJ_PRI_DSA_BASE(key); break; } src.big_value_len = value_len; if ((src.big_value = malloc(value_len)) == NULL) { rv = CKR_HOST_MEMORY; goto cleanexit; } (void) memcpy(src.big_value, value, value_len); /* Copy the attribute in the key object. */ copy_bigint_attr(&src, dst); cleanexit: /* No need to free big_value because dst holds it now after copy. */ return (rv); } CK_RV generate_dsa_key(DSAkey *key, boolean_t token_obj) { BIG_ERR_CODE err; do { if ((err = random_bignum(&(key->x), DSA_SUBPRIME_BITS, token_obj)) != BIG_OK) { return (convert_rv(err)); } } while (big_cmp_abs(&(key->x), &(key->q)) > 0); if ((err = big_modexp(&(key->y), &(key->g), (&key->x), (&key->p), NULL)) != BIG_OK) return (convert_rv(err)); return (CKR_OK); } CK_RV soft_dsa_genkey_pair(soft_object_t *pubkey, soft_object_t *prikey) { BIG_ERR_CODE brv; CK_RV rv; uchar_t prime[MAX_KEY_ATTR_BUFLEN]; uint32_t prime_len = sizeof (prime); uchar_t subprime[MAX_KEY_ATTR_BUFLEN]; uint32_t subprime_len = sizeof (subprime); uchar_t base[MAX_KEY_ATTR_BUFLEN]; uint32_t base_len = sizeof (base); uchar_t *pubvalue; uint32_t pubvalue_len; uchar_t *privalue; uint32_t privalue_len; DSAkey dsakey = {0}; pubvalue = NULL; privalue = NULL; if ((pubkey == NULL) || (prikey == NULL)) { return (CKR_ARGUMENTS_BAD); } /* lookup prime, subprime and base */ rv = soft_get_public_attr(pubkey, CKA_PRIME, prime, &prime_len); if (rv != CKR_OK) { rv = CKR_TEMPLATE_INCOMPLETE; goto cleanexit; } if ((prime_len < MIN_DSA_KEY_LEN) || (prime_len > MAX_DSA_KEY_LEN)) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto cleanexit; } rv = soft_get_public_attr(pubkey, CKA_SUBPRIME, subprime, &subprime_len); if (rv != CKR_OK) { rv = CKR_TEMPLATE_INCOMPLETE; goto cleanexit; } if (subprime_len != DSA_SUBPRIME_BYTES) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto cleanexit; } rv = soft_get_public_attr(pubkey, CKA_BASE, base, &base_len); if (rv != CKR_OK) { rv = CKR_TEMPLATE_INCOMPLETE; goto cleanexit; } /* * initialize the dsa key * Note: big_extend takes length in words */ if ((brv = DSA_key_init(&dsakey, prime_len * 8)) != BIG_OK) { rv = convert_rv(brv); goto cleanexit; } if ((brv = big_extend(&dsakey.p, (prime_len + 3) / 4)) != BIG_OK) { rv = convert_rv(brv); goto cleanexit; } bytestring2bignum(&dsakey.p, prime, prime_len); if ((brv = big_extend(&dsakey.q, (subprime_len + 3) / 4)) != BIG_OK) { rv = convert_rv(brv); goto cleanexit; } bytestring2bignum(&dsakey.q, subprime, subprime_len); if ((brv = big_extend(&dsakey.g, (base_len + 3) / 4)) != BIG_OK) { rv = convert_rv(brv); goto cleanexit; } bytestring2bignum(&dsakey.g, base, base_len); /* * generate DSA key pair * Note: bignum.len is length of value in words */ if ((rv = generate_dsa_key(&dsakey, (IS_TOKEN_OBJECT(pubkey) || IS_TOKEN_OBJECT(prikey)))) != CKR_OK) { goto cleanexit; } pubvalue_len = dsakey.y.len * (int)sizeof (uint32_t); if ((pubvalue = malloc(pubvalue_len)) == NULL) { rv = CKR_HOST_MEMORY; goto cleanexit; } bignum2bytestring(pubvalue, &dsakey.y, pubvalue_len); privalue_len = dsakey.x.len * (int)sizeof (uint32_t); if ((privalue = malloc(privalue_len)) == NULL) { rv = CKR_HOST_MEMORY; goto cleanexit; } bignum2bytestring(privalue, &dsakey.x, privalue_len); /* Update attribute in public key. */ if ((rv = soft_genDSAkey_set_attribute(pubkey, CKA_VALUE, pubvalue, pubvalue_len, B_TRUE)) != CKR_OK) { goto cleanexit; } /* Update attributes in private key. */ if ((rv = soft_genDSAkey_set_attribute(prikey, CKA_PRIME, prime, prime_len, B_FALSE)) != CKR_OK) { goto cleanexit; } if ((rv = soft_genDSAkey_set_attribute(prikey, CKA_SUBPRIME, subprime, subprime_len, B_FALSE)) != CKR_OK) { goto cleanexit; } if ((rv = soft_genDSAkey_set_attribute(prikey, CKA_BASE, base, base_len, B_FALSE)) != CKR_OK) { goto cleanexit; } if ((rv = soft_genDSAkey_set_attribute(prikey, CKA_VALUE, privalue, privalue_len, B_FALSE)) != CKR_OK) { goto cleanexit; } cleanexit: DSA_key_finish(&dsakey); if (pubvalue != NULL) { free(pubvalue); } if (privalue != NULL) { free(privalue); } return (rv); }