#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <security/cryptoki.h>
#include <sys/crypto/common.h>
#include <aes_impl.h>
#include <blowfish_impl.h>
#include <des_impl.h>
#include <arcfour.h>
#include <cryptoutil.h>
#include "softGlobal.h"
#include "softSession.h"
#include "softObject.h"
#include "softDSA.h"
#include "softRSA.h"
#include "softDH.h"
#include "softEC.h"
#include "softMAC.h"
#include "softOps.h"
#include "softKeys.h"
#include "softKeystore.h"
#include "softSSL.h"
#include "softASN1.h"
#define local_min(a, b) ((a) < (b) ? (a) : (b))
static CK_RV
soft_pkcs12_pbe(soft_session_t *, CK_MECHANISM_PTR, soft_object_t *);
CK_RV
soft_gen_keyobject(CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount,
soft_object_t **objp, soft_session_t *sp,
CK_OBJECT_CLASS class, CK_KEY_TYPE key_type, CK_ULONG keylen, CK_ULONG mode,
boolean_t internal)
{
CK_RV rv;
soft_object_t *new_objp = NULL;
new_objp = calloc(1, sizeof (soft_object_t));
if (new_objp == NULL) {
return (CKR_HOST_MEMORY);
}
new_objp->extra_attrlistp = NULL;
rv = soft_build_key(pTemplate, ulCount, new_objp, class, key_type,
keylen, mode);
if (rv != CKR_OK) {
goto fail_cleanup1;
}
if (!internal) {
rv = soft_pin_expired_check(new_objp);
if (rv != CKR_OK) {
goto fail_cleanup2;
}
rv = soft_object_write_access_check(sp, new_objp);
if (rv != CKR_OK) {
goto fail_cleanup2;
}
}
(void) pthread_mutex_init(&new_objp->object_mutex, NULL);
new_objp->magic_marker = SOFTTOKEN_OBJECT_MAGIC;
if (IS_TOKEN_OBJECT(new_objp)) {
new_objp->version = 1;
new_objp->session_handle = CK_INVALID_HANDLE;
soft_add_token_object_to_slot(new_objp);
set_objecthandle(new_objp);
*objp = new_objp;
return (CKR_OK);
}
new_objp->session_handle = sp->handle;
soft_add_object_to_session(new_objp, sp);
set_objecthandle(new_objp);
*objp = new_objp;
return (CKR_OK);
fail_cleanup2:
soft_cleanup_object(new_objp);
fail_cleanup1:
if (new_objp) {
free(new_objp);
}
return (rv);
}
CK_RV
soft_genkey(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism,
CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phKey)
{
CK_RV rv = CKR_OK;
soft_object_t *secret_key;
CK_KEY_TYPE key_type;
CK_ULONG keylen = 0;
CK_ULONG i;
int des_strength = 0;
int retry = 0;
int keyfound = 0;
boolean_t is_ssl_mech = B_FALSE;
switch (pMechanism->mechanism) {
case CKM_DES_KEY_GEN:
key_type = CKK_DES;
break;
case CKM_DES2_KEY_GEN:
key_type = CKK_DES2;
break;
case CKM_DES3_KEY_GEN:
key_type = CKK_DES3;
break;
case CKM_AES_KEY_GEN:
key_type = CKK_AES;
break;
case CKM_BLOWFISH_KEY_GEN:
key_type = CKK_BLOWFISH;
break;
case CKM_RC4_KEY_GEN:
key_type = CKK_RC4;
break;
case CKM_SSL3_PRE_MASTER_KEY_GEN:
case CKM_TLS_PRE_MASTER_KEY_GEN:
if (pMechanism->pParameter == NULL ||
pMechanism->ulParameterLen != sizeof (CK_VERSION))
return (CKR_TEMPLATE_INCOMPLETE);
is_ssl_mech = B_TRUE;
key_type = CKK_GENERIC_SECRET;
keylen = 48;
break;
case CKM_PKCS5_PBKD2:
keyfound = 0;
for (i = 0; i < ulCount && !keyfound; i++) {
if (pTemplate[i].type == CKA_KEY_TYPE &&
pTemplate[i].pValue != NULL) {
key_type = *((CK_KEY_TYPE*)pTemplate[i].pValue);
keyfound = 1;
}
}
if (!keyfound)
return (CKR_TEMPLATE_INCOMPLETE);
if (pMechanism->pParameter == NULL ||
pMechanism->ulParameterLen !=
sizeof (CK_PKCS5_PBKD2_PARAMS))
return (CKR_TEMPLATE_INCOMPLETE);
break;
case CKM_PBE_SHA1_RC4_128:
keyfound = 0;
for (i = 0; i < ulCount; i++) {
if (pTemplate[i].type == CKA_KEY_TYPE &&
pTemplate[i].pValue != NULL) {
key_type = *((CK_KEY_TYPE*)pTemplate[i].pValue);
keyfound = 1;
}
if (pTemplate[i].type == CKA_VALUE_LEN &&
pTemplate[i].pValue != NULL) {
keylen = *((CK_ULONG*)pTemplate[i].pValue);
}
}
if (keyfound && key_type != CKK_RC4)
return (CKR_TEMPLATE_INCONSISTENT);
else if (!keyfound)
key_type = CKK_RC4;
if (keylen != 0 && keylen != 16)
return (CKR_TEMPLATE_INCONSISTENT);
if (pMechanism->pParameter == NULL ||
pMechanism->ulParameterLen !=
sizeof (CK_PBE_PARAMS))
return (CKR_TEMPLATE_INCOMPLETE);
break;
default:
return (CKR_MECHANISM_INVALID);
}
rv = soft_gen_keyobject(pTemplate, ulCount, &secret_key, session_p,
CKO_SECRET_KEY, key_type, keylen, SOFT_GEN_KEY, B_FALSE);
if (rv != CKR_OK) {
return (rv);
}
switch (pMechanism->mechanism) {
case CKM_DES_KEY_GEN:
keylen = OBJ_SEC_VALUE_LEN(secret_key) = DES_KEYSIZE;
des_strength = DES;
break;
case CKM_DES2_KEY_GEN:
keylen = OBJ_SEC_VALUE_LEN(secret_key) = DES2_KEYSIZE;
des_strength = DES2;
break;
case CKM_DES3_KEY_GEN:
keylen = OBJ_SEC_VALUE_LEN(secret_key) = DES3_KEYSIZE;
des_strength = DES3;
break;
case CKM_SSL3_PRE_MASTER_KEY_GEN:
case CKM_TLS_PRE_MASTER_KEY_GEN:
secret_key->bool_attr_mask |= DERIVE_BOOL_ON;
case CKM_AES_KEY_GEN:
case CKM_BLOWFISH_KEY_GEN:
case CKM_PBE_SHA1_RC4_128:
case CKM_RC4_KEY_GEN:
keylen = OBJ_SEC_VALUE_LEN(secret_key);
break;
case CKM_PKCS5_PBKD2:
if (key_type == CKK_DES) {
OBJ_SEC_VALUE_LEN(secret_key) = DES_KEYSIZE;
des_strength = DES;
} else if (key_type == CKK_DES3) {
OBJ_SEC_VALUE_LEN(secret_key) = DES3_KEYSIZE;
des_strength = DES3;
}
keylen = OBJ_SEC_VALUE_LEN(secret_key);
break;
}
if ((OBJ_SEC_VALUE(secret_key) = malloc(keylen)) == NULL) {
if (IS_TOKEN_OBJECT(secret_key))
soft_delete_token_object(secret_key, B_FALSE, B_FALSE);
else
soft_delete_object(session_p, secret_key,
B_FALSE, B_FALSE);
return (CKR_HOST_MEMORY);
}
switch (pMechanism->mechanism) {
case CKM_PBE_SHA1_RC4_128:
rv = soft_pkcs12_pbe(session_p, pMechanism, secret_key);
break;
case CKM_PKCS5_PBKD2:
rv = soft_generate_pkcs5_pbkdf2_key(session_p, pMechanism,
secret_key);
if (rv == CKR_OK && des_strength > 0) {
if (des_keycheck(OBJ_SEC_VALUE(secret_key),
des_strength, OBJ_SEC_VALUE(secret_key)) ==
B_FALSE) {
rv = CKR_FUNCTION_FAILED;
}
}
break;
default:
do {
rv = CKR_OK;
if (pkcs11_get_urandom(
OBJ_SEC_VALUE(secret_key), keylen) < 0) {
rv = CKR_DEVICE_ERROR;
break;
}
if (des_strength > 0) {
rv = CKR_OK;
if (des_keycheck(OBJ_SEC_VALUE(secret_key),
des_strength, OBJ_SEC_VALUE(secret_key)) ==
B_FALSE) {
retry++;
rv = CKR_FUNCTION_FAILED;
}
}
if (is_ssl_mech)
bcopy(pMechanism->pParameter,
OBJ_SEC_VALUE(secret_key),
sizeof (CK_VERSION));
} while (rv != CKR_OK && retry < KEYGEN_RETRY);
if (retry == KEYGEN_RETRY)
rv = CKR_FUNCTION_FAILED;
break;
}
if (rv != CKR_OK)
if (IS_TOKEN_OBJECT(secret_key))
soft_delete_token_object(secret_key, B_FALSE, B_FALSE);
else
soft_delete_object(session_p, secret_key,
B_FALSE, B_FALSE);
if (IS_TOKEN_OBJECT(secret_key)) {
rv = soft_put_object_to_keystore(secret_key);
if (rv != CKR_OK)
soft_delete_token_object(secret_key, B_FALSE, B_FALSE);
}
*phKey = secret_key->handle;
return (rv);
}
CK_RV
soft_genkey_pair(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism,
CK_ATTRIBUTE_PTR pPublicKeyTemplate, CK_ULONG ulPublicAttrCount,
CK_ATTRIBUTE_PTR pPrivateKeyTemplate, CK_ULONG ulPrivateAttrCount,
CK_OBJECT_HANDLE_PTR phPublicKey, CK_OBJECT_HANDLE_PTR phPrivateKey)
{
CK_RV rv;
soft_object_t *public_key, *private_key;
CK_KEY_TYPE key_type;
switch (pMechanism->mechanism) {
case CKM_RSA_PKCS_KEY_PAIR_GEN:
key_type = CKK_RSA;
break;
case CKM_DSA_KEY_PAIR_GEN:
key_type = CKK_DSA;
break;
case CKM_DH_PKCS_KEY_PAIR_GEN:
key_type = CKK_DH;
break;
case CKM_EC_KEY_PAIR_GEN:
key_type = CKK_EC;
break;
default:
return (CKR_MECHANISM_INVALID);
}
rv = soft_gen_keyobject(pPublicKeyTemplate, ulPublicAttrCount,
&public_key, session_p, CKO_PUBLIC_KEY, key_type, 0,
SOFT_GEN_KEY, B_FALSE);
if (rv != CKR_OK) {
return (rv);
}
rv = soft_gen_keyobject(pPrivateKeyTemplate, ulPrivateAttrCount,
&private_key, session_p, CKO_PRIVATE_KEY, key_type, 0,
SOFT_GEN_KEY, B_FALSE);
if (rv != CKR_OK) {
if (IS_TOKEN_OBJECT(public_key))
soft_delete_token_object(public_key, B_FALSE, B_FALSE);
else
soft_delete_object(session_p, public_key,
B_FALSE, B_FALSE);
return (rv);
}
switch (key_type) {
case CKK_RSA:
rv = soft_rsa_genkey_pair(public_key, private_key);
break;
case CKK_DSA:
rv = soft_dsa_genkey_pair(public_key, private_key);
break;
case CKK_DH:
rv = soft_dh_genkey_pair(public_key, private_key);
private_key->bool_attr_mask |= DERIVE_BOOL_ON;
break;
case CKK_EC:
rv = soft_ec_genkey_pair(public_key, private_key);
private_key->bool_attr_mask |= DERIVE_BOOL_ON;
break;
}
if (rv != CKR_OK) {
if (IS_TOKEN_OBJECT(public_key)) {
soft_delete_token_object(public_key, B_FALSE, B_FALSE);
soft_delete_token_object(private_key, B_FALSE, B_FALSE);
} else {
soft_delete_object(session_p, public_key,
B_FALSE, B_FALSE);
soft_delete_object(session_p, private_key,
B_FALSE, B_FALSE);
}
return (rv);
}
if (IS_TOKEN_OBJECT(public_key)) {
rv = soft_put_object_to_keystore(public_key);
if (rv != CKR_OK) {
soft_delete_token_object(public_key, B_FALSE, B_FALSE);
soft_delete_token_object(private_key, B_FALSE, B_FALSE);
return (rv);
}
}
if (IS_TOKEN_OBJECT(private_key)) {
rv = soft_put_object_to_keystore(private_key);
if (rv != CKR_OK) {
soft_delete_token_object(public_key, B_TRUE, B_FALSE);
soft_delete_token_object(private_key, B_FALSE, B_FALSE);
}
}
*phPublicKey = public_key->handle;
*phPrivateKey = private_key->handle;
return (rv);
}
CK_RV
soft_key_derive_check_length(soft_object_t *secret_key, CK_ULONG max_keylen)
{
switch (secret_key->key_type) {
case CKK_GENERIC_SECRET:
if (OBJ_SEC_VALUE_LEN(secret_key) == 0) {
OBJ_SEC_VALUE_LEN(secret_key) = max_keylen;
return (CKR_OK);
} else if (OBJ_SEC_VALUE_LEN(secret_key) > max_keylen) {
return (CKR_ATTRIBUTE_VALUE_INVALID);
}
break;
case CKK_RC4:
case CKK_AES:
case CKK_BLOWFISH:
if ((OBJ_SEC_VALUE_LEN(secret_key) == 0) ||
(OBJ_SEC_VALUE_LEN(secret_key) > max_keylen)) {
return (CKR_ATTRIBUTE_VALUE_INVALID);
}
break;
case CKK_DES:
if (OBJ_SEC_VALUE_LEN(secret_key) == 0) {
OBJ_SEC_VALUE_LEN(secret_key) = DES_KEYSIZE;
return (CKR_OK);
} else if (OBJ_SEC_VALUE_LEN(secret_key) != DES_KEYSIZE) {
return (CKR_ATTRIBUTE_VALUE_INVALID);
}
break;
case CKK_DES2:
if (OBJ_SEC_VALUE_LEN(secret_key) == 0) {
OBJ_SEC_VALUE_LEN(secret_key) = DES2_KEYSIZE;
return (CKR_OK);
} else if (OBJ_SEC_VALUE_LEN(secret_key) != DES2_KEYSIZE) {
return (CKR_ATTRIBUTE_VALUE_INVALID);
}
break;
default:
return (CKR_MECHANISM_INVALID);
}
return (CKR_OK);
}
#define PKCS12_BUFFER_SIZE 64
#define PBE_ID_ENCRYPT 1
#define PBE_ID_IV 2
#define PBE_ID_MAC 3
#define PBE_CEIL(a, b) (((a)/(b)) + (((a)%(b)) > 0))
static CK_RV
soft_pkcs12_pbe(soft_session_t *session_p,
CK_MECHANISM_PTR pMechanism, soft_object_t *derived_key)
{
CK_RV rv = CKR_OK;
CK_PBE_PARAMS *params = pMechanism->pParameter;
CK_ULONG c, i, j, k;
CK_ULONG hashSize;
CK_ULONG buffSize;
CK_BYTE *A = NULL;
CK_BYTE *Ai = NULL;
CK_BYTE *B = NULL;
CK_BYTE *D = NULL;
CK_BYTE *I = NULL, *S, *P;
CK_BYTE *keybuf = NULL;
CK_ULONG Alen, Ilen, Slen, Plen, AiLen, Blen, Dlen;
CK_ULONG keysize = OBJ_SEC_VALUE_LEN(derived_key);
CK_MECHANISM digest_mech;
if (pMechanism->mechanism == CKM_PBE_SHA1_RC4_128) {
hashSize = SHA1_HASH_SIZE;
buffSize = PKCS12_BUFFER_SIZE;
digest_mech.mechanism = CKM_SHA_1;
digest_mech.pParameter = NULL;
digest_mech.ulParameterLen = 0;
} else {
return (CKR_MECHANISM_INVALID);
}
keybuf = OBJ_SEC_VALUE(derived_key);
Blen = Dlen = buffSize;
D = (CK_BYTE *)malloc(Dlen);
if (D == NULL) {
rv = CKR_HOST_MEMORY;
goto cleanup;
}
B = (CK_BYTE *)malloc(Blen);
if (B == NULL) {
rv = CKR_HOST_MEMORY;
goto cleanup;
}
Slen = buffSize * PBE_CEIL(params->ulSaltLen, buffSize);
Plen = buffSize * PBE_CEIL(params->ulPasswordLen, buffSize);
Ilen = Slen + Plen;
I = (CK_BYTE *)malloc(Ilen);
if (I == NULL) {
rv = CKR_HOST_MEMORY;
goto cleanup;
}
S = I;
P = I + Slen;
(void) memset(D, PBE_ID_ENCRYPT, Dlen);
for (i = 0; i < Slen; i += params->ulSaltLen) {
(void) memcpy(S+i, params->pSalt,
((Slen - i) > params->ulSaltLen ?
params->ulSaltLen : (Slen - i)));
}
for (i = 0; i < Plen; i += params->ulPasswordLen) {
(void) memcpy(P+i, params->pPassword,
((Plen - i) > params->ulPasswordLen ?
params->ulPasswordLen : (Plen - i)));
}
c = PBE_CEIL(keysize, hashSize);
Alen = c * hashSize;
A = (CK_BYTE *)malloc(Alen);
if (A == NULL) {
rv = CKR_HOST_MEMORY;
goto cleanup;
}
AiLen = hashSize;
Ai = (CK_BYTE *)malloc(AiLen);
if (Ai == NULL) {
rv = CKR_HOST_MEMORY;
goto cleanup;
}
for (i = 0; i < c; i++) {
(void) pthread_mutex_lock(&session_p->session_mutex);
if (session_p->sign.flags & CRYPTO_OPERATION_ACTIVE) {
(void) pthread_mutex_unlock(&session_p->session_mutex);
rv = CKR_OPERATION_ACTIVE;
goto cleanup;
}
session_p->sign.flags |= CRYPTO_OPERATION_ACTIVE;
(void) pthread_mutex_unlock(&session_p->session_mutex);
for (j = 0; j < params->ulIteration; j++) {
rv = soft_digest_init(session_p, &digest_mech);
if (rv != CKR_OK)
goto digest_done;
if (j == 0) {
rv = soft_digest_update(session_p, D, Dlen);
if (rv != CKR_OK)
goto digest_done;
rv = soft_digest_update(session_p, I, Ilen);
} else {
rv = soft_digest_update(session_p, Ai, AiLen);
}
if (rv != CKR_OK)
goto digest_done;
rv = soft_digest_final(session_p, Ai, &AiLen);
if (rv != CKR_OK)
goto digest_done;
}
digest_done:
(void) pthread_mutex_lock(&session_p->session_mutex);
session_p->sign.flags &= ~CRYPTO_OPERATION_ACTIVE;
(void) pthread_mutex_unlock(&session_p->session_mutex);
if (rv != CKR_OK)
goto cleanup;
for (j = 0; j < Blen; j += hashSize) {
(void) memcpy(B+j, Ai, ((Blen - j > hashSize) ?
hashSize : Blen - j));
}
k = Ilen / Blen;
for (j = 0; j < k; j++) {
uchar_t idx;
CK_ULONG m, q = 1, cbit = 0;
for (m = Blen - 1; m >= (CK_ULONG)0; m--, q = 0) {
idx = m + j*Blen;
q += (CK_ULONG)I[idx] + (CK_ULONG)B[m];
q += cbit;
I[idx] = (CK_BYTE)(q & 0xff);
cbit = (q > 0xff);
}
}
(void) memcpy(A + i*hashSize, Ai, AiLen);
}
(void) memcpy(keybuf, A, keysize);
cleanup:
freezero(A, Alen);
freezero(Ai, AiLen);
freezero(B, Blen);
freezero(D, Dlen);
freezero(I, Ilen);
return (rv);
}
CK_RV
soft_derivekey(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism,
soft_object_t *basekey_p, CK_ATTRIBUTE_PTR pTemplate,
CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey)
{
CK_RV rv = CKR_OK;
soft_object_t *secret_key;
CK_MECHANISM digest_mech;
CK_BYTE hash[SHA512_DIGEST_LENGTH];
CK_ULONG hash_len = SHA512_DIGEST_LENGTH;
CK_ULONG secret_key_len;
CK_ULONG hash_size;
switch (pMechanism->mechanism) {
case CKM_DH_PKCS_DERIVE:
if (phKey == NULL_PTR)
return (CKR_ARGUMENTS_BAD);
rv = soft_gen_keyobject(pTemplate, ulAttributeCount,
&secret_key, session_p, CKO_SECRET_KEY, (CK_KEY_TYPE)~0UL,
0, SOFT_DERIVE_KEY_DH, B_FALSE);
if (rv != CKR_OK) {
return (rv);
}
rv = soft_dh_key_derive(basekey_p, secret_key,
(CK_BYTE *)pMechanism->pParameter,
pMechanism->ulParameterLen);
if (rv != CKR_OK) {
if (IS_TOKEN_OBJECT(secret_key))
soft_delete_token_object(secret_key, B_FALSE,
B_FALSE);
else
soft_delete_object(session_p, secret_key,
B_FALSE, B_FALSE);
return (rv);
}
break;
case CKM_ECDH1_DERIVE:
if (phKey == NULL_PTR)
return (CKR_ARGUMENTS_BAD);
rv = soft_gen_keyobject(pTemplate, ulAttributeCount,
&secret_key, session_p, CKO_SECRET_KEY, (CK_KEY_TYPE)~0UL,
0, SOFT_DERIVE_KEY_DH, B_FALSE);
if (rv != CKR_OK) {
return (rv);
}
rv = soft_ec_key_derive(basekey_p, secret_key,
(CK_BYTE *)pMechanism->pParameter,
pMechanism->ulParameterLen);
if (rv != CKR_OK) {
if (IS_TOKEN_OBJECT(secret_key))
soft_delete_token_object(secret_key, B_FALSE,
B_FALSE);
else
soft_delete_object(session_p, secret_key,
B_FALSE, B_FALSE);
return (rv);
}
break;
case CKM_SHA1_KEY_DERIVATION:
hash_size = SHA1_HASH_SIZE;
digest_mech.mechanism = CKM_SHA_1;
goto common;
case CKM_MD5_KEY_DERIVATION:
hash_size = MD5_HASH_SIZE;
digest_mech.mechanism = CKM_MD5;
goto common;
case CKM_SHA256_KEY_DERIVATION:
hash_size = SHA256_DIGEST_LENGTH;
digest_mech.mechanism = CKM_SHA256;
goto common;
case CKM_SHA384_KEY_DERIVATION:
hash_size = SHA384_DIGEST_LENGTH;
digest_mech.mechanism = CKM_SHA384;
goto common;
case CKM_SHA512_KEY_DERIVATION:
hash_size = SHA512_DIGEST_LENGTH;
digest_mech.mechanism = CKM_SHA512;
goto common;
case CKM_SHA512_224_KEY_DERIVATION:
hash_size = SHA512_224_DIGEST_LENGTH;
digest_mech.mechanism = CKM_SHA512_224;
goto common;
case CKM_SHA512_256_KEY_DERIVATION:
hash_size = SHA512_256_DIGEST_LENGTH;
digest_mech.mechanism = CKM_SHA512_256;
goto common;
common:
if (phKey == NULL_PTR)
return (CKR_ARGUMENTS_BAD);
rv = soft_gen_keyobject(pTemplate, ulAttributeCount,
&secret_key, session_p, CKO_SECRET_KEY,
(CK_KEY_TYPE)CKK_GENERIC_SECRET, 0,
SOFT_DERIVE_KEY_OTHER, B_FALSE);
if (rv != CKR_OK) {
return (rv);
}
rv = soft_key_derive_check_length(secret_key, hash_size);
if (rv != CKR_OK) {
if (IS_TOKEN_OBJECT(secret_key))
soft_delete_token_object(secret_key, B_FALSE,
B_FALSE);
else
soft_delete_object(session_p, secret_key,
B_FALSE, B_FALSE);
return (rv);
}
rv = soft_digest_init_internal(session_p, &digest_mech);
if (rv != CKR_OK) {
if (IS_TOKEN_OBJECT(secret_key))
soft_delete_token_object(secret_key, B_FALSE,
B_FALSE);
else
soft_delete_object(session_p, secret_key,
B_FALSE, B_FALSE);
return (rv);
}
rv = soft_digest(session_p, OBJ_SEC_VALUE(basekey_p),
OBJ_SEC_VALUE_LEN(basekey_p), hash, &hash_len);
(void) pthread_mutex_lock(&session_p->session_mutex);
session_p->digest.flags = 0;
(void) pthread_mutex_unlock(&session_p->session_mutex);
if (rv != CKR_OK) {
if (IS_TOKEN_OBJECT(secret_key))
soft_delete_token_object(secret_key, B_FALSE,
B_FALSE);
else
soft_delete_object(session_p, secret_key,
B_FALSE, B_FALSE);
return (rv);
}
secret_key_len = OBJ_SEC_VALUE_LEN(secret_key);
if ((OBJ_SEC_VALUE(secret_key) = malloc(secret_key_len)) ==
NULL) {
if (IS_TOKEN_OBJECT(secret_key))
soft_delete_token_object(secret_key, B_FALSE,
B_FALSE);
else
soft_delete_object(session_p, secret_key,
B_FALSE, B_FALSE);
return (CKR_HOST_MEMORY);
}
(void) memcpy(OBJ_SEC_VALUE(secret_key),
(hash + hash_len - secret_key_len),
secret_key_len);
break;
case CKM_SSL3_MASTER_KEY_DERIVE:
case CKM_SSL3_MASTER_KEY_DERIVE_DH:
case CKM_TLS_MASTER_KEY_DERIVE:
case CKM_TLS_MASTER_KEY_DERIVE_DH:
if (phKey == NULL_PTR)
return (CKR_ARGUMENTS_BAD);
return (soft_ssl_master_key_derive(session_p, pMechanism,
basekey_p, pTemplate, ulAttributeCount, phKey));
case CKM_SSL3_KEY_AND_MAC_DERIVE:
case CKM_TLS_KEY_AND_MAC_DERIVE:
return (soft_ssl_key_and_mac_derive(session_p, pMechanism,
basekey_p, pTemplate, ulAttributeCount));
case CKM_TLS_PRF:
if (pMechanism->pParameter == NULL ||
pMechanism->ulParameterLen != sizeof (CK_TLS_PRF_PARAMS) ||
phKey != NULL)
return (CKR_ARGUMENTS_BAD);
if (pTemplate != NULL)
return (CKR_TEMPLATE_INCONSISTENT);
return (derive_tls_prf(
(CK_TLS_PRF_PARAMS_PTR)pMechanism->pParameter, basekey_p));
default:
return (CKR_MECHANISM_INVALID);
}
soft_derive_enforce_flags(basekey_p, secret_key);
if (IS_TOKEN_OBJECT(secret_key)) {
rv = soft_put_object_to_keystore(secret_key);
if (rv != CKR_OK)
soft_delete_token_object(secret_key, B_FALSE, B_FALSE);
}
*phKey = secret_key->handle;
return (rv);
}
void
soft_derive_enforce_flags(soft_object_t *basekey, soft_object_t *newkey)
{
boolean_t new_sensitive = B_FALSE;
boolean_t new_extractable = B_FALSE;
if (newkey->bool_attr_mask & SENSITIVE_BOOL_ON) {
new_sensitive = B_TRUE;
}
if (newkey->bool_attr_mask & EXTRACTABLE_BOOL_ON) {
new_extractable = B_TRUE;
}
if (!basekey->bool_attr_mask & ALWAYS_SENSITIVE_BOOL_ON) {
newkey->bool_attr_mask &= ~ALWAYS_SENSITIVE_BOOL_ON;
} else {
if (new_sensitive) {
newkey->bool_attr_mask |= ALWAYS_SENSITIVE_BOOL_ON;
} else {
newkey->bool_attr_mask &= ~ALWAYS_SENSITIVE_BOOL_ON;
}
}
if (!basekey->bool_attr_mask & NEVER_EXTRACTABLE_BOOL_ON) {
newkey->bool_attr_mask &= ~NEVER_EXTRACTABLE_BOOL_ON;
} else {
if (new_extractable) {
newkey->bool_attr_mask &= ~NEVER_EXTRACTABLE_BOOL_ON;
} else {
newkey->bool_attr_mask |= NEVER_EXTRACTABLE_BOOL_ON;
}
}
newkey->bool_attr_mask &= ~LOCAL_BOOL_ON;
}
static CK_RV
do_prf(soft_session_t *session_p, CK_PKCS5_PBKD2_PARAMS_PTR params,
soft_object_t *hmac_key, CK_BYTE *newsalt, CK_ULONG saltlen,
CK_BYTE *blockdata, CK_ULONG blocklen)
{
CK_RV rv = CKR_OK;
CK_MECHANISM digest_mech = {CKM_SHA_1_HMAC, NULL, 0};
CK_BYTE buffer[2][SHA1_HASH_SIZE];
CK_ULONG hmac_outlen = SHA1_HASH_SIZE;
CK_ULONG inlen;
CK_BYTE *input, *output;
CK_ULONG i, j;
input = newsalt;
inlen = saltlen;
output = buffer[1];
(void) pthread_mutex_lock(&session_p->session_mutex);
if (session_p->sign.flags & CRYPTO_OPERATION_ACTIVE) {
(void) pthread_mutex_unlock(&session_p->session_mutex);
return (CKR_OPERATION_ACTIVE);
}
session_p->sign.flags |= CRYPTO_OPERATION_ACTIVE;
(void) pthread_mutex_unlock(&session_p->session_mutex);
for (i = 0; i < params->iterations; i++) {
rv = soft_sign_init(session_p, &digest_mech, hmac_key);
if (rv != CKR_OK) {
goto cleanup;
}
rv = soft_sign(session_p, input, inlen, output, &hmac_outlen);
if (rv != CKR_OK) {
goto cleanup;
}
if (i == 0) {
(void) memcpy(blockdata, output,
local_min(blocklen, hmac_outlen));
} else {
for (j = 0; j < hmac_outlen && j < blocklen; j++)
blockdata[j] ^= output[j];
}
input = output;
inlen = hmac_outlen;
output = buffer[i%2];
}
cleanup:
(void) pthread_mutex_lock(&session_p->session_mutex);
session_p->sign.flags &= ~CRYPTO_OPERATION_ACTIVE;
(void) pthread_mutex_unlock(&session_p->session_mutex);
return (rv);
}
static CK_RV
soft_create_hmac_key(soft_session_t *session_p, CK_BYTE *passwd,
CK_ULONG passwd_len, soft_object_t **keyp)
{
CK_RV rv = CKR_OK;
CK_OBJECT_CLASS keyclass = CKO_SECRET_KEY;
CK_KEY_TYPE keytype = CKK_GENERIC_SECRET;
CK_BBOOL True = TRUE;
CK_ATTRIBUTE keytemplate[4];
keytemplate[0].type = CKA_CLASS;
keytemplate[0].pValue = &keyclass;
keytemplate[0].ulValueLen = sizeof (keyclass);
keytemplate[1].type = CKA_KEY_TYPE;
keytemplate[1].pValue = &keytype;
keytemplate[1].ulValueLen = sizeof (keytype);
keytemplate[2].type = CKA_SIGN;
keytemplate[2].pValue = &True;
keytemplate[2].ulValueLen = sizeof (True);
keytemplate[3].type = CKA_VALUE;
keytemplate[3].pValue = passwd;
keytemplate[3].ulValueLen = passwd_len;
rv = soft_gen_keyobject(keytemplate,
sizeof (keytemplate)/sizeof (CK_ATTRIBUTE), keyp, session_p,
CKO_SECRET_KEY, (CK_KEY_TYPE)CKK_GENERIC_SECRET, 0,
SOFT_CREATE_OBJ, B_TRUE);
return (rv);
}
CK_RV
soft_generate_pkcs5_pbkdf2_key(soft_session_t *session_p,
CK_MECHANISM_PTR pMechanism, soft_object_t *secret_key)
{
CK_RV rv = CKR_OK;
CK_PKCS5_PBKD2_PARAMS *params =
(CK_PKCS5_PBKD2_PARAMS *)pMechanism->pParameter;
CK_ULONG hLen = SHA1_HASH_SIZE;
CK_ULONG dkLen, i;
CK_ULONG blocks, remainder;
soft_object_t *hmac_key = NULL;
CK_BYTE *salt = NULL;
CK_BYTE *keydata = NULL;
params = (CK_PKCS5_PBKD2_PARAMS_PTR) pMechanism->pParameter;
if (params->prf != CKP_PKCS5_PBKD2_HMAC_SHA1)
return (CKR_MECHANISM_PARAM_INVALID);
if (params->pPrfData != NULL || params->ulPrfDataLen != 0)
return (CKR_DATA_INVALID);
if (params->saltSource != CKZ_SALT_SPECIFIED ||
params->iterations == 0)
return (CKR_MECHANISM_PARAM_INVALID);
rv = soft_create_hmac_key(session_p, params->pPassword,
*params->ulPasswordLen, &hmac_key);
if (rv != CKR_OK)
return (rv);
dkLen = OBJ_SEC_VALUE_LEN(secret_key);
if (dkLen > ((((u_longlong_t)1)<<32)-1)*hLen) {
(void) soft_delete_object(session_p, hmac_key, B_FALSE,
B_FALSE);
return (CKR_KEY_SIZE_RANGE);
}
blocks = dkLen / hLen;
if (blocks * hLen != dkLen)
blocks++;
remainder = dkLen - ((blocks - 1) * hLen);
salt = (CK_BYTE *)malloc(params->ulSaltSourceDataLen + 4);
if (salt == NULL) {
(void) soft_delete_object(session_p, hmac_key, B_FALSE,
B_FALSE);
return (CKR_HOST_MEMORY);
}
if (params->pSaltSourceData != NULL && params->ulSaltSourceDataLen > 0)
(void) memcpy(salt, params->pSaltSourceData,
params->ulSaltSourceDataLen);
keydata = (CK_BYTE *)OBJ_SEC_VALUE(secret_key);
for (i = 0; i < blocks && (rv == CKR_OK); i++) {
CK_BYTE *s;
s = salt + params->ulSaltSourceDataLen;
*s++ = ((i+1) >> 24) & 0xff;
*s++ = ((i+1) >> 16) & 0xff;
*s++ = ((i+1) >> 8) & 0xff;
*s = ((i+1)) & 0xff;
rv = do_prf(session_p, params, hmac_key,
salt, params->ulSaltSourceDataLen + 4, keydata,
((i + 1) == blocks ? remainder : hLen));
keydata += hLen;
}
(void) soft_delete_object(session_p, hmac_key, B_FALSE, B_FALSE);
freezero(salt, params->ulSaltSourceDataLen);
return (rv);
}
CK_RV
soft_wrapkey(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism,
soft_object_t *wrappingKey_p, soft_object_t *hkey_p,
CK_BYTE_PTR pWrappedKey, CK_ULONG_PTR pulWrappedKeyLen)
{
CK_RV rv = CKR_OK;
CK_ULONG plain_len = 0;
CK_BYTE_PTR plain_data = NULL;
CK_ULONG padded_len = 0;
CK_BYTE_PTR padded_data = NULL;
CK_ULONG wkey_blksz = 1;
switch (pMechanism->mechanism) {
case CKM_DES_CBC_PAD:
case CKM_DES3_CBC_PAD:
case CKM_AES_CBC_PAD:
if (hkey_p->class != CKO_SECRET_KEY && hkey_p->class !=
CKO_PRIVATE_KEY)
return (CKR_MECHANISM_INVALID);
break;
case CKM_RSA_PKCS:
case CKM_RSA_X_509:
case CKM_DES_ECB:
case CKM_DES3_ECB:
case CKM_AES_ECB:
case CKM_DES_CBC:
case CKM_DES3_CBC:
case CKM_AES_CBC:
case CKM_AES_CTR:
case CKM_BLOWFISH_CBC:
if (hkey_p->class != CKO_SECRET_KEY)
return (CKR_MECHANISM_INVALID);
break;
default:
return (CKR_MECHANISM_INVALID);
}
if (hkey_p->class == CKO_SECRET_KEY) {
plain_data = OBJ_SEC_VALUE(hkey_p);
plain_len = OBJ_SEC_VALUE_LEN(hkey_p);
} else {
rv = soft_object_to_asn1(hkey_p, NULL, &plain_len);
if (rv != CKR_OK)
return (rv);
if ((plain_data = malloc(plain_len)) == NULL)
return (CKR_HOST_MEMORY);
(void) memset(plain_data, 0x0, plain_len);
rv = soft_object_to_asn1(hkey_p, plain_data, &plain_len);
if (rv != CKR_OK)
goto cleanup_wrap;
}
padded_len = plain_len;
padded_data = plain_data;
switch (pMechanism->mechanism) {
case CKM_DES_ECB:
case CKM_DES3_ECB:
case CKM_AES_ECB:
case CKM_DES_CBC:
case CKM_DES3_CBC:
case CKM_AES_CBC:
case CKM_BLOWFISH_CBC:
if (wrappingKey_p->class == CKO_SECRET_KEY) {
switch (wrappingKey_p->key_type) {
case CKK_DES:
case CKK_DES2:
case CKK_DES3:
wkey_blksz = DES_BLOCK_LEN;
break;
case CKK_AES:
wkey_blksz = AES_BLOCK_LEN;
break;
case CKK_BLOWFISH:
wkey_blksz = BLOWFISH_BLOCK_LEN;
break;
default:
break;
}
} else {
rv = CKR_WRAPPING_KEY_TYPE_INCONSISTENT;
goto cleanup_wrap;
}
if ((padded_len % wkey_blksz) != 0) {
padded_len += (wkey_blksz - (plain_len % wkey_blksz));
if ((padded_data = malloc(padded_len)) == NULL) {
rv = CKR_HOST_MEMORY;
goto cleanup_wrap;
}
(void) memset(padded_data, 0x0, padded_len);
(void) memcpy(padded_data, plain_data, plain_len);
}
break;
default:
break;
}
rv = soft_encrypt_init(session_p, pMechanism, wrappingKey_p);
if (rv != CKR_OK)
goto cleanup_wrap;
rv = soft_encrypt(session_p, padded_data, padded_len,
pWrappedKey, pulWrappedKeyLen);
cleanup_wrap:
if (padded_data != NULL && padded_len != plain_len) {
freezero(padded_data, padded_len);
}
if ((hkey_p->class != CKO_SECRET_KEY) && (plain_data != NULL)) {
freezero(plain_data, plain_len);
}
return (rv);
}
static CK_RV
soft_unwrap_secret_len_check(CK_KEY_TYPE keytype, CK_MECHANISM_TYPE mechtype,
CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount)
{
CK_ULONG i;
boolean_t isValueLen = B_FALSE;
for (i = 0; i < ulAttributeCount; i++) {
if (pTemplate[i].type == CKA_VALUE_LEN &&
pTemplate[i].pValue != NULL) {
isValueLen = B_TRUE;
break;
}
}
switch (mechtype) {
case CKM_DES_ECB:
case CKM_DES3_ECB:
case CKM_AES_ECB:
case CKM_DES_CBC:
case CKM_DES3_CBC:
case CKM_AES_CBC:
case CKM_BLOWFISH_CBC:
switch (keytype) {
case CKK_DES:
case CKK_DES2:
case CKK_DES3:
if (isValueLen)
return (CKR_TEMPLATE_INCONSISTENT);
break;
case CKK_GENERIC_SECRET:
case CKK_RC4:
case CKK_AES:
case CKK_BLOWFISH:
if (!isValueLen)
return (CKR_TEMPLATE_INCOMPLETE);
break;
default:
return (CKR_FUNCTION_NOT_SUPPORTED);
}
break;
default:
if (isValueLen)
return (CKR_TEMPLATE_INCONSISTENT);
break;
}
return (CKR_OK);
}
CK_RV
soft_unwrapkey(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism,
soft_object_t *unwrappingkey_p, CK_BYTE_PTR pWrappedKey,
CK_ULONG ulWrappedKeyLen, CK_ATTRIBUTE_PTR pTemplate,
CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey)
{
CK_RV rv = CKR_OK;
CK_OBJECT_CLASS new_obj_class = ~0UL;
int i = 0;
soft_object_t *new_objp = NULL;
boolean_t persistent = B_FALSE;
CK_BYTE_PTR plain_data = NULL;
CK_ULONG plain_len = 0;
secret_key_obj_t *sck = NULL;
if (pTemplate != NULL && ulAttributeCount != 0) {
for (i = 0; i < ulAttributeCount; i++) {
if (pTemplate[i].type == CKA_CLASS) {
new_obj_class =
*((CK_OBJECT_CLASS *)pTemplate[i].pValue);
break;
}
}
if (new_obj_class == ~0UL)
return (CKR_TEMPLATE_INCOMPLETE);
}
switch (pMechanism->mechanism) {
case CKM_RSA_PKCS:
case CKM_RSA_X_509:
case CKM_DES_ECB:
case CKM_DES3_ECB:
case CKM_AES_ECB:
case CKM_DES_CBC:
case CKM_DES3_CBC:
case CKM_AES_CBC:
case CKM_BLOWFISH_CBC:
if (new_obj_class != CKO_SECRET_KEY)
return (CKR_MECHANISM_INVALID);
break;
case CKM_DES_CBC_PAD:
case CKM_DES3_CBC_PAD:
case CKM_AES_CBC_PAD:
if (new_obj_class != CKO_SECRET_KEY && new_obj_class !=
CKO_PRIVATE_KEY)
return (CKR_MECHANISM_INVALID);
break;
default:
return (CKR_MECHANISM_INVALID);
}
rv = soft_gen_keyobject(pTemplate, ulAttributeCount,
&new_objp, session_p, (CK_OBJECT_CLASS)~0UL,
(CK_KEY_TYPE)~0UL, 0, SOFT_UNWRAP_KEY, B_FALSE);
if (rv != CKR_OK)
return (rv);
if ((new_objp->bool_attr_mask & NEVER_EXTRACTABLE_BOOL_ON) ||
(new_objp->bool_attr_mask & ALWAYS_SENSITIVE_BOOL_ON)) {
rv = CKR_TEMPLATE_INCONSISTENT;
goto cleanup_unwrap;
}
rv = soft_decrypt_init(session_p, pMechanism, unwrappingkey_p);
if (rv != CKR_OK)
goto cleanup_unwrap;
rv = soft_decrypt(session_p, pWrappedKey, ulWrappedKeyLen, NULL,
&plain_len);
if (rv != CKR_OK)
goto cleanup_unwrap;
if ((plain_data = malloc(plain_len)) == NULL) {
rv = CKR_HOST_MEMORY;
goto cleanup_unwrap;
}
(void) memset(plain_data, 0x0, plain_len);
rv = soft_decrypt(session_p, pWrappedKey, ulWrappedKeyLen, plain_data,
&plain_len);
if (rv != CKR_OK)
goto cleanup_unwrap;
if (new_objp->class == CKO_SECRET_KEY) {
rv = soft_unwrap_secret_len_check(new_objp->key_type,
pMechanism->mechanism, pTemplate, ulAttributeCount);
if (rv != CKR_OK)
goto cleanup_unwrap;
if (OBJ_SEC(new_objp) == NULL) {
if ((sck = calloc(1, sizeof (secret_key_obj_t))) ==
NULL) {
rv = CKR_HOST_MEMORY;
goto cleanup_unwrap;
}
OBJ_SEC(new_objp) = sck;
}
switch (new_objp->key_type) {
case CKK_DES:
OBJ_SEC_VALUE_LEN(new_objp) = DES_KEYSIZE;
break;
case CKK_DES2:
OBJ_SEC_VALUE_LEN(new_objp) = DES2_KEYSIZE;
break;
case CKK_DES3:
OBJ_SEC_VALUE_LEN(new_objp) = DES3_KEYSIZE;
break;
case CKK_GENERIC_SECRET:
case CKK_RC4:
case CKK_AES:
case CKK_BLOWFISH:
break;
default:
rv = CKR_WRAPPED_KEY_INVALID;
goto cleanup_unwrap;
};
if (OBJ_SEC_VALUE_LEN(new_objp) == 0) {
OBJ_SEC_VALUE_LEN(new_objp) = plain_len;
OBJ_SEC_VALUE(new_objp) = plain_data;
} else if (OBJ_SEC_VALUE_LEN(new_objp) == plain_len) {
OBJ_SEC_VALUE(new_objp) = plain_data;
} else if (OBJ_SEC_VALUE_LEN(new_objp) > plain_len) {
rv = CKR_WRAPPED_KEY_LEN_RANGE;
goto cleanup_unwrap;
} else {
OBJ_SEC_VALUE(new_objp) = realloc(plain_data,
OBJ_SEC_VALUE_LEN(new_objp));
if (OBJ_SEC_VALUE(new_objp) == NULL) {
rv = CKR_HOST_MEMORY;
goto cleanup_unwrap;
}
}
} else {
rv = soft_asn1_to_object(new_objp, plain_data, plain_len);
if (rv != CKR_OK)
goto cleanup_unwrap;
}
if (IS_TOKEN_OBJECT(new_objp)) {
persistent = B_TRUE;
rv = soft_put_object_to_keystore(new_objp);
if (rv != CKR_OK)
goto cleanup_unwrap;
}
if (new_objp->class != CKO_SECRET_KEY) {
freezero(plain_data, plain_len);
}
*phKey = (CK_OBJECT_HANDLE)new_objp;
return (CKR_OK);
cleanup_unwrap:
if ((new_objp->class != CKO_SECRET_KEY) && (plain_data != NULL)) {
freezero(plain_data, plain_len);
}
if (IS_TOKEN_OBJECT(new_objp))
soft_delete_token_object(new_objp, persistent, B_FALSE);
else
soft_delete_object(session_p, new_objp, B_FALSE, B_FALSE);
return (rv);
}