opensc/src/pkcs11/mechanism.c

1076 lines
27 KiB
C

/*
* Generic handling of PKCS11 mechanisms
*
* Copyright (C) 2002 Olaf Kirch <okir@suse.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include "sc-pkcs11.h"
/* Also used for verification data */
struct hash_signature_info {
CK_MECHANISM_TYPE mech;
CK_MECHANISM_TYPE hash_mech;
CK_MECHANISM_TYPE sign_mech;
sc_pkcs11_mechanism_type_t *hash_type;
sc_pkcs11_mechanism_type_t *sign_type;
};
/* Also used for verification and decryption data */
struct signature_data {
struct sc_pkcs11_object *key;
struct hash_signature_info *info;
sc_pkcs11_operation_t * md;
CK_BYTE buffer[4096/8];
unsigned int buffer_len;
};
/*
* Register a mechanism
*/
CK_RV
sc_pkcs11_register_mechanism(struct sc_pkcs11_card *p11card,
sc_pkcs11_mechanism_type_t *mt)
{
sc_pkcs11_mechanism_type_t **p;
if (mt == NULL)
return CKR_HOST_MEMORY;
p = (sc_pkcs11_mechanism_type_t **) realloc(p11card->mechanisms,
(p11card->nmechanisms + 2) * sizeof(*p));
if (p == NULL)
return CKR_HOST_MEMORY;
p11card->mechanisms = p;
p[p11card->nmechanisms++] = mt;
p[p11card->nmechanisms] = NULL;
return CKR_OK;
}
/*
* Look up a mechanism
*/
sc_pkcs11_mechanism_type_t *
sc_pkcs11_find_mechanism(struct sc_pkcs11_card *p11card, CK_MECHANISM_TYPE mech, unsigned int flags)
{
sc_pkcs11_mechanism_type_t *mt;
unsigned int n;
for (n = 0; n < p11card->nmechanisms; n++) {
mt = p11card->mechanisms[n];
if (mt && mt->mech == mech && ((mt->mech_info.flags & flags) == flags))
return mt;
}
return NULL;
}
/*
* Query mechanisms.
* All of this is greatly simplified by having the framework
* register all supported mechanisms at initialization
* time.
*/
CK_RV
sc_pkcs11_get_mechanism_list(struct sc_pkcs11_card *p11card,
CK_MECHANISM_TYPE_PTR pList,
CK_ULONG_PTR pulCount)
{
sc_pkcs11_mechanism_type_t *mt;
unsigned int n, count = 0;
int rv;
if (!p11card)
return CKR_TOKEN_NOT_PRESENT;
for (n = 0; n < p11card->nmechanisms; n++) {
if (!(mt = p11card->mechanisms[n]))
continue;
if (pList && count < *pulCount)
pList[count] = mt->mech;
count++;
}
rv = CKR_OK;
if (pList && count > *pulCount)
rv = CKR_BUFFER_TOO_SMALL;
*pulCount = count;
return rv;
}
CK_RV
sc_pkcs11_get_mechanism_info(struct sc_pkcs11_card *p11card,
CK_MECHANISM_TYPE mechanism,
CK_MECHANISM_INFO_PTR pInfo)
{
sc_pkcs11_mechanism_type_t *mt;
if (!(mt = sc_pkcs11_find_mechanism(p11card, mechanism, 0)))
return CKR_MECHANISM_INVALID;
memcpy(pInfo, &mt->mech_info, sizeof(*pInfo));
return CKR_OK;
}
/*
* Create/destroy operation handle
*/
sc_pkcs11_operation_t *
sc_pkcs11_new_operation(sc_pkcs11_session_t *session,
sc_pkcs11_mechanism_type_t *type)
{
sc_pkcs11_operation_t *res;
res = calloc(1, type->obj_size);
if (res) {
res->session = session;
res->type = type;
}
return res;
}
void
sc_pkcs11_release_operation(sc_pkcs11_operation_t **ptr)
{
sc_pkcs11_operation_t *operation = *ptr;
if (!operation)
return;
if (operation->type && operation->type->release)
operation->type->release(operation);
memset(operation, 0, sizeof(*operation));
free(operation);
*ptr = NULL;
}
CK_RV
sc_pkcs11_md_init(struct sc_pkcs11_session *session,
CK_MECHANISM_PTR pMechanism)
{
struct sc_pkcs11_card *p11card;
sc_pkcs11_operation_t *operation;
sc_pkcs11_mechanism_type_t *mt;
int rv;
LOG_FUNC_CALLED(context);
if (!session || !session->slot || !(p11card = session->slot->p11card))
LOG_FUNC_RETURN(context, CKR_ARGUMENTS_BAD);
/* See if we support this mechanism type */
mt = sc_pkcs11_find_mechanism(p11card, pMechanism->mechanism, CKF_DIGEST);
if (mt == NULL)
LOG_FUNC_RETURN(context, CKR_MECHANISM_INVALID);
rv = session_start_operation(session, SC_PKCS11_OPERATION_DIGEST, mt, &operation);
if (rv != CKR_OK)
LOG_FUNC_RETURN(context, rv);
memcpy(&operation->mechanism, pMechanism, sizeof(CK_MECHANISM));
rv = mt->md_init(operation);
if (rv != CKR_OK)
session_stop_operation(session, SC_PKCS11_OPERATION_DIGEST);
LOG_FUNC_RETURN(context, rv);
}
CK_RV
sc_pkcs11_md_update(struct sc_pkcs11_session *session,
CK_BYTE_PTR pData, CK_ULONG ulDataLen)
{
sc_pkcs11_operation_t *op;
int rv;
rv = session_get_operation(session, SC_PKCS11_OPERATION_DIGEST, &op);
if (rv != CKR_OK)
goto done;
rv = op->type->md_update(op, pData, ulDataLen);
done:
if (rv != CKR_OK)
session_stop_operation(session, SC_PKCS11_OPERATION_DIGEST);
LOG_FUNC_RETURN(context, rv);
}
CK_RV
sc_pkcs11_md_final(struct sc_pkcs11_session *session,
CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen)
{
sc_pkcs11_operation_t *op;
CK_RV rv;
rv = session_get_operation(session, SC_PKCS11_OPERATION_DIGEST, &op);
if (rv != CKR_OK)
LOG_FUNC_RETURN(context, rv);
/* This is a request for the digest length */
if (pData == NULL)
*pulDataLen = 0;
rv = op->type->md_final(op, pData, pulDataLen);
if (rv == CKR_BUFFER_TOO_SMALL)
LOG_FUNC_RETURN(context, pData == NULL ? CKR_OK : CKR_BUFFER_TOO_SMALL);
session_stop_operation(session, SC_PKCS11_OPERATION_DIGEST);
LOG_FUNC_RETURN(context, rv);
}
/*
* Initialize a signing context. When we get here, we know
* the key object is capable of signing _something_
*/
CK_RV
sc_pkcs11_sign_init(struct sc_pkcs11_session *session, CK_MECHANISM_PTR pMechanism,
struct sc_pkcs11_object *key, CK_MECHANISM_TYPE key_type)
{
struct sc_pkcs11_card *p11card;
sc_pkcs11_operation_t *operation;
sc_pkcs11_mechanism_type_t *mt;
int rv;
LOG_FUNC_CALLED(context);
if (!session || !session->slot || !(p11card = session->slot->p11card))
LOG_FUNC_RETURN(context, CKR_ARGUMENTS_BAD);
/* See if we support this mechanism type */
sc_log(context, "mechanism 0x%lX, key-type 0x%lX",
pMechanism->mechanism, key_type);
mt = sc_pkcs11_find_mechanism(p11card, pMechanism->mechanism, CKF_SIGN);
if (mt == NULL)
LOG_FUNC_RETURN(context, CKR_MECHANISM_INVALID);
/* See if compatible with key type */
if (mt->key_type != key_type)
LOG_FUNC_RETURN(context, CKR_KEY_TYPE_INCONSISTENT);
rv = session_start_operation(session, SC_PKCS11_OPERATION_SIGN, mt, &operation);
if (rv != CKR_OK)
LOG_FUNC_RETURN(context, rv);
memcpy(&operation->mechanism, pMechanism, sizeof(CK_MECHANISM));
rv = mt->sign_init(operation, key);
if (rv != CKR_OK)
session_stop_operation(session, SC_PKCS11_OPERATION_SIGN);
LOG_FUNC_RETURN(context, rv);
}
CK_RV
sc_pkcs11_sign_update(struct sc_pkcs11_session *session,
CK_BYTE_PTR pData, CK_ULONG ulDataLen)
{
sc_pkcs11_operation_t *op;
int rv;
LOG_FUNC_CALLED(context);
rv = session_get_operation(session, SC_PKCS11_OPERATION_SIGN, &op);
if (rv != CKR_OK)
LOG_FUNC_RETURN(context, rv);
if (op->type->sign_update == NULL) {
rv = CKR_KEY_TYPE_INCONSISTENT;
goto done;
}
rv = op->type->sign_update(op, pData, ulDataLen);
done:
if (rv != CKR_OK)
session_stop_operation(session, SC_PKCS11_OPERATION_SIGN);
LOG_FUNC_RETURN(context, rv);
}
CK_RV
sc_pkcs11_sign_final(struct sc_pkcs11_session *session,
CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen)
{
sc_pkcs11_operation_t *op;
int rv;
LOG_FUNC_CALLED(context);
rv = session_get_operation(session, SC_PKCS11_OPERATION_SIGN, &op);
if (rv != CKR_OK)
LOG_FUNC_RETURN(context, rv);
/* Bail out for signature mechanisms that don't do hashing */
if (op->type->sign_final == NULL) {
rv = CKR_KEY_TYPE_INCONSISTENT;
goto done;
}
rv = op->type->sign_final(op, pSignature, pulSignatureLen);
done:
if (rv != CKR_BUFFER_TOO_SMALL && pSignature != NULL)
session_stop_operation(session, SC_PKCS11_OPERATION_SIGN);
LOG_FUNC_RETURN(context, rv);
}
CK_RV
sc_pkcs11_sign_size(struct sc_pkcs11_session *session, CK_ULONG_PTR pLength)
{
sc_pkcs11_operation_t *op;
int rv;
rv = session_get_operation(session, SC_PKCS11_OPERATION_SIGN, &op);
if (rv != CKR_OK)
LOG_FUNC_RETURN(context, rv);
/* Bail out for signature mechanisms that don't do hashing */
if (op->type->sign_size == NULL) {
rv = CKR_KEY_TYPE_INCONSISTENT;
goto done;
}
rv = op->type->sign_size(op, pLength);
done:
if (rv != CKR_OK)
session_stop_operation(session, SC_PKCS11_OPERATION_SIGN);
LOG_FUNC_RETURN(context, rv);
}
/*
* Initialize a signature operation
*/
static CK_RV
sc_pkcs11_signature_init(sc_pkcs11_operation_t *operation,
struct sc_pkcs11_object *key)
{
struct hash_signature_info *info;
struct signature_data *data;
CK_RV rv;
int can_do_it = 0;
LOG_FUNC_CALLED(context);
if (!(data = calloc(1, sizeof(*data))))
LOG_FUNC_RETURN(context, CKR_HOST_MEMORY);
data->info = NULL;
data->key = key;
if (key->ops->can_do) {
rv = key->ops->can_do(operation->session, key, operation->type->mech, CKF_SIGN);
if (rv == CKR_OK) {
/* Mechanism recognised and can be performed by pkcs#15 card */
can_do_it = 1;
}
else if (rv == CKR_FUNCTION_NOT_SUPPORTED) {
/* Mechanism not recognised by pkcs#15 card */
can_do_it = 0;
}
else {
/* Mechanism recognised but cannot be performed by pkcs#15 card, or some general error. */
free(data);
LOG_FUNC_RETURN(context, rv);
}
}
/* If this is a signature with hash operation,
* and card cannot perform itself signature with hash operation,
* set up the hash operation */
info = (struct hash_signature_info *) operation->type->mech_data;
if (info != NULL && !can_do_it) {
/* Initialize hash operation */
data->md = sc_pkcs11_new_operation(operation->session, info->hash_type);
if (data->md == NULL)
rv = CKR_HOST_MEMORY;
else
rv = info->hash_type->md_init(data->md);
if (rv != CKR_OK) {
sc_pkcs11_release_operation(&data->md);
free(data);
LOG_FUNC_RETURN(context, rv);
}
data->info = info;
}
operation->priv_data = data;
LOG_FUNC_RETURN(context, CKR_OK);
}
static CK_RV
sc_pkcs11_signature_update(sc_pkcs11_operation_t *operation,
CK_BYTE_PTR pPart, CK_ULONG ulPartLen)
{
struct signature_data *data;
LOG_FUNC_CALLED(context);
sc_log(context, "data part length %li", ulPartLen);
data = (struct signature_data *) operation->priv_data;
if (data->md) {
CK_RV rv = data->md->type->md_update(data->md, pPart, ulPartLen);
LOG_FUNC_RETURN(context, rv);
}
/* This signature mechanism operates on the raw data */
if (data->buffer_len + ulPartLen > sizeof(data->buffer))
LOG_FUNC_RETURN(context, CKR_DATA_LEN_RANGE);
memcpy(data->buffer + data->buffer_len, pPart, ulPartLen);
data->buffer_len += ulPartLen;
sc_log(context, "data length %u", data->buffer_len);
LOG_FUNC_RETURN(context, CKR_OK);
}
static CK_RV
sc_pkcs11_signature_final(sc_pkcs11_operation_t *operation,
CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen)
{
struct signature_data *data;
CK_RV rv;
LOG_FUNC_CALLED(context);
data = (struct signature_data *) operation->priv_data;
sc_log(context, "data length %u", data->buffer_len);
if (data->md) {
sc_pkcs11_operation_t *md = data->md;
CK_ULONG len = sizeof(data->buffer);
rv = md->type->md_final(md, data->buffer, &len);
if (rv == CKR_BUFFER_TOO_SMALL)
rv = CKR_FUNCTION_FAILED;
if (rv != CKR_OK)
LOG_FUNC_RETURN(context, rv);
data->buffer_len = len;
}
sc_log(context, "%u bytes to sign", data->buffer_len);
rv = data->key->ops->sign(operation->session, data->key, &operation->mechanism,
data->buffer, data->buffer_len, pSignature, pulSignatureLen);
LOG_FUNC_RETURN(context, rv);
}
static CK_RV
sc_pkcs11_signature_size(sc_pkcs11_operation_t *operation, CK_ULONG_PTR pLength)
{
struct sc_pkcs11_object *key;
CK_ATTRIBUTE attr = { CKA_MODULUS_BITS, pLength, sizeof(*pLength) };
CK_KEY_TYPE key_type;
CK_ATTRIBUTE attr_key_type = { CKA_KEY_TYPE, &key_type, sizeof(key_type) };
CK_RV rv;
key = ((struct signature_data *) operation->priv_data)->key;
/*
* EC and GOSTR do not have CKA_MODULUS_BITS attribute.
* But other code in framework treats them as if they do.
* So should do switch(key_type)
* and then get what ever attributes are needed.
*/
rv = key->ops->get_attribute(operation->session, key, &attr_key_type);
if (rv == CKR_OK) {
switch(key_type) {
case CKK_RSA:
rv = key->ops->get_attribute(operation->session, key, &attr);
/* convert bits to bytes */
if (rv == CKR_OK)
*pLength = (*pLength + 7) / 8;
break;
case CKK_EC:
/* TODO: -DEE we should use something other then CKA_MODULUS_BITS... */
rv = key->ops->get_attribute(operation->session, key, &attr);
*pLength = ((*pLength + 7)/8) * 2 ; /* 2*nLen in bytes */
break;
case CKK_GOSTR3410:
rv = key->ops->get_attribute(operation->session, key, &attr);
if (rv == CKR_OK)
*pLength = (*pLength + 7) / 8 * 2;
break;
default:
rv = CKR_MECHANISM_INVALID;
}
}
LOG_FUNC_RETURN(context, rv);
}
static void
sc_pkcs11_signature_release(sc_pkcs11_operation_t *operation)
{
struct signature_data *data;
data = (struct signature_data *) operation->priv_data;
if (!data)
return;
sc_pkcs11_release_operation(&data->md);
memset(data, 0, sizeof(*data));
free(data);
}
#ifdef ENABLE_OPENSSL
/*
* Initialize a verify context. When we get here, we know
* the key object is capable of verifying _something_
*/
CK_RV
sc_pkcs11_verif_init(struct sc_pkcs11_session *session, CK_MECHANISM_PTR pMechanism,
struct sc_pkcs11_object *key, CK_MECHANISM_TYPE key_type)
{
struct sc_pkcs11_card *p11card;
sc_pkcs11_operation_t *operation;
sc_pkcs11_mechanism_type_t *mt;
int rv;
if (!session || !session->slot
|| !(p11card = session->slot->p11card))
return CKR_ARGUMENTS_BAD;
/* See if we support this mechanism type */
mt = sc_pkcs11_find_mechanism(p11card, pMechanism->mechanism, CKF_VERIFY);
if (mt == NULL)
return CKR_MECHANISM_INVALID;
/* See if compatible with key type */
if (mt->key_type != key_type)
return CKR_KEY_TYPE_INCONSISTENT;
rv = session_start_operation(session, SC_PKCS11_OPERATION_VERIFY, mt, &operation);
if (rv != CKR_OK)
return rv;
memcpy(&operation->mechanism, pMechanism, sizeof(CK_MECHANISM));
rv = mt->verif_init(operation, key);
if (rv != CKR_OK)
session_stop_operation(session, SC_PKCS11_OPERATION_VERIFY);
return rv;
}
CK_RV
sc_pkcs11_verif_update(struct sc_pkcs11_session *session,
CK_BYTE_PTR pData, CK_ULONG ulDataLen)
{
sc_pkcs11_operation_t *op;
int rv;
rv = session_get_operation(session, SC_PKCS11_OPERATION_VERIFY, &op);
if (rv != CKR_OK)
return rv;
if (op->type->verif_update == NULL) {
rv = CKR_KEY_TYPE_INCONSISTENT;
goto done;
}
rv = op->type->verif_update(op, pData, ulDataLen);
done:
if (rv != CKR_OK)
session_stop_operation(session, SC_PKCS11_OPERATION_VERIFY);
return rv;
}
CK_RV
sc_pkcs11_verif_final(struct sc_pkcs11_session *session,
CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen)
{
sc_pkcs11_operation_t *op;
int rv;
rv = session_get_operation(session, SC_PKCS11_OPERATION_VERIFY, &op);
if (rv != CKR_OK)
return rv;
if (op->type->verif_final == NULL) {
rv = CKR_KEY_TYPE_INCONSISTENT;
goto done;
}
rv = op->type->verif_final(op, pSignature, ulSignatureLen);
done:
session_stop_operation(session, SC_PKCS11_OPERATION_VERIFY);
return rv;
}
/*
* Initialize a verify operation
*/
static CK_RV
sc_pkcs11_verify_init(sc_pkcs11_operation_t *operation,
struct sc_pkcs11_object *key)
{
struct hash_signature_info *info;
struct signature_data *data;
int rv;
if (!(data = calloc(1, sizeof(*data))))
return CKR_HOST_MEMORY;
data->info = NULL;
data->key = key;
if (key->ops->can_do) {
rv = key->ops->can_do(operation->session, key, operation->type->mech, CKF_SIGN);
if ((rv == CKR_OK) || (rv == CKR_FUNCTION_NOT_SUPPORTED)) {
/* Mechanism recognized and can be performed by pkcs#15 card or algorithm references not supported */
}
else {
/* Mechanism cannot be performed by pkcs#15 card, or some general error. */
free(data);
LOG_FUNC_RETURN(context, rv);
}
}
/* If this is a verify with hash operation, set up the
* hash operation */
info = (struct hash_signature_info *) operation->type->mech_data;
if (info != NULL) {
/* Initialize hash operation */
data->md = sc_pkcs11_new_operation(operation->session,
info->hash_type);
if (data->md == NULL)
rv = CKR_HOST_MEMORY;
else
rv = info->hash_type->md_init(data->md);
if (rv != CKR_OK) {
sc_pkcs11_release_operation(&data->md);
free(data);
return rv;
}
data->info = info;
}
operation->priv_data = data;
return CKR_OK;
}
static CK_RV
sc_pkcs11_verify_update(sc_pkcs11_operation_t *operation,
CK_BYTE_PTR pPart, CK_ULONG ulPartLen)
{
struct signature_data *data;
data = (struct signature_data *) operation->priv_data;
if (data->md) {
sc_pkcs11_operation_t *md = data->md;
return md->type->md_update(md, pPart, ulPartLen);
}
/* This verification mechanism operates on the raw data */
if (data->buffer_len + ulPartLen > sizeof(data->buffer))
return CKR_DATA_LEN_RANGE;
memcpy(data->buffer + data->buffer_len, pPart, ulPartLen);
data->buffer_len += ulPartLen;
return CKR_OK;
}
static CK_RV
sc_pkcs11_verify_final(sc_pkcs11_operation_t *operation,
CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen)
{
struct signature_data *data;
struct sc_pkcs11_object *key;
unsigned char *pubkey_value = NULL;
CK_KEY_TYPE key_type;
CK_BYTE params[9 /* GOST_PARAMS_ENCODED_OID_SIZE */] = { 0 };
CK_ATTRIBUTE attr = {CKA_VALUE, NULL, 0};
CK_ATTRIBUTE attr_key_type = {CKA_KEY_TYPE, &key_type, sizeof(key_type)};
CK_ATTRIBUTE attr_key_params = {CKA_GOSTR3410_PARAMS, &params, sizeof(params)};
int rv;
data = (struct signature_data *) operation->priv_data;
if (pSignature == NULL)
return CKR_ARGUMENTS_BAD;
key = data->key;
rv = key->ops->get_attribute(operation->session, key, &attr_key_type);
if (rv != CKR_OK)
return rv;
if (key_type != CKK_GOSTR3410)
attr.type = CKA_SPKI;
rv = key->ops->get_attribute(operation->session, key, &attr);
if (rv != CKR_OK)
return rv;
pubkey_value = calloc(1, attr.ulValueLen);
if (!pubkey_value) {
rv = CKR_HOST_MEMORY;
goto done;
}
attr.pValue = pubkey_value;
rv = key->ops->get_attribute(operation->session, key, &attr);
if (rv != CKR_OK)
goto done;
if (key_type == CKK_GOSTR3410) {
rv = key->ops->get_attribute(operation->session, key, &attr_key_params);
if (rv != CKR_OK)
goto done;
}
rv = sc_pkcs11_verify_data(pubkey_value, attr.ulValueLen,
params, sizeof(params),
operation->mechanism.mechanism, data->md,
data->buffer, data->buffer_len, pSignature, ulSignatureLen);
done:
free(pubkey_value);
return rv;
}
#endif
/*
* Initialize a decryption context. When we get here, we know
* the key object is capable of decrypting _something_
*/
CK_RV
sc_pkcs11_decr_init(struct sc_pkcs11_session *session,
CK_MECHANISM_PTR pMechanism,
struct sc_pkcs11_object *key,
CK_MECHANISM_TYPE key_type)
{
struct sc_pkcs11_card *p11card;
sc_pkcs11_operation_t *operation;
sc_pkcs11_mechanism_type_t *mt;
CK_RV rv;
if (!session || !session->slot
|| !(p11card = session->slot->p11card))
return CKR_ARGUMENTS_BAD;
/* See if we support this mechanism type */
mt = sc_pkcs11_find_mechanism(p11card, pMechanism->mechanism, CKF_DECRYPT);
if (mt == NULL)
return CKR_MECHANISM_INVALID;
/* See if compatible with key type */
if (mt->key_type != key_type)
return CKR_KEY_TYPE_INCONSISTENT;
rv = session_start_operation(session, SC_PKCS11_OPERATION_DECRYPT, mt, &operation);
if (rv != CKR_OK)
return rv;
memcpy(&operation->mechanism, pMechanism, sizeof(CK_MECHANISM));
rv = mt->decrypt_init(operation, key);
if (rv != CKR_OK)
session_stop_operation(session, SC_PKCS11_OPERATION_DECRYPT);
return rv;
}
CK_RV
sc_pkcs11_decr(struct sc_pkcs11_session *session,
CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen,
CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen)
{
sc_pkcs11_operation_t *op;
int rv;
rv = session_get_operation(session, SC_PKCS11_OPERATION_DECRYPT, &op);
if (rv != CKR_OK)
return rv;
rv = op->type->decrypt(op, pEncryptedData, ulEncryptedDataLen,
pData, pulDataLen);
if (rv != CKR_BUFFER_TOO_SMALL && pData != NULL)
session_stop_operation(session, SC_PKCS11_OPERATION_DECRYPT);
return rv;
}
/* Derive one key from another, and return results in created object */
CK_RV
sc_pkcs11_deri(struct sc_pkcs11_session *session,
CK_MECHANISM_PTR pMechanism,
struct sc_pkcs11_object * basekey,
CK_KEY_TYPE key_type,
CK_SESSION_HANDLE hSession,
CK_OBJECT_HANDLE hdkey,
struct sc_pkcs11_object * dkey)
{
struct sc_pkcs11_card *p11card;
sc_pkcs11_operation_t *operation;
sc_pkcs11_mechanism_type_t *mt;
CK_BYTE_PTR keybuf = NULL;
CK_ULONG ulDataLen = 0;
CK_ATTRIBUTE template[] = {
{CKA_VALUE, keybuf, 0}
};
CK_RV rv;
if (!session || !session->slot
|| !(p11card = session->slot->p11card))
return CKR_ARGUMENTS_BAD;
/* See if we support this mechanism type */
mt = sc_pkcs11_find_mechanism(p11card, pMechanism->mechanism, CKF_DERIVE);
if (mt == NULL)
return CKR_MECHANISM_INVALID;
/* See if compatible with key type */
if (mt->key_type != key_type)
return CKR_KEY_TYPE_INCONSISTENT;
rv = session_start_operation(session, SC_PKCS11_OPERATION_DERIVE, mt, &operation);
if (rv != CKR_OK)
return rv;
memcpy(&operation->mechanism, pMechanism, sizeof(CK_MECHANISM));
/* Get the size of the data to be returned
* If the card could derive a key an leave it on the card
* then no data is returned.
* If the card returns the data, we will store it in the secret key CKA_VALUE
*/
ulDataLen = 0;
rv = operation->type->derive(operation, basekey,
pMechanism->pParameter, pMechanism->ulParameterLen,
NULL, &ulDataLen);
if (rv != CKR_OK)
goto out;
if (ulDataLen > 0)
keybuf = calloc(1,ulDataLen);
else
keybuf = calloc(1,8); /* pass in dummy buffer */
if (!keybuf) {
rv = CKR_HOST_MEMORY;
goto out;
}
/* Now do the actual derivation */
rv = operation->type->derive(operation, basekey,
pMechanism->pParameter, pMechanism->ulParameterLen,
keybuf, &ulDataLen);
if (rv != CKR_OK)
goto out;
/* add the CKA_VALUE attribute to the template if it was returned
* if not assume it is on the card...
* But for now PIV with ECDH returns the generic key data
* TODO need to support truncation, if CKA_VALUE_LEN < ulDataLem
*/
if (ulDataLen > 0) {
template[0].pValue = keybuf;
template[0].ulValueLen = ulDataLen;
dkey->ops->set_attribute(session, dkey, &template[0]);
memset(keybuf,0,ulDataLen);
}
out:
session_stop_operation(session, SC_PKCS11_OPERATION_DERIVE);
if (keybuf)
free(keybuf);
return rv;
}
/*
* Initialize a decrypt operation
*/
static CK_RV
sc_pkcs11_decrypt_init(sc_pkcs11_operation_t *operation,
struct sc_pkcs11_object *key)
{
struct signature_data *data;
CK_RV rv;
if (!(data = calloc(1, sizeof(*data))))
return CKR_HOST_MEMORY;
data->key = key;
if (key->ops->can_do) {
rv = key->ops->can_do(operation->session, key, operation->type->mech, CKF_DECRYPT);
if ((rv == CKR_OK) || (rv == CKR_FUNCTION_NOT_SUPPORTED)) {
/* Mechanism recognized and can be performed by pkcs#15 card or algorithm references not supported */
}
else {
/* Mechanism cannot be performed by pkcs#15 card, or some general error. */
free(data);
LOG_FUNC_RETURN(context, rv);
}
}
operation->priv_data = data;
return CKR_OK;
}
static CK_RV
sc_pkcs11_decrypt(sc_pkcs11_operation_t *operation,
CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen,
CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen)
{
struct signature_data *data;
struct sc_pkcs11_object *key;
data = (struct signature_data*) operation->priv_data;
key = data->key;
return key->ops->decrypt(operation->session,
key, &operation->mechanism,
pEncryptedData, ulEncryptedDataLen,
pData, pulDataLen);
}
static CK_RV
sc_pkcs11_derive(sc_pkcs11_operation_t *operation,
struct sc_pkcs11_object *basekey,
CK_BYTE_PTR pmechParam, CK_ULONG ulmechParamLen,
CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen)
{
return basekey->ops->derive(operation->session,
basekey,
&operation->mechanism,
pmechParam, ulmechParamLen,
pData, pulDataLen);
}
/*
* Create new mechanism type for a mechanism supported by
* the card
*/
sc_pkcs11_mechanism_type_t *
sc_pkcs11_new_fw_mechanism(CK_MECHANISM_TYPE mech,
CK_MECHANISM_INFO_PTR pInfo,
CK_KEY_TYPE key_type,
const void *priv_data,
void (*free_priv_data)(const void *priv_data))
{
sc_pkcs11_mechanism_type_t *mt;
mt = calloc(1, sizeof(*mt));
if (mt == NULL)
return mt;
mt->mech = mech;
mt->mech_info = *pInfo;
mt->key_type = key_type;
mt->mech_data = priv_data;
mt->free_mech_data = free_priv_data;
mt->obj_size = sizeof(sc_pkcs11_operation_t);
mt->release = sc_pkcs11_signature_release;
if (pInfo->flags & CKF_SIGN) {
mt->sign_init = sc_pkcs11_signature_init;
mt->sign_update = sc_pkcs11_signature_update;
mt->sign_final = sc_pkcs11_signature_final;
mt->sign_size = sc_pkcs11_signature_size;
#ifdef ENABLE_OPENSSL
mt->verif_init = sc_pkcs11_verify_init;
mt->verif_update = sc_pkcs11_verify_update;
mt->verif_final = sc_pkcs11_verify_final;
#endif
}
if (pInfo->flags & CKF_UNWRAP) {
/* TODO */
}
if (pInfo->flags & CKF_DERIVE) {
mt->derive = sc_pkcs11_derive;
}
if (pInfo->flags & CKF_DECRYPT) {
mt->decrypt_init = sc_pkcs11_decrypt_init;
mt->decrypt = sc_pkcs11_decrypt;
}
return mt;
}
/*
* Register generic mechanisms
*/
CK_RV
sc_pkcs11_register_generic_mechanisms(struct sc_pkcs11_card *p11card)
{
#ifdef ENABLE_OPENSSL
sc_pkcs11_register_openssl_mechanisms(p11card);
#endif
return CKR_OK;
}
void free_info(const void *info)
{
free((void *) info);
}
/*
* Register a sign+hash algorithm derived from an algorithm supported
* by the token + a software hash mechanism
*/
CK_RV
sc_pkcs11_register_sign_and_hash_mechanism(struct sc_pkcs11_card *p11card,
CK_MECHANISM_TYPE mech,
CK_MECHANISM_TYPE hash_mech,
sc_pkcs11_mechanism_type_t *sign_type)
{
sc_pkcs11_mechanism_type_t *hash_type, *new_type;
struct hash_signature_info *info;
CK_MECHANISM_INFO mech_info = sign_type->mech_info;
CK_RV rv;
if (!(hash_type = sc_pkcs11_find_mechanism(p11card, hash_mech, CKF_DIGEST)))
return CKR_MECHANISM_INVALID;
/* These hash-based mechs can only be used for sign/verify */
mech_info.flags &= (CKF_SIGN | CKF_SIGN_RECOVER | CKF_VERIFY | CKF_VERIFY_RECOVER);
info = calloc(1, sizeof(*info));
if (!info)
LOG_FUNC_RETURN(p11card->card->ctx, SC_ERROR_OUT_OF_MEMORY);
info->mech = mech;
info->sign_type = sign_type;
info->hash_type = hash_type;
info->sign_mech = sign_type->mech;
info->hash_mech = hash_mech;
new_type = sc_pkcs11_new_fw_mechanism(mech, &mech_info, sign_type->key_type, info, free_info);
if (!new_type) {
free_info(info);
return CKR_HOST_MEMORY;
}
rv = sc_pkcs11_register_mechanism(p11card, new_type);
if (CKR_OK != rv) {
new_type->free_mech_data(new_type->mech_data);
free(new_type);
}
return rv;
}