563 changes: 562 additions & 1 deletion usr/lib/soft_stdll/soft_specific.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@
#include <stdlib.h>
#include <unistd.h>

#include <openssl/opensslv.h>

#if OPENSSL_VERSION_NUMBER < 0x10101000L
#define NO_EC 1
#endif

#include "pkcs11types.h"
#include "defs.h"
#include "host_defs.h"
Expand All @@ -47,6 +53,7 @@
#include <openssl/sha.h>
#include <openssl/crypto.h>
#include <openssl/cmac.h>
#include <openssl/ec.h>

/*
* In order to make opencryptoki compatible with
Expand Down Expand Up @@ -165,7 +172,24 @@ static const MECH_LIST_ELEMENT soft_mech_list[] = {
{CKM_AES_CMAC, {16, 32, CKF_SIGN | CKF_VERIFY}},
{CKM_AES_CMAC_GENERAL, {16, 32, CKF_SIGN | CKF_VERIFY}},
#endif
{CKM_GENERIC_SECRET_KEY_GEN, {80, 2048, CKF_GENERATE}}
{CKM_GENERIC_SECRET_KEY_GEN, {80, 2048, CKF_GENERATE}},
#if !(NO_EC)
{CKM_EC_KEY_PAIR_GEN, {160, 521, CKF_GENERATE_KEY_PAIR |
CKF_EC_NAMEDCURVE | CKF_EC_F_P}},
{CKM_ECDSA, {160, 521, CKF_SIGN | CKF_VERIFY | CKF_EC_NAMEDCURVE |
CKF_EC_F_P}},
{CKM_ECDSA_SHA1, {160, 521, CKF_SIGN | CKF_VERIFY | CKF_EC_NAMEDCURVE |
CKF_EC_F_P}},
{CKM_ECDSA_SHA224, {160, 521, CKF_SIGN | CKF_VERIFY | CKF_EC_NAMEDCURVE |
CKF_EC_F_P}},
{CKM_ECDSA_SHA256, {160, 521, CKF_SIGN | CKF_VERIFY | CKF_EC_NAMEDCURVE |
CKF_EC_F_P}},
{CKM_ECDSA_SHA384, {160, 521, CKF_SIGN | CKF_VERIFY | CKF_EC_NAMEDCURVE |
CKF_EC_F_P}},
{CKM_ECDSA_SHA512, {160, 521, CKF_SIGN | CKF_VERIFY | CKF_EC_NAMEDCURVE |
CKF_EC_F_P}},
{CKM_ECDH1_DERIVE, {160, 521, CKF_DERIVE | CKF_EC_NAMEDCURVE | CKF_EC_F_P}},
#endif
};

static const CK_ULONG soft_mech_list_len =
Expand Down Expand Up @@ -3973,3 +3997,540 @@ CK_RV token_specific_aes_cmac(STDLL_TokData_t *tokdata, CK_BYTE *message,
return rv;
#endif
}

#ifndef NO_EC

static CK_RV make_ec_key_from_params(const CK_BYTE *params, CK_ULONG params_len,
EC_KEY **key)
{
const unsigned char *oid;
ASN1_OBJECT *obj = NULL;
EC_KEY *ec_key = NULL;
int nid;
CK_RV rc = CKR_OK;

oid = params;
obj = d2i_ASN1_OBJECT(NULL, &oid, params_len);
if (obj == NULL) {
TRACE_ERROR("curve not supported by OpenSSL.\n");
rc = CKR_CURVE_NOT_SUPPORTED;
goto out;
}

nid = OBJ_obj2nid(obj);
if (nid == NID_undef) {
TRACE_ERROR("curve not supported by OpenSSL.\n");
rc = CKR_CURVE_NOT_SUPPORTED;
goto out;
}

ec_key = EC_KEY_new_by_curve_name(nid);
if (ec_key == NULL) {
TRACE_ERROR("curve not supported by OpenSSL.\n");
rc = CKR_CURVE_NOT_SUPPORTED;
goto out;
}

out:
if (obj != NULL)
ASN1_OBJECT_free(obj);

if (rc != CKR_OK) {
if (ec_key != NULL)
EC_KEY_free(ec_key);

return rc;
}

*key = ec_key;

return CKR_OK;
}

static CK_RV fill_ec_key_from_pubkey(EC_KEY *ec_key, const CK_BYTE *data,
CK_ULONG data_len)
{
CK_BYTE *ecpoint = NULL;
CK_ULONG ecpoint_len, field_len, privlen, padlen;
CK_BYTE form, *temp = NULL;
CK_RV rc;

/* CKA_EC_POINT contains the EC point as OCTET STRING */
rc = ber_decode_OCTET_STRING((CK_BYTE *)data, &ecpoint, &ecpoint_len,
&field_len);
if (rc != CKR_OK || field_len != data_len) {
TRACE_DEVEL("ber_decode_OCTET_STRING failed\n");
rc = CKR_ATTRIBUTE_VALUE_INVALID;
goto out;
}

/* Check for public key without format byte */
privlen = (EC_GROUP_order_bits(EC_KEY_get0_group(ec_key)) + 7) / 8;
form = ecpoint[0] & ~0x01;
if (ecpoint_len <= 2 * privlen &&
form != POINT_CONVERSION_COMPRESSED &&
form != POINT_CONVERSION_UNCOMPRESSED &&
form != POINT_CONVERSION_HYBRID) {
temp = malloc(1 + 2 * privlen);
if (temp == NULL) {
rc = CKR_HOST_MEMORY;
goto out;
}

padlen = 2 * privlen - ecpoint_len;
temp[0] = POINT_CONVERSION_UNCOMPRESSED;
memset(temp + 1, 0, padlen);
memcpy(temp + 1 + padlen, ecpoint, ecpoint_len);

ecpoint = temp;
ecpoint_len = 1 + 2 * privlen;
}

if (!EC_KEY_oct2key(ec_key, ecpoint, ecpoint_len, NULL)) {
TRACE_ERROR("EC_KEY_oct2key failed\n");
rc = CKR_FUNCTION_FAILED;
goto out;
}

out:
if (temp != NULL)
free(temp);

return rc;
}

static CK_RV fill_ec_key_from_privkey(EC_KEY *ec_key, const CK_BYTE *data,
CK_ULONG data_len)
{
EC_POINT *point = NULL;
CK_RV rc = CKR_OK;

if (!EC_KEY_oct2priv(ec_key, data, data_len)) {
TRACE_ERROR("EC_KEY_oct2priv failed\n");
rc = CKR_FUNCTION_FAILED;
goto out;
}

point = EC_POINT_new(EC_KEY_get0_group(ec_key));
if (point == NULL) {
TRACE_ERROR("EC_POINT_new failed\n");
rc = CKR_FUNCTION_FAILED;
goto out;
}

if (!EC_POINT_mul(EC_KEY_get0_group(ec_key), point,
EC_KEY_get0_private_key(ec_key), NULL, NULL, NULL)) {
TRACE_ERROR("EC_POINT_mul failed\n");
rc = CKR_FUNCTION_FAILED;
goto out;
}

if (!EC_KEY_set_public_key(ec_key, point)) {
TRACE_ERROR("EC_KEY_set_public_key failed\n");
rc = CKR_FUNCTION_FAILED;
goto out;
}

out:
if (point != NULL)
EC_POINT_free(point);

return rc;
}

static CK_RV make_ec_key_from_template(TEMPLATE *template, EC_KEY **key)
{
CK_ATTRIBUTE *attr = NULL;
CK_OBJECT_CLASS keyclass;
EC_KEY *ec_key = NULL;
CK_RV rc;

rc = template_attribute_find(template, CKA_CLASS, &attr);
if (rc == FALSE) {
TRACE_ERROR("Could not find CKA_CLASS in the template\n");
rc = CKR_TEMPLATE_INCOMPLETE;
goto out;
}

keyclass = *(CK_OBJECT_CLASS *) attr->pValue;

if (!template_attribute_find(template, CKA_ECDSA_PARAMS, &attr)) {
TRACE_ERROR("Could not find CKA_ECDSA_PARAMS in the template\n");
rc = CKR_TEMPLATE_INCOMPLETE;
goto out;
}

rc = make_ec_key_from_params(attr->pValue, attr->ulValueLen, &ec_key);
if (rc != CKR_OK)
goto out;

switch (keyclass) {
case CKO_PUBLIC_KEY:
rc = template_attribute_find(template, CKA_EC_POINT, &attr);
if (rc == FALSE) {
TRACE_ERROR("Could not find CKA_EC_POINT in the template\n");
rc = CKR_TEMPLATE_INCOMPLETE;
goto out;
}

rc = fill_ec_key_from_pubkey(ec_key, attr->pValue, attr->ulValueLen);
if (rc != CKR_OK) {
TRACE_DEVEL("fill_ec_key_from_pubkey failed\n");
goto out;
}
break;

case CKO_PRIVATE_KEY:
rc = template_attribute_find(template, CKA_VALUE, &attr);
if (rc == FALSE) {
TRACE_ERROR("Could not find CKA_VALUE in the template\n");
rc = CKR_TEMPLATE_INCOMPLETE;
goto out;
}

rc = fill_ec_key_from_privkey(ec_key, attr->pValue, attr->ulValueLen);
if (rc != CKR_OK) {
TRACE_DEVEL("fill_ec_key_from_privkey failed\n");
goto out;
}
break;

default:
rc = CKR_KEY_FUNCTION_NOT_PERMITTED;
goto out;
}

rc = CKR_OK;

out:
if (rc != CKR_OK) {
if (ec_key != NULL)
EC_KEY_free(ec_key);

return rc;
}

*key = ec_key;

return CKR_OK;
}

CK_RV token_specific_ec_generate_keypair(STDLL_TokData_t *tokdata,
TEMPLATE *publ_tmpl,
TEMPLATE *priv_tmpl)
{

CK_ATTRIBUTE *attr = NULL, *ec_point_attr, *value_attr, *parms_attr;
EC_KEY *ec_key = NULL;
BN_CTX *ctx = NULL;
CK_BYTE *ecpoint = NULL, *enc_ecpoint = NULL, *d = NULL;
CK_ULONG ecpoint_len, enc_ecpoint_len, d_len;
CK_RV rc;

UNUSED(tokdata);

if (!template_attribute_find(publ_tmpl, CKA_ECDSA_PARAMS, &attr)) {
TRACE_ERROR("Could not find CKA_ECDSA_PARAMS in the template\n");
rc = CKR_TEMPLATE_INCOMPLETE;
goto out;
}

rc = make_ec_key_from_params(attr->pValue, attr->ulValueLen, &ec_key);
if (rc != CKR_OK)
goto out;

if (!EC_KEY_generate_key(ec_key)) {
TRACE_ERROR("Failed to generate an EC key.\n");
rc = CKR_FUNCTION_FAILED;
goto out;
}

ctx = BN_CTX_new();
if (ctx == NULL) {
rc = CKR_HOST_MEMORY;
goto out;
}

ecpoint_len = EC_KEY_key2buf(ec_key, POINT_CONVERSION_UNCOMPRESSED,
&ecpoint, ctx);
if (ecpoint_len == 0) {
TRACE_ERROR("Failed to get the EC Point compressed.\n");
rc = CKR_FUNCTION_FAILED;
goto out;
}

rc = ber_encode_OCTET_STRING(FALSE, &enc_ecpoint, &enc_ecpoint_len,
ecpoint, ecpoint_len);
if (rc != CKR_OK) {
TRACE_DEVEL("ber_encode_OCTET_STRING failed\n");
goto out;
}

rc = build_attribute(CKA_EC_POINT, enc_ecpoint, enc_ecpoint_len,
&ec_point_attr);
if (rc != CKR_OK) {
TRACE_ERROR("build_attribute for CKA_EC_POINT failed rc=0x%lx\n", rc);
goto out;
}
template_update_attribute(publ_tmpl, ec_point_attr);

d_len = EC_KEY_priv2buf(ec_key, &d);
if (d_len == 0) {
TRACE_ERROR("Failed to get the EC private key.\n");
rc = CKR_FUNCTION_FAILED;
goto out;
}

rc = build_attribute(CKA_VALUE, d, d_len, &value_attr);
if (rc != CKR_OK) {
TRACE_ERROR("build_attribute for CKA_VALUE failed, rc=0x%lx\n", rc);
goto out;
}
template_update_attribute(priv_tmpl, value_attr);

/* Add CKA_ECDSA_PARAMS to private template also */
rc = build_attribute(CKA_ECDSA_PARAMS, attr->pValue, attr->ulValueLen,
&parms_attr);
if (rc != CKR_OK) {
TRACE_ERROR("build_attribute for CKA_ECDSA_PARAMS failed, rc=0x%lx\n",
rc);
goto out;
}
template_update_attribute(priv_tmpl, parms_attr);

rc = CKR_OK;

out:
if (ctx)
BN_CTX_free(ctx);
if (ec_key != NULL)
EC_KEY_free(ec_key);
if (ecpoint != NULL)
OPENSSL_free(ecpoint);
if (enc_ecpoint != NULL)
free(enc_ecpoint);
if (d != NULL)
OPENSSL_free(d);

return rc;
}

CK_RV token_specific_ec_sign(STDLL_TokData_t *tokdata, SESSION *sess,
CK_BYTE *in_data, CK_ULONG in_data_len,
CK_BYTE *out_data, CK_ULONG *out_data_len,
OBJECT *key_obj)
{
EC_KEY *ec_key;
ECDSA_SIG *sig;
const BIGNUM *r, *s;
CK_ULONG privlen, n;
CK_RV rc = CKR_OK;

UNUSED(tokdata);
UNUSED(sess);

*out_data_len = 0;

rc = make_ec_key_from_template(key_obj->template, &ec_key);
if (rc != CKR_OK)
return rc;

sig = ECDSA_do_sign(in_data, in_data_len, ec_key);
if (sig == NULL) {
TRACE_ERROR("ECDSA_do_sign failed\n");
rc = CKR_FUNCTION_FAILED;
goto out;
}

ECDSA_SIG_get0(sig, &r, &s);

privlen = (EC_GROUP_order_bits(EC_KEY_get0_group(ec_key)) + 7) / 8;

/* Insert leading 0x00's if r or s shorter than privlen */
n = privlen - BN_num_bytes(r);
memset(out_data, 0x00, n);
BN_bn2bin(r, &out_data[n]);

n = privlen - BN_num_bytes(s);
memset(out_data + privlen, 0x00, n);
BN_bn2bin(s, &out_data[privlen + n]);

*out_data_len = 2 * privlen;

out:
if (sig != NULL)
ECDSA_SIG_free(sig);
if (ec_key != NULL)
EC_KEY_free(ec_key);

return rc;
}

CK_RV token_specific_ec_verify(STDLL_TokData_t *tokdata,
SESSION *sess,
CK_BYTE *in_data,
CK_ULONG in_data_len,
CK_BYTE *signature,
CK_ULONG signature_len, OBJECT *key_obj)
{
EC_KEY *ec_key;
CK_ULONG privlen;
ECDSA_SIG *sig = NULL;
BIGNUM *r = NULL, *s = NULL;
CK_RV rc = CKR_OK;

UNUSED(tokdata);
UNUSED(sess);

rc = make_ec_key_from_template(key_obj->template, &ec_key);
if (rc != CKR_OK)
return rc;

privlen = (EC_GROUP_order_bits(EC_KEY_get0_group(ec_key)) + 7) / 8;

if (signature_len < 2 * privlen) {
TRACE_ERROR("Signature is too short\n");
rc = CKR_SIGNATURE_INVALID;
goto out;
}

sig = ECDSA_SIG_new();
if (sig == NULL) {
rc = CKR_HOST_MEMORY;
goto out;
}

r = BN_bin2bn(signature, privlen, NULL);
s = BN_bin2bn(signature + privlen, privlen, NULL);
if (r == NULL || s == NULL) {
TRACE_ERROR("BN_bin2bn failed\n");
rc = CKR_FUNCTION_FAILED;
goto out;
}

if (!ECDSA_SIG_set0(sig, r, s)) {
TRACE_ERROR("ECDSA_SIG_set0 failed\n");
rc = CKR_FUNCTION_FAILED;
goto out;
}

rc = ECDSA_do_verify(in_data, in_data_len, sig, ec_key);
switch (rc) {
case 0:
rc = CKR_SIGNATURE_INVALID;
break;
case 1:
rc = CKR_OK;
break;
default:
rc = CKR_FUNCTION_FAILED;
break;
}

out:
if (sig != NULL)
ECDSA_SIG_free(sig);
if (ec_key != NULL)
EC_KEY_free(ec_key);

return rc;
}

CK_RV token_specific_ecdh_pkcs_derive(STDLL_TokData_t *tokdata,
CK_BYTE *priv_bytes,
CK_ULONG priv_length,
CK_BYTE *pub_bytes,
CK_ULONG pub_length,
CK_BYTE *secret_value,
CK_ULONG *secret_value_len,
CK_BYTE *oid, CK_ULONG oid_length)
{
EC_KEY *ec_pub = NULL, *ec_priv = NULL;
CK_ULONG privlen;
int secret_len;
CK_RV rc;

UNUSED(tokdata);

rc = make_ec_key_from_params(oid, oid_length, &ec_priv);
if (rc != CKR_OK) {
TRACE_DEVEL("make_ec_key_from_params failed\n");
goto out;
}

rc = fill_ec_key_from_privkey(ec_priv, priv_bytes, priv_length);
if (rc != CKR_OK) {
TRACE_DEVEL("fill_ec_key_from_privkey failed\n");
goto out;
}

rc = make_ec_key_from_params(oid, oid_length, &ec_pub);
if (rc != CKR_OK) {
TRACE_DEVEL("make_ec_key_from_params failed\n");
goto out;
}

rc = fill_ec_key_from_pubkey(ec_pub, pub_bytes, pub_length);
if (rc != CKR_OK) {
TRACE_DEVEL("fill_ec_key_from_privkey failed\n");
goto out;
}

privlen = (EC_GROUP_order_bits(EC_KEY_get0_group(ec_priv)) + 7) / 8;

secret_len = ECDH_compute_key(secret_value, privlen,
EC_KEY_get0_public_key(ec_pub), ec_priv,
NULL);
if (secret_len <= 0) {
TRACE_DEVEL("ECDH_compute_key failed\n");
rc = CKR_FUNCTION_FAILED;
*secret_value_len = 0;
goto out;
}

*secret_value_len = secret_len;

out:
if (ec_priv != NULL)
EC_KEY_free(ec_priv);
if (ec_pub != NULL)
EC_KEY_free(ec_pub);

return rc;
}

#endif

CK_RV token_specific_object_add(STDLL_TokData_t * tokdata, SESSION * sess,
OBJECT * obj)
{
CK_ATTRIBUTE *attr = NULL;
CK_KEY_TYPE keytype;
#ifndef NO_EC
EC_KEY *ec_key = NULL;
CK_RV rc;
#endif

UNUSED(tokdata);
UNUSED(sess);

if (template_attribute_find(obj->template, CKA_KEY_TYPE, &attr) == FALSE)
return CKR_OK;

keytype = *(CK_KEY_TYPE *)attr->pValue;

switch (keytype) {
#ifndef NO_EC
case CKK_EC:
/* Check if OpenSSL supports the curve */
rc = make_ec_key_from_template(obj->template, &ec_key);
if (ec_key != NULL)
EC_KEY_free(ec_key);
return rc;
#endif

default:
return CKR_OK;;
}
}

9 changes: 8 additions & 1 deletion usr/lib/soft_stdll/tok_struct.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,18 @@ token_spec_t token_specific = {
&token_specific_rsa_pss_sign,
&token_specific_rsa_pss_verify,
&token_specific_rsa_generate_keypair,
#ifndef NO_EC
// Elliptic Curve
&token_specific_ec_sign,
&token_specific_ec_verify,
&token_specific_ec_generate_keypair,
&token_specific_ecdh_pkcs_derive,
#else
NULL, // ec_sign
NULL, // ec_verify
NULL, // ec_generate_keypair
NULL, // ecdh_derive
#endif
/* Begin code contributed by Corrent corp. */
// DH
#ifndef NODH
Expand Down Expand Up @@ -158,7 +165,7 @@ token_spec_t token_specific = {
NULL, // dsa_verify
&token_specific_get_mechanism_list,
&token_specific_get_mechanism_info,
NULL // object_add
&token_specific_object_add,
};

#endif