opensc/src/pkcs15init/pkcs15-sc-hsm.c

833 lines
27 KiB
C
Raw Normal View History

/*
* pkcs15-sc-hsm.c : PKCS#15 emulation for write support
*
* Copyright (C) 2012 Andreas Schwier, CardContact, Minden, Germany
*
* 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
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include "../libopensc/opensc.h"
#include "../libopensc/cardctl.h"
#include "../libopensc/log.h"
#include "../libopensc/pkcs15.h"
#include "../libopensc/cards.h"
#include "../libopensc/card-sc-hsm.h"
#include "../libopensc/asn1.h"
#include "../libopensc/pkcs15.h"
#include "pkcs15-init.h"
#include "profile.h"
static u8 pubexp[] = { 0x01, 0x00, 0x01 };
#define C_ASN1_EC_POINTQ_SIZE 2
static struct sc_asn1_entry c_asn1_ec_pointQ[C_ASN1_EC_POINTQ_SIZE] = {
{ "ecpointQ", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_OCTET_STRING, SC_ASN1_ALLOC, NULL, NULL },
{ NULL, 0, 0, 0, NULL, NULL }
};
struct ec_curve {
const struct sc_lv_data oid;
const struct sc_lv_data prime;
const struct sc_lv_data coefficientA;
const struct sc_lv_data coefficientB;
const struct sc_lv_data basePointG;
const struct sc_lv_data order;
const struct sc_lv_data coFactor;
};
static struct ec_curve curves[] = {
{
{ "\x2A\x86\x48\xCE\x3D\x03\x01\x01", 8}, // secp192r1 aka prime192r1
{ "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 24},
{ "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFC", 24},
{ "\x64\x21\x05\x19\xE5\x9C\x80\xE7\x0F\xA7\xE9\xAB\x72\x24\x30\x49\xFE\xB8\xDE\xEC\xC1\x46\xB9\xB1", 24},
{ "\x04\x18\x8D\xA8\x0E\xB0\x30\x90\xF6\x7C\xBF\x20\xEB\x43\xA1\x88\x00\xF4\xFF\x0A\xFD\x82\xFF\x10\x12\x07\x19\x2B\x95\xFF\xC8\xDA\x78\x63\x10\x11\xED\x6B\x24\xCD\xD5\x73\xF9\x77\xA1\x1E\x79\x48\x11", 49},
{ "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x99\xDE\xF8\x36\x14\x6B\xC9\xB1\xB4\xD2\x28\x31", 24},
{ "\x01", 1}
},
{
{ "\x2A\x86\x48\xCE\x3D\x03\x01\x07", 8}, // secp256r1 aka prime256r1
{ "\xFF\xFF\xFF\xFF\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 32},
{ "\xFF\xFF\xFF\xFF\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFC", 32},
{ "\x5A\xC6\x35\xD8\xAA\x3A\x93\xE7\xB3\xEB\xBD\x55\x76\x98\x86\xBC\x65\x1D\x06\xB0\xCC\x53\xB0\xF6\x3B\xCE\x3C\x3E\x27\xD2\x60\x4B", 32},
{ "\x04\x6B\x17\xD1\xF2\xE1\x2C\x42\x47\xF8\xBC\xE6\xE5\x63\xA4\x40\xF2\x77\x03\x7D\x81\x2D\xEB\x33\xA0\xF4\xA1\x39\x45\xD8\x98\xC2\x96\x4F\xE3\x42\xE2\xFE\x1A\x7F\x9B\x8E\xE7\xEB\x4A\x7C\x0F\x9E\x16\x2B\xCE\x33\x57\x6B\x31\x5E\xCE\xCB\xB6\x40\x68\x37\xBF\x51\xF5", 65},
{ "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xBC\xE6\xFA\xAD\xA7\x17\x9E\x84\xF3\xB9\xCA\xC2\xFC\x63\x25\x51", 32},
{ "\x01", 1}
},
{
{ "\x2B\x24\x03\x03\x02\x08\x01\x01\x03", 9}, // brainpoolP192r1
{ "\xC3\x02\xF4\x1D\x93\x2A\x36\xCD\xA7\xA3\x46\x30\x93\xD1\x8D\xB7\x8F\xCE\x47\x6D\xE1\xA8\x62\x97", 24},
{ "\x6A\x91\x17\x40\x76\xB1\xE0\xE1\x9C\x39\xC0\x31\xFE\x86\x85\xC1\xCA\xE0\x40\xE5\xC6\x9A\x28\xEF", 24},
{ "\x46\x9A\x28\xEF\x7C\x28\xCC\xA3\xDC\x72\x1D\x04\x4F\x44\x96\xBC\xCA\x7E\xF4\x14\x6F\xBF\x25\xC9", 24},
{ "\x04\xC0\xA0\x64\x7E\xAA\xB6\xA4\x87\x53\xB0\x33\xC5\x6C\xB0\xF0\x90\x0A\x2F\x5C\x48\x53\x37\x5F\xD6\x14\xB6\x90\x86\x6A\xBD\x5B\xB8\x8B\x5F\x48\x28\xC1\x49\x00\x02\xE6\x77\x3F\xA2\xFA\x29\x9B\x8F", 49},
{ "\xC3\x02\xF4\x1D\x93\x2A\x36\xCD\xA7\xA3\x46\x2F\x9E\x9E\x91\x6B\x5B\xE8\xF1\x02\x9A\xC4\xAC\xC1", 24},
{ "\x01", 1}
},
{
{ "\x2B\x24\x03\x03\x02\x08\x01\x01\x05", 9}, // brainpoolP224r1
{ "\xD7\xC1\x34\xAA\x26\x43\x66\x86\x2A\x18\x30\x25\x75\xD1\xD7\x87\xB0\x9F\x07\x57\x97\xDA\x89\xF5\x7E\xC8\xC0\xFF", 28},
{ "\x68\xA5\xE6\x2C\xA9\xCE\x6C\x1C\x29\x98\x03\xA6\xC1\x53\x0B\x51\x4E\x18\x2A\xD8\xB0\x04\x2A\x59\xCA\xD2\x9F\x43", 28},
{ "\x25\x80\xF6\x3C\xCF\xE4\x41\x38\x87\x07\x13\xB1\xA9\x23\x69\xE3\x3E\x21\x35\xD2\x66\xDB\xB3\x72\x38\x6C\x40\x0B", 28},
{ "\x04\x0D\x90\x29\xAD\x2C\x7E\x5C\xF4\x34\x08\x23\xB2\xA8\x7D\xC6\x8C\x9E\x4C\xE3\x17\x4C\x1E\x6E\xFD\xEE\x12\xC0\x7D\x58\xAA\x56\xF7\x72\xC0\x72\x6F\x24\xC6\xB8\x9E\x4E\xCD\xAC\x24\x35\x4B\x9E\x99\xCA\xA3\xF6\xD3\x76\x14\x02\xCD", 57},
{ "\xD7\xC1\x34\xAA\x26\x43\x66\x86\x2A\x18\x30\x25\x75\xD0\xFB\x98\xD1\x16\xBC\x4B\x6D\xDE\xBC\xA3\xA5\xA7\x93\x9F", 28},
{ "\x01", 1}
},
{
{ "\x2B\x24\x03\x03\x02\x08\x01\x01\x07", 9}, // brainpoolP256r1
{ "\xA9\xFB\x57\xDB\xA1\xEE\xA9\xBC\x3E\x66\x0A\x90\x9D\x83\x8D\x72\x6E\x3B\xF6\x23\xD5\x26\x20\x28\x20\x13\x48\x1D\x1F\x6E\x53\x77", 32},
{ "\x7D\x5A\x09\x75\xFC\x2C\x30\x57\xEE\xF6\x75\x30\x41\x7A\xFF\xE7\xFB\x80\x55\xC1\x26\xDC\x5C\x6C\xE9\x4A\x4B\x44\xF3\x30\xB5\xD9", 32},
{ "\x26\xDC\x5C\x6C\xE9\x4A\x4B\x44\xF3\x30\xB5\xD9\xBB\xD7\x7C\xBF\x95\x84\x16\x29\x5C\xF7\xE1\xCE\x6B\xCC\xDC\x18\xFF\x8C\x07\xB6", 32},
{ "\x04\x8B\xD2\xAE\xB9\xCB\x7E\x57\xCB\x2C\x4B\x48\x2F\xFC\x81\xB7\xAF\xB9\xDE\x27\xE1\xE3\xBD\x23\xC2\x3A\x44\x53\xBD\x9A\xCE\x32\x62\x54\x7E\xF8\x35\xC3\xDA\xC4\xFD\x97\xF8\x46\x1A\x14\x61\x1D\xC9\xC2\x77\x45\x13\x2D\xED\x8E\x54\x5C\x1D\x54\xC7\x2F\x04\x69\x97", 65},
{ "\xA9\xFB\x57\xDB\xA1\xEE\xA9\xBC\x3E\x66\x0A\x90\x9D\x83\x8D\x71\x8C\x39\x7A\xA3\xB5\x61\xA6\xF7\x90\x1E\x0E\x82\x97\x48\x56\xA7", 32},
{ "\x01", 1}
},
{
{ "\x2B\x24\x03\x03\x02\x08\x01\x01\x09", 9}, // brainpoolP320r1
{ "\xD3\x5E\x47\x20\x36\xBC\x4F\xB7\xE1\x3C\x78\x5E\xD2\x01\xE0\x65\xF9\x8F\xCF\xA6\xF6\xF4\x0D\xEF\x4F\x92\xB9\xEC\x78\x93\xEC\x28\xFC\xD4\x12\xB1\xF1\xB3\x2E\x27", 40},
{ "\x3E\xE3\x0B\x56\x8F\xBA\xB0\xF8\x83\xCC\xEB\xD4\x6D\x3F\x3B\xB8\xA2\xA7\x35\x13\xF5\xEB\x79\xDA\x66\x19\x0E\xB0\x85\xFF\xA9\xF4\x92\xF3\x75\xA9\x7D\x86\x0E\xB4", 40},
{ "\x52\x08\x83\x94\x9D\xFD\xBC\x42\xD3\xAD\x19\x86\x40\x68\x8A\x6F\xE1\x3F\x41\x34\x95\x54\xB4\x9A\xCC\x31\xDC\xCD\x88\x45\x39\x81\x6F\x5E\xB4\xAC\x8F\xB1\xF1\xA6", 40},
{ "\x04\x43\xBD\x7E\x9A\xFB\x53\xD8\xB8\x52\x89\xBC\xC4\x8E\xE5\xBF\xE6\xF2\x01\x37\xD1\x0A\x08\x7E\xB6\xE7\x87\x1E\x2A\x10\xA5\x99\xC7\x10\xAF\x8D\x0D\x39\xE2\x06\x11\x14\xFD\xD0\x55\x45\xEC\x1C\xC8\xAB\x40\x93\x24\x7F\x77\x27\x5E\x07\x43\xFF\xED\x11\x71\x82\xEA\xA9\xC7\x78\x77\xAA\xAC\x6A\xC7\xD3\x52\x45\xD1\x69\x2E\x8E\xE1", 81},
{ "\xD3\x5E\x47\x20\x36\xBC\x4F\xB7\xE1\x3C\x78\x5E\xD2\x01\xE0\x65\xF9\x8F\xCF\xA5\xB6\x8F\x12\xA3\x2D\x48\x2E\xC7\xEE\x86\x58\xE9\x86\x91\x55\x5B\x44\xC5\x93\x11", 40},
{ "\x01", 1}
},
{
{ NULL, 0}
}
};
static int sc_hsm_delete_ef(sc_pkcs15_card_t *p15card, u8 prefix, u8 id)
{
sc_card_t *card = p15card->card;
sc_path_t path;
u8 fid[2];
int r;
fid[0] = prefix;
fid[1] = id;
sc_path_set(&path, SC_PATH_TYPE_FILE_ID, fid, 2, 0, -1);
r = sc_delete_file(card, &path);
LOG_TEST_RET(card->ctx, r, "Could not delete file");
LOG_FUNC_RETURN(card->ctx, r);
}
static int sc_hsm_update_ef(sc_pkcs15_card_t *p15card, u8 prefix, u8 id, int erase, u8 *buf, size_t buflen)
{
sc_card_t *card = p15card->card;
sc_file_t *file = NULL;
sc_file_t newfile;
sc_path_t path;
u8 fid[2];
int r;
fid[0] = prefix;
fid[1] = id;
sc_path_set(&path, SC_PATH_TYPE_FILE_ID, fid, 2, 0, -1);
r = sc_select_file(card, &path, NULL);
if ((r == SC_SUCCESS) && erase) {
r = sc_delete_file(card, &path);
LOG_TEST_RET(card->ctx, r, "Could not delete file");
r = SC_ERROR_FILE_NOT_FOUND;
}
if (r == SC_ERROR_FILE_NOT_FOUND) {
file = sc_file_new();
file->id = (path.value[0] << 8) | path.value[1];
file->type = SC_FILE_TYPE_WORKING_EF;
file->ef_structure = SC_FILE_EF_TRANSPARENT;
file->size = (size_t) 0;
file->status = SC_FILE_STATUS_ACTIVATED;
r = sc_create_file(card, file);
sc_file_free(file);
LOG_TEST_RET(card->ctx, r, "Could not create file");
}
r = sc_update_binary(card, 0, buf, buflen, 0);
LOG_FUNC_RETURN(card->ctx, r);
}
static int sc_hsm_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card,
sc_pkcs15_object_t *obj)
{
// Keys are automatically generated in GENERATE ASYMMETRIC KEY PAIR command
LOG_FUNC_CALLED(p15card->card->ctx);
LOG_FUNC_RETURN(p15card->card->ctx, SC_SUCCESS);
}
static int sc_hsm_determine_free_id(struct sc_pkcs15_card *p15card, u8 range)
{
struct sc_card *card = p15card->card;
u8 filelist[MAX_EXT_APDU_LENGTH];
int filelistlength, i, j;
LOG_FUNC_CALLED(p15card->card->ctx);
filelistlength = sc_list_files(card, filelist, sizeof(filelist));
LOG_TEST_RET(card->ctx, filelistlength, "Could not enumerate file and key identifier");
for (j = 0; j < 256; j++) {
for (i = 0; i < filelistlength; i += 2) {
if ((filelist[i] == range) && (filelist[i + 1] == j)) {
break;
}
}
if (i >= filelistlength) {
LOG_FUNC_RETURN(p15card->card->ctx, j);
}
}
LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY);
}
static int sc_hsm_encode_gakp_rsa(struct sc_pkcs15_card *p15card, sc_cvc_t *cvc, int keysize) {
struct sc_object_id rsa15withSHA256 = { { 0,4,0,127,0,7,2,2,2,1,2,-1 } };
LOG_FUNC_CALLED(p15card->card->ctx);
cvc->coefficientAorExponentlen = sizeof(pubexp);
cvc->coefficientAorExponent = malloc(sizeof(pubexp));
if (!cvc->coefficientAorExponent) {
LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_OUT_OF_MEMORY);
}
memcpy(cvc->coefficientAorExponent, pubexp, sizeof(pubexp));
cvc->pukoid = rsa15withSHA256;
cvc->modulusSize = keysize;
LOG_FUNC_RETURN(p15card->card->ctx, SC_SUCCESS);
}
static int sc_hsm_get_curve(struct sc_pkcs15_card *p15card, struct ec_curve **curve, u8 *oid, size_t oidlen) {
int i;
LOG_FUNC_CALLED(p15card->card->ctx);
for (i = 0; curves[i].oid.value; i++) {
if ((curves[i].oid.len == oidlen) && !memcmp(curves[i].oid.value, oid, oidlen)) {
*curve = &curves[i];
return SC_SUCCESS;
}
}
sc_log(p15card->card->ctx, "Unknown curve");
LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_INVALID_DATA);
}
static int sc_hsm_encode_gakp_ec(struct sc_pkcs15_card *p15card, sc_cvc_t *cvc, struct sc_pkcs15_prkey_info *key_info) {
struct sc_object_id ecdsaWithSHA256 = { { 0,4,0,127,0,7,2,2,2,2,3,-1 } };
struct sc_pkcs15_ec_parameters *ecparams = (struct sc_pkcs15_ec_parameters *)key_info->params.data;
struct ec_curve *curve;
u8 *curveoid;
int curveoidlen;
int r;
LOG_FUNC_CALLED(p15card->card->ctx);
curveoid = ecparams->der.value;
if ((ecparams->der.len < 3) || (*curveoid++ != 0x06)) {
sc_log(p15card->card->ctx, "EC_PARAMS does not contain curve object identifier");
LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_INVALID_DATA);
}
curveoidlen = *curveoid++;
r = sc_hsm_get_curve(p15card, &curve, curveoid, curveoidlen);
cvc->primeOrModuluslen = curve->prime.len;
cvc->primeOrModulus = malloc(cvc->primeOrModuluslen);
if (!cvc->primeOrModulus) {
LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_OUT_OF_MEMORY);
}
memcpy(cvc->primeOrModulus, curve->prime.value, cvc->primeOrModuluslen);
cvc->coefficientAorExponentlen = curve->coefficientA.len;
cvc->coefficientAorExponent = malloc(cvc->coefficientAorExponentlen);
if (!cvc->coefficientAorExponent) {
LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_OUT_OF_MEMORY);
}
memcpy(cvc->coefficientAorExponent, curve->coefficientA.value, cvc->coefficientAorExponentlen);
cvc->coefficientBlen = curve->coefficientB.len;
cvc->coefficientB = malloc(cvc->coefficientBlen);
if (!cvc->coefficientB) {
LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_OUT_OF_MEMORY);
}
memcpy(cvc->coefficientB, curve->coefficientB.value, cvc->coefficientBlen);
cvc->basePointGlen = curve->basePointG.len;
cvc->basePointG = malloc(cvc->basePointGlen);
if (!cvc->basePointG) {
LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_OUT_OF_MEMORY);
}
memcpy(cvc->basePointG, curve->basePointG.value, cvc->basePointGlen);
cvc->orderlen = curve->order.len;
cvc->order = malloc(cvc->orderlen);
if (!cvc->order) {
LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_OUT_OF_MEMORY);
}
memcpy(cvc->order, curve->order.value, cvc->orderlen);
cvc->cofactorlen = curve->coFactor.len;
cvc->cofactor = malloc(cvc->cofactorlen);
if (!cvc->cofactor) {
LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_OUT_OF_MEMORY);
}
memcpy(cvc->cofactor, curve->coFactor.value, cvc->cofactorlen);
cvc->pukoid = ecdsaWithSHA256;
LOG_FUNC_RETURN(p15card->card->ctx, SC_SUCCESS);
}
static int sc_hsm_decode_gakp_rsa(struct sc_pkcs15_card *p15card,
sc_cvc_t *cvc,
struct sc_pkcs15_prkey_info *key_info,
struct sc_pkcs15_pubkey *pubkey)
{
u8 *buf;
size_t buflen;
int r;
LOG_FUNC_CALLED(p15card->card->ctx);
if (((key_info->modulus_length + 7) / 8) != cvc->primeOrModuluslen) {
sc_log(p15card->card->ctx, "Modulus size in request does not match generated public key");
LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_OUT_OF_MEMORY);
}
pubkey->algorithm = SC_ALGORITHM_RSA;
pubkey->u.rsa.modulus.len = cvc->primeOrModuluslen;
pubkey->u.rsa.modulus.data = malloc(pubkey->u.rsa.modulus.len);
pubkey->u.rsa.exponent.len = sizeof(pubexp);
pubkey->u.rsa.exponent.data = malloc(pubkey->u.rsa.exponent.len);
if (!pubkey->u.rsa.modulus.data || !pubkey->u.rsa.exponent.data) {
LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_OUT_OF_MEMORY);
}
memcpy(pubkey->u.rsa.exponent.data, pubexp, pubkey->u.rsa.exponent.len);
memcpy(pubkey->u.rsa.modulus.data, cvc->primeOrModulus, pubkey->u.rsa.modulus.len);
LOG_FUNC_RETURN(p15card->card->ctx, SC_SUCCESS);
}
static int sc_hsm_decode_gakp_ec(struct sc_pkcs15_card *p15card,
sc_cvc_t *cvc,
struct sc_pkcs15_prkey_info *key_info,
struct sc_pkcs15_pubkey *pubkey)
{
struct sc_asn1_entry asn1_ec_pointQ[C_ASN1_EC_POINTQ_SIZE];
struct sc_pkcs15_ec_parameters *ecparams = (struct sc_pkcs15_ec_parameters *)(key_info->params.data);
struct sc_ec_params *ecp;
u8 *buf;
size_t buflen;
int r;
LOG_FUNC_CALLED(p15card->card->ctx);
pubkey->algorithm = SC_ALGORITHM_EC;
pubkey->u.ec.params.named_curve = strdup(ecparams->named_curve);
sc_pkcs15_fix_ec_parameters(p15card->card->ctx, &pubkey->u.ec.params);
ecp = calloc(1, sizeof(struct sc_ec_params));
if (!ecp) {
LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_OUT_OF_MEMORY);
}
ecp->der = malloc(ecparams->der.len);
if (!ecp->der) {
LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_OUT_OF_MEMORY);
}
ecp->der_len = ecparams->der.len;
memcpy(ecp->der, ecparams->der.value, ecp->der_len);
pubkey->alg_id = (struct sc_algorithm_id *)calloc(1, sizeof(struct sc_algorithm_id));
if (!pubkey->alg_id) {
LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_OUT_OF_MEMORY);
}
pubkey->alg_id->algorithm = SC_ALGORITHM_EC;
pubkey->alg_id->params = ecp;
sc_copy_asn1_entry(c_asn1_ec_pointQ, asn1_ec_pointQ);
sc_format_asn1_entry(asn1_ec_pointQ + 0, cvc->publicPoint, &cvc->publicPointlen, 1);
r = sc_asn1_encode(p15card->card->ctx, asn1_ec_pointQ, &pubkey->u.ec.ecpointQ.value, &pubkey->u.ec.ecpointQ.len);
LOG_TEST_RET(p15card->card->ctx, r, "ASN.1 encoding failed");
LOG_FUNC_RETURN(p15card->card->ctx, SC_SUCCESS);
}
static int sc_hsm_generate_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card,
struct sc_pkcs15_object *object,
struct sc_pkcs15_pubkey *pubkey)
{
struct sc_context *ctx = p15card->card->ctx;
struct sc_card *card = p15card->card;
struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *)object->data;
sc_cardctl_sc_hsm_keygen_info_t sc_hsm_keyinfo;
sc_cvc_t cvc;
u8 *cvcbin, *cvcpo;
unsigned int cla,tag;
size_t taglen, cvclen;
int r;
LOG_FUNC_CALLED(p15card->card->ctx);
key_info->key_reference = sc_hsm_determine_free_id(p15card, KEY_PREFIX);
LOG_TEST_RET(card->ctx, key_info->key_reference, "Could not determine key reference");
memset(&cvc, 0, sizeof(cvc));
strcpy(cvc.car, "UTCA00001");
strcpy(cvc.chr, "UTTM00001");
switch(object->type) {
case SC_PKCS15_TYPE_PRKEY_RSA:
r = sc_hsm_encode_gakp_rsa(p15card, &cvc, key_info->modulus_length);
break;
case SC_PKCS15_TYPE_PRKEY_EC:
r = sc_hsm_encode_gakp_ec(p15card, &cvc, key_info);
break;
default:
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_IMPLEMENTED);
break;
}
r = sc_pkcs15emu_sc_hsm_encode_cvc(p15card, &cvc, &cvcbin, &cvclen);
sc_pkcs15emu_sc_hsm_free_cvc(&cvc);
LOG_TEST_RET(p15card->card->ctx, r, "Could not encode GAKP cdata");
cvcpo = cvcbin;
sc_asn1_read_tag((const u8 **)&cvcpo, cvclen, &cla, &tag, &taglen);
sc_asn1_read_tag((const u8 **)&cvcpo, cvclen, &cla, &tag, &taglen);
sc_hsm_keyinfo.key_id = key_info->key_reference;
sc_hsm_keyinfo.auth_key_id = 0;
sc_hsm_keyinfo.gakprequest = cvcpo;
sc_hsm_keyinfo.gakprequest_len = taglen;
sc_hsm_keyinfo.gakpresponse = NULL;
sc_hsm_keyinfo.gakpresponse_len = 0;
r = sc_card_ctl(card, SC_CARDCTL_SC_HSM_GENERATE_KEY, &sc_hsm_keyinfo);
if (r < 0)
goto out;
cvcpo = sc_hsm_keyinfo.gakpresponse;
cvclen = sc_hsm_keyinfo.gakpresponse_len;
r = sc_pkcs15emu_sc_hsm_decode_cvc(p15card, (const u8 **)&cvcpo, &cvclen, &cvc);
if (r < 0) {
sc_log(p15card->card->ctx, "Could not decode GAKP rdata");
r = SC_ERROR_OBJECT_NOT_VALID;
goto out;
}
r = sc_hsm_update_ef(p15card, EE_CERTIFICATE_PREFIX, key_info->key_reference, 1, sc_hsm_keyinfo.gakpresponse, sc_hsm_keyinfo.gakpresponse_len);
if (r < 0) {
sc_log(p15card->card->ctx, "Could not save certificate signing request");
goto out;
}
if (pubkey != NULL) {
switch(object->type) {
case SC_PKCS15_TYPE_PRKEY_RSA:
r = sc_hsm_decode_gakp_rsa(p15card, &cvc, key_info, pubkey);
break;
case SC_PKCS15_TYPE_PRKEY_EC:
r = sc_hsm_decode_gakp_ec(p15card, &cvc, key_info, pubkey);
break;
}
}
out:
sc_pkcs15emu_sc_hsm_free_cvc(&cvc);
if (cvcbin) {
free(cvcbin);
}
if (sc_hsm_keyinfo.gakpresponse) {
free(sc_hsm_keyinfo.gakpresponse);
}
LOG_FUNC_RETURN(p15card->card->ctx, r);
}
/*
* Certificates with a related private key are stored in the fid range CE00 - CEFF. The
* second byte in the fid matches the key id.
* Certificates without a related private key (e.g. CA certificates) are stored in the fid range
* CA00 - CAFF. The second byte is a free selected id.
*/
static int sc_hsm_emu_store_cert(struct sc_pkcs15_card *p15card, struct sc_profile *profile,
struct sc_pkcs15_object *object,
struct sc_pkcs15_der *data)
{
struct sc_context *ctx = p15card->card->ctx;
struct sc_pkcs15_cert_info *cert_info = (struct sc_pkcs15_cert_info *) object->data;
struct sc_pkcs15_object *prkey;
sc_path_t path;
u8 id[2];
int r;
r = sc_pkcs15_find_object_by_id(p15card, SC_PKCS15_TYPE_PRKEY, &cert_info->id , &prkey);
if (r == SC_ERROR_OBJECT_NOT_FOUND) {
r = sc_hsm_determine_free_id(p15card, CA_CERTIFICATE_PREFIX);
LOG_TEST_RET(p15card->card->ctx, r, "Out of identifier to store certificate description");
id[0] = CA_CERTIFICATE_PREFIX;
id[1] = r;
} else {
LOG_TEST_RET(p15card->card->ctx, r, "Error locating matching private key");
id[0] = EE_CERTIFICATE_PREFIX;
id[1] = ((struct sc_pkcs15_prkey_info *)prkey->data)->key_reference;
}
sc_path_set(&path, SC_PATH_TYPE_FILE_ID, id, 2, 0, -1);
cert_info->path = path;
r = sc_hsm_update_ef(p15card, id[0], id[1], 1, data->value, data->len);
return r;
}
static int sc_hsm_emu_delete_cert(struct sc_pkcs15_card *p15card, struct sc_profile *profile,
struct sc_pkcs15_object *object)
{
struct sc_context *ctx = p15card->card->ctx;
struct sc_pkcs15_cert_info *cert_info = (struct sc_pkcs15_cert_info *) object->data;
struct sc_pkcs15_object *prkey;
int r;
r = sc_pkcs15_find_object_by_id(p15card, SC_PKCS15_TYPE_PRKEY, &cert_info->id , &prkey);
if (r == SC_ERROR_OBJECT_NOT_FOUND) {
r = sc_hsm_delete_ef(p15card, CA_CERTIFICATE_PREFIX, cert_info->path.value[1]);
} else {
LOG_TEST_RET(p15card->card->ctx, r, "Error locating matching private key");
r = sc_hsm_delete_ef(p15card, EE_CERTIFICATE_PREFIX, ((struct sc_pkcs15_prkey_info *)prkey->data)->key_reference);
}
return r;
}
static int sc_hsm_emu_store_binary(struct sc_pkcs15_card *p15card, struct sc_profile *profile,
struct sc_pkcs15_object *object,
struct sc_pkcs15_der *data)
{
struct sc_context *ctx = p15card->card->ctx;
struct sc_pkcs15_data_info *data_info = (struct sc_pkcs15_data_info *) object->data;
sc_path_t path;
u8 id[2];
int r;
r = sc_hsm_determine_free_id(p15card, DCOD_PREFIX);
LOG_TEST_RET(p15card->card->ctx, r, "Out of identifier to store data description");
if (object->flags & SC_PKCS15_CO_FLAG_PRIVATE) {
id[0] = PROT_DATA_PREFIX;
} else {
id[0] = DATA_PREFIX;
}
id[1] = r;
sc_path_set(&path, SC_PATH_TYPE_FILE_ID, id, 2, 0, -1);
data_info->path = path;
r = sc_hsm_update_ef(p15card, id[0], id[1], 1, data->value, data->len);
return r;
}
static int sc_hsm_emu_store_data(struct sc_pkcs15_card *p15card, struct sc_profile *profile,
struct sc_pkcs15_object *object,
struct sc_pkcs15_der *data, struct sc_path *path)
{
struct sc_context *ctx = p15card->card->ctx;
int r;
LOG_FUNC_CALLED(ctx);
switch (object->type & SC_PKCS15_TYPE_CLASS_MASK) {
case SC_PKCS15_TYPE_PRKEY:
case SC_PKCS15_TYPE_PUBKEY:
r = SC_SUCCESS;
break;
case SC_PKCS15_TYPE_CERT:
r = sc_hsm_emu_store_cert(p15card, profile, object, data);
break;
case SC_PKCS15_TYPE_DATA_OBJECT:
r = sc_hsm_emu_store_binary(p15card, profile, object, data);
break;
default:
r = SC_ERROR_NOT_IMPLEMENTED;
break;
}
LOG_FUNC_RETURN(ctx, r);
}
static int sc_hsm_emu_delete_object(struct sc_profile *profile, struct sc_pkcs15_card *p15card,
struct sc_pkcs15_object *object, const struct sc_path *path)
{
struct sc_context *ctx = p15card->card->ctx;
int r;
LOG_FUNC_CALLED(ctx);
switch (object->type & SC_PKCS15_TYPE_CLASS_MASK) {
case SC_PKCS15_TYPE_PRKEY:
r = sc_hsm_delete_ef(p15card, KEY_PREFIX, ((struct sc_pkcs15_prkey_info *)object->data)->key_reference);
break;
case SC_PKCS15_TYPE_CERT:
r = sc_hsm_emu_delete_cert(p15card, profile, object);
break;
case SC_PKCS15_TYPE_DATA_OBJECT:
r = sc_delete_file(p15card->card, path);
break;
case SC_PKCS15_TYPE_PUBKEY:
r = SC_SUCCESS;
break;
default:
r = SC_ERROR_NOT_IMPLEMENTED;
break;
}
LOG_FUNC_RETURN(ctx, r);
}
static int sc_hsm_emu_update_prkd(struct sc_profile *profile, struct sc_pkcs15_card *p15card,
struct sc_pkcs15_object *object)
{
struct sc_context *ctx = p15card->card->ctx;
struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *)object->data;
u8 *buf;
size_t buflen;
int r;
r = sc_pkcs15_encode_prkdf_entry(p15card->card->ctx, object, &buf, &buflen);
LOG_TEST_RET(p15card->card->ctx, r, "Error encoding PRKD entry");
r = sc_hsm_update_ef(p15card, PRKD_PREFIX, key_info->key_reference, 0, buf, buflen);
free(buf);
return r;
}
static int sc_hsm_emu_update_dcod(struct sc_profile *profile, struct sc_pkcs15_card *p15card,
struct sc_pkcs15_object *object)
{
struct sc_context *ctx = p15card->card->ctx;
struct sc_pkcs15_data_info *data_info = (struct sc_pkcs15_data_info *) object->data;
u8 *buf;
size_t buflen;
int r;
r = sc_pkcs15_encode_dodf_entry(p15card->card->ctx, object, &buf, &buflen);
LOG_TEST_RET(p15card->card->ctx, r, "Error encoding DCOD entry");
r = sc_hsm_update_ef(p15card, DCOD_PREFIX, data_info->path.value[1], 0, buf, buflen);
free(buf);
return r;
}
static int sc_hsm_emu_update_cd(struct sc_profile *profile, struct sc_pkcs15_card *p15card,
struct sc_pkcs15_object *object)
{
struct sc_context *ctx = p15card->card->ctx;
struct sc_pkcs15_cert_info *cert_info = (struct sc_pkcs15_cert_info *) object->data;
u8 *buf;
size_t buflen;
int r;
if ((cert_info->path.len < 2) ||
((cert_info->path.value[cert_info->path.len - 2]) != CA_CERTIFICATE_PREFIX)) {
// Certificates associated with stored private keys don't get a separate CD entry
return SC_SUCCESS;
}
r = sc_pkcs15_encode_cdf_entry(p15card->card->ctx, object, &buf, &buflen);
LOG_TEST_RET(p15card->card->ctx, r, "Error encoding CD entry");
r = sc_hsm_update_ef(p15card, CD_PREFIX, cert_info->path.value[1], 0, buf, buflen);
free(buf);
return r;
}
static int sc_hsm_emu_delete_cd(struct sc_profile *profile, struct sc_pkcs15_card *p15card,
struct sc_pkcs15_object *object)
{
struct sc_context *ctx = p15card->card->ctx;
struct sc_pkcs15_cert_info *cert_info = (struct sc_pkcs15_cert_info *) object->data;
u8 *buf;
size_t buflen;
int r;
if ((cert_info->path.len < 2) ||
((cert_info->path.value[cert_info->path.len - 2]) != CA_CERTIFICATE_PREFIX)) {
// Certificates associated with stored private keys don't get a separate CD entry
return SC_SUCCESS;
}
return sc_hsm_delete_ef(p15card, CD_PREFIX, ((struct sc_pkcs15_data_info *)object->data)->path.value[1]);
}
static int sc_hsm_emu_update_any_df(struct sc_profile *profile, struct sc_pkcs15_card *p15card,
unsigned op, struct sc_pkcs15_object *object)
{
struct sc_context *ctx = p15card->card->ctx;
int rv = SC_ERROR_NOT_SUPPORTED;
SC_FUNC_CALLED(ctx, 1);
switch(op) {
case SC_AC_OP_ERASE:
sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Update DF; erase object('%s',type:%X)", object->label, object->type);
switch(object->type & SC_PKCS15_TYPE_CLASS_MASK) {
case SC_PKCS15_TYPE_PRKEY:
rv = sc_hsm_delete_ef(p15card, PRKD_PREFIX, ((struct sc_pkcs15_prkey_info *)object->data)->key_reference);
break;
case SC_PKCS15_TYPE_PUBKEY:
rv = SC_SUCCESS;
break;
case SC_PKCS15_TYPE_CERT:
rv = sc_hsm_emu_delete_cd(profile, p15card, object);
break;
case SC_PKCS15_TYPE_DATA_OBJECT:
rv = sc_hsm_delete_ef(p15card, DCOD_PREFIX, ((struct sc_pkcs15_data_info *)object->data)->path.value[1]);
break;
}
break;
case SC_AC_OP_UPDATE:
case SC_AC_OP_CREATE:
sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Update DF; create object('%s',type:%X)", object->label, object->type);
switch(object->type & SC_PKCS15_TYPE_CLASS_MASK) {
case SC_PKCS15_TYPE_PUBKEY:
rv = SC_SUCCESS;
break;
case SC_PKCS15_TYPE_PRKEY:
rv = sc_hsm_emu_update_prkd(profile, p15card, object);
break;
case SC_PKCS15_TYPE_CERT:
rv = sc_hsm_emu_update_cd(profile, p15card, object);
break;
case SC_PKCS15_TYPE_DATA_OBJECT:
rv = sc_hsm_emu_update_dcod(profile, p15card, object);
break;
}
break;
}
SC_FUNC_RETURN(ctx, 1, rv);
}
static struct sc_pkcs15init_operations
sc_pkcs15init_sc_hsm_operations = {
NULL, /* erase_card */
NULL, /* init_card */
NULL, /* create_dir */
NULL, /* create_domain */
NULL, /* select_pin_reference */
NULL, /* create_pin */
NULL, /* select key reference */
sc_hsm_create_key,
NULL, /* store_key */
sc_hsm_generate_key,
NULL, /* encode private key */
NULL, /* encode public key */
NULL, /* finalize_card */
sc_hsm_emu_delete_object, /* delete object */
NULL, /* pkcs15init emulation update_dir */
sc_hsm_emu_update_any_df, /* pkcs15init emulation update_any_df */
NULL, /* pkcs15init emulation update_tokeninfo */
NULL, /* pkcs15init emulation write_info */
sc_hsm_emu_store_data,
NULL, /* sanity_check */
};
struct sc_pkcs15init_operations *
sc_pkcs15init_get_sc_hsm_ops(void)
{
return &sc_pkcs15init_sc_hsm_operations;
}