/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <fcntl.h>
#include <strings.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/sha1.h>
#include <sys/md5.h>
#include <sys/sysmacros.h>
#include <security/cryptoki.h>
#include "softGlobal.h"
#include "softKeys.h"
#include "softKeystore.h"
#include "softMAC.h"
#include "softObject.h"
#include "softSession.h"
#include "softSSL.h"
/*
* This files contains the implementation of the following PKCS#11
* mechanisms needed by SSL:
* CKM_SSL3_MASTER_KEY_DERIVE
* CKM_SSL3_MASTER_KEY_DERIVE_DH
* CKM_SSL3_KEY_AND_DERIVE
* CKM_TLS_MASTER_KEY_DERIVE
* CKM_TLS_MASTER_KEY_DERIVE_DH
* CKM_TLS_KEY_AND_DERIVE
*
* SSL refers to common functions between SSL v3.0 and SSL v3.1 (a.k.a TLS.)
*/
#define MAX_KEYBLOCK 160 /* should be plenty for all known cipherspecs */
#define MAX_DEFAULT_ATTRS 10 /* Enough for major applicarions */
static char *ssl3_const_vals[] = {
"A",
"BB",
"CCC",
"DDDD",
"EEEEE",
"FFFFFF",
"GGGGGGG",
"HHHHHHHH",
"IIIIIIIII",
"JJJJJJJJJJ",
};
static uint_t ssl3_const_lens[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
static uchar_t TLS_MASTER_SECRET_LABEL[] = {"master secret"};
#define TLS_MASTER_SECRET_LABEL_LEN 13
static uchar_t TLS_KEY_EXPANSION_LABEL[] = {"key expansion"};
#define TLS_KEY_EXPANSION_LABEL_LEN 13
static uchar_t TLS_CLIENT_KEY_LABEL[] = {"client write key"};
#define TLS_CLIENT_KEY_LABEL_LEN 16
static uchar_t TLS_SERVER_KEY_LABEL[] = {"server write key"};
#define TLS_SERVER_KEY_LABEL_LEN 16
static uchar_t TLS_IV_BLOCK_LABEL[] = {"IV block"};
#define TLS_IV_BLOCK_LABEL_LEN 8
static void P_MD5(uchar_t *, uint_t, uchar_t *, uint_t, uchar_t *, uint_t,
uchar_t *, uint_t, uchar_t *, uint_t, boolean_t);
static void P_SHA1(uchar_t *, uint_t, uchar_t *, uint_t, uchar_t *, uint_t,
uchar_t *, uint_t, uchar_t *, uint_t, boolean_t);
static CK_RV soft_add_derived_key(CK_ATTRIBUTE_PTR, CK_ULONG,
CK_OBJECT_HANDLE_PTR, soft_session_t *, soft_object_t *);
static void soft_delete_derived_key(soft_session_t *, soft_object_t *);
static void soft_ssl_weaken_key(CK_MECHANISM_PTR, uchar_t *, uint_t,
uchar_t *, uint_t, uchar_t *, uint_t, uchar_t *, boolean_t);
/*
* soft_ssl3_churn()
* Called for derivation of the master secret from the pre-master secret,
* and for the derivation of the key_block in an SSL3 handshake
* result is assumed to be larger than rounds * MD5_HASH_SIZE.
*/
static void
soft_ssl3_churn(uchar_t *secret, uint_t secretlen, uchar_t *rand1,
uint_t rand1len, uchar_t *rand2, uint_t rand2len, int rounds,
uchar_t *result)
{
SHA1_CTX sha1_ctx;
MD5_CTX md5_ctx;
uchar_t sha1_digest[SHA1_HASH_SIZE];
int i;
uchar_t *ms = result;
for (i = 0; i < rounds; i++) {
SHA1Init(&sha1_ctx);
SHA1Update(&sha1_ctx, (const uint8_t *)ssl3_const_vals[i],
ssl3_const_lens[i]);
SHA1Update(&sha1_ctx, secret, secretlen);
SHA1Update(&sha1_ctx, rand1, rand1len);
SHA1Update(&sha1_ctx, rand2, rand2len);
SHA1Final(sha1_digest, &sha1_ctx);
MD5Init(&md5_ctx);
MD5Update(&md5_ctx, secret, secretlen);
MD5Update(&md5_ctx, sha1_digest, SHA1_HASH_SIZE);
MD5Final(ms, &md5_ctx);
ms += MD5_HASH_SIZE;
}
}
/*
* This TLS generic Pseudo Random Function expands a triplet
* {secret, label, seed} into any arbitrary length string of pseudo
* random bytes.
* Here, it is called for the derivation of the master secret from the
* pre-master secret, and for the derivation of the key_block in a TLS
* handshake
*/
static void
soft_tls_prf(uchar_t *secret, uint_t secretlen, uchar_t *label, uint_t labellen,
uchar_t *rand1, uint_t rand1len, uchar_t *rand2, uint_t rand2len,
uchar_t *result, uint_t resultlen)
{
uchar_t *S1, *S2;
uchar_t md5_digested_key[MD5_HASH_SIZE];
uchar_t sha1_digested_key[SHA1_HASH_SIZE];
uint_t L_S, L_S1, L_S2;
/* secret is NULL for IV's in exportable ciphersuites */
if (secret == NULL) {
L_S = 0;
L_S2 = L_S1 = 0;
S1 = NULL;
S2 = NULL;
goto do_P_HASH;
}
L_S = roundup(secretlen, 2) / 2;
L_S1 = L_S;
L_S2 = L_S;
S1 = secret;
S2 = secret + (secretlen / 2); /* Possible overlap of S1 and S2. */
/* Reduce the half secrets if bigger than the HASH's block size */
if (L_S > MD5_HMAC_BLOCK_SIZE) {
MD5_CTX md5_ctx;
SHA1_CTX sha1_ctx;
MD5Init(&md5_ctx);
MD5Update(&md5_ctx, S1, L_S);
MD5Final(md5_digested_key, &md5_ctx);
S1 = md5_digested_key;
L_S1 = MD5_HASH_SIZE;
SHA1Init(&sha1_ctx);
SHA1Update(&sha1_ctx, S2, L_S);
SHA1Final(sha1_digested_key, &sha1_ctx);
S2 = sha1_digested_key;
L_S2 = SHA1_HASH_SIZE;
}
/*
* PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR
* P_SHA-1(S2, label + seed);
* the 'seed' here is rand1 + rand2
*/
do_P_HASH:
/* The first one writes directly to the result */
P_MD5(S1, L_S1, label, labellen, rand1, rand1len, rand2, rand2len,
result, resultlen, B_FALSE);
/* The second one XOR's with the result. */
P_SHA1(S2, L_S2, label, labellen, rand1, rand1len, rand2, rand2len,
result, resultlen, B_TRUE);
}
/*
* These two expansion routines are very similar. (they can merge one day).
* They implement the P_HASH() function for MD5 and for SHA1, as defined in
* RFC2246:
*
* P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
* HMAC_hash(secret, A(2) + seed) +
* HMAC_hash(secret, A(3) + seed) + ...
* Where + indicates concatenation.
* A() is defined as:
* A(0) = seed
* A(i) = HMAC_hash(secret, A(i-1))
*
* The seed is the concatenation of 'babel', 'rand1', and 'rand2'.
*/
static void
P_MD5(uchar_t *secret, uint_t secretlen, uchar_t *label, uint_t labellen,
uchar_t *rand1, uint_t rand1len, uchar_t *rand2, uint_t rand2len,
uchar_t *result, uint_t resultlen, boolean_t xor_it)
{
uint32_t md5_ipad[MD5_HMAC_INTS_PER_BLOCK];
uint32_t md5_opad[MD5_HMAC_INTS_PER_BLOCK];
uchar_t md5_hmac[MD5_HASH_SIZE];
uchar_t A[MD5_HASH_SIZE];
md5_hc_ctx_t md5_hmac_ctx;
uchar_t *res, *cur;
uint_t left = resultlen;
int i;
/* good compilers will leverage the aligment */
bzero(md5_ipad, MD5_HMAC_BLOCK_SIZE);
bzero(md5_opad, MD5_HMAC_BLOCK_SIZE);
if (secretlen > 0) {
bcopy(secret, md5_ipad, secretlen);
bcopy(secret, md5_opad, secretlen);
}
/* A(1) = HMAC_MD5(secret, rand1 + rand2) */
md5_hmac_ctx_init(&md5_hmac_ctx, md5_ipad, md5_opad);
SOFT_MAC_UPDATE(MD5, &md5_hmac_ctx, label, labellen);
SOFT_MAC_UPDATE(MD5, &md5_hmac_ctx, rand1, rand1len);
SOFT_MAC_UPDATE(MD5, &md5_hmac_ctx, rand2, rand2len);
SOFT_MAC_FINAL(MD5, &md5_hmac_ctx, A);
if (xor_it) {
res = md5_hmac;
cur = result;
} else {
res = result;
}
while (left > 0) {
/*
* Compute HMAC_MD5(secret, A(i) + seed);
* The secret is already expanded in the ictx and octx, so
* we can call the SOFT_MAC_INIT_CTX() directly.
*/
SOFT_MAC_INIT_CTX(MD5, &md5_hmac_ctx, md5_ipad, md5_opad,
MD5_HMAC_BLOCK_SIZE);
SOFT_MAC_UPDATE(MD5, &md5_hmac_ctx, A, MD5_HASH_SIZE);
SOFT_MAC_UPDATE(MD5, &md5_hmac_ctx, label, labellen);
SOFT_MAC_UPDATE(MD5, &md5_hmac_ctx, rand1, rand1len);
SOFT_MAC_UPDATE(MD5, &md5_hmac_ctx, rand2, rand2len);
if (left > MD5_HASH_SIZE) {
SOFT_MAC_FINAL(MD5, &md5_hmac_ctx, res);
if (xor_it) {
for (i = 0; i < MD5_HASH_SIZE; i++) {
*cur ^= res[i];
cur++;
}
} else {
res += MD5_HASH_SIZE;
}
left -= MD5_HASH_SIZE;
} else {
SOFT_MAC_FINAL(MD5, &md5_hmac_ctx, md5_hmac);
if (xor_it) {
for (i = 0; i < left; i++) {
*cur ^= md5_hmac[i];
cur++;
}
} else {
bcopy(md5_hmac, res, left);
}
break;
}
/* A(i) = HMAC_MD5(secret, A(i-1) */
SOFT_MAC_INIT_CTX(MD5, &md5_hmac_ctx, md5_ipad, md5_opad,
MD5_HMAC_BLOCK_SIZE);
SOFT_MAC_UPDATE(MD5, &md5_hmac_ctx, A, MD5_HASH_SIZE);
SOFT_MAC_FINAL(MD5, &md5_hmac_ctx, A);
}
}
static void
P_SHA1(uchar_t *secret, uint_t secretlen, uchar_t *label, uint_t labellen,
uchar_t *rand1, uint_t rand1len, uchar_t *rand2, uint_t rand2len,
uchar_t *result, uint_t resultlen, boolean_t xor_it)
{
uint32_t sha1_ipad[SHA1_HMAC_INTS_PER_BLOCK];
uint32_t sha1_opad[SHA1_HMAC_INTS_PER_BLOCK];
uchar_t sha1_hmac[SHA1_HASH_SIZE];
uchar_t A[SHA1_HASH_SIZE];
sha1_hc_ctx_t sha1_hmac_ctx;
uchar_t *res, *cur;
uint_t left = resultlen;
int i;
/* good compilers will leverage the aligment */
bzero(sha1_ipad, SHA1_HMAC_BLOCK_SIZE);
bzero(sha1_opad, SHA1_HMAC_BLOCK_SIZE);
if (secretlen > 0) {
bcopy(secret, sha1_ipad, secretlen);
bcopy(secret, sha1_opad, secretlen);
}
/* A(1) = HMAC_SHA1(secret, rand1 + rand2) */
sha1_hmac_ctx_init(&sha1_hmac_ctx, sha1_ipad, sha1_opad);
SOFT_MAC_UPDATE(SHA1, &sha1_hmac_ctx, label, labellen);
SOFT_MAC_UPDATE(SHA1, &sha1_hmac_ctx, rand1, rand1len);
SOFT_MAC_UPDATE(SHA1, &sha1_hmac_ctx, rand2, rand2len);
SOFT_MAC_FINAL(SHA1, &sha1_hmac_ctx, A);
if (xor_it) {
res = sha1_hmac;
cur = result;
} else {
res = result;
}
while (left > 0) {
/*
* Compute HMAC_SHA1(secret, A(i) + seed);
* The secret is already expanded in the ictx and octx, so
* we can call the SOFT_MAC_INIT_CTX() directly.
*/
SOFT_MAC_INIT_CTX(SHA1, &sha1_hmac_ctx,
(const uchar_t *)sha1_ipad, (const uchar_t *)sha1_opad,
SHA1_HMAC_BLOCK_SIZE);
SOFT_MAC_UPDATE(SHA1, &sha1_hmac_ctx, A, SHA1_HASH_SIZE);
SOFT_MAC_UPDATE(SHA1, &sha1_hmac_ctx, label, labellen);
SOFT_MAC_UPDATE(SHA1, &sha1_hmac_ctx, rand1, rand1len);
SOFT_MAC_UPDATE(SHA1, &sha1_hmac_ctx, rand2, rand2len);
if (left > SHA1_HASH_SIZE) {
SOFT_MAC_FINAL(SHA1, &sha1_hmac_ctx, res);
if (xor_it) {
for (i = 0; i < SHA1_HASH_SIZE; i++) {
*cur ^= res[i];
cur++;
}
} else {
res += SHA1_HASH_SIZE;
}
left -= SHA1_HASH_SIZE;
} else {
SOFT_MAC_FINAL(SHA1, &sha1_hmac_ctx, sha1_hmac);
if (xor_it) {
for (i = 0; i < left; i++) {
*cur ^= sha1_hmac[i];
cur++;
}
} else {
bcopy(sha1_hmac, res, left);
}
break;
}
/* A(i) = HMAC_SHA1(secret, A(i-1) */
SOFT_MAC_INIT_CTX(SHA1, &sha1_hmac_ctx,
(const uchar_t *)sha1_ipad, (const uchar_t *)sha1_opad,
SHA1_HMAC_BLOCK_SIZE);
SOFT_MAC_UPDATE(SHA1, &sha1_hmac_ctx, A, SHA1_HASH_SIZE);
SOFT_MAC_FINAL(SHA1, &sha1_hmac_ctx, A);
}
}
/* This function handles the call from C_DeriveKey for CKM_TLS_PRF */
CK_RV
derive_tls_prf(CK_TLS_PRF_PARAMS_PTR param, soft_object_t *basekey_p)
{
if (param->pOutput == NULL || param->pulOutputLen == 0)
return (CKR_BUFFER_TOO_SMALL);
(void) soft_tls_prf(OBJ_SEC_VALUE(basekey_p),
OBJ_SEC_VALUE_LEN(basekey_p), param->pLabel, param->ulLabelLen,
param->pSeed, param->ulSeedLen, NULL, 0, param->pOutput,
*param->pulOutputLen);
return (CKR_OK);
}
/*
* soft_ssl_master_key_derive()
*
* Arguments:
* . session_p
* . mech_p: key derivation mechanism. the mechanism parameter carries the
* client and master random from the Hello handshake messages.
* . basekey_p: The pre-master secret key.
* . pTemplate & ulAttributeCount: Any extra attributes for the key to be
* created.
* . phKey: store for handle to the derived key.
*
* Description:
* Derive the SSL master secret from the pre-master secret, the client
* and server random.
* In SSL 3.0, master_secret =
* MD5(pre_master_secret + SHA('A' + pre_master_secret +
* ClientHello.random + ServerHello.random)) +
* MD5(pre_master_secret + SHA('BB' + pre_master_secret +
* ClientHello.random + ServerHello.random)) +
* MD5(pre_master_secret + SHA('CCC' + pre_master_secret +
* ClientHello.random + ServerHello.random));
*
* In TLS 1.0 (a.k.a. SSL 3.1), master_secret =
* PRF(pre_master_secret, "master secret",
* ClientHello.random + ServerHello.random)
*/
CK_RV
soft_ssl_master_key_derive(soft_session_t *sp, CK_MECHANISM_PTR mech,
soft_object_t *basekey_p, CK_ATTRIBUTE_PTR pTemplate,
CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey)
{
uchar_t *pmsecret = OBJ_SEC_VALUE(basekey_p);
#ifdef __sparcv9
/* LINTED */
uint_t pmlen = (uint_t)OBJ_SEC_VALUE_LEN(basekey_p);
#else /* __sparcv9 */
uint_t pmlen = OBJ_SEC_VALUE_LEN(basekey_p);
#endif /* __sparcv9 */
CK_SSL3_MASTER_KEY_DERIVE_PARAMS *mkd_params;
CK_SSL3_RANDOM_DATA *random_data;
CK_VERSION_PTR pVersion;
uchar_t ssl_master_secret[48];
CK_OBJECT_CLASS class = CKO_SECRET_KEY;
CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;
CK_BBOOL true = TRUE;
CK_ATTRIBUTE obj_tmpl[MAX_DEFAULT_ATTRS];
CK_ATTRIBUTE_PTR new_tmpl;
CK_ULONG newattrcount;
boolean_t new_tmpl_allocated = B_FALSE, is_tls = B_FALSE;
ulong_t i;
CK_RV rv = CKR_OK;
uint_t ClientRandomLen, ServerRandomLen;
/* Check the validity of the mechanism's parameter */
mkd_params = (CK_SSL3_MASTER_KEY_DERIVE_PARAMS *)mech->pParameter;
if (mkd_params == NULL ||
mech->ulParameterLen != sizeof (CK_SSL3_MASTER_KEY_DERIVE_PARAMS))
return (CKR_MECHANISM_PARAM_INVALID);
pVersion = mkd_params->pVersion;
switch (mech->mechanism) {
case CKM_TLS_MASTER_KEY_DERIVE:
is_tls = B_TRUE;
/* FALLTHRU */
case CKM_SSL3_MASTER_KEY_DERIVE:
/* Invalid pre-master key length. What else to return? */
if (pmlen != 48)
return (CKR_ARGUMENTS_BAD);
/* Get the SSL version number from the premaster secret */
if (pVersion == NULL_PTR)
return (CKR_MECHANISM_PARAM_INVALID);
bcopy(pmsecret, pVersion, sizeof (CK_VERSION));
break;
case CKM_TLS_MASTER_KEY_DERIVE_DH:
is_tls = B_TRUE;
/* FALLTHRU */
case CKM_SSL3_MASTER_KEY_DERIVE_DH:
if (pVersion != NULL_PTR)
return (CKR_MECHANISM_PARAM_INVALID);
}
random_data = &mkd_params->RandomInfo;
#ifdef __sparcv9
/* LINTED */
ClientRandomLen = (uint_t)random_data->ulClientRandomLen;
/* LINTED */
ServerRandomLen = (uint_t)random_data->ulServerRandomLen;
#else /* __sparcv9 */
ClientRandomLen = random_data->ulClientRandomLen;
ServerRandomLen = random_data->ulServerRandomLen;
#endif /* __sparcv9 */
if (random_data->pClientRandom == NULL_PTR || ClientRandomLen == 0 ||
random_data->pServerRandom == NULL_PTR || ServerRandomLen == 0) {
return (CKR_MECHANISM_PARAM_INVALID);
}
/* Now the actual secret derivation */
if (!is_tls) {
soft_ssl3_churn(pmsecret, pmlen, random_data->pClientRandom,
ClientRandomLen, random_data->pServerRandom,
ServerRandomLen, 3, ssl_master_secret);
} else {
soft_tls_prf(pmsecret, pmlen, TLS_MASTER_SECRET_LABEL,
TLS_MASTER_SECRET_LABEL_LEN, random_data->pClientRandom,
ClientRandomLen, random_data->pServerRandom,
ServerRandomLen, ssl_master_secret, 48);
}
/*
* The object creation attributes need to be in one contiguous
* array. In addition to the attrs from the application supplied
* pTemplates, We need to add the class, type, value, valuelen and
* CKA_DERIVE.
* In the most likely case, the application passes between zero and
* handful of attributes, We optimize for that case by allocating
* the new template on the stack. Oherwise we malloc() it.
*/
newattrcount = ulAttributeCount + 4;
if (newattrcount > MAX_DEFAULT_ATTRS) {
new_tmpl = malloc(sizeof (CK_ATTRIBUTE) * newattrcount);
if (new_tmpl == NULL)
return (CKR_HOST_MEMORY);
new_tmpl_allocated = B_TRUE;
} else
new_tmpl = obj_tmpl;
/*
* Fill in the new template.
* We put the attributes contributed by the mechanism first
* so that they override the application supplied ones.
*/
new_tmpl[0].type = CKA_CLASS;
new_tmpl[0].pValue = &class;
new_tmpl[0].ulValueLen = sizeof (class);
new_tmpl[1].type = CKA_KEY_TYPE;
new_tmpl[1].pValue = &keyType;
new_tmpl[1].ulValueLen = sizeof (keyType);
new_tmpl[2].type = CKA_DERIVE;
new_tmpl[2].pValue = &true;
new_tmpl[2].ulValueLen = sizeof (true);
new_tmpl[3].type = CKA_VALUE;
new_tmpl[3].pValue = ssl_master_secret;
new_tmpl[3].ulValueLen = 48;
/* Any attributes left? */
if (ulAttributeCount > 0) {
/* Validate the default class and type attributes */
for (i = 0; i < ulAttributeCount; i++) {
/* The caller is responsible for proper alignment */
if ((pTemplate[i].type == CKA_CLASS) &&
(*((CK_OBJECT_CLASS *)pTemplate[i].pValue) !=
CKO_SECRET_KEY)) {
rv = CKR_TEMPLATE_INCONSISTENT;
goto out;
}
if ((pTemplate[i].type == CKA_KEY_TYPE) &&
(*((CK_KEY_TYPE *)pTemplate[i].pValue) !=
CKK_GENERIC_SECRET)) {
rv = CKR_TEMPLATE_INCONSISTENT;
goto out;
}
}
bcopy(pTemplate, &new_tmpl[4],
ulAttributeCount * sizeof (CK_ATTRIBUTE));
}
rv = soft_add_derived_key(new_tmpl, newattrcount, phKey, sp, basekey_p);
out:
if (new_tmpl_allocated)
free(new_tmpl);
return (rv);
}
/*
* soft_ssl3_key_and_mac_derive()
*
* Arguments:
* . session_p
* . mech_p: key derivation mechanism. the mechanism parameter carries the
* client and mastter random from the Hello handshake messages,
* the specification of the key and IV sizes, and the location
* for the resulting keys and IVs.
* . basekey_p: The master secret key.
* . pTemplate & ulAttributeCount: Any extra attributes for the key to be
* created.
*
* Description:
* Derive the SSL key material (Client and server MAC secrets, symmetric
* keys and IVs), from the master secret and the client
* and server random.
* First a keyblock is generated usining the following formula:
* key_block =
* MD5(master_secret + SHA(`A' + master_secret +
* ServerHello.random +
* ClientHello.random)) +
* MD5(master_secret + SHA(`BB' + master_secret +
* ServerHello.random +
* ClientHello.random)) +
* MD5(master_secret + SHA(`CCC' + master_secret +
* ServerHello.random +
* ClientHello.random)) + [...];
*
* In TLS 1.0 (a.k.a. SSL 3.1), key_block =
* PRF(master_secret, "key expansion",
* ServerHello.random + ClientHello.random)
*
* Then the keys materials are taken from the keyblock.
*/
CK_RV
soft_ssl_key_and_mac_derive(soft_session_t *sp, CK_MECHANISM_PTR mech,
soft_object_t *basekey_p, CK_ATTRIBUTE_PTR pTemplate,
CK_ULONG ulAttributeCount)
{
uchar_t *msecret = OBJ_SEC_VALUE(basekey_p);
#ifdef __sparcv9
/* LINTED */
uint_t mslen = (uint_t)OBJ_SEC_VALUE_LEN(basekey_p);
#else /* __sparcv9 */
uint_t mslen = OBJ_SEC_VALUE_LEN(basekey_p);
#endif /* __sparcv9 */
CK_SSL3_KEY_MAT_PARAMS *km_params;
CK_SSL3_RANDOM_DATA *random_data;
CK_SSL3_KEY_MAT_OUT *kmo;
uchar_t key_block[MAX_KEYBLOCK], *kb, *export_keys = NULL;
CK_OBJECT_CLASS class = CKO_SECRET_KEY;
CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;
CK_BBOOL true = TRUE;
CK_ATTRIBUTE obj_tmpl[MAX_DEFAULT_ATTRS];
CK_ATTRIBUTE_PTR new_tmpl;
ulong_t newattrcount, mac_key_bytes, secret_key_bytes, iv_bytes;
ulong_t extra_attr_count;
uint_t size;
int rounds, n = 0;
boolean_t new_tmpl_allocated = B_FALSE, isExport;
CK_RV rv = CKR_OK;
uint_t ClientRandomLen, ServerRandomLen;
/* Check the validity of the mechanism's parameter */
km_params = (CK_SSL3_KEY_MAT_PARAMS *)mech->pParameter;
if (km_params == NULL ||
mech->ulParameterLen != sizeof (CK_SSL3_KEY_MAT_PARAMS) ||
(kmo = km_params->pReturnedKeyMaterial) == NULL)
return (CKR_MECHANISM_PARAM_INVALID);
isExport = (km_params->bIsExport == TRUE);
random_data = &km_params->RandomInfo;
#ifdef __sparcv9
/* LINTED */
ClientRandomLen = (uint_t)random_data->ulClientRandomLen;
/* LINTED */
ServerRandomLen = (uint_t)random_data->ulServerRandomLen;
#else /* __sparcv9 */
ClientRandomLen = random_data->ulClientRandomLen;
ServerRandomLen = random_data->ulServerRandomLen;
#endif /* __sparcv9 */
if (random_data->pClientRandom == NULL_PTR || ClientRandomLen == 0 ||
random_data->pServerRandom == NULL_PTR || ServerRandomLen == 0) {
return (CKR_MECHANISM_PARAM_INVALID);
}
mac_key_bytes = km_params->ulMacSizeInBits / 8;
secret_key_bytes = km_params->ulKeySizeInBits / 8;
iv_bytes = km_params->ulIVSizeInBits / 8;
if ((iv_bytes > 0) &&
((kmo->pIVClient == NULL) || (kmo->pIVServer == NULL)))
return (CKR_MECHANISM_PARAM_INVALID);
/*
* For exportable ciphersuites, the IV's aren't taken from the
* key block. They are directly derived from the client and
* server random data.
* For SSL3.0:
* client_write_IV = MD5(ClientHello.random + ServerHello.random);
* server_write_IV = MD5(ServerHello.random + ClientHello.random);
* For TLS1.0:
* iv_block = PRF("", "IV block", client_random +
* server_random)[0..15]
* client_write_IV = iv_block[0..7]
* server_write_IV = iv_block[8..15]
*/
if ((isExport) && (iv_bytes > 0)) {
if (mech->mechanism == CKM_SSL3_KEY_AND_MAC_DERIVE) {
MD5_CTX exp_md5_ctx;
if (iv_bytes > MD5_HASH_SIZE)
return (CKR_MECHANISM_PARAM_INVALID);
MD5Init(&exp_md5_ctx);
MD5Update(&exp_md5_ctx, random_data->pClientRandom,
ClientRandomLen);
MD5Update(&exp_md5_ctx, random_data->pServerRandom,
ServerRandomLen);
/* there's room in key_block. use it */
MD5Final(key_block, &exp_md5_ctx);
bcopy(key_block, kmo->pIVClient, iv_bytes);
MD5Init(&exp_md5_ctx);
MD5Update(&exp_md5_ctx, random_data->pServerRandom,
ServerRandomLen);
MD5Update(&exp_md5_ctx, random_data->pClientRandom,
ClientRandomLen);
MD5Final(key_block, &exp_md5_ctx);
bcopy(key_block, kmo->pIVServer, iv_bytes);
} else {
uchar_t iv_block[16];
if (iv_bytes != 8)
return (CKR_MECHANISM_PARAM_INVALID);
soft_tls_prf(NULL, 0, TLS_IV_BLOCK_LABEL,
TLS_IV_BLOCK_LABEL_LEN,
random_data->pClientRandom, ClientRandomLen,
random_data->pServerRandom, ServerRandomLen,
iv_block, 16);
bcopy(iv_block, kmo->pIVClient, 8);
bcopy(iv_block + 8, kmo->pIVServer, 8);
}
/* so we won't allocate a key_block bigger than needed */
iv_bytes = 0;
}
/* Now the actual secret derivation */
#ifdef __sparcv9
/* LINTED */
size = (uint_t)((mac_key_bytes + secret_key_bytes + iv_bytes) * 2);
#else /* __sparcv9 */
size = (mac_key_bytes + secret_key_bytes + iv_bytes) * 2;
#endif /* __sparcv9 */
/* Need to handle this better */
if (size > MAX_KEYBLOCK)
return (CKR_MECHANISM_PARAM_INVALID);
rounds = howmany(size, MD5_HASH_SIZE);
kb = key_block;
if (mech->mechanism == CKM_SSL3_KEY_AND_MAC_DERIVE) {
soft_ssl3_churn(msecret, mslen, random_data->pServerRandom,
ServerRandomLen, random_data->pClientRandom,
ClientRandomLen, rounds, kb);
} else {
soft_tls_prf(msecret, mslen, TLS_KEY_EXPANSION_LABEL,
TLS_KEY_EXPANSION_LABEL_LEN,
random_data->pServerRandom, ServerRandomLen,
random_data->pClientRandom, ClientRandomLen,
kb, size);
}
/* Now create the objects */
kmo->hClientMacSecret = CK_INVALID_HANDLE;
kmo->hServerMacSecret = CK_INVALID_HANDLE;
kmo->hClientKey = CK_INVALID_HANDLE;
kmo->hServerKey = CK_INVALID_HANDLE;
/* First the MAC secrets */
if (mac_key_bytes > 0) {
obj_tmpl[0].type = CKA_CLASS;
obj_tmpl[0].pValue = &class; /* CKO_SECRET_KEY */
obj_tmpl[0].ulValueLen = sizeof (class);
obj_tmpl[1].type = CKA_KEY_TYPE;
obj_tmpl[1].pValue = &keyType; /* CKK_GENERIC_SECRET */
obj_tmpl[1].ulValueLen = sizeof (keyType);
obj_tmpl[2].type = CKA_DERIVE;
obj_tmpl[2].pValue = &true;
obj_tmpl[2].ulValueLen = sizeof (true);
obj_tmpl[3].type = CKA_SIGN;
obj_tmpl[3].pValue = &true;
obj_tmpl[3].ulValueLen = sizeof (true);
obj_tmpl[4].type = CKA_VERIFY;
obj_tmpl[4].pValue = &true;
obj_tmpl[4].ulValueLen = sizeof (true);
obj_tmpl[5].type = CKA_VALUE;
obj_tmpl[5].pValue = kb;
obj_tmpl[5].ulValueLen = mac_key_bytes;
rv = soft_add_derived_key(obj_tmpl, 6,
&(kmo->hClientMacSecret), sp, basekey_p);
if (rv != CKR_OK)
goto out_err;
kb += mac_key_bytes;
obj_tmpl[5].pValue = kb;
rv = soft_add_derived_key(obj_tmpl, 6,
&(kmo->hServerMacSecret), sp, basekey_p);
if (rv != CKR_OK)
goto out_err;
kb += mac_key_bytes;
}
/* Then the symmetric ciphers keys */
extra_attr_count = (secret_key_bytes == 0) ? 6 : 5;
newattrcount = ulAttributeCount + extra_attr_count;
if (newattrcount > MAX_DEFAULT_ATTRS) {
new_tmpl = malloc(sizeof (CK_ATTRIBUTE) * newattrcount);
if (new_tmpl == NULL)
return (CKR_HOST_MEMORY);
new_tmpl_allocated = B_TRUE;
} else
new_tmpl = obj_tmpl;
new_tmpl[n].type = CKA_CLASS;
new_tmpl[n].pValue = &class; /* CKO_SECRET_KEY */
new_tmpl[n].ulValueLen = sizeof (class);
++n;
/*
* The keyType comes from the application's template, and depends
* on the ciphersuite. The only exception is authentication only
* ciphersuites which do not use cipher keys.
*/
if (secret_key_bytes == 0) {
new_tmpl[n].type = CKA_KEY_TYPE;
new_tmpl[n].pValue = &keyType; /* CKK_GENERIC_SECRET */
new_tmpl[n].ulValueLen = sizeof (keyType);
n++;
}
new_tmpl[n].type = CKA_DERIVE;
new_tmpl[n].pValue = &true;
new_tmpl[n].ulValueLen = sizeof (true);
n++;
new_tmpl[n].type = CKA_ENCRYPT;
new_tmpl[n].pValue = &true;
new_tmpl[n].ulValueLen = sizeof (true);
n++;
new_tmpl[n].type = CKA_DECRYPT;
new_tmpl[n].pValue = &true;
new_tmpl[n].ulValueLen = sizeof (true);
n++;
new_tmpl[n].type = CKA_VALUE;
new_tmpl[n].pValue = NULL;
new_tmpl[n].ulValueLen = 0;
if (secret_key_bytes > 0) {
if (isExport) {
if (secret_key_bytes > MD5_HASH_SIZE) {
rv = CKR_MECHANISM_PARAM_INVALID;
goto out_err;
}
if ((export_keys = malloc(2 * MD5_HASH_SIZE)) == NULL) {
rv = CKR_HOST_MEMORY;
goto out_err;
}
#ifdef __sparcv9
/* LINTED */
soft_ssl_weaken_key(mech, kb, (uint_t)secret_key_bytes,
#else /* __sparcv9 */
soft_ssl_weaken_key(mech, kb, secret_key_bytes,
#endif /* __sparcv9 */
random_data->pClientRandom, ClientRandomLen,
random_data->pServerRandom, ServerRandomLen,
export_keys, B_TRUE);
new_tmpl[n].pValue = export_keys;
new_tmpl[n].ulValueLen = MD5_HASH_SIZE;
} else {
new_tmpl[n].pValue = kb;
new_tmpl[n].ulValueLen = secret_key_bytes;
}
}
if (ulAttributeCount > 0)
bcopy(pTemplate, &new_tmpl[extra_attr_count],
ulAttributeCount * sizeof (CK_ATTRIBUTE));
rv = soft_add_derived_key(new_tmpl, newattrcount,
&(kmo->hClientKey), sp, basekey_p);
if (rv != CKR_OK)
goto out_err;
kb += secret_key_bytes;
if (secret_key_bytes > 0) {
if (isExport) {
#ifdef __sparcv9
/* LINTED */
soft_ssl_weaken_key(mech, kb, (uint_t)secret_key_bytes,
#else /* __sparcv9 */
soft_ssl_weaken_key(mech, kb, secret_key_bytes,
#endif /* __sparcv9 */
random_data->pServerRandom, ServerRandomLen,
random_data->pClientRandom, ClientRandomLen,
export_keys + MD5_HASH_SIZE, B_FALSE);
new_tmpl[n].pValue = export_keys + MD5_HASH_SIZE;
} else
new_tmpl[n].pValue = kb;
}
rv = soft_add_derived_key(new_tmpl, newattrcount,
&(kmo->hServerKey), sp, basekey_p);
if (rv != CKR_OK)
goto out_err;
kb += secret_key_bytes;
/* Finally, the IVs */
if (iv_bytes > 0) {
bcopy(kb, kmo->pIVClient, iv_bytes);
kb += iv_bytes;
bcopy(kb, kmo->pIVServer, iv_bytes);
}
if (new_tmpl_allocated)
free(new_tmpl);
if (export_keys != NULL)
free(export_keys);
return (rv);
out_err:
if (kmo->hClientMacSecret != CK_INVALID_HANDLE) {
(void) soft_delete_derived_key(sp,
(soft_object_t *)(kmo->hClientMacSecret));
kmo->hClientMacSecret = CK_INVALID_HANDLE;
}
if (kmo->hServerMacSecret != CK_INVALID_HANDLE) {
(void) soft_delete_derived_key(sp,
(soft_object_t *)(kmo->hServerMacSecret));
kmo->hServerMacSecret = CK_INVALID_HANDLE;
}
if (kmo->hClientKey != CK_INVALID_HANDLE) {
(void) soft_delete_derived_key(sp,
(soft_object_t *)(kmo->hClientKey));
kmo->hClientKey = CK_INVALID_HANDLE;
}
if (kmo->hServerKey != CK_INVALID_HANDLE) {
(void) soft_delete_derived_key(sp,
(soft_object_t *)(kmo->hServerKey));
kmo->hServerKey = CK_INVALID_HANDLE;
}
if (new_tmpl_allocated)
free(new_tmpl);
if (export_keys != NULL)
free(export_keys);
return (rv);
}
/*
* Add the derived key to the session, and, if it's a token object,
* write it to the token.
*/
static CK_RV
soft_add_derived_key(CK_ATTRIBUTE_PTR tmpl, CK_ULONG attrcount,
CK_OBJECT_HANDLE_PTR phKey, soft_session_t *sp, soft_object_t *basekey_p)
{
CK_RV rv;
soft_object_t *secret_key;
if ((secret_key = calloc(1, sizeof (soft_object_t))) == NULL) {
return (CKR_HOST_MEMORY);
}
if (((rv = soft_build_secret_key_object(tmpl, attrcount, secret_key,
SOFT_CREATE_OBJ_INT, 0, (CK_KEY_TYPE)~0UL)) != CKR_OK) ||
((rv = soft_pin_expired_check(secret_key)) != CKR_OK) ||
((rv = soft_object_write_access_check(sp, secret_key)) != CKR_OK)) {
free(secret_key);
return (rv);
}
/* Set the sensitivity and extractability attributes as a needed */
soft_derive_enforce_flags(basekey_p, secret_key);
/* Initialize the rest of stuffs in soft_object_t. */
(void) pthread_mutex_init(&secret_key->object_mutex, NULL);
secret_key->magic_marker = SOFTTOKEN_OBJECT_MAGIC;
/* ... and, if it needs to persist, write on the token */
if (IS_TOKEN_OBJECT(secret_key)) {
secret_key->session_handle = (CK_SESSION_HANDLE)NULL;
soft_add_token_object_to_slot(secret_key);
rv = soft_put_object_to_keystore(secret_key);
if (rv != CKR_OK) {
soft_delete_token_object(secret_key, B_FALSE, B_FALSE);
return (rv);
}
*phKey = (CK_OBJECT_HANDLE)secret_key;
return (CKR_OK);
}
/* Add the new object to the session's object list. */
soft_add_object_to_session(secret_key, sp);
secret_key->session_handle = (CK_SESSION_HANDLE)sp;
*phKey = (CK_OBJECT_HANDLE)secret_key;
return (rv);
}
/*
* Delete the derived key from the session, and, if it's a token object,
* remove it from the token.
*/
static void
soft_delete_derived_key(soft_session_t *sp, soft_object_t *key)
{
/* session_handle is the creating session. It's NULL for token objs */
if (IS_TOKEN_OBJECT(key))
soft_delete_token_object(key, B_FALSE, B_FALSE);
else
soft_delete_object(sp, key, B_FALSE, B_FALSE);
}
/*
* soft_ssl_weaken_key()
* Reduce the key length to an exportable size.
* For SSL3.0:
* final_client_write_key = MD5(client_write_key +
* ClientHello.random +
* ServerHello.random);
* final_server_write_key = MD5(server_write_key +
* ServerHello.random +
* ClientHello.random);
* For TLS1.0:
* final_client_write_key = PRF(SecurityParameters.client_write_key,
* "client write key",
* SecurityParameters.client_random +
* SecurityParameters.server_random)[0..15];
* final_server_write_key = PRF(SecurityParameters.server_write_key,
* "server write key",
* SecurityParameters.client_random +
* SecurityParameters.server_random)[0..15];
*/
static void
soft_ssl_weaken_key(CK_MECHANISM_PTR mech, uchar_t *secret, uint_t secretlen,
uchar_t *rand1, uint_t rand1len, uchar_t *rand2, uint_t rand2len,
uchar_t *result, boolean_t isclient)
{
MD5_CTX exp_md5_ctx;
uchar_t *label;
uint_t labellen;
if (mech->mechanism == CKM_SSL3_KEY_AND_MAC_DERIVE) {
MD5Init(&exp_md5_ctx);
MD5Update(&exp_md5_ctx, secret, secretlen);
MD5Update(&exp_md5_ctx, rand1, rand1len);
MD5Update(&exp_md5_ctx, rand2, rand2len);
MD5Final(result, &exp_md5_ctx);
} else {
if (isclient) {
label = TLS_CLIENT_KEY_LABEL;
labellen = TLS_CLIENT_KEY_LABEL_LEN;
soft_tls_prf(secret, secretlen, label, labellen,
rand1, rand1len, rand2, rand2len, result, 16);
} else {
label = TLS_SERVER_KEY_LABEL;
labellen = TLS_SERVER_KEY_LABEL_LEN;
soft_tls_prf(secret, secretlen, label, labellen,
rand2, rand2len, rand1, rand1len, result, 16);
}
}
}