/*
* COPYRIGHT (c) International Business Machines Corp. 2001-2017
*
* This program is provided under the terms of the Common Public License,
* version 1.0 (CPL-1.0). Any use, reproduction or distribution for this
* software constitutes recipient's acceptance of CPL-1.0 terms which can be
* found in the file LICENSE file or at
* https://opensource.org/licenses/cpl1.0.php
*/
// File: mech_ssl3.c
//
// Mechanisms for SSL v3 support
//
#include <pthread.h>
#include <string.h> // for memcmp() et al
#include <stdlib.h>
#include <stdio.h>
#include "pkcs11types.h"
#include "defs.h"
#include "host_defs.h"
#include "h_extern.h"
#include "tok_spec_struct.h"
#include "trace.h"
#include <openssl/crypto.h>
CK_RV ssl3_kmd_process_mac_keys(STDLL_TokData_t *tokdata,
SESSION *sess,
CK_ATTRIBUTE *pTemplate,
CK_ULONG ulCount,
CK_OBJECT_HANDLE *client_handle,
CK_BYTE *client_value,
CK_OBJECT_HANDLE *server_handle,
CK_BYTE *server_value, CK_ULONG mac_len);
CK_RV ssl3_kmd_process_write_keys(STDLL_TokData_t *tokdata,
SESSION *sess,
CK_ATTRIBUTE *pTemplate,
CK_ULONG ulCount,
CK_KEY_TYPE keytype,
CK_OBJECT_HANDLE *client_handle,
CK_BYTE *client_value,
CK_OBJECT_HANDLE *server_handle,
CK_BYTE *server_value, CK_ULONG write_len);
// The 'ssl3_mac_*' routines are used with the following mechanisms
//
// CKM_SSL3_MD5_MAC
// CKM_SSL3_SHA1_MAC
//
//
//
CK_RV ssl3_mac_sign(STDLL_TokData_t *tokdata,
SESSION *sess,
CK_BBOOL length_only,
SIGN_VERIFY_CONTEXT *ctx,
CK_BYTE *in_data,
CK_ULONG in_data_len,
CK_BYTE *out_data, CK_ULONG *out_data_len)
{
OBJECT *key_obj = NULL;
CK_ATTRIBUTE *attr = NULL;
CK_BYTE hash[SHA1_HASH_SIZE];
CK_BYTE *key_data = NULL;
CK_BYTE inner[48], outer[48];
DIGEST_CONTEXT digest_ctx;
CK_MECHANISM digest_mech;
CK_ULONG key_bytes, hash_len, mac_len;
CK_RV rc;
if (!sess || !ctx || !out_data_len) {
TRACE_ERROR("%s received bad argument(s)\n", __func__);
return CKR_FUNCTION_FAILED;
}
mac_len = *(CK_ULONG *) ctx->mech.pParameter;
if (length_only == TRUE) {
*out_data_len = mac_len;
return CKR_OK;
}
if (*out_data_len < mac_len) {
*out_data_len = mac_len;
TRACE_ERROR("%s\n", ock_err(ERR_BUFFER_TOO_SMALL));
return CKR_BUFFER_TOO_SMALL;
}
memset(&digest_ctx, 0x0, sizeof(DIGEST_CONTEXT));
rc = object_mgr_find_in_map1(tokdata, ctx->key, &key_obj, READ_LOCK);
if (rc != CKR_OK) {
TRACE_ERROR("Failed to acquire key from specified handle");
if (rc == CKR_OBJECT_HANDLE_INVALID)
return CKR_KEY_HANDLE_INVALID;
else
return rc;
}
rc = template_attribute_find(key_obj->template, CKA_VALUE, &attr);
if (rc == FALSE) {
TRACE_ERROR("Could not find CKA_VALUE in the template\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
key_bytes = attr->ulValueLen;
key_data = attr->pValue;
// unlike an HMAC operation, we don't XOR the key with the 0x36 or 0x5C.
// we just append 48 bytes to the key data
//
memset(inner, 0x36, 48);
memset(outer, 0x5C, 48);
if (ctx->mech.mechanism == CKM_SSL3_MD5_MAC)
digest_mech.mechanism = CKM_MD5;
else
digest_mech.mechanism = CKM_SHA_1;
digest_mech.ulParameterLen = 0;
digest_mech.pParameter = NULL;
// inner hash
//
rc = digest_mgr_init(tokdata, sess, &digest_ctx, &digest_mech);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest Init failed.\n");
goto done;
}
rc = digest_mgr_digest_update(tokdata, sess, &digest_ctx, key_data,
key_bytes);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest update failed.\n");
goto done;
}
if (ctx->mech.mechanism == CKM_SSL3_MD5_MAC) {
rc = digest_mgr_digest_update(tokdata, sess, &digest_ctx, inner, 48);
} else {
rc = digest_mgr_digest_update(tokdata, sess, &digest_ctx, inner, 40);
}
if (rc != CKR_OK) {
TRACE_DEVEL("Digest update failed.\n");
goto done;
}
rc = digest_mgr_digest_update(tokdata, sess, &digest_ctx, in_data,
in_data_len);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest update failed.\n");
goto done;
}
hash_len = sizeof(hash);
rc = digest_mgr_digest_final(tokdata, sess, FALSE, &digest_ctx, hash,
&hash_len);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest final failed.\n");
goto done;
}
memset(&digest_ctx, 0x0, sizeof(DIGEST_CONTEXT));
// outer hash
//
rc = digest_mgr_init(tokdata, sess, &digest_ctx, &digest_mech);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest Init failed.\n");
goto done;
}
rc = digest_mgr_digest_update(tokdata, sess, &digest_ctx, key_data,
key_bytes);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest update failed.\n");
goto done;
}
if (ctx->mech.mechanism == CKM_SSL3_MD5_MAC)
rc = digest_mgr_digest_update(tokdata, sess, &digest_ctx, outer, 48);
else
rc = digest_mgr_digest_update(tokdata, sess, &digest_ctx, outer, 40);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest update failed.\n");
goto done;
}
rc = digest_mgr_digest_update(tokdata, sess, &digest_ctx, hash, hash_len);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest update failed.\n");
goto done;
}
hash_len = sizeof(hash);
rc = digest_mgr_digest_final(tokdata, sess, FALSE, &digest_ctx, hash,
&hash_len);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest final failed.\n");
goto done;
}
memcpy(out_data, hash, mac_len);
*out_data_len = mac_len;
done:
object_put(tokdata, key_obj, TRUE);
key_obj = NULL;
return rc;
}
//
//
CK_RV ssl3_mac_sign_update(STDLL_TokData_t *tokdata,
SESSION *sess,
SIGN_VERIFY_CONTEXT *ctx,
CK_BYTE *in_data, CK_ULONG in_data_len)
{
OBJECT *key_obj = NULL;
CK_ATTRIBUTE *attr = NULL;
CK_BYTE *key_data = NULL;
SSL3_MAC_CONTEXT *context = NULL;
CK_BYTE inner[48];
CK_MECHANISM digest_mech;
CK_ULONG key_bytes;
CK_RV rc;
if (!sess || !ctx) {
TRACE_ERROR("%s received bad argument(s)\n", __func__);
return CKR_FUNCTION_FAILED;
}
context = (SSL3_MAC_CONTEXT *) ctx->context;
if (context->flag == FALSE) {
rc = object_mgr_find_in_map1(tokdata, ctx->key, &key_obj, READ_LOCK);
if (rc != CKR_OK) {
TRACE_ERROR("Failed to acquire key from specified handle");
if (rc == CKR_OBJECT_HANDLE_INVALID)
return CKR_KEY_HANDLE_INVALID;
else
return rc;
}
rc = template_attribute_find(key_obj->template, CKA_VALUE, &attr);
if (rc == FALSE) {
TRACE_ERROR("Could not find CKA_VALUE in the template\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
key_bytes = attr->ulValueLen;
key_data = attr->pValue;
// unlike an HMAC operation, we don't XOR the key with the 0x36 or 0x5C.
// we just append 48 bytes to the key data
//
memset(inner, 0x36, 48);
if (ctx->mech.mechanism == CKM_SSL3_MD5_MAC)
digest_mech.mechanism = CKM_MD5;
else
digest_mech.mechanism = CKM_SHA_1;
digest_mech.ulParameterLen = 0;
digest_mech.pParameter = NULL;
// inner hash
//
rc = digest_mgr_init(tokdata, sess, &context->hash_context,
&digest_mech);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest Init failed.\n");
goto done;
}
rc = digest_mgr_digest_update(tokdata, sess, &context->hash_context,
key_data, key_bytes);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest update failed.\n");
goto done;
}
if (ctx->mech.mechanism == CKM_SSL3_MD5_MAC)
rc = digest_mgr_digest_update(tokdata, sess, &context->hash_context,
inner, 48);
else
rc = digest_mgr_digest_update(tokdata, sess, &context->hash_context,
inner, 40);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest update failed.\n");
goto done;
}
context->flag = TRUE;
}
rc = digest_mgr_digest_update(tokdata, sess, &context->hash_context,
in_data, in_data_len);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest update failed.\n");
goto done;
}
done:
object_put(tokdata, key_obj, TRUE);
key_obj = NULL;
return rc;
}
//
//
CK_RV ssl3_mac_sign_final(STDLL_TokData_t *tokdata,
SESSION *sess,
CK_BBOOL length_only,
SIGN_VERIFY_CONTEXT *ctx,
CK_BYTE *out_data, CK_ULONG *out_data_len)
{
OBJECT *key_obj = NULL;
CK_ATTRIBUTE *attr = NULL;
CK_BYTE *key_data = NULL;
CK_BYTE hash[SHA1_HASH_SIZE];
SSL3_MAC_CONTEXT *context = NULL;
CK_BYTE outer[48];
CK_MECHANISM digest_mech;
CK_ULONG key_bytes, hash_len, mac_len;
CK_RV rc;
if (!sess || !ctx || !out_data_len) {
TRACE_ERROR("%s received bad argument(s)\n", __func__);
return CKR_FUNCTION_FAILED;
}
mac_len = *(CK_ULONG *) ctx->mech.pParameter;
if (length_only == TRUE) {
*out_data_len = mac_len;
return CKR_OK;
}
if (*out_data_len < mac_len) {
*out_data_len = mac_len;
TRACE_ERROR("%s\n", ock_err(ERR_BUFFER_TOO_SMALL));
return CKR_BUFFER_TOO_SMALL;
}
context = (SSL3_MAC_CONTEXT *) ctx->context;
rc = object_mgr_find_in_map1(tokdata, ctx->key, &key_obj, READ_LOCK);
if (rc != CKR_OK) {
TRACE_ERROR("Failed to acquire key from specified handle");
if (rc == CKR_OBJECT_HANDLE_INVALID)
return CKR_KEY_HANDLE_INVALID;
else
return rc;
}
rc = template_attribute_find(key_obj->template, CKA_VALUE, &attr);
if (rc == FALSE) {
TRACE_ERROR("Could not find CKA_VALUE in the template\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
key_bytes = attr->ulValueLen;
key_data = attr->pValue;
// finish the inner hash
//
hash_len = sizeof(hash);
rc = digest_mgr_digest_final(tokdata, sess, FALSE, &context->hash_context,
hash, &hash_len);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest Final failed.\n");
goto done;
}
// now, do the outer hash
//
memset(context, 0x0, sizeof(SSL3_MAC_CONTEXT));
memset(outer, 0x5C, 48);
if (ctx->mech.mechanism == CKM_SSL3_MD5_MAC)
digest_mech.mechanism = CKM_MD5;
else
digest_mech.mechanism = CKM_SHA_1;
digest_mech.ulParameterLen = 0;
digest_mech.pParameter = NULL;
rc = digest_mgr_init(tokdata, sess, &context->hash_context, &digest_mech);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest Init failed.\n");
goto done;
}
rc = digest_mgr_digest_update(tokdata, sess, &context->hash_context,
key_data, key_bytes);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest Update failed.\n");
goto done;
}
if (ctx->mech.mechanism == CKM_SSL3_MD5_MAC)
rc = digest_mgr_digest_update(tokdata, sess, &context->hash_context,
outer, 48);
else
rc = digest_mgr_digest_update(tokdata, sess, &context->hash_context,
outer, 40);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest Update failed.\n");
goto done;
}
rc = digest_mgr_digest_update(tokdata, sess, &context->hash_context, hash,
hash_len);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest Update failed.\n");
goto done;
}
hash_len = sizeof(hash);
rc = digest_mgr_digest_final(tokdata, sess, FALSE, &context->hash_context,
hash, &hash_len);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest Final failed.\n");
goto done;
}
memcpy(out_data, hash, mac_len);
*out_data_len = mac_len;
done:
object_put(tokdata, key_obj, TRUE);
key_obj = NULL;
return rc;
}
// This routine could replace the HMAC verification routines
//
CK_RV ssl3_mac_verify(STDLL_TokData_t *tokdata,
SESSION *sess,
SIGN_VERIFY_CONTEXT *ctx,
CK_BYTE *in_data,
CK_ULONG in_data_len,
CK_BYTE *signature, CK_ULONG sig_len)
{
CK_BYTE mac[SHA1_HASH_SIZE];
SIGN_VERIFY_CONTEXT mac_ctx;
CK_ULONG mac_len, len;
CK_RV rc;
if (!sess || !ctx || !in_data || !signature) {
TRACE_ERROR("%s received bad argument(s)\n", __func__);
return CKR_FUNCTION_FAILED;
}
mac_len = *(CK_ULONG *) ctx->mech.pParameter;
memset(&mac_ctx, 0, sizeof(SIGN_VERIFY_CONTEXT));
rc = sign_mgr_init(tokdata, sess, &mac_ctx, &ctx->mech, FALSE, ctx->key);
if (rc != CKR_OK) {
TRACE_DEVEL("Sign Init failed.\n");
goto error;
}
len = sizeof(mac);
rc = sign_mgr_sign(tokdata, sess, FALSE, &mac_ctx,
in_data, in_data_len, mac, &len);
if (rc != CKR_OK) {
TRACE_DEVEL("Sign failed.\n");
goto error;
}
if ((len != mac_len) || (len != sig_len)) {
rc = CKR_SIGNATURE_LEN_RANGE;
TRACE_ERROR("%s\n", ock_err(ERR_SIGNATURE_LEN_RANGE));
goto error;
}
if (CRYPTO_memcmp(mac, signature, mac_len) != 0) {
TRACE_ERROR("%s\n", ock_err(ERR_SIGNATURE_INVALID));
rc = CKR_SIGNATURE_INVALID;
}
error:
sign_mgr_cleanup(&mac_ctx);
return rc;
}
//
//
CK_RV ssl3_mac_verify_update(STDLL_TokData_t *tokdata,
SESSION *sess,
SIGN_VERIFY_CONTEXT *ctx,
CK_BYTE *in_data, CK_ULONG in_data_len)
{
OBJECT *key_obj = NULL;
CK_ATTRIBUTE *attr = NULL;
CK_BYTE *key_data = NULL;
SSL3_MAC_CONTEXT *context = NULL;
CK_BYTE inner[48];
CK_MECHANISM digest_mech;
CK_ULONG key_bytes;
CK_RV rc;
if (!sess || !ctx) {
TRACE_ERROR("%s received bad argument(s)\n", __func__);
return CKR_FUNCTION_FAILED;
}
context = (SSL3_MAC_CONTEXT *) ctx->context;
if (context->flag == FALSE) {
rc = object_mgr_find_in_map1(tokdata, ctx->key, &key_obj, READ_LOCK);
if (rc != CKR_OK) {
TRACE_ERROR("Failed to acquire key from specified handle");
if (rc == CKR_OBJECT_HANDLE_INVALID)
return CKR_KEY_HANDLE_INVALID;
else
return rc;
}
rc = template_attribute_find(key_obj->template, CKA_VALUE, &attr);
if (rc == FALSE) {
TRACE_ERROR("Could not find CKA_VALUE in the template\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
key_bytes = attr->ulValueLen;
key_data = attr->pValue;
// unlike an HMAC operation, we don't XOR the key with the 0x36 or 0x5C.
// we just append 48 bytes to the key data
//
memset(inner, 0x36, 48);
if (ctx->mech.mechanism == CKM_SSL3_MD5_MAC)
digest_mech.mechanism = CKM_MD5;
else
digest_mech.mechanism = CKM_SHA_1;
digest_mech.ulParameterLen = 0;
digest_mech.pParameter = NULL;
// inner hash
//
rc = digest_mgr_init(tokdata, sess, &context->hash_context,
&digest_mech);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest Init failed.\n");
goto done;
}
rc = digest_mgr_digest_update(tokdata, sess, &context->hash_context,
key_data, key_bytes);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest Update failed.\n");
goto done;
}
if (ctx->mech.mechanism == CKM_SSL3_MD5_MAC)
rc = digest_mgr_digest_update(tokdata, sess, &context->hash_context,
inner, 48);
else
rc = digest_mgr_digest_update(tokdata, sess, &context->hash_context,
inner, 40);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest Update failed.\n");
goto done;
}
context->flag = TRUE;
}
rc = digest_mgr_digest_update(tokdata, sess, &context->hash_context,
in_data, in_data_len);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest Update failed.\n");
goto done;
}
done:
object_put(tokdata, key_obj, TRUE);
key_obj = NULL;
return rc;
}
//
//
CK_RV ssl3_mac_verify_final(STDLL_TokData_t *tokdata,
SESSION *sess,
SIGN_VERIFY_CONTEXT *ctx,
CK_BYTE *signature, CK_ULONG sig_len)
{
OBJECT *key_obj = NULL;
CK_ATTRIBUTE *attr = NULL;
CK_BYTE *key_data = NULL;
SSL3_MAC_CONTEXT *context = NULL;
CK_BYTE hash[SHA1_HASH_SIZE];
CK_BYTE outer[48];
CK_MECHANISM digest_mech;
CK_ULONG key_bytes, hash_len, mac_len;
CK_RV rc;
if (!sess || !ctx || !signature) {
TRACE_ERROR("%s received bad argument(s)\n", __func__);
return CKR_FUNCTION_FAILED;
}
mac_len = *(CK_ULONG *) ctx->mech.pParameter;
context = (SSL3_MAC_CONTEXT *) ctx->context;
rc = object_mgr_find_in_map1(tokdata, ctx->key, &key_obj, READ_LOCK);
if (rc != CKR_OK) {
TRACE_ERROR("Failed to acquire key from specified handle");
if (rc == CKR_OBJECT_HANDLE_INVALID)
return CKR_KEY_HANDLE_INVALID;
else
return rc;
}
rc = template_attribute_find(key_obj->template, CKA_VALUE, &attr);
if (rc == FALSE) {
TRACE_ERROR("Could not find CKA_VALUE in the template\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
key_bytes = attr->ulValueLen;
key_data = attr->pValue;
// finish the inner hash
//
hash_len = sizeof(hash);
rc = digest_mgr_digest_final(tokdata, sess, FALSE, &context->hash_context,
hash, &hash_len);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest Final failed.\n");
goto done;
}
// now, do the outer hash
//
memset(context, 0x0, sizeof(SSL3_MAC_CONTEXT));
memset(outer, 0x5C, 48);
if (ctx->mech.mechanism == CKM_SSL3_MD5_MAC)
digest_mech.mechanism = CKM_MD5;
else
digest_mech.mechanism = CKM_SHA_1;
digest_mech.ulParameterLen = 0;
digest_mech.pParameter = NULL;
rc = digest_mgr_init(tokdata, sess, &context->hash_context, &digest_mech);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest Init failed.\n");
goto done;
}
rc = digest_mgr_digest_update(tokdata, sess, &context->hash_context,
key_data, key_bytes);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest Update failed.\n");
goto done;
}
if (ctx->mech.mechanism == CKM_SSL3_MD5_MAC)
rc = digest_mgr_digest_update(tokdata, sess, &context->hash_context,
outer, 48);
else
rc = digest_mgr_digest_update(tokdata, sess, &context->hash_context,
outer, 40);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest Update failed.\n");
goto done;
}
rc = digest_mgr_digest_update(tokdata, sess, &context->hash_context, hash,
hash_len);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest Update failed.\n");
goto done;
}
hash_len = sizeof(hash);
rc = digest_mgr_digest_final(tokdata, sess, FALSE, &context->hash_context,
hash, &hash_len);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest Final failed.\n");
goto done;
}
if ((mac_len != sig_len) || (mac_len > hash_len)) {
TRACE_ERROR("%s\n", ock_err(ERR_SIGNATURE_INVALID));
rc = CKR_SIGNATURE_INVALID;
} else if (CRYPTO_memcmp(signature, hash, sig_len) != 0) {
rc = CKR_SIGNATURE_INVALID;
TRACE_ERROR("%s\n", ock_err(ERR_SIGNATURE_INVALID));
}
done:
object_put(tokdata, key_obj, TRUE);
key_obj = NULL;
return rc;
}
//
//
CK_RV ckm_ssl3_pre_master_key_gen(STDLL_TokData_t *tokdata,
TEMPLATE *tmpl, CK_MECHANISM *mech)
{
CK_ATTRIBUTE *value_attr = NULL;
CK_ATTRIBUTE *value_len_attr = NULL;
CK_ATTRIBUTE *key_type_attr = NULL;
CK_ATTRIBUTE *class_attr = NULL;
CK_ATTRIBUTE *local_attr = NULL;
CK_ATTRIBUTE *derive_attr = NULL;
CK_VERSION *version = NULL;
CK_BYTE key[48];
CK_ULONG rc;
rc = rng_generate(tokdata, key, 48);
if (rc != CKR_OK) {
TRACE_DEVEL("rng_generate failed.\n");
return rc;
}
value_attr = (CK_ATTRIBUTE *) malloc(sizeof(CK_ATTRIBUTE) + 48);
value_len_attr =
(CK_ATTRIBUTE *) malloc(sizeof(CK_ATTRIBUTE) + sizeof(CK_ULONG));
key_type_attr =
(CK_ATTRIBUTE *) malloc(sizeof(CK_ATTRIBUTE) + sizeof(CK_KEY_TYPE));
class_attr =
(CK_ATTRIBUTE *) malloc(sizeof(CK_ATTRIBUTE) + sizeof(CK_OBJECT_CLASS));
local_attr =
(CK_ATTRIBUTE *) malloc(sizeof(CK_ATTRIBUTE) + sizeof(CK_BBOOL));
derive_attr =
(CK_ATTRIBUTE *) malloc(sizeof(CK_ATTRIBUTE) + sizeof(CK_BBOOL));
if (!value_attr || !value_len_attr || !key_type_attr ||
!class_attr || !local_attr || !derive_attr) {
if (value_attr)
free(value_attr);
if (value_len_attr)
free(value_len_attr);
if (key_type_attr)
free(key_type_attr);
if (class_attr)
free(class_attr);
if (local_attr)
free(local_attr);
if (derive_attr)
free(derive_attr);
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
return CKR_HOST_MEMORY;
}
version = (CK_VERSION *) mech->pParameter;
key[0] = version->major;
key[1] = version->minor;
value_attr->type = CKA_VALUE;
value_attr->ulValueLen = 48;
value_attr->pValue = (CK_BYTE *) value_attr + sizeof(CK_ATTRIBUTE);
memcpy(value_attr->pValue, key, 48);
value_len_attr->type = CKA_VALUE_LEN;
value_len_attr->ulValueLen = sizeof(CK_ULONG);
value_len_attr->pValue = (CK_BYTE *) value_len_attr + sizeof(CK_ATTRIBUTE);
*(CK_ULONG *) value_len_attr->pValue = 48;
key_type_attr->type = CKA_KEY_TYPE;
key_type_attr->ulValueLen = sizeof(CK_KEY_TYPE);
key_type_attr->pValue = (CK_BYTE *) key_type_attr + sizeof(CK_ATTRIBUTE);
*(CK_ATTRIBUTE_TYPE *) key_type_attr->pValue = CKK_GENERIC_SECRET;
class_attr->type = CKA_CLASS;
class_attr->ulValueLen = sizeof(CK_OBJECT_CLASS);
class_attr->pValue = (CK_BYTE *) class_attr + sizeof(CK_ATTRIBUTE);
*(CK_OBJECT_CLASS *) class_attr->pValue = CKO_SECRET_KEY;
local_attr->type = CKA_LOCAL;
local_attr->ulValueLen = sizeof(CK_BBOOL);
local_attr->pValue = (CK_BYTE *) local_attr + sizeof(CK_ATTRIBUTE);
*(CK_BBOOL *) local_attr->pValue = TRUE;
derive_attr->type = CKA_DERIVE;
derive_attr->ulValueLen = sizeof(CK_BBOOL);
derive_attr->pValue = (CK_BYTE *) derive_attr + sizeof(CK_ATTRIBUTE);
*(CK_BBOOL *) derive_attr->pValue = TRUE;
template_update_attribute(tmpl, value_attr);
template_update_attribute(tmpl, value_len_attr);
template_update_attribute(tmpl, key_type_attr);
template_update_attribute(tmpl, class_attr);
template_update_attribute(tmpl, local_attr);
template_update_attribute(tmpl, derive_attr);
return CKR_OK;
}
//
//
static CK_RV ssl3_sha_then_md5(STDLL_TokData_t *tokdata,
SESSION *sess,
CK_BYTE *secret,
CK_BYTE *firstRandom,
CK_ULONG firstRandomLen,
CK_BYTE *secondRandom,
CK_ULONG secondRandomLen,
CK_BYTE *variableData,
CK_ULONG variableDataLen, CK_BYTE *outBuff)
{
DIGEST_CONTEXT digest_ctx;
CK_MECHANISM digest_mech;
CK_BYTE hash[SHA1_HASH_SIZE];
CK_ULONG len;
CK_RV rc;
// SHA(variableData + secret + firstRandom + secondRandom)
//
memset(&digest_ctx, 0x0, sizeof(DIGEST_CONTEXT));
digest_mech.mechanism = CKM_SHA_1;
digest_mech.ulParameterLen = 0;
digest_mech.pParameter = NULL;
rc = digest_mgr_init(tokdata, sess, &digest_ctx, &digest_mech);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest Init failed.\n");
return rc;
}
rc = digest_mgr_digest_update(tokdata, sess,
&digest_ctx, variableData, variableDataLen);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest Update failed.\n");
return rc;
}
rc = digest_mgr_digest_update(tokdata, sess, &digest_ctx, secret, 48);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest Update failed.\n");
return rc;
}
rc = digest_mgr_digest_update(tokdata, sess,
&digest_ctx, firstRandom, firstRandomLen);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest Update failed.\n");
return rc;
}
rc = digest_mgr_digest_update(tokdata, sess,
&digest_ctx, secondRandom, secondRandomLen);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest Update failed.\n");
return rc;
}
len = sizeof(hash);
rc = digest_mgr_digest_final(tokdata, sess, FALSE, &digest_ctx, hash, &len);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest Final failed.\n");
return rc;
}
// MD5(secret + SHA(...))
//
memset(&digest_ctx, 0x0, sizeof(DIGEST_CONTEXT));
digest_mech.mechanism = CKM_MD5;
digest_mech.ulParameterLen = 0;
digest_mech.pParameter = NULL;
rc = digest_mgr_init(tokdata, sess, &digest_ctx, &digest_mech);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest Init failed.\n");
return rc;
}
rc = digest_mgr_digest_update(tokdata, sess, &digest_ctx, secret, 48);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest Update failed.\n");
return rc;
}
rc = digest_mgr_digest_update(tokdata, sess, &digest_ctx, hash, len);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest Update failed.\n");
return rc;
}
len = sizeof(hash);
rc = digest_mgr_digest_final(tokdata, sess, FALSE, &digest_ctx, hash, &len);
if (rc == CKR_OK)
memcpy(outBuff, hash, len);
else
TRACE_DEVEL("Digest Final failed.\n");
return rc;
}
//
//
static CK_RV ssl3_md5_only(STDLL_TokData_t *tokdata,
SESSION *sess,
CK_BYTE *firstString,
CK_ULONG firstStringLen,
CK_BYTE *secondString,
CK_ULONG secondStringLen,
CK_BYTE *thirdString,
CK_ULONG thirdStringLen, CK_BYTE *outBuff)
{
DIGEST_CONTEXT digest_ctx;
CK_MECHANISM digest_mech;
CK_ULONG len;
CK_RV rc;
// If firstString is not NULL,
//
// MD5(firstString + secondString + thirdString)
//
// If firstString is NULL
//
// MD5(secondString + thirdString)
//
memset(&digest_ctx, 0x0, sizeof(DIGEST_CONTEXT));
digest_mech.mechanism = CKM_MD5;
digest_mech.ulParameterLen = 0;
digest_mech.pParameter = NULL;
rc = digest_mgr_init(tokdata, sess, &digest_ctx, &digest_mech);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest Init failed.\n");
return rc;
}
if (firstString != NULL) {
rc = digest_mgr_digest_update(tokdata, sess,
&digest_ctx, firstString, firstStringLen);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest Update failed.\n");
return rc;
}
}
rc = digest_mgr_digest_update(tokdata, sess,
&digest_ctx, secondString, secondStringLen);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest Update failed.\n");
return rc;
}
rc = digest_mgr_digest_update(tokdata, sess,
&digest_ctx, thirdString, thirdStringLen);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest Update failed.\n");
return rc;
}
len = MD5_HASH_SIZE;
rc = digest_mgr_digest_final(tokdata, sess, FALSE, &digest_ctx, outBuff,
&len);
if (rc != CKR_OK) {
TRACE_DEVEL("Digest Final failed.\n");
}
return rc;
}
//
//
CK_RV ssl3_master_key_derive(STDLL_TokData_t *tokdata,
SESSION *sess,
CK_MECHANISM *mech,
CK_OBJECT_HANDLE base_key,
CK_ATTRIBUTE *pTemplate,
CK_ULONG ulCount, CK_OBJECT_HANDLE *handle)
{
OBJECT *derived_key_obj = NULL;
OBJECT *base_key_obj = NULL;
CK_ATTRIBUTE *attr = NULL;
CK_ATTRIBUTE *value_attr = NULL;
CK_ATTRIBUTE *value_len_attr = NULL;
CK_ATTRIBUTE *always_sens_attr = NULL;
CK_ATTRIBUTE *extract_attr = NULL;
CK_BYTE *base_key_value = NULL;
CK_BYTE key_data[48];
CK_ULONG i, base_key_len;
CK_BBOOL flag;
CK_RV rc;
CK_SSL3_MASTER_KEY_DERIVE_PARAMS *params = NULL;
CK_SSL3_RANDOM_DATA *random_data = NULL;
if (!sess || !mech) {
TRACE_ERROR("%s received bad argument(s)\n", __func__);
return CKR_FUNCTION_FAILED;
}
params = (CK_SSL3_MASTER_KEY_DERIVE_PARAMS *) mech->pParameter;
rc = object_mgr_find_in_map1(tokdata, base_key, &base_key_obj, READ_LOCK);
if (rc != CKR_OK) {
TRACE_ERROR("Failed to acquire key from specified handle");
if (rc == CKR_OBJECT_HANDLE_INVALID)
return CKR_KEY_HANDLE_INVALID;
else
return rc;
}
rc = template_attribute_find(base_key_obj->template, CKA_VALUE, &attr);
if (rc == FALSE) {
TRACE_ERROR("Could not find <the_attribute_name> in the template\n");
rc = CKR_FUNCTION_FAILED;
goto error;
} else {
base_key_len = attr->ulValueLen;
base_key_value = attr->pValue;
if (base_key_len != 48) {
TRACE_ERROR("The base key's length is not 48.\n");
rc = CKR_KEY_FUNCTION_NOT_PERMITTED;
goto error;
}
}
// this mechanism implies the following attributes:
// CKA_CLASS : CKO_SECRET_KEY
// CKA_KEY_TYPE : CKK_GENERIC_SECRET
// CKA_VALUE_LEN : 48
// but we need to make sure the caller didn't specify any
// wacky values. it would have been better if Cryptoki had forbidden
// these attributes from appearing in the template
//
for (i = 0, attr = pTemplate; i < ulCount; i++, attr++) {
CK_OBJECT_CLASS class;
CK_KEY_TYPE keytype;
CK_ULONG value_len;
if (attr->type == CKA_CLASS) {
class = *(CK_OBJECT_CLASS *) attr->pValue;
if (class != CKO_SECRET_KEY) {
TRACE_ERROR("This operation requires a secret key.\n");
rc = CKR_KEY_FUNCTION_NOT_PERMITTED;
goto error;
}
} else if (attr->type == CKA_KEY_TYPE) {
keytype = *(CK_KEY_TYPE *) attr->pValue;
if (keytype != CKK_GENERIC_SECRET) {
TRACE_ERROR("%s\n", ock_err(ERR_KEY_TYPE_INCONSISTENT));
rc = CKR_KEY_TYPE_INCONSISTENT;
goto error;
}
} else if (attr->type == CKA_VALUE_LEN) {
value_len = *(CK_ULONG *) attr->pValue;
if (value_len != 48) {
TRACE_ERROR("The derived key's length is not 48.\n");
rc = CKR_TEMPLATE_INCONSISTENT;
goto error;
}
}
}
memset(key_data, 0x0, sizeof(key_data));
random_data = (CK_SSL3_RANDOM_DATA *) (¶ms->RandomInfo);
// derive the master key data
//
rc = ssl3_sha_then_md5(tokdata, sess,
base_key_value,
random_data->pClientRandom,
random_data->ulClientRandomLen,
random_data->pServerRandom,
random_data->ulServerRandomLen,
(unsigned char *) "A", 1, key_data);
if (rc != CKR_OK) {
TRACE_DEVEL("ssl3_sha_then_md5 failed.\n");
goto error;
}
rc = ssl3_sha_then_md5(tokdata, sess,
base_key_value,
random_data->pClientRandom,
random_data->ulClientRandomLen,
random_data->pServerRandom,
random_data->ulServerRandomLen,
(unsigned char *) "BB", 2, &key_data[16]);
if (rc != CKR_OK) {
TRACE_DEVEL("ssl3_sha_then_md5 failed.\n");
goto error;
}
rc = ssl3_sha_then_md5(tokdata, sess,
base_key_value,
random_data->pClientRandom,
random_data->ulClientRandomLen,
random_data->pServerRandom,
random_data->ulServerRandomLen,
(unsigned char *) "CCC", 3, &key_data[32]);
if (rc != CKR_OK) {
TRACE_DEVEL("ssl3_sha_then_md5 failed.\n");
goto error;
}
// build the key skeleton
//
rc = object_mgr_create_skel(tokdata, sess,
pTemplate, ulCount,
MODE_DERIVE,
CKO_SECRET_KEY, CKK_GENERIC_SECRET,
&derived_key_obj);
if (rc != CKR_OK) {
TRACE_DEVEL("Object Mgr Create Skeleton failed.\n");
goto error;
}
rc = build_attribute(CKA_VALUE, key_data, 48, &value_attr);
if (rc != CKR_OK) {
TRACE_DEVEL("Failed to build CKA_VALUE attribute.\n");
goto error;
}
rc = build_attribute(CKA_VALUE_LEN, (CK_BYTE *) & base_key_len,
sizeof(CK_ULONG), &value_len_attr);
if (rc != CKR_OK) {
TRACE_DEVEL("Failed to build CKA_VALUE_LEN attribute.\n");
goto error;
}
//
// now, adjust the CKA_ALWAYS_SENSITIVE and CKA_NEVER_EXTRACTABLE
// attributes based on the corresponding values from the base key
//
// if base key has ALWAYS_SENSITIVE = FALSE, then new key does too
// otherwise, the value of CKA_ALWAYS_SENSITIVE = CKA_SENSITIVE
//
rc = template_attribute_find(base_key_obj->template, CKA_ALWAYS_SENSITIVE,
&attr);
if (rc == FALSE) {
TRACE_ERROR("Could not find CKA_ALWAYS_SENSITIVE in the template\n");
rc = CKR_FUNCTION_FAILED;
goto error;
}
flag = *(CK_BBOOL *) attr->pValue;
if (flag == TRUE) {
rc = template_attribute_find(derived_key_obj->template, CKA_SENSITIVE,
&attr);
if (rc == FALSE) {
TRACE_ERROR("Could not find CKA_SENSITIVE in the template\n");
rc = CKR_FUNCTION_FAILED;
goto error;
}
flag = *(CK_BBOOL *) attr->pValue;
}
rc = build_attribute(CKA_ALWAYS_SENSITIVE, &flag, sizeof(CK_BBOOL),
&always_sens_attr);
if (rc != CKR_OK) {
TRACE_DEVEL("Failed to build CKA_ALWAYS_SENSITIVE attribute.\n");
goto error;
}
// if base key has NEVER_EXTRACTABLE = FASE, the new key does too
// otherwise, the value of CKA_NEVER_EXTRACTABLE = !CKA_EXTRACTABLE
//
rc = template_attribute_find(base_key_obj->template, CKA_NEVER_EXTRACTABLE,
&attr);
if (rc == FALSE) {
TRACE_DEVEL("Failed to build CKA_NEVER_EXTRACTABLE attribute.\n");
rc = CKR_FUNCTION_FAILED;
goto error;
}
flag = *(CK_BBOOL *) attr->pValue;
if (flag == TRUE) {
rc = template_attribute_find(derived_key_obj->template, CKA_EXTRACTABLE,
&attr);
if (rc == FALSE) {
TRACE_DEVEL("Failed to build CKA_EXTRACTABLE attribute.\n");
rc = CKR_FUNCTION_FAILED;
goto error;
}
flag = *(CK_BBOOL *) attr->pValue;
flag = (~flag) & 0x1;
}
rc = build_attribute(CKA_NEVER_EXTRACTABLE, &flag, sizeof(CK_BBOOL),
&extract_attr);
if (rc != CKR_OK) {
TRACE_DEVEL("Failed to build CKA_NEVER_EXTRACTABLE attribute.\n");
goto error;
}
template_update_attribute(derived_key_obj->template, value_attr);
template_update_attribute(derived_key_obj->template, value_len_attr);
template_update_attribute(derived_key_obj->template, always_sens_attr);
template_update_attribute(derived_key_obj->template, extract_attr);
// at this point, the derived key is fully constructed...assign an
// object handle and store the key
//
rc = object_mgr_create_final(tokdata, sess, derived_key_obj, handle);
if (rc != CKR_OK) {
TRACE_DEVEL("Object Mgr create final failed.\n");
object_free(derived_key_obj);
derived_key_obj = NULL;
object_put(tokdata, base_key_obj, TRUE);
base_key_obj = NULL;
return rc; // do NOT goto error
}
// should we destroy the base key? SSL3 says yes but that might
// occur in a separate call to C_DestroyObject
//
object_put(tokdata, base_key_obj, TRUE);
base_key_obj = NULL;
return CKR_OK;
error:
if (value_attr)
free(value_attr);
if (value_len_attr)
free(value_len_attr);
if (always_sens_attr)
free(always_sens_attr);
if (extract_attr)
free(extract_attr);
if (derived_key_obj)
object_free(derived_key_obj);
object_put(tokdata, base_key_obj, TRUE);
base_key_obj = NULL;
return rc;
}
//
//
CK_RV ssl3_key_and_mac_derive(STDLL_TokData_t *tokdata,
SESSION *sess,
CK_MECHANISM *mech,
CK_OBJECT_HANDLE base_key,
CK_ATTRIBUTE *pTemplate, CK_ULONG ulCount)
{
OBJECT *base_key_obj = NULL;
CK_ATTRIBUTE *attr = NULL;
CK_BYTE *client_MAC_key_value = NULL;
CK_BYTE *server_MAC_key_value = NULL;
CK_BYTE *client_write_key_value = NULL;
CK_BYTE *server_write_key_value = NULL;
CK_BYTE *client_IV = NULL;
CK_BYTE *server_IV = NULL;
CK_KEY_TYPE keytype = 0xFFFFFFFF;
CK_BYTE variable_data[26];
CK_BYTE key_block[(16 * 26) + (4 * 16)];
CK_ULONG i, key_material_loop_count;
CK_ULONG iv_len = 0, MAC_len, write_len;
CK_RV rc;
CK_BYTE *base_key_value = NULL;
CK_BBOOL base_sensitive;
CK_BBOOL base_always_sensitive;
CK_BBOOL base_extractable;
CK_BBOOL base_never_extractable;
CK_OBJECT_HANDLE client_MAC_handle = 0;
CK_OBJECT_HANDLE server_MAC_handle = 0;
CK_OBJECT_HANDLE client_write_handle = 0;
CK_OBJECT_HANDLE server_write_handle = 0;
CK_SSL3_KEY_MAT_PARAMS *params = NULL;
ATTRIBUTE_PARSE_LIST base_attrs[] = {
{CKA_SENSITIVE, &base_sensitive, sizeof(CK_BBOOL), FALSE},
{CKA_EXTRACTABLE, &base_extractable, sizeof(CK_BBOOL), FALSE},
{CKA_ALWAYS_SENSITIVE, &base_always_sensitive, sizeof(CK_BBOOL), FALSE},
{CKA_NEVER_EXTRACTABLE,
&base_never_extractable, sizeof(CK_BBOOL), FALSE},
};
if (!sess || !mech) {
TRACE_ERROR("%s received bad argument(s)\n", __func__);
return CKR_FUNCTION_FAILED;
}
params = (CK_SSL3_KEY_MAT_PARAMS *) mech->pParameter;
rc = object_mgr_find_in_map1(tokdata, base_key, &base_key_obj, READ_LOCK);
if (rc != CKR_OK) {
TRACE_ERROR("Failed to acquire key from specified handle");
if (rc == CKR_OBJECT_HANDLE_INVALID)
return CKR_KEY_HANDLE_INVALID;
else
return rc;
}
rc = template_attribute_find(base_key_obj->template, CKA_VALUE, &attr);
if (rc == FALSE) {
TRACE_ERROR("Could not find CKA_VALUE in the template\n");
rc = CKR_FUNCTION_FAILED;
goto error;
}
base_key_value = attr->pValue;
template_attribute_find_multiple(base_key_obj->template, base_attrs, 4);
for (i = 0; i < 4; i++) {
if (base_attrs[i].found == FALSE) {
TRACE_ERROR("Could not find attribute in the template\n");
rc = CKR_FUNCTION_FAILED;
goto error;
}
}
// The SSL3 spec says the IVs are 16 bytes long in the exportable case.
// For now, we'll barf if someone asks for an exportable output and asks
// for more than 128 bits of IV...
//
if (params->bIsExport != FALSE && params->ulIVSizeInBits > 128) {
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_PARAM_INVALID));
rc = CKR_MECHANISM_PARAM_INVALID;
goto error;
}
// the template must specify the key type for the client and server keys
//
// also, CKA_SENSITIVE, CKA_ALWAYS_SENSITIVE, CKA_EXTRACTABLE and
// CKA_NEVER_EXTRACTABLE, if present, are not allowed to differ from
// the base key. We also check for stupid stuff.
//
for (i = 0, attr = pTemplate; i < ulCount; i++, attr++) {
CK_BBOOL tmp;
if (attr->type == CKA_KEY_TYPE) {
keytype = *(CK_KEY_TYPE *) attr->pValue;
} else if (attr->type == CKA_SENSITIVE) {
tmp = *(CK_BBOOL *) attr->pValue;
if (tmp != base_sensitive) {
TRACE_ERROR("%s\n", ock_err(ERR_TEMPLATE_INCONSISTENT));
rc = CKR_TEMPLATE_INCONSISTENT;
goto error;
}
} else if (attr->type == CKA_ALWAYS_SENSITIVE) {
tmp = *(CK_BBOOL *) attr->pValue;
if (tmp != base_always_sensitive) {
TRACE_ERROR("%s\n", ock_err(ERR_TEMPLATE_INCONSISTENT));
rc = CKR_TEMPLATE_INCONSISTENT;
goto error;
}
} else if (attr->type == CKA_EXTRACTABLE) {
tmp = *(CK_BBOOL *) attr->pValue;
if (tmp != base_extractable) {
TRACE_ERROR("%s\n", ock_err(ERR_TEMPLATE_INCONSISTENT));
rc = CKR_TEMPLATE_INCONSISTENT;
goto error;
}
} else if (attr->type == CKA_NEVER_EXTRACTABLE) {
tmp = *(CK_BBOOL *) attr->pValue;
if (tmp != base_never_extractable) {
TRACE_ERROR("%s\n", ock_err(ERR_TEMPLATE_INCONSISTENT));
rc = CKR_TEMPLATE_INCONSISTENT;
goto error;
}
} else if (attr->type == CKA_CLASS) {
CK_OBJECT_CLASS cl = *(CK_OBJECT_CLASS *) attr->pValue;
if (cl != CKO_SECRET_KEY) {
TRACE_ERROR("%s\n", ock_err(ERR_TEMPLATE_INCONSISTENT));
rc = CKR_TEMPLATE_INCONSISTENT;
goto error;
}
}
}
// a key type must be specified for the client and server write keys
//
if (keytype == 0xFFFFFFFF) {
TRACE_ERROR("%s\n", ock_err(ERR_TEMPLATE_INCOMPLETE));
rc = CKR_TEMPLATE_INCOMPLETE;
goto error;
}
// figure out how much key material we need to generate
//
key_material_loop_count = 2 * ((params->ulMacSizeInBits + 7) / 8) +
2 * ((params->ulKeySizeInBits + 7) / 8);
if (params->bIsExport == FALSE)
key_material_loop_count += 2 * ((params->ulIVSizeInBits + 7) / 8);
// we stop at 'ZZZZ....' presumably this is enough for all cases?
//
if (key_material_loop_count > 26 * 16) {
TRACE_DEVEL("key_material_loop_count is too big.\n");
rc = CKR_FUNCTION_FAILED;
goto error;
}
key_material_loop_count = (key_material_loop_count + 15) / 16;
// generate the key material
//
for (i = 0; i < key_material_loop_count; i++) {
memset(variable_data, ('A' + i), i + 1);
rc = ssl3_sha_then_md5(tokdata, sess,
base_key_value,
params->RandomInfo.pServerRandom,
params->RandomInfo.ulServerRandomLen,
params->RandomInfo.pClientRandom,
params->RandomInfo.ulClientRandomLen,
variable_data, i + 1, &(key_block[i * 16]));
if (rc != CKR_OK) {
TRACE_DEVEL("ssl3_sha_then_md5 failed.\n");
goto error;
}
}
// Break key material into pieces
//
MAC_len = (params->ulMacSizeInBits + 7) / 8;
write_len = (params->ulKeySizeInBits + 7) / 8; // check this
client_MAC_key_value = key_block;
server_MAC_key_value = client_MAC_key_value + MAC_len;
client_write_key_value = server_MAC_key_value + MAC_len;
server_write_key_value =
client_write_key_value + (params->ulKeySizeInBits + 7) / 8;
if (params->ulIVSizeInBits != 0) {
iv_len = (params->ulIVSizeInBits + 7) / 8;
client_IV = server_write_key_value + write_len;
server_IV = client_IV + iv_len;
}
// Exportable ciphers require additional processing
//
if (params->bIsExport == TRUE) {
rc = ssl3_md5_only(tokdata, sess,
client_write_key_value,
(params->ulKeySizeInBits + 7) / 8,
params->RandomInfo.pClientRandom,
params->RandomInfo.ulClientRandomLen,
params->RandomInfo.pServerRandom,
params->RandomInfo.ulServerRandomLen,
&(key_block[16 * 26]));
if (rc != CKR_OK) {
TRACE_DEVEL("ssl3_md5_only failed.\n");
goto error;
}
client_write_key_value = &(key_block[16 * 26]);
rc = ssl3_md5_only(tokdata, sess,
server_write_key_value,
(params->ulKeySizeInBits + 7) / 8,
params->RandomInfo.pServerRandom,
params->RandomInfo.ulServerRandomLen,
params->RandomInfo.pClientRandom,
params->RandomInfo.ulClientRandomLen,
&(key_block[16 * 26 + 16]));
if (rc != CKR_OK) {
TRACE_DEVEL("ssl3_md5_only failed.\n");
goto error;
}
server_write_key_value = &(key_block[16 * 26 + 16]);
if (params->ulIVSizeInBits != 0) {
rc = ssl3_md5_only(tokdata, sess,
NULL,
0,
params->RandomInfo.pClientRandom,
params->RandomInfo.ulClientRandomLen,
params->RandomInfo.pServerRandom,
params->RandomInfo.ulServerRandomLen,
&(key_block[16 * 26 + 2 * 16]));
if (rc != CKR_OK) {
TRACE_DEVEL("ssl3_md5_only failed.\n");
goto error;
}
client_IV = &(key_block[16 * 26 + 2 * 16]);
rc = ssl3_md5_only(tokdata, sess,
NULL,
0,
params->RandomInfo.pServerRandom,
params->RandomInfo.ulServerRandomLen,
params->RandomInfo.pClientRandom,
params->RandomInfo.ulClientRandomLen,
&(key_block[16 * 26 + 3 * 16]));
if (rc != CKR_OK) {
TRACE_DEVEL("ssl3_md5_only failed.\n");
goto error;
}
server_IV = &(key_block[16 * 26 + 3 * 16]);
}
}
rc = ssl3_kmd_process_mac_keys(tokdata, sess, pTemplate, ulCount,
&client_MAC_handle, client_MAC_key_value,
&server_MAC_handle, server_MAC_key_value,
MAC_len);
if (rc != CKR_OK) {
TRACE_DEVEL("ssl3_kmd_process_mac_keys failed.\n");
goto error;
}
rc = ssl3_kmd_process_write_keys(tokdata, sess, pTemplate, ulCount, keytype,
&client_write_handle,
client_write_key_value,
&server_write_handle,
server_write_key_value, write_len);
if (rc != CKR_OK) {
TRACE_DEVEL("ssl3_kmd_process_write_keys failed.\n");
goto error;
}
params->pReturnedKeyMaterial->hClientMacSecret = client_MAC_handle;
params->pReturnedKeyMaterial->hServerMacSecret = server_MAC_handle;
params->pReturnedKeyMaterial->hClientKey = client_write_handle;
params->pReturnedKeyMaterial->hServerKey = server_write_handle;
if (params->ulIVSizeInBits != 0) {
if (params->pReturnedKeyMaterial->pIVClient)
memcpy(params->pReturnedKeyMaterial->pIVClient, client_IV, iv_len);
if (params->pReturnedKeyMaterial->pIVServer)
memcpy(params->pReturnedKeyMaterial->pIVServer, server_IV, iv_len);
#if 0
CK_BYTE *p1, *p2;
p1 = (CK_BYTE *) malloc(iv_len);
p2 = (CK_BYTE *) malloc(iv_len);
if (!p1 || !p2) {
rc = CKR_HOST_MEMORY;
goto error;
}
memcpy(p1, client_IV, iv_len);
memcpy(p2, server_IV, iv_len);
params->pReturnedKeyMaterial->pIVClient = p1;
params->pReturnedKeyMaterial->pIVServer = p2;
#endif
}
error:
object_put(tokdata, base_key_obj, TRUE);
base_key_obj = NULL;
return rc;
}
CK_RV ssl3_kmd_process_mac_keys(STDLL_TokData_t *tokdata,
SESSION *sess,
CK_ATTRIBUTE *pTemplate,
CK_ULONG ulCount,
CK_OBJECT_HANDLE *client_handle,
CK_BYTE *client_value,
CK_OBJECT_HANDLE *server_handle,
CK_BYTE *server_value, CK_ULONG mac_len)
{
OBJECT *client_obj = NULL;
OBJECT *server_obj = NULL;
CK_ATTRIBUTE *client_val_attr = NULL;
CK_ATTRIBUTE *client_val_len_attr = NULL;
CK_ATTRIBUTE *server_val_attr = NULL;
CK_ATTRIBUTE *server_val_len_attr = NULL;
CK_ATTRIBUTE *attr = NULL;
CK_ATTRIBUTE *new_attrs = NULL;
CK_ULONG i, cnt;
CK_ULONG true_vals[] = { CKA_SIGN, CKA_VERIFY, CKA_DERIVE };
CK_ULONG false_vals[] = { CKA_ENCRYPT, CKA_DECRYPT, CKA_WRAP, CKA_UNWRAP };
CK_RV rc = 0;
// for the MAC keys, we want the following default values:
// CKA_SIGN, CKA_VERIFY, CKA_DERIVE = TRUE
// CKA_ENCRYPT, CKA_DECRYPT, CKA_WRAP, CKA_UNWRAP = FALSE
//
// attributes are added in sequential order so we stick the defaults
// at the beginning so that they may be overridden by caller-specified
// values.
//
new_attrs = (CK_ATTRIBUTE *) malloc((ulCount + 7) * (sizeof(CK_ATTRIBUTE)));
if (!new_attrs)
goto error;
// we have to treat these attributes a bit differently. normally, we
// allocate the CK_ATTRIBUTE and the value with a single malloc and just
// point the pValue member to the extra space. we can't do that here
// because we have to "emulate" the way attributes are passed in from the
// cryptoki application...as an array of CK_ATTRIBUTEs with no extra space
// (that is, pValue must be allocated separately).
//
attr = new_attrs;
for (i = 0; i < sizeof(true_vals) / sizeof(CK_ULONG); i++, attr++) {
attr->type = true_vals[i];
attr->ulValueLen = sizeof(CK_BBOOL);
attr->pValue = (CK_BBOOL *) malloc(sizeof(CK_BBOOL));
if (!attr->pValue) {
rc = CKR_HOST_MEMORY;
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
goto error;
}
*(CK_BBOOL *) attr->pValue = TRUE;
}
for (i = 0; i < sizeof(false_vals) / sizeof(CK_ULONG); i++, attr++) {
attr->type = false_vals[i];
attr->ulValueLen = sizeof(CK_BBOOL);
attr->pValue = (CK_BBOOL *) malloc(sizeof(CK_BBOOL));
if (!attr->pValue) {
rc = CKR_HOST_MEMORY;
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
goto error;
}
*(CK_BBOOL *) attr->pValue = FALSE;
}
for (i = 0, cnt = 0; i < ulCount; i++) {
if (pTemplate[i].type != CKA_KEY_TYPE &&
pTemplate[i].type != CKA_VALUE &&
pTemplate[i].type != CKA_VALUE_LEN) {
attr->type = pTemplate[i].type;
attr->ulValueLen = pTemplate[i].ulValueLen;
attr->pValue = (char *) malloc(attr->ulValueLen);
if (!attr->pValue) {
rc = CKR_HOST_MEMORY;
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
goto error;
}
memcpy(attr->pValue, pTemplate[i].pValue, attr->ulValueLen);
cnt++;
attr++;
}
}
ulCount = 7 + cnt;
// create the key skeletons
//
rc = object_mgr_create_skel(tokdata, sess,
new_attrs, ulCount,
MODE_DERIVE,
CKO_SECRET_KEY,
CKK_GENERIC_SECRET, &client_obj);
if (rc != CKR_OK) {
TRACE_DEVEL("Object Mgr Create Skeleton failed.\n");
goto error;
}
rc = object_mgr_create_skel(tokdata, sess,
new_attrs, ulCount,
MODE_DERIVE,
CKO_SECRET_KEY,
CKK_GENERIC_SECRET, &server_obj);
if (rc != CKR_OK) {
TRACE_DEVEL("Object Mgr Create Skeleton failed.\n");
goto error;
}
for (i = 0; i < ulCount; i++)
if (new_attrs[i].pValue)
free(new_attrs[i].pValue);
free(new_attrs);
new_attrs = NULL;
rc = build_attribute(CKA_VALUE, client_value, mac_len, &client_val_attr);
if (rc != CKR_OK) {
TRACE_DEVEL("Failed to build CKA_VALUE attribute.\n");
goto error;
}
rc = build_attribute(CKA_VALUE, server_value, mac_len, &server_val_attr);
if (rc != CKR_OK) {
TRACE_DEVEL("Failed to build CKA_VALUE attribute.\n");
goto error;
}
rc = build_attribute(CKA_VALUE_LEN, (CK_BYTE *) & mac_len, sizeof(CK_ULONG),
&client_val_len_attr);
if (rc != CKR_OK) {
TRACE_DEVEL("Failed to build CKA_VALUE_LEN attribute.\n");
goto error;
}
rc = build_attribute(CKA_VALUE_LEN, (CK_BYTE *) & mac_len, sizeof(CK_ULONG),
&server_val_len_attr);
if (rc != CKR_OK) {
TRACE_DEVEL("Failed to build CKA_VALUE_LEN attribute.\n");
goto error;
}
template_update_attribute(client_obj->template, client_val_attr);
template_update_attribute(client_obj->template, client_val_len_attr);
// the object owns the attributes now...
client_val_attr = NULL;
client_val_len_attr = NULL;
template_update_attribute(server_obj->template, server_val_attr);
template_update_attribute(server_obj->template, server_val_len_attr);
// the object owns the attributes now...
server_val_attr = NULL;
server_val_len_attr = NULL;
rc = object_mgr_create_final(tokdata, sess, client_obj, client_handle);
if (rc != CKR_OK) {
TRACE_DEVEL("Object Mgr Create Final failed.\n");
goto error;
}
rc = object_mgr_create_final(tokdata, sess, server_obj, server_handle);
if (rc != CKR_OK) {
TRACE_DEVEL("Object Mgr Create Final failed.\n");
object_mgr_destroy_object(tokdata, sess, *client_handle);
client_obj = NULL;
goto error;
}
return CKR_OK;
error:
*client_handle = 0;
*server_handle = 0;
if (client_obj) {
object_free(client_obj);
client_val_attr = NULL; // these get freed with the object
client_val_len_attr = NULL;
}
if (server_obj) {
object_free(server_obj);
server_val_attr = NULL; // these get freed with the object
server_val_len_attr = NULL;
}
if (client_val_attr)
free(client_val_attr);
if (client_val_len_attr)
free(client_val_len_attr);
if (server_val_attr)
free(server_val_attr);
if (server_val_len_attr)
free(server_val_len_attr);
if (new_attrs) {
for (i = 0; i < ulCount; i++) {
if (new_attrs[i].pValue)
free(new_attrs[i].pValue);
}
free(new_attrs);
}
return rc;
}
CK_RV ssl3_kmd_process_write_keys(STDLL_TokData_t *tokdata,
SESSION *sess,
CK_ATTRIBUTE *pTemplate,
CK_ULONG ulCount,
CK_KEY_TYPE keytype,
CK_OBJECT_HANDLE *client_handle,
CK_BYTE *client_value,
CK_OBJECT_HANDLE *server_handle,
CK_BYTE *server_value, CK_ULONG write_len)
{
CK_ATTRIBUTE *client_val_attr = NULL;
CK_ATTRIBUTE *client_val_len_attr = NULL;
CK_ATTRIBUTE *server_val_attr = NULL;
CK_ATTRIBUTE *server_val_len_attr = NULL;
CK_ATTRIBUTE *new_attrs = NULL;
CK_ATTRIBUTE *attr = NULL;
OBJECT *client_obj = NULL;
OBJECT *server_obj = NULL;
CK_ULONG i, cnt;
CK_ULONG true_vals[] = { CKA_ENCRYPT, CKA_DECRYPT, CKA_DERIVE };
CK_ULONG false_vals[] = { CKA_SIGN, CKA_VERIFY, CKA_WRAP, CKA_UNWRAP };
CK_RV rc = CKR_HOST_MEMORY;
// for the write keys, we want the following default values:
// CKA_ENCRYPT, CKA_DECRYPT, CKA_DERIVE = TRUE
// CKA_SIGN, CKA_VERIFY, CKA_WRAP, CKA_UNWRAP = FALSE
//
// attributes are added in sequential order so we stick the defaults
// at the beginning so that they may be overridden by caller-specified
// values.
//
new_attrs = (CK_ATTRIBUTE *) malloc((ulCount + 7) * (sizeof(CK_ATTRIBUTE)));
if (!new_attrs)
goto error;
// we have to treat these attributes a bit differently. normally, we
// allocate the CK_ATTRIBUTE and the value with a single malloc and just
// point the pValue member to the extra space. we can't do that here because
// we have to "emulate" the way attributes are passed in from the cryptoki
// application...as an array of CK_ATTRIBUTEs with no extra space (that is,
// pValue must be allocated separately).
//
attr = new_attrs;
for (i = 0; i < sizeof(true_vals) / sizeof(CK_ULONG); i++, attr++) {
attr->type = true_vals[i];
attr->ulValueLen = sizeof(CK_BBOOL);
attr->pValue = (CK_BBOOL *) malloc(sizeof(CK_BBOOL));
if (!attr->pValue) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
goto error;
}
*(CK_BBOOL *) attr->pValue = TRUE;
}
for (i = 0; i < sizeof(false_vals) / sizeof(CK_ULONG); i++, attr++) {
attr->type = false_vals[i];
attr->ulValueLen = sizeof(CK_BBOOL);
attr->pValue = (CK_BBOOL *) malloc(sizeof(CK_BBOOL));
if (!attr->pValue) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
goto error;
}
*(CK_BBOOL *) attr->pValue = FALSE;
}
for (i = 0, cnt = 0; i < ulCount; i++) {
if (pTemplate[i].type != CKA_KEY_TYPE &&
pTemplate[i].type != CKA_VALUE &&
pTemplate[i].type != CKA_VALUE_LEN) {
attr->type = pTemplate[i].type;
attr->ulValueLen = pTemplate[i].ulValueLen;
attr->pValue = (char *) malloc(attr->ulValueLen);
if (!attr->pValue) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
goto error;
}
memcpy(attr->pValue, pTemplate[i].pValue, attr->ulValueLen);
cnt++;
attr++;
}
}
ulCount = 7 + cnt;
rc = object_mgr_create_skel(tokdata, sess,
new_attrs, ulCount,
MODE_DERIVE,
CKO_SECRET_KEY, keytype, &client_obj);
if (rc != CKR_OK) {
TRACE_DEVEL("Object Mgr Create Skeleton failed.\n");
goto error;
}
rc = object_mgr_create_skel(tokdata, sess,
new_attrs, ulCount,
MODE_DERIVE,
CKO_SECRET_KEY, keytype, &server_obj);
if (rc != CKR_OK) {
TRACE_DEVEL("Object Mgr Create Skeleton failed.\n");
goto error;
}
for (i = 0; i < ulCount; i++) {
if (new_attrs[i].pValue)
free(new_attrs[i].pValue);
}
free(new_attrs);
new_attrs = NULL;
rc = build_attribute(CKA_VALUE, client_value, write_len, &client_val_attr);
rc |= build_attribute(CKA_VALUE, server_value, write_len, &server_val_attr);
if (rc != CKR_OK) {
TRACE_DEVEL("Failed to build CKA_VALUE attribute.\n");
goto error;
}
switch (keytype) {
case CKK_GENERIC_SECRET:
case CKK_DES:
case CKK_DES2:
case CKK_DES3:
case CKK_RC2:
case CKK_RC4:
case CKK_RC5:
case CKK_CAST:
case CKK_CAST3:
case CKK_CAST5:
rc = build_attribute(CKA_VALUE_LEN, (CK_BYTE *) & write_len,
sizeof(CK_ULONG), &client_val_len_attr);
rc |=
build_attribute(CKA_VALUE_LEN, (CK_BYTE *) & write_len,
sizeof(CK_ULONG), &server_val_len_attr);
if (rc != CKR_OK) {
TRACE_DEVEL("Failed to build CKA_VALUE_LEN attribute.\n");
goto error;
}
rc = template_validate_attribute(tokdata, client_obj->template,
client_val_len_attr,
CKO_SECRET_KEY, keytype, MODE_DERIVE);
rc |= template_validate_attribute(tokdata, server_obj->template,
server_val_len_attr,
CKO_SECRET_KEY, keytype, MODE_DERIVE);
// for these I use MODE_CREATE because I want to validate the
// value/length. no othe modes are allowed to mess wiht CKA_VALUE (see
// for instance, des_validate_attribute())
//
rc |= template_validate_attribute(tokdata, client_obj->template,
client_val_attr,
CKO_SECRET_KEY, keytype, MODE_CREATE);
rc |= template_validate_attribute(tokdata, server_obj->template,
server_val_attr,
CKO_SECRET_KEY, keytype, MODE_CREATE);
if (rc != CKR_OK) {
TRACE_DEVEL("template_validate_attribute failed.\n");
goto error;
}
template_update_attribute(client_obj->template, client_val_attr);
template_update_attribute(server_obj->template, server_val_attr);
template_update_attribute(client_obj->template, client_val_len_attr);
template_update_attribute(server_obj->template, server_val_len_attr);
// the object owns the attributes now...
//
client_val_attr = NULL;
server_val_attr = NULL;
client_val_len_attr = NULL;
server_val_len_attr = NULL;
break;
default:
rc = template_validate_attribute(tokdata, client_obj->template,
client_val_attr,
CKO_SECRET_KEY, keytype, MODE_CREATE);
rc |= template_validate_attribute(tokdata, server_obj->template,
server_val_attr,
CKO_SECRET_KEY, keytype, MODE_CREATE);
if (rc != CKR_OK) {
TRACE_DEVEL("template_validate_attribute failed.\n");
goto error;
}
template_update_attribute(client_obj->template, client_val_attr);
template_update_attribute(server_obj->template, server_val_attr);
// the object owns the attributes now...
//
client_val_attr = NULL;
server_val_attr = NULL;
}
// finally, assign a handle to each key
//
rc = object_mgr_create_final(tokdata, sess, client_obj, client_handle);
if (rc != CKR_OK) {
TRACE_DEVEL("Object Mgr Create Final failed.\n");
goto error;
}
rc = object_mgr_create_final(tokdata, sess, server_obj, server_handle);
if (rc != CKR_OK) {
TRACE_DEVEL("Object Mgr Create Final failed.\n");
object_mgr_destroy_object(tokdata, sess, *client_handle);
client_obj = NULL;
goto error;
}
return CKR_OK;
error:
*client_handle = 0;
*server_handle = 0;
if (client_obj)
object_free(client_obj);
if (server_obj)
object_free(server_obj);
// the only way these guys are non-NULL is if they were created but
// not yet to added to an object
//
if (client_val_attr)
free(client_val_attr);
if (client_val_len_attr)
free(client_val_len_attr);
if (server_val_attr)
free(server_val_attr);
if (server_val_len_attr)
free(server_val_len_attr);
if (new_attrs) {
for (i = 0; i < ulCount; i++) {
if (new_attrs[i].pValue)
free(new_attrs[i].pValue);
}
free(new_attrs);
}
return rc;
}