diff --git a/src/libopensc/Makefile.am b/src/libopensc/Makefile.am index 33bc3e6e..557c32ea 100644 --- a/src/libopensc/Makefile.am +++ b/src/libopensc/Makefile.am @@ -41,6 +41,7 @@ libopensc_la_SOURCES = \ card-itacns.c card-authentic.c \ card-iasecc.c iasecc-sdo.c iasecc-sm.c card-sc-hsm.c \ card-dnie.c cwa14890.c cwa-dnie.c user-interface.c \ + card-isoApplet.c \ \ pkcs15-openpgp.c pkcs15-infocamere.c pkcs15-starcert.c \ pkcs15-tcos.c pkcs15-esteid.c pkcs15-postecert.c pkcs15-gemsafeGPK.c \ diff --git a/src/libopensc/Makefile.mak b/src/libopensc/Makefile.mak index 6a004e3b..4e572f7b 100644 --- a/src/libopensc/Makefile.mak +++ b/src/libopensc/Makefile.mak @@ -23,7 +23,7 @@ OBJECTS = \ card-rtecp.obj card-westcos.obj card-myeid.obj card-ias.obj \ card-itacns.obj card-authentic.obj \ card-iasecc.obj iasecc-sdo.obj iasecc-sm.obj cwa-dnie.obj cwa14890.obj \ - card-sc-hsm.obj card-dnie.obj user-interface.obj \ + card-sc-hsm.obj card-dnie.obj user-interface.obj card-isoApplet.obj \ \ pkcs15-openpgp.obj pkcs15-infocamere.obj pkcs15-starcert.obj \ pkcs15-tcos.obj pkcs15-esteid.obj pkcs15-postecert.obj pkcs15-gemsafeGPK.obj \ diff --git a/src/libopensc/card-isoApplet.c b/src/libopensc/card-isoApplet.c new file mode 100644 index 00000000..131ac08f --- /dev/null +++ b/src/libopensc/card-isoApplet.c @@ -0,0 +1,1264 @@ +/* + * Support for the IsoApplet JavaCard Applet. + * + * Copyright (C) 2014 Philip Wendland + * + * 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 +#include + +#include "asn1.h" +#include "cardctl.h" +#include "internal.h" +#include "log.h" +#include "opensc.h" +#include "pkcs15.h" +#include "types.h" + +#define ISOAPPLET_ALG_REF_ECDSA 0x21 +#define ISOAPPLET_ALG_REF_RSA_PAD_PKCS1 0x11 + +#define ISOAPPLET_API_VERSION_MAJOR 0x00 +#define ISOAPPLET_API_VERSION_MINOR 0x05 +#define ISOAPPLET_API_FEATURE_EXT_APDU 0x01 + +#define ISOAPPLET_AID_LEN 12 +static const u8 isoApplet_aid[] = {0xf2,0x76,0xa2,0x88,0xbc,0xfb,0xa6,0x9d,0x34,0xf3,0x10,0x01}; + +struct isoApplet_drv_data +{ + /* Save the current algorithm reference + * (ISOAPPLET_ALG_REF_ECDSA, ISOAPPLET_ALG_REF_RSA_PAD_PKCS1) + * to be able to distiguish between RSA and ECC operations. + * If ECC is being used, the signatures generated by the card + * have to be modified. */ + unsigned int sec_env_alg_ref; +}; +#define DRVDATA(card) ((struct isoApplet_drv_data *) ((card)->drv_data)) + +/* Operations supported by the applet. */ +static struct sc_card_operations isoApplet_ops; + +/* A reference to the iso7816_* functions. + * Initialized in sc_get_driver. */ +static const struct sc_card_operations *iso_ops = NULL; + +/* The description of the driver. */ +static struct sc_card_driver isoApplet_drv = +{ + "Javacard with IsoApplet", + "isoApplet", + &isoApplet_ops, + NULL, 0, NULL +}; + + +/* + * SELECT an applet on the smartcard. (Not in the emulated filesystem.) + * The response will be written to resp. + * + * @param[in] card + * @param[in] aid The applet ID. + * @param[in] aid_len The legth of aid. + * @param[out] resp The response of the applet upon selection. + * @param[in,out] resp_len In: The buffer size of resp. Out: The length of the response. + * + * @return SC_SUCCESS: The applet is present and could be selected. + * any other: Transmit failure or the card returned an error. + * The card will return an error when the applet is + * not present. + */ +static int +isoApplet_select_applet(sc_card_t *card, const u8 *aid, const size_t aid_len, u8 *resp, size_t *resp_len) +{ + int rv; + sc_context_t *ctx = card->ctx; + sc_apdu_t apdu; + + LOG_FUNC_CALLED(card->ctx); + + if(aid_len > SC_MAX_APDU_BUFFER_SIZE) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xa4, 0x04, 0x00); + apdu.lc = aid_len; + apdu.data = aid; + apdu.datalen = aid_len; + apdu.resp = resp; + apdu.resplen = *resp_len; + apdu.le = 0; + + rv = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(ctx, rv, "APDU transmit faiure."); + + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(card->ctx, rv, "Card returned error"); + + *resp_len = apdu.resplen; + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int +isoApplet_finish(sc_card_t *card) +{ + struct isoApplet_drv_data *drvdata=DRVDATA(card); + + LOG_FUNC_CALLED(card->ctx); + if (drvdata) + { + free(drvdata); + card->drv_data=NULL; + } + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int +isoApplet_match_card(sc_card_t *card) +{ + size_t rlen = SC_MAX_APDU_BUFFER_SIZE; + u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; + int rv; + + rv = isoApplet_select_applet(card, isoApplet_aid, ISOAPPLET_AID_LEN, rbuf, &rlen); + + if(rv != SC_SUCCESS) + { + return 0; + } + + /* The IsoApplet should return an API version (major and minor) and a feature bitmap. + * If applet does not return API version, versions 0x00 will match */ + if(rlen == 0) + { + rbuf[0] = 0x00; + rbuf[1] = 0x00; + rbuf[2] = 0x00; + rlen = 3; + } + + /* We expect 3 bytes: MAJOR API version - MINOR API version - API feature bitmap */ + if(rlen != 3) + { + return 0; + } + + if(rbuf[0] != ISOAPPLET_API_VERSION_MAJOR) + { + sc_log(card->ctx, "IsoApplet: Mismatching major API version. Not proceeding. " + "API versions: Driver (%02X-%02X), applet (%02X-%02X). Please update accordingly.", + ISOAPPLET_API_VERSION_MAJOR, ISOAPPLET_API_VERSION_MINOR, rbuf[0], rbuf[1]); + return 0; + } + + if(rbuf[1] != ISOAPPLET_API_VERSION_MINOR) + { + sc_log(card->ctx, "IsoApplet: Mismatching minor API version. Proceeding anyway. " + "API versions: Driver (%02X-%02X), applet (%02X-%02X). " + "Please update accordingly whenever possible.", + ISOAPPLET_API_VERSION_MAJOR, ISOAPPLET_API_VERSION_MINOR, rbuf[0], rbuf[1]); + } + + if(rbuf[2] & ISOAPPLET_API_FEATURE_EXT_APDU) + { + card->caps |= SC_CARD_CAP_APDU_EXT; + } + + return 1; +} + +static int +isoApplet_init(sc_card_t *card) +{ + int r; + unsigned long flags = 0; + unsigned long ext_flags = 0; + struct isoApplet_drv_data *drvdata; + struct sc_object_id curve_oid; + + LOG_FUNC_CALLED(card->ctx); + + drvdata=malloc(sizeof(*drvdata)); + if (!drvdata) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + memset(drvdata, 0, sizeof(*drvdata)); + drvdata->sec_env_alg_ref = 0; + + card->drv_data = drvdata; + card->cla = 0x00; + + /* ECDSA + * Curves supported by the pkcs15-init driver are indicated per curve. This + * should be kept in sync with the explicit parameters in the pkcs15-init + * driver. */ + flags = 0; + flags |= SC_ALGORITHM_ECDSA_RAW; + flags |= SC_ALGORITHM_ONBOARD_KEY_GEN; + ext_flags = SC_ALGORITHM_EXT_EC_NAMEDCURVE; + ext_flags |= SC_ALGORITHM_EXT_EC_F_P; + /* secp192r1, prime192r1, ansiX9p192r1*/ + r = sc_format_oid(&curve_oid, "1.2.840.10045.3.1.1"); + LOG_TEST_RET(card->ctx, r, "Error obtaining EC curve OID"); + _sc_card_add_ec_alg(card, 192, flags, ext_flags, &curve_oid); + /* prime256v1, secp256r1, ansiX9p256r1 */ + r = sc_format_oid(&curve_oid, "1.2.840.10045.3.1.7"); + LOG_TEST_RET(card->ctx, r, "Error obtaining EC curve OID"); + _sc_card_add_ec_alg(card, 256, flags, ext_flags, &curve_oid); + /* secp384r1, prime384v1, ansiX9p384r1 */ + r = sc_format_oid(&curve_oid, "1.3.132.0.34"); + LOG_TEST_RET(card->ctx, r, "Error obtaining EC curve OID"); + _sc_card_add_ec_alg(card, 384, flags, ext_flags, &curve_oid); + /* brainpoolP192r1 */ + r = sc_format_oid(&curve_oid, "1.3.36.3.3.2.8.1.1.3"); + LOG_TEST_RET(card->ctx, r, "Error obtaining EC curve OID"); + _sc_card_add_ec_alg(card, 192, flags, ext_flags, &curve_oid); + /* brainpoolP224r1 */ + r = sc_format_oid(&curve_oid, "1.3.36.3.3.2.8.1.1.5"); + LOG_TEST_RET(card->ctx, r, "Error obtaining EC curve OID"); + _sc_card_add_ec_alg(card, 224, flags, ext_flags, &curve_oid); + /* brainpoolP256r1 */ + r = sc_format_oid(&curve_oid, "1.3.36.3.3.2.8.1.1.7"); + LOG_TEST_RET(card->ctx, r, "Error obtaining EC curve OID"); + _sc_card_add_ec_alg(card, 256, flags, ext_flags, &curve_oid); + /* brainpoolP320r1 */ + r = sc_format_oid(&curve_oid, "1.3.36.3.3.2.8.1.1.9"); + LOG_TEST_RET(card->ctx, r, "Error obtaining EC curve OID"); + _sc_card_add_ec_alg(card, 320, flags, ext_flags, &curve_oid); + + /* RSA */ + flags = 0; + /* Padding schemes: */ + flags |= SC_ALGORITHM_RSA_PAD_PKCS1; + /* Hashes are to be done by the host for RSA */ + flags |= SC_ALGORITHM_RSA_HASH_NONE; + /* Key-generation: */ + flags |= SC_ALGORITHM_ONBOARD_KEY_GEN; + /* Modulus lengths: */ + _sc_card_add_rsa_alg(card, 2048, flags, 0); + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +/* + * @brief convert an OpenSC ACL entry to the security condition + * byte used by the IsoApplet. + * + * Used by IsoApplet_create_file to parse OpenSC ACL entries + * into ISO 7816-4 Table 20 security condition bytes. + * + * @param entry The OpenSC ACL entry. + * + * @return The security condition byte. No restriction (0x00) + * if unknown operation. + */ +static u8 +isoApplet_acl_to_security_condition_byte(const sc_acl_entry_t *entry) +{ + if(!entry) + return 0x00; + switch(entry->method) + { + case SC_AC_CHV: + return 0x90; + case SC_AC_NEVER: + return 0xFF; + case SC_AC_NONE: + default: + return 0x00; + } +} + +/* + * The reason for this function is that OpenSC doesn't set any + * Security Attribute Tag in the FCI upon file creation if there + * is no file->sec_attr. I set the file->sec_attr to a format + * understood by the applet (ISO 7816-4 tables 16, 17 and 20). + * The iso7816_create_file will then set this as Tag 86 - Sec. + * Attr. Prop. Format. + * The applet will then be able to set and enforce access rights + * for any file created by OpenSC. Without this function, the + * applet would not know where to enforce security rules and + * when. + * + * Note: IsoApplet currently only supports a "onepin" option. + * + * Format of the sec_attr: 8 Bytes: + * 7 - ISO 7816-4 table 16 or 17 + * 6 to 0 - ISO 7816-4 table 20 + */ +static int +isoApplet_create_file(sc_card_t *card, sc_file_t *file) +{ + int r = 0; + + LOG_FUNC_CALLED(card->ctx); + + if(file->sec_attr_len == 0) + { + u8 access_buf[8]; + int idx[8], i; + + if(file->type == SC_FILE_TYPE_DF) + { + const int df_idx[8] = /* These are the SC operations. */ + { + 0, /* Reserved. */ + SC_AC_OP_DELETE_SELF, /* b6 */ + SC_AC_OP_LOCK, /* b5 */ + SC_AC_OP_ACTIVATE, /* b4 */ + SC_AC_OP_DEACTIVATE, /* b3 */ + SC_AC_OP_CREATE_DF, /* b2 */ + SC_AC_OP_CREATE_EF, /* b1 */ + SC_AC_OP_DELETE /* b0 */ + }; + for(i=0; i<8; i++) + { + idx[i] = df_idx[i]; + } + } + else /* EF */ + { + const int ef_idx[8] = + { + 0, /* Reserved. */ + SC_AC_OP_DELETE_SELF, /* b6 */ + SC_AC_OP_LOCK, /* b5 */ + SC_AC_OP_ACTIVATE, /* b4 */ + SC_AC_OP_DEACTIVATE, /* b3 */ + SC_AC_OP_WRITE, /* b2 */ + SC_AC_OP_UPDATE, /* b1 */ + SC_AC_OP_READ /* b0 */ + }; + for(i=0; i<8; i++) + { + idx[i] = ef_idx[i]; + } + } + /* Now idx contains the operation identifiers. + * We now search for the OPs. */ + access_buf[0] = 0xFF; /* A security condition byte is present for every OP. (Table 19) */ + for(i=1; i<8; i++) + { + const sc_acl_entry_t *entry; + entry = sc_file_get_acl_entry(file, idx[i]); + access_buf[i] = isoApplet_acl_to_security_condition_byte(entry); + } + + r = sc_file_set_sec_attr(file, access_buf, 8); + LOG_TEST_RET(card->ctx, r, "Error adding security attribute."); + } + + r = iso_ops->create_file(card, file); + LOG_FUNC_RETURN(card->ctx, r); +} + +/* + * Add an ACL entry to the OpenSC file struct, according to the operation + * and the saByte (Encoded according to IsoApplet FCI proprietary security + * information, see also ISO 7816-4 table 20). + * + * @param[in,out] file + * @param[in] operation The OpenSC operation. + * @param[in] saByte The security condition byte returned by the applet. + */ +static int +isoApplet_add_sa_to_acl(sc_file_t *file, unsigned int operation, u8 saByte) +{ + int r; + + switch(saByte) + { + case 0x90: + r = sc_file_add_acl_entry(file, operation, SC_AC_CHV, 1); + if(r < 0) + return r; + break; + case 0xFF: + r = sc_file_add_acl_entry(file, operation, SC_AC_NEVER, SC_AC_KEY_REF_NONE); + if(r < 0) + return r; + break; + case 0x00: + r = sc_file_add_acl_entry(file, operation, SC_AC_NONE, SC_AC_KEY_REF_NONE); + if(r < 0) + return r; + break; + default: + r = sc_file_add_acl_entry(file, operation, SC_AC_UNKNOWN, SC_AC_KEY_REF_NONE); + if(r < 0) + return r; + } + return SC_SUCCESS; +} + + +/* + * This function first calls the iso7816.c process_fci() for any other FCI + * information and then updates the ACL of the OpenSC file struct according + * to the FCI from the applet. + */ +static int +isoApplet_process_fci(sc_card_t *card, sc_file_t *file, + const u8 *buf, size_t buflen) +{ + int r; + u8 *sa = NULL; + + LOG_FUNC_CALLED(card->ctx); + + r = iso_ops->process_fci(card, file, buf, buflen); + LOG_TEST_RET(card->ctx, r, "Error while processing the FCI."); + /* Construct the ACL from the sec_attr. */ + if(file->sec_attr && file->sec_attr_len == 8) + { + sa = file->sec_attr; + if(sa[0] != 0xFF) + { + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, + "File security attribute does not contain a ACL byte for every operation."); + } + if(file->type == SC_FILE_TYPE_DF) + { + r = isoApplet_add_sa_to_acl(file, SC_AC_OP_DELETE_SELF, sa[1]); + LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); + r = isoApplet_add_sa_to_acl(file, SC_AC_OP_LOCK, sa[2]); + LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); + r = isoApplet_add_sa_to_acl(file, SC_AC_OP_ACTIVATE, sa[3]); + LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); + r = isoApplet_add_sa_to_acl(file, SC_AC_OP_DEACTIVATE, sa[4]); + LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); + r = isoApplet_add_sa_to_acl(file, SC_AC_OP_CREATE_DF, sa[5]); + LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); + r = isoApplet_add_sa_to_acl(file, SC_AC_OP_CREATE_EF, sa[6]); + LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); + r = isoApplet_add_sa_to_acl(file, SC_AC_OP_DELETE, sa[7]); + LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); + } + else if(file->type == SC_FILE_TYPE_INTERNAL_EF + || file->type == SC_FILE_TYPE_WORKING_EF) + { + r = isoApplet_add_sa_to_acl(file, SC_AC_OP_DELETE_SELF, sa[1]); + LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); + r = isoApplet_add_sa_to_acl(file, SC_AC_OP_LOCK, sa[2]); + LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); + r = isoApplet_add_sa_to_acl(file, SC_AC_OP_ACTIVATE, sa[3]); + LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); + r = isoApplet_add_sa_to_acl(file, SC_AC_OP_DEACTIVATE, sa[4]); + LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); + r = isoApplet_add_sa_to_acl(file, SC_AC_OP_WRITE, sa[5]); + LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); + r = isoApplet_add_sa_to_acl(file, SC_AC_OP_UPDATE, sa[6]); + LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); + r = isoApplet_add_sa_to_acl(file, SC_AC_OP_READ, sa[7]); + LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); + } + + } + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +/* + * @brief Encode the EC parameters as a concatenation of TLV enrties. + * + * The format is: + * 81 - prime + * 82 - coefficient A + * 83 - coefficient B + * 84 - base point G + * 85 - order + * 87 - cofactor + * + * @param[in] card + * @param[in] params The ECparameters containing the information of the curve. + * @param[out] out The array the encoded parameters are written to. + * @param[in] out_len The size of out + * @param[out] ptr A pointer pointing to the end of the parameters in out + * (the first untouched byte behind the parameters). + */ +static int +isoApplet_put_ec_params(sc_card_t *card, sc_cardctl_isoApplet_ec_parameters_t *params, u8 *out, size_t out_len, u8 **ptr) +{ + u8 *p = out; + int r; + + LOG_FUNC_CALLED(card->ctx); + + if(!params + || !params->prime.value + || !params->coefficientA.value + || !params->coefficientB.value + || !params->basePointG.value + || !params->order.value + || !params->coFactor.value) + { + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "Error: EC params not present."); + } + + if(out == NULL || out_len == 0) + { + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "Error: Parameter out is NULL or outlen is zero."); + } + + r = sc_asn1_put_tag(0x81, params->prime.value, params->prime.len, p, out_len - (p - out), &p); + LOG_TEST_RET(card->ctx, r, "Error in handling TLV."); + r = sc_asn1_put_tag(0x82, params->coefficientA.value, params->coefficientA.len, p, out_len - (p - out), &p); + LOG_TEST_RET(card->ctx, r, "Error in handling TLV."); + r = sc_asn1_put_tag(0x83, params->coefficientB.value, params->coefficientB.len, p, out_len - (p - out), &p); + LOG_TEST_RET(card->ctx, r, "Error in handling TLV."); + r = sc_asn1_put_tag(0x84, params->basePointG.value, params->basePointG.len, p, out_len - (p - out), &p); + LOG_TEST_RET(card->ctx, r, "Error in handling TLV."); + r = sc_asn1_put_tag(0x85, params->order.value, params->order.len, p, out_len - (p - out), &p); + LOG_TEST_RET(card->ctx, r, "Error in handling TLV."); + r = sc_asn1_put_tag(0x87, params->coFactor.value, params->coFactor.len, p, out_len - (p - out), &p); + LOG_TEST_RET(card->ctx, r, "Error in handling TLV."); + + if (ptr != NULL) + *ptr = p; + LOG_FUNC_RETURN(card->ctx, r); +} + +/* + * @brief Generate a private key on the card. + */ +static int +isoApplet_ctl_generate_key(sc_card_t *card, sc_cardctl_isoApplet_genkey_t *args) +{ + int r; + sc_apdu_t apdu; + u8 rbuf[SC_MAX_EXT_APDU_BUFFER_SIZE]; + u8 sbuf[SC_MAX_EXT_APDU_BUFFER_SIZE]; + u8 *p; + const u8 *inner_tag_value; + const u8 *outer_tag_value; + unsigned int tag; + size_t outer_tag_len; + size_t inner_tag_len; + unsigned int cla; + + LOG_FUNC_CALLED(card->ctx); + + /* MANAGE SECURITY ENVIRONMENT (SET). Set the algorithm and key references. */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0x00); + + p = sbuf; + *p++ = 0x80; /* algorithm reference */ + *p++ = 0x01; + *p++ = args->algorithm_ref; + + *p++ = 0x84; /* Private key reference */ + *p++ = 0x01; + *p++ = args->priv_key_ref; + + r = p - sbuf; + p = NULL; + + apdu.lc = r; + apdu.datalen = r; + apdu.data = sbuf; + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(card->ctx, r, "Card returned error"); + + + /* GENERATE ASYMMETRIC KEY PAIR + * We use a larger buffer here, even if the card does not support extended apdus. + * There are two cases: + * 1) The card can do ext. apdus: The data fits in one apdu. + * 2) The card can't do ext. apdus: sc_transmit_apdu will handle that - the + * card will send SW_BYTES_REMAINING, OpenSC will automaticall do a + * GET RESPONSE to get the remaining data, and will append it to the data + * buffer. */ + if(args->algorithm_ref == SC_ISOAPPLET_ALG_REF_EC_GEN) + { + sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x46, 0x00, 0x00); + apdu.data = sbuf; + p = sbuf; + r = isoApplet_put_ec_params(card, &args->pubkey.ec.params, p, sizeof(sbuf), &p); + LOG_TEST_RET(card->ctx, r, "Error composing EC params."); + apdu.datalen = p - sbuf; + apdu.lc = p - sbuf; + /* Use APDU chaining if the card does not support extended apdus + * and the data does not fit in one short apdu. */ + if ((apdu.datalen > 255) && !(card->caps & SC_CARD_CAP_APDU_EXT)) + { + apdu.flags |= SC_APDU_FLAGS_CHAINING; + } + } + else + { + sc_format_apdu(card, &apdu, SC_APDU_CASE_2, 0x46, 0x42, 0x00); + } + + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + apdu.le = 256; + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + if(apdu.sw1 == 0x6A && apdu.sw2 == 0x81) + { + sc_log(card->ctx, "Key generation not supported by the card with that particular key type. " + "Your card may not support the specified algorithm used by the applet / specified by you. " + "In most cases, this happens when trying to generate EC keys not supported by your java card. " + "In this case, look for supported field lengths and whether FP and/or F2M are supported."); + } + LOG_TEST_RET(card->ctx, r, "Card returned error"); + + /* Parse the public key / response. */ + outer_tag_value = apdu.resp; + r = sc_asn1_read_tag(&outer_tag_value, apdu.resplen, &cla, &tag, &outer_tag_len); + LOG_TEST_RET(card->ctx, r, "Error in ASN1 handling."); + /* Interindustry template for nesting one set of public key data objects */ + if((tag != 0x1F49) || (cla != 0x60)) + { + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, + "The data returned by the card is unexpected."); + } + + switch(args->algorithm_ref) + { + + case SC_ISOAPPLET_ALG_REF_RSA_GEN_2048: + /* Search for the modulus tag (81). */ + inner_tag_value = sc_asn1_find_tag(card->ctx, outer_tag_value, outer_tag_len, (unsigned int) 0x81, &inner_tag_len); + if(inner_tag_value == NULL || inner_tag_len != 256) + { + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid modulus."); + } + if(inner_tag_len > args->pubkey.rsa.modulus.len) + { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL); + } + memcpy(args->pubkey.rsa.modulus.value, inner_tag_value, inner_tag_len); + args->pubkey.rsa.modulus.len = inner_tag_len; + + /* Exponent tag (82) */ + inner_tag_value = sc_asn1_find_tag(card->ctx, outer_tag_value, outer_tag_len, (unsigned int) 0x82, &inner_tag_len); + if(inner_tag_value == NULL || inner_tag_len != 3) + { + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid exponent."); + } + if(inner_tag_len > args->pubkey.rsa.exponent.len) + { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL); + } + if(memcmp(inner_tag_value, "\x01\x00\x01", 3) != 0) + { + LOG_TEST_RET(card->ctx, SC_ERROR_INCOMPATIBLE_KEY, + "Key generation error: Unexpected public key exponent."); + } + memcpy(args->pubkey.rsa.exponent.value, inner_tag_value, inner_tag_len); + args->pubkey.rsa.exponent.len = inner_tag_len; + p = NULL; + break; + + case SC_ISOAPPLET_ALG_REF_EC_GEN: + /* Compare the parameters received from the card to the ones sent to the card. */ + inner_tag_value = sc_asn1_find_tag(card->ctx, outer_tag_value, outer_tag_len, (unsigned int) 0x81, &inner_tag_len); + if(inner_tag_value == NULL || inner_tag_len != args->pubkey.ec.params.prime.len + || memcmp(inner_tag_value, args->pubkey.ec.params.prime.value, inner_tag_len) != 0) + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid prime."); + + inner_tag_value = sc_asn1_find_tag(card->ctx, outer_tag_value, outer_tag_len, (unsigned int) 0x82, &inner_tag_len); + if(inner_tag_value == NULL || inner_tag_len != args->pubkey.ec.params.coefficientA.len + || memcmp(inner_tag_value, args->pubkey.ec.params.coefficientA.value, inner_tag_len) != 0) + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid coefficient A."); + + inner_tag_value = sc_asn1_find_tag(card->ctx, outer_tag_value, outer_tag_len, (unsigned int) 0x83, &inner_tag_len); + if(inner_tag_value == NULL || inner_tag_len != args->pubkey.ec.params.coefficientB.len + || memcmp(inner_tag_value, args->pubkey.ec.params.coefficientB.value, inner_tag_len) != 0) + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid coefficient B."); + + inner_tag_value = sc_asn1_find_tag(card->ctx, outer_tag_value, outer_tag_len, (unsigned int) 0x84, &inner_tag_len); + if(inner_tag_value == NULL || inner_tag_len != args->pubkey.ec.params.basePointG.len + || memcmp(inner_tag_value, args->pubkey.ec.params.basePointG.value, inner_tag_len) != 0) + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid base point G."); + + inner_tag_value = sc_asn1_find_tag(card->ctx, outer_tag_value, outer_tag_len, (unsigned int) 0x85, &inner_tag_len); + if(inner_tag_value == NULL || inner_tag_len != args->pubkey.ec.params.order.len + || memcmp(inner_tag_value, args->pubkey.ec.params.order.value, inner_tag_len) != 0) + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid order."); + + inner_tag_value = sc_asn1_find_tag(card->ctx, outer_tag_value, outer_tag_len, (unsigned int) 0x87, &inner_tag_len); + if(inner_tag_value == NULL || inner_tag_len != args->pubkey.ec.params.coFactor.len + || memcmp(inner_tag_value, args->pubkey.ec.params.coFactor.value, inner_tag_len) != 0) + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid cofactor."); + + /* Extract public key */ + inner_tag_value = sc_asn1_find_tag(card->ctx, outer_tag_value, outer_tag_len, (unsigned int) 0x86, &inner_tag_len); + if(inner_tag_value == NULL || inner_tag_len != args->pubkey.ec.ecPointQ.len) + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid EC point Q."); + memcpy(args->pubkey.ec.ecPointQ.value, inner_tag_value, inner_tag_len); + + break; + default: + LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "Unable to parse public key: Unsupported algorithm."); + }/* switch */ + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +/* + * @brief Use PUT DATA to import a private RSA key. + * + * For simplicity, command chaining has to be used. One chunk (apdu) must contain + * one RSA field (P, Q, etc.). The first apdu must contain the outer tag (7F48). + * + * @param card + * @param rsa The RSA private key to import. + * + * @return SC_ERROR_INVALID_ARGUMENTS: The RSA key does not contain CRT fields. + * other errors: Transmit errors / errors returned by card. + */ +static int +isoApplet_put_data_prkey_rsa(sc_card_t *card, sc_cardctl_isoApplet_import_key_t *args) +{ + sc_apdu_t apdu; + u8 sbuf[SC_MAX_EXT_APDU_BUFFER_SIZE]; + u8 *p = NULL; + int r; + size_t tags_len; + + LOG_FUNC_CALLED(card->ctx); + + if(!args->privkey.rsa.p.value + || !args->privkey.rsa.q.value + || !args->privkey.rsa.iqmp.value + || !args->privkey.rsa.dmp1.value + || !args->privkey.rsa.dmq1.value) + { + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "RSA key is missing information."); + } + + /* Note: The format is according to ISO 2-byte tag 7F48 + * "T-L pair to indicate a private key data object" */ + + /* Calculate the length of all inner tag-length-value entries, but do not write anything yet. */ + tags_len = 0; + r = sc_asn1_put_tag(0x92, NULL, args->privkey.rsa.p.len, NULL, 0, NULL); + LOG_TEST_RET(card->ctx, r, "Error handling TLV."); + tags_len += r; + r = sc_asn1_put_tag(0x93, NULL, args->privkey.rsa.q.len, NULL, 0, NULL); + LOG_TEST_RET(card->ctx, r, "Error handling TLV."); + tags_len += r; + r = sc_asn1_put_tag(0x94, NULL, args->privkey.rsa.iqmp.len, NULL, 0, NULL); + LOG_TEST_RET(card->ctx, r, "Error handling TLV."); + tags_len += r; + r = sc_asn1_put_tag(0x95, NULL, args->privkey.rsa.dmp1.len, NULL, 0, NULL); + LOG_TEST_RET(card->ctx, r, "Error handling TLV."); + tags_len += r; + r = sc_asn1_put_tag(0x96, NULL, args->privkey.rsa.dmq1.len, NULL, 0, NULL); + LOG_TEST_RET(card->ctx, r, "Error handling TLV."); + tags_len += r; + + /* Write the outer tag and length information. */ + p = sbuf; + r = sc_asn1_put_tag(0x7F48, NULL, tags_len, p, sizeof(sbuf), &p); + LOG_TEST_RET(card->ctx, r, "Error handling TLV."); + + /* Write inner tags. */ + /* p */ + r = sc_asn1_put_tag(0x92, args->privkey.rsa.p.value, args->privkey.rsa.p.len, p, sizeof(sbuf) - (p - sbuf), &p); + if(r < 0) + goto out; + /* q */ + r = sc_asn1_put_tag(0x93, args->privkey.rsa.q.value, args->privkey.rsa.q.len, p, sizeof(sbuf) - (p - sbuf), &p); + if(r < 0) + goto out; + /* 1/q mod p */ + r = sc_asn1_put_tag(0x94, args->privkey.rsa.iqmp.value, args->privkey.rsa.iqmp.len, p, sizeof(sbuf) - (p - sbuf), &p); + if(r < 0) + goto out; + /* d mod (p-1) */ + r = sc_asn1_put_tag(0x95, args->privkey.rsa.dmp1.value, args->privkey.rsa.dmp1.len, p, sizeof(sbuf) - (p - sbuf), &p); + if(r < 0) + goto out; + /* d mod (q-1) */ + r = sc_asn1_put_tag(0x96, args->privkey.rsa.dmq1.value, args->privkey.rsa.dmq1.len, p, sizeof(sbuf) - (p - sbuf), &p); + if(r < 0) + goto out; + + /* Send to card, using chaining or extended APDUs. */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xDB, 0x3F, 0xFF); + apdu.data = sbuf; + apdu.datalen = p - sbuf; + apdu.lc = p - sbuf; + if ((card->caps & SC_CARD_CAP_APDU_EXT) == 0) + { + /* The lower layers will automatically do chaining */ + apdu.flags |= SC_APDU_FLAGS_CHAINING; + } + r = sc_transmit_apdu(card, &apdu); + if(r < 0) + goto out; + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + if(apdu.sw1 == 0x6A && apdu.sw2 == 0x81) + { + sc_log(card->ctx, "Key import not supported by the card with that particular key type. " + "Your card may not support the specified algorithm used by the applet / specified by you. " + "In most cases, this happens when trying to import EC keys not supported by your java card. " + "In this case, look for supported field lengths and whether FP and/or F2M are supported. " + "If you tried to import a private RSA key, check the key length."); + } + if(apdu.sw1 == 0x69 && apdu.sw2 == 0x00) + { + sc_log(card->ctx, "Key import not allowed by the applet's security policy. " + "If you want to allow key import, set DEF_PRIVATE_KEY_IMPORT_ALLOWED in the IsoApplet," + " rebuild and reinstall the applet."); + } + if(r < 0) + goto out; + + r = SC_SUCCESS; +out: + sc_mem_clear(sbuf, sizeof(sbuf)); + LOG_FUNC_RETURN(card->ctx, r); +} + +/* + * @brief Use PUT DATA to import a private EC key. + * + * Format of transmitted data: + * 0xE0 - Private class, constructed encoding, number one. + * 0x81 - prime + * 0x82 - coefficient A + * 0x83 - coefficient B + * 0x84 - base point G + * 0x85 - order + * 0x87 - cofactor + * 0x88 - private D (private key) + * + * @param card + * @param ec The EC private key to import. + * + * @return SC_ERROR_INVALID_ARGUMENTS: Curve parameters or private component is missing. + * other errors: Transmit errors / errors returned by card. + * ASN1 errors. + */ +static int +isoApplet_put_data_prkey_ec(sc_card_t *card, sc_cardctl_isoApplet_import_key_t *args) +{ + sc_apdu_t apdu; + u8 sbuf[SC_MAX_EXT_APDU_BUFFER_SIZE]; + int r; + u8 *p; + size_t tags_len; + + LOG_FUNC_CALLED(card->ctx); + + if(!args->privkey.ec.privateD.value + || !args->privkey.ec.params.prime.value + || !args->privkey.ec.params.coefficientA.value + || !args->privkey.ec.params.coefficientB.value + || !args->privkey.ec.params.basePointG.value + || !args->privkey.ec.params.order.value + || !args->privkey.ec.params.coFactor.value + ) + { + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "Missing information about EC private key."); + } + + /* Calculate the length of all inner tag-length-value entries, but do not write anything yet. */ + tags_len = 0; + r = sc_asn1_put_tag(0x81, NULL, args->privkey.ec.params.prime.len, NULL, 0, NULL); + LOG_TEST_RET(card->ctx, r, "Error handling TLV."); + tags_len += r; + r = sc_asn1_put_tag(0x82, NULL, args->privkey.ec.params.coefficientA.len, NULL, 0, NULL); + LOG_TEST_RET(card->ctx, r, "Error handling TLV."); + tags_len += r; + r = sc_asn1_put_tag(0x83, NULL, args->privkey.ec.params.coefficientB.len, NULL, 0, NULL); + LOG_TEST_RET(card->ctx, r, "Error handling TLV."); + tags_len += r; + r = sc_asn1_put_tag(0x84, NULL, args->privkey.ec.params.basePointG.len, NULL, 0, NULL); + LOG_TEST_RET(card->ctx, r, "Error handling TLV."); + tags_len += r; + r = sc_asn1_put_tag(0x85, NULL, args->privkey.ec.params.order.len, NULL, 0, NULL); + LOG_TEST_RET(card->ctx, r, "Error handling TLV."); + tags_len += r; + r = sc_asn1_put_tag(0x87, NULL, args->privkey.ec.params.coFactor.len, NULL, 0, NULL); + LOG_TEST_RET(card->ctx, r, "Error handling TLV."); + tags_len += r; + r = sc_asn1_put_tag(0x88, NULL, args->privkey.ec.privateD.len, NULL, 0, NULL); + LOG_TEST_RET(card->ctx, r, "Error handling TLV."); + tags_len += r; + + /* Write the outer tag and length information. */ + p = sbuf; + r = sc_asn1_put_tag(0xE0, NULL, tags_len, p, sizeof(sbuf), &p); + LOG_TEST_RET(card->ctx, r, "Error handling TLV."); + + /* Write inner tags. */ + r = isoApplet_put_ec_params(card, &args->privkey.ec.params, p, sizeof(sbuf) - (p - sbuf), &p); + if(r < 0) + { + sc_log(card->ctx, "Error composing EC params."); + goto out; + } + r = sc_asn1_put_tag(0x88, args->privkey.ec.privateD.value, args->privkey.ec.privateD.len, p, sizeof(sbuf) - (p - sbuf), &p); + if(r < 0) + goto out; + + /* Send to card. */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xDB, 0x3F, 0xFF); + apdu.lc = p - sbuf; + apdu.datalen = p - sbuf; + apdu.data = sbuf; + r = sc_transmit_apdu(card, &apdu); + if(r < 0) + { + sc_log(card->ctx, "APDU transmit failed"); + goto out; + } + + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + if(apdu.sw1 == 0x6D && apdu.sw2 == 0x00) + { + sc_log(card->ctx, "The applet returned that the PUT DATA instruction byte is not supported. " + "If you are using an older applet version and are trying to import keys, please update your applet first."); + } + else if(apdu.sw1 == 0x6A && apdu.sw2 == 0x81) + { + sc_log(card->ctx, "Key import not supported by the card with that particular key type. " + "Your card may not support the specified algorithm used by the applet / specified by you. " + "In most cases, this happens when trying to import EC keys not supported by your java card. " + "In this case, look for supported field lengths and whether FP and/or F2M are supported. " + "If you tried to import a private RSA key, check the key length."); + } + else if(apdu.sw1 == 0x69 && apdu.sw2 == 0x00) + { + sc_log(card->ctx, "Key import not allowed by the applet's security policy. " + "If you want to allow key import, set DEF_PRIVATE_KEY_IMPORT_ALLOWED in the IsoApplet," + " rebuild and reinstall the applet."); + } + if(r < 0) + { + sc_log(card->ctx, "Card returned error"); + goto out; + } + + r = SC_SUCCESS; +out: + sc_mem_clear(sbuf, sizeof(sbuf)); + LOG_FUNC_RETURN(card->ctx, r); +} + +/* + * @brief Import a private key. + */ +static int +isoApplet_ctl_import_key(sc_card_t *card, sc_cardctl_isoApplet_import_key_t *args) +{ + int r; + sc_apdu_t apdu; + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; + u8 *p; + + LOG_FUNC_CALLED(card->ctx); + + /* + * Private keys are not stored in the filesystem. + * ISO 7816-8 - section C.2 describes: + * "Usage of the PUT DATA command for private key import" + * The applet uses this PUT DATA to import private keys, if private key import is allowed. + * + * The first step is to perform a MANAGE SECURITY ENVIRONMENT as it would be done + * with on-card key generation. The second step is PUT DATA (instead of + * GENERATE ASYMMETRIC KEYPAIR). + */ + + /* MANAGE SECURITY ENVIRONMENT (SET). Set the algorithm and key references. */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0x00); + + p = sbuf; + *p++ = 0x80; /* algorithm reference */ + *p++ = 0x01; + *p++ = args->algorithm_ref; + + *p++ = 0x84; /* Private key reference */ + *p++ = 0x01; + *p++ = args->priv_key_ref; + + r = p - sbuf; + p = NULL; + + apdu.lc = r; + apdu.datalen = r; + apdu.data = sbuf; + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "%s: APDU transmit failed"); + + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(card->ctx, r, "Card returned error"); + + + /* PUT DATA */ + switch(args->algorithm_ref) + { + + case SC_ISOAPPLET_ALG_REF_RSA_GEN_2048: + r = isoApplet_put_data_prkey_rsa(card, args); + LOG_TEST_RET(card->ctx, r, "Error in PUT DATA."); + break; + + case SC_ISOAPPLET_ALG_REF_EC_GEN: + r = isoApplet_put_data_prkey_ec(card, args); + LOG_TEST_RET(card->ctx, r, "Error in PUT DATA."); + break; + + default: + LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "Uknown algorithm refernce."); + } + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int +isoApplet_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) +{ + int r = 0; + + LOG_FUNC_CALLED(card->ctx); + switch (cmd) + { + case SC_CARDCTL_ISOAPPLET_GENERATE_KEY: + r = isoApplet_ctl_generate_key(card, + (sc_cardctl_isoApplet_genkey_t *) ptr); + break; + case SC_CARDCTL_ISOAPPLET_IMPORT_KEY: + r = isoApplet_ctl_import_key(card, + (sc_cardctl_isoApplet_import_key_t *) ptr); + break; + default: + r = SC_ERROR_NOT_SUPPORTED; + } + LOG_FUNC_RETURN(card->ctx, r); +} + +static int +isoApplet_set_security_env(sc_card_t *card, + const sc_security_env_t *env, int se_num) +{ + sc_apdu_t apdu; + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; + u8 *p; + int r, locked = 0; + struct isoApplet_drv_data *drvdata = DRVDATA(card); + + LOG_FUNC_CALLED(card->ctx); + + if(se_num != 0) + { + LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, + "IsoApplet does not support storing of security environments."); + } + assert(card != NULL && env != NULL); + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0); + switch (env->operation) + { + case SC_SEC_OPERATION_DECIPHER: + apdu.p2 = 0xB8; + break; + case SC_SEC_OPERATION_SIGN: + apdu.p2 = 0xB6; + break; + default: + return SC_ERROR_INVALID_ARGUMENTS; + } + p = sbuf; + + if (env->flags & SC_SEC_ENV_ALG_PRESENT) + { + + switch(env->algorithm) + { + + case SC_ALGORITHM_RSA: + if( env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1 ) + { + drvdata->sec_env_alg_ref = ISOAPPLET_ALG_REF_RSA_PAD_PKCS1; + } + else + { + LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "IsoApplet only supports RSA with PKCS1 padding."); + } + break; + + case SC_ALGORITHM_EC: + if( env->algorithm_flags & SC_ALGORITHM_ECDSA_RAW ) + { + drvdata->sec_env_alg_ref = ISOAPPLET_ALG_REF_ECDSA; + } + else + { + LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "IsoApplet only supports raw ECDSA."); + } + break; + + default: + LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "Unsupported algorithm."); + } + + *p++ = 0x80; /* algorithm reference */ + *p++ = 0x01; + *p++ = drvdata->sec_env_alg_ref; + } + + if (env->flags & SC_SEC_ENV_FILE_REF_PRESENT) + { + *p++ = 0x81; + *p++ = env->file_ref.len; + assert(sizeof(sbuf) - (p - sbuf) >= env->file_ref.len); + memcpy(p, env->file_ref.value, env->file_ref.len); + p += env->file_ref.len; + } + + if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) + { + if (env->flags & SC_SEC_ENV_KEY_REF_ASYMMETRIC) + *p++ = 0x83; + else + *p++ = 0x84; + *p++ = env->key_ref_len; + assert(sizeof(sbuf) - (p - sbuf) >= env->key_ref_len); + memcpy(p, env->key_ref, env->key_ref_len); + p += env->key_ref_len; + } + r = p - sbuf; + apdu.lc = r; + apdu.datalen = r; + apdu.data = sbuf; + + if (se_num > 0) + { + r = sc_lock(card); + LOG_TEST_RET(card->ctx, r, "sc_lock() failed"); + locked = 1; + } + + if (apdu.datalen != 0) + { + r = sc_transmit_apdu(card, &apdu); + if (r) + { + sc_log(card->ctx, "%s: APDU transmit failed", sc_strerror(r)); + goto err; + } + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + if (r) + { + sc_log(card->ctx, "%s: Card returned error", sc_strerror(r)); + goto err; + } + } + + if (se_num <= 0) + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0xF2, se_num); + r = sc_transmit_apdu(card, &apdu); + sc_unlock(card); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_FUNC_RETURN(card->ctx, r); +err: + if (locked) + sc_unlock(card); + LOG_FUNC_RETURN(card->ctx, r); +} + +static int +isoApplet_compute_signature(struct sc_card *card, + const u8 *data, size_t datalen, + u8 *out, size_t outlen) +{ + struct isoApplet_drv_data *drvdata = DRVDATA(card); + int r; + + LOG_FUNC_CALLED(card->ctx); + + r = iso_ops->compute_signature(card, data, datalen, out, outlen); + if(r < 0) + { + LOG_FUNC_RETURN(card->ctx, r); + } + + /* If we used ECDSA for the signature op, OpenSC thinks it has to + * convert it to {sequence{integer,integer}} which is already done by the + * card actually. + * To fix this, I strip the {sequence, sequence} structual information, + * which will be added again later.*/ + if(drvdata->sec_env_alg_ref == ISOAPPLET_ALG_REF_ECDSA) + { + u8* p = NULL; + size_t len; + + r = sc_asn1_sig_value_sequence_to_rs(card->ctx, out, r, &p, &len); + if(r < 0) { + if(p) + free(p); + LOG_FUNC_RETURN(card->ctx, r); + } + memcpy(out, p, len); + r = len; + free(p); + } + LOG_FUNC_RETURN(card->ctx, r); +} + +static struct sc_card_driver *sc_get_driver(void) +{ + sc_card_driver_t *iso_drv = sc_get_iso7816_driver(); + + if(iso_ops == NULL) + { + iso_ops = iso_drv->ops; + } + + isoApplet_ops = *iso_drv->ops; + + isoApplet_ops.match_card = isoApplet_match_card; + isoApplet_ops.init = isoApplet_init; + isoApplet_ops.finish = isoApplet_finish; + + isoApplet_ops.card_ctl = isoApplet_card_ctl; + + isoApplet_ops.create_file = isoApplet_create_file; + isoApplet_ops.process_fci = isoApplet_process_fci; + isoApplet_ops.set_security_env = isoApplet_set_security_env; + isoApplet_ops.compute_signature = isoApplet_compute_signature; + + /* unsupported functions */ + isoApplet_ops.write_binary = NULL; + isoApplet_ops.read_record = NULL; + isoApplet_ops.write_record = NULL; + isoApplet_ops.append_record = NULL; + isoApplet_ops.update_record = NULL; + isoApplet_ops.get_challenge = NULL; + isoApplet_ops.restore_security_env = NULL; + + return &isoApplet_drv; +} + +struct sc_card_driver * sc_get_isoApplet_driver(void) +{ + return sc_get_driver(); +} diff --git a/src/libopensc/cardctl.h b/src/libopensc/cardctl.h index b46185ab..edfac57d 100644 --- a/src/libopensc/cardctl.h +++ b/src/libopensc/cardctl.h @@ -255,7 +255,14 @@ enum { */ SC_CARDCTL_DNIE_BASE = _CTL_PREFIX('D', 'N', 'I'), SC_CARDCTL_DNIE_GENERATE_KEY, - SC_CARDCTL_DNIE_GET_INFO + SC_CARDCTL_DNIE_GET_INFO, + + /* + * isoApplet Java Card Applet + */ + SC_CARDCTL_ISOAPPLET_BASE = _CTL_PREFIX('I','S','O'), + SC_CARDCTL_ISOAPPLET_GENERATE_KEY, + SC_CARDCTL_ISOAPPLET_IMPORT_KEY }; enum { @@ -957,6 +964,59 @@ typedef struct sc_cardctl_sc_hsm_wrapped_key { size_t wrapped_key_length; /* Length of key blob */ } sc_cardctl_sc_hsm_wrapped_key_t; +/* + * isoApplet + */ + +#define SC_ISOAPPLET_ALG_REF_RSA_GEN_2048 0xF3 +#define SC_ISOAPPLET_ALG_REF_EC_GEN 0xEC + +typedef struct sc_cardctl_isoApplet_ec_parameters { + struct sc_lv_data prime; + struct sc_lv_data coefficientA; + struct sc_lv_data coefficientB; + struct sc_lv_data basePointG; + struct sc_lv_data order; + struct sc_lv_data coFactor; +} sc_cardctl_isoApplet_ec_parameters_t; + +typedef struct sc_cardctl_isoApplet_genkey { + u8 algorithm_ref; /* Algorithm reference sent to card */ + unsigned int priv_key_ref; /* Private key refernce sent to card */ + union { + struct + { + struct sc_lv_data modulus; + struct sc_lv_data exponent; + } rsa; + struct + { + sc_cardctl_isoApplet_ec_parameters_t params; + struct sc_lv_data ecPointQ; + } ec; + } pubkey; +} sc_cardctl_isoApplet_genkey_t; + +typedef struct sc_cardctl_isoApplet_import_key { + u8 algorithm_ref; /* Algorithm reference sent to card */ + unsigned int priv_key_ref; /* Private key refernce sent to card */ + union { + struct + { + struct sc_lv_data p; + struct sc_lv_data q; + struct sc_lv_data iqmp; + struct sc_lv_data dmp1; + struct sc_lv_data dmq1; + } rsa; + struct + { + sc_cardctl_isoApplet_ec_parameters_t params; + struct sc_lv_data privateD; + } ec; + } privkey; +} sc_cardctl_isoApplet_import_key_t; + #ifdef __cplusplus } #endif diff --git a/src/libopensc/cards.h b/src/libopensc/cards.h index 7be66671..229940b1 100644 --- a/src/libopensc/cards.h +++ b/src/libopensc/cards.h @@ -199,7 +199,11 @@ enum { SC_CARD_TYPE_DNIE_BLANK, /* ATR LC byte: 00 */ SC_CARD_TYPE_DNIE_ADMIN, /* ATR LC byte: 01 */ SC_CARD_TYPE_DNIE_USER, /* ATR LC byte: 03 */ - SC_CARD_TYPE_DNIE_TERMINATED /* ATR LC byte: 0F */ + SC_CARD_TYPE_DNIE_TERMINATED, /* ATR LC byte: 0F */ + + /* JavaCards with isoApplet */ + SC_CARD_TYPE_ISO_APPLET_BASE = 28000, + SC_CARD_TYPE_ISO_APPLET_GENERIC }; extern sc_card_driver_t *sc_get_default_driver(void); @@ -236,6 +240,7 @@ extern sc_card_driver_t *sc_get_authentic_driver(void); extern sc_card_driver_t *sc_get_iasecc_driver(void); extern sc_card_driver_t *sc_get_epass2003_driver(void); extern sc_card_driver_t *sc_get_dnie_driver(void); +extern sc_card_driver_t *sc_get_isoApplet_driver(void); #ifdef __cplusplus } diff --git a/src/libopensc/ctx.c b/src/libopensc/ctx.c index fa94dc14..19d23770 100644 --- a/src/libopensc/ctx.c +++ b/src/libopensc/ctx.c @@ -108,6 +108,7 @@ static const struct _sc_driver_entry internal_card_drivers[] = { { "atrust-acos",(void *(*)(void)) sc_get_atrust_acos_driver }, { "PIV-II", (void *(*)(void)) sc_get_piv_driver }, { "itacns", (void *(*)(void)) sc_get_itacns_driver }, + { "isoApplet", (void *(*)(void)) sc_get_isoApplet_driver }, /* The default driver should be last, as it handles all the * unrecognized cards. */ { "default", (void *(*)(void)) sc_get_default_driver }, diff --git a/src/pkcs15init/Makefile.am b/src/pkcs15init/Makefile.am index 1bffba0f..bf267df8 100644 --- a/src/pkcs15init/Makefile.am +++ b/src/pkcs15init/Makefile.am @@ -29,7 +29,8 @@ dist_pkgdata_DATA = \ iasecc.profile \ ias_adele_admin1.profile ias_adele_admin2.profile ias_adele_common.profile \ iasecc_generic_pki.profile iasecc_admin_eid.profile iasecc_generic_oberthur.profile \ - openpgp.profile sc-hsm.profile + openpgp.profile sc-hsm.profile \ + isoApplet.profile AM_CPPFLAGS = -DSC_PKCS15_PROFILE_DIRECTORY=\"$(pkgdatadir)\" \ -I$(top_srcdir)/src @@ -46,4 +47,5 @@ libpkcs15init_la_SOURCES = \ pkcs15-rtecp.c pkcs15-myeid.c \ pkcs15-oberthur.c pkcs15-oberthur-awp.c \ pkcs15-authentic.c pkcs15-iasecc.c pkcs15-openpgp.c \ - pkcs15-sc-hsm.c + pkcs15-sc-hsm.c \ + pkcs15-isoApplet.c diff --git a/src/pkcs15init/Makefile.mak b/src/pkcs15init/Makefile.mak index 348534ee..fb4b28fb 100644 --- a/src/pkcs15init/Makefile.mak +++ b/src/pkcs15init/Makefile.mak @@ -9,7 +9,8 @@ OBJECTS = pkcs15-lib.obj profile.obj \ pkcs15-muscle.obj pkcs15-asepcos.obj pkcs15-rutoken.obj \ pkcs15-entersafe.obj pkcs15-rtecp.obj pkcs15-westcos.obj \ pkcs15-myeid.obj pkcs15-authentic.obj pkcs15-iasecc.obj \ - pkcs15-epass2003.obj pkcs15-openpgp.obj pkcs15-sc-hsm.obj + pkcs15-epass2003.obj pkcs15-openpgp.obj pkcs15-sc-hsm.obj \ + pkcs15-isoApplet.obj all: $(TARGET) diff --git a/src/pkcs15init/isoApplet.profile b/src/pkcs15init/isoApplet.profile new file mode 100644 index 00000000..acf252bf --- /dev/null +++ b/src/pkcs15init/isoApplet.profile @@ -0,0 +1,164 @@ +# +# PKCS15 profile for the isoApplet JavaCard Applet. +# - init driver: pkcs15-isoApplet.c +# - card driver: card-isoApplet.c +# + +cardinfo { + label = "JavaCard isoApplet"; + manufacturer = "unknown"; + min-pin-length = 4; + max-pin-length = 16; + pin-pad-char = 0x00; +} + +pkcs15 { + # Method to calculate ID of the crypto objects + # mozilla: SHA1(modulus) for RSA, SHA1(pub) for DSA + # rfc2459: SHA1(SequenceASN1 of public key components as ASN1 integers) + # native: 'E' + number_of_present_objects_of_the_same_type + # default value: 'native' + pkcs15-id-style = native; +} + +option default { + macros { + unusedspace-size = 128; + odf-size = 256; + aodf-size = 256; + cdf-size = 512; + prkdf-size = 512; + pukdf-size = 512; + dodf-size = 256; + } +} + +PIN so-pin { + attempts = 3; + max-length = 16; + min-length = 4; + reference = 1; + flags = case-sensitive, needs-padding; +} + +PIN so-puk { + attempts = 3; + max-length = 16; + min-length = 16; + reference = 2; + flags = unblockingPin, unblock-disabled, case-sensitive, change-disabled; +} + +filesystem { + DF MF { + path = 3F00; + type = DF; + + # This is the DIR file + EF DIR { + type = EF; + file-id = 2F00; + size = 128; + acl = *=NONE; + } + + # Here comes the application DF + DF PKCS15-AppDF { + type = DF; + file-id = 5015; + aid = A0:00:00:00:63:50:4B:43:53:2D:31:35; + acl = *=NONE, DELETE=$PIN; + size = 5000; + + EF PKCS15-ODF { + file-id = 5031; + size = $odf-size; + ACL = *=NONE; + } + + EF PKCS15-TokenInfo { + file-id = 5032; + ACL = *=NONE; + } + + EF PKCS15-UnusedSpace { + file-id = 5033; + size = $unusedspace-size; + ACL = *=NONE; + } + + EF PKCS15-AODF { + file-id = 4401; + size = $aodf-size; + ACL = *=$PIN, READ=NONE; + } + + EF PKCS15-PrKDF { + file-id = 4402; + size = $prkdf-size; + acl = *=$PIN, READ=NONE; + } + + EF PKCS15-PuKDF { + file-id = 4403; + size = $pukdf-size; + acl = *=$PIN, READ=NONE; + } + + EF PKCS15-CDF { + file-id = 4404; + size = $cdf-size; + acl = *=$PIN, READ=NONE; + } + + EF PKCS15-DODF { + file-id = 4405; + size = $dodf-size; + ACL = *=$PIN, READ=NONE; + } + + template key-domain { + + BSO private-key { + ACL = *=$PIN, READ=NEVER; + } + + # EF private-key { + # file-id = 3000; + # acl = *=NEVER, UPDATE=$PIN, ERASE=$PIN; + # } + + # EF extractable-key { + # file-id = 3100; + # acl = *=NEVER, READ=$PIN, UPDATE=$PIN, + # ERASE=$PIN; + # } + + EF data { + file-id = 3200; + acl = *=NEVER, UPDATE=$PIN, READ=NONE, + DELETE-SELF=$PIN, ERASE=$PIN; + } + + EF privdata { + file-id = 3500; + acl = *=NEVER, UPDATE=$PIN, READ=$PIN, + DELETE-SELF=$PIN, ERASE=$PIN; + } + + EF public-key { + file-id = 3300; + acl = *=NEVER, UPDATE=$PIN, READ=NONE, + DELETE-SELF=$PIN, ERASE=$PIN; + } + + EF certificate { + file-id = 3400; + acl = *=NEVER, UPDATE=$PIN, READ=NONE, + DELETE-SELF=$PIN, ERASE=$PIN; + } + + } + } + } +} diff --git a/src/pkcs15init/pkcs15-init.h b/src/pkcs15init/pkcs15-init.h index c40ff7f6..9fc9a97d 100644 --- a/src/pkcs15init/pkcs15-init.h +++ b/src/pkcs15init/pkcs15-init.h @@ -420,6 +420,7 @@ extern struct sc_pkcs15init_operations *sc_pkcs15init_get_iasecc_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_piv_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_openpgp_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_sc_hsm_ops(void); +extern struct sc_pkcs15init_operations *sc_pkcs15init_get_isoApplet_ops(void); #ifdef __cplusplus } diff --git a/src/pkcs15init/pkcs15-isoApplet.c b/src/pkcs15init/pkcs15-isoApplet.c new file mode 100644 index 00000000..b4095a85 --- /dev/null +++ b/src/pkcs15init/pkcs15-isoApplet.c @@ -0,0 +1,763 @@ +/* + * pkcs15-init driver for JavaCards with IsoApplet installed. + * + * Copyright (C) 2014 Philip Wendland + * + * 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 +#include +#include +#include +#include + +#include "../libopensc/log.h" +#include "../libopensc/internal.h" +#include "../libopensc/opensc.h" +#include "../libopensc/cardctl.h" +#include "../libopensc/asn1.h" +#include "pkcs15-init.h" +#include "profile.h" + +#define ISOAPPLET_KEY_ID_MIN 0 +#define ISOAPPLET_KEY_ID_MAX 15 + +/* Curve parameters of a curve specified by the OID. */ +struct ec_curve +{ + const struct sc_lv_data oid; /* Object ID in hex, including structural information */ + 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; +}; + +/* OpenSC only works with named curves, but we need the + * explicit parameters for ECC key generation or import. */ +static const struct ec_curve curves[] = +{ + { + /* brainpoolP192r1 */ + { (unsigned char *) "\x06\x09\x2B\x24\x03\x03\x02\x08\x01\x01\x03", 11}, + { (unsigned char *) "\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}, + { (unsigned char *) "\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}, + { (unsigned char *) "\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}, + { (unsigned char *) "\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}, + { (unsigned char *) "\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}, + { (unsigned char *) "\x00\x01", 2} + }, + + { + /* brainpoolP224r1 */ + { (unsigned char *) "\x06\x09\x2B\x24\x03\x03\x02\x08\x01\x01\x05", 11}, + { (unsigned char *) "\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}, + { (unsigned char *) "\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}, + { (unsigned char *) "\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}, + { (unsigned char *) "\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}, + { (unsigned char *) "\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}, + { (unsigned char *) "\x00\x01", 2} + }, + + { + /* brainpoolP256r1 */ + { (unsigned char *) "\x06\x09\x2B\x24\x03\x03\x02\x08\x01\x01\x07", 11}, + { (unsigned char *) "\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}, + { (unsigned char *) "\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}, + { (unsigned char *) "\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}, + { (unsigned char *) "\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}, + { (unsigned char *) "\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}, + { (unsigned char *) "\x00\x01", 2} + }, + + { + /* brainpoolP320r1 */ + { (unsigned char *) "\x06\x09\x2B\x24\x03\x03\x02\x08\x01\x01\x09", 11}, + { (unsigned char *) "\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}, + { (unsigned char *) "\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}, + { (unsigned char *) "\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}, + { (unsigned char *) "\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}, + { (unsigned char *) "\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}, + { (unsigned char *) "\x00\x01", 2} + }, + + { + /* prime192r1, secp192r1, ansiX9p192r1 */ + { (unsigned char *) "\x06\x08\x2A\x86\x48\xCE\x3D\x03\x01\x01", 10}, + { (unsigned char *) "\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}, + { (unsigned char *) "\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}, + { (unsigned char *) "\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}, + { (unsigned char *) "\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}, + { (unsigned char *) "\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}, + { (unsigned char *) "\x00\x01", 2} + }, + + { + /* prime256v1, secp256r1, ansiX9p256r1 */ + { (unsigned char *) "\x06\x08\x2A\x86\x48\xCE\x3D\x03\x01\x07", 10}, + { (unsigned char *) "\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}, + { (unsigned char *) "\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}, + { (unsigned char *) "\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}, + { (unsigned char *) "\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}, + { (unsigned char *) "\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}, + { (unsigned char *) "\x00\x01", 2} + }, + + { + /* prime384v1, secp384r1, ansiX9p384r1 */ + { (unsigned char *) "\x06\x05\x2B\x81\x04\x00\x22", 7}, + { (unsigned char *) "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF", 48}, + { (unsigned char *) "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFC", 48}, + { (unsigned char *) "\xB3\x31\x2F\xA7\xE2\x3E\xE7\xE4\x98\x8E\x05\x6B\xE3\xF8\x2D\x19\x18\x1D\x9C\x6E\xFE\x81\x41\x12\x03\x14\x08\x8F\x50\x13\x87\x5A\xC6\x56\x39\x8D\x8A\x2E\xD1\x9D\x2A\x85\xC8\xED\xD3\xEC\x2A\xEF", 48}, + { (unsigned char *) "\x04\xAA\x87\xCA\x22\xBE\x8B\x05\x37\x8E\xB1\xC7\x1E\xF3\x20\xAD\x74\x6E\x1D\x3B\x62\x8B\xA7\x9B\x98\x59\xF7\x41\xE0\x82\x54\x2A\x38\x55\x02\xF2\x5D\xBF\x55\x29\x6C\x3A\x54\x5E\x38\x72\x76\x0A\xB7\x36\x17\xDE\x4A\x96\x26\x2C\x6F\x5D\x9E\x98\xBF\x92\x92\xDC\x29\xF8\xF4\x1D\xBD\x28\x9A\x14\x7C\xE9\xDA\x31\x13\xB5\xF0\xB8\xC0\x0A\x60\xB1\xCE\x1D\x7E\x81\x9D\x7A\x43\x1D\x7C\x90\xEA\x0E\x5F", 97}, + { (unsigned char *) "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xC7\x63\x4D\x81\xF4\x37\x2D\xDF\x58\x1A\x0D\xB2\x48\xB0\xA7\x7A\xEC\xEC\x19\x6A\xCC\xC5\x29\x73", 48}, + { (unsigned char *) "\x00\x01", 2} + }, + + { + { NULL, 0}, + { NULL, 0}, + { NULL, 0}, + { NULL, 0}, + { NULL, 0}, + { NULL, 0}, + { NULL, 0} + } +}; + + +/* + * Create DF, using default pkcs15init functions. + */ +static int +isoApplet_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df) +{ + sc_card_t *card = p15card->card; + int r = SC_SUCCESS; + + LOG_FUNC_CALLED(card->ctx); + + if(!profile || !p15card || !df || !p15card->card || !p15card->card->ctx) + { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + } + r = sc_pkcs15init_create_file(profile, p15card, df); + LOG_FUNC_RETURN(card->ctx, r); +} + +/* + * Select a PIN reference. + * + * Basically (as I understand it) the caller passes an auth_info object and the + * auth_info->attrs.pin.reference is supposed to be set accordingly and return. + * + * The IsoApplet only supports a PIN and a PUK at the moment. + * The reference for the PIN is 1, for the PUK 2. + */ +static int +isoApplet_select_pin_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card, + sc_pkcs15_auth_info_t *auth_info) +{ + sc_card_t *card = p15card->card; + int preferred; + int current; + + LOG_FUNC_CALLED(card->ctx); + + if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) + { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OBJECT_NOT_VALID); + } + + current = auth_info->attrs.pin.reference; + if (current < 0) + { + current = 0; + } + + if(current > 2) + { + /* Only two PINs supported: User PIN and PUK. */ + LOG_FUNC_RETURN(card->ctx, SC_ERROR_TOO_MANY_OBJECTS); + } + else + { + if(auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN) + { + /* PUK */ + preferred = 2; + } + else + { + /* PIN */ + preferred = 1; + } + } + + auth_info->attrs.pin.reference = preferred; + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +/* + * Create a PIN and store it on the card using CHANGE REFERENCE DATA for PIN transmission. + * First, the PUK is transmitted, then the PIN. Now, the IsoApplet is in the + * "STATE_OPERATIONAL_ACTIVATED" lifecycle state. + */ +static int +isoApplet_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df, + sc_pkcs15_object_t *pin_obj, + const u8 *pin, size_t pin_len, + const u8 *puk, size_t puk_len) +{ + sc_card_t *card = p15card->card; + sc_pkcs15_auth_info_t *auth_info = (sc_pkcs15_auth_info_t *) pin_obj->data; + struct sc_pkcs15_pin_attributes *pin_attrs = &auth_info->attrs.pin; + int r; + + LOG_FUNC_CALLED(card->ctx); + + if(!pin || !pin_len || !p15card || !p15card->card || !df || !&df->path) + { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + } + + if(pin_attrs->reference != 1 && pin_attrs->reference != 2) + { + /* Reject PIN reference. */ + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_PIN_REFERENCE); + } + + /* If we have a PUK, set it first. */ + if(puk && puk_len) + { + /* The PUK has a incremented reference, i.e. pins are odd, puks are equal (+1). */ + r = sc_change_reference_data(p15card->card, SC_AC_CHV, + pin_attrs->reference+1, + NULL, 0, + puk, puk_len, NULL); + if(r < 0) + { + LOG_FUNC_RETURN(card->ctx, r); + } + } + + /* Store PIN: (use CHANGE REFERENCE DATA). */ + r = sc_change_reference_data(p15card->card, SC_AC_CHV, + pin_attrs->reference, + NULL, 0, + pin, pin_len, NULL); + + LOG_FUNC_RETURN(card->ctx, r); +} + +/* + * @brief Get the curve parameters associated with the curve specified by an OID. + * + * @param[in] oid The DER encoded OID of the curve. + * @param[in] oid_len The length of oid. + * @param[out] curve_out The ec_curve containing the set of parameters. + * + * @returns SC_SUCCESS: If the curve was found. + * SC_ERROR_INVALID_ARGUMENTS: If named_curve was null or the curve + * was not found + */ +static int +isoApplet_get_curve(u8 *oid, size_t oid_len, const struct ec_curve **curve_out) +{ + int i; + + if(!oid) + return SC_ERROR_INVALID_ARGUMENTS; + + /* Search the curve parameters. */ + for (i = 0; curves[i].oid.value; i++) + { + if (oid_len == curves[i].oid.len && memcmp(oid, curves[i].oid.value, curves[i].oid.len) == 0) + { + *curve_out = &curves[i]; + return SC_SUCCESS; + } + } + + return SC_ERROR_INVALID_ARGUMENTS; +} + + +/* + * @brief Generate a RSA private key on the card. + * + * A MANAGE SECURITY ENVIRONMENT apdu must have been sent before. + * This function uses card_ctl to access the card-isoApplet driver. + * + * @param[in] key_info + * @param[in] card + * @param[in] pubkey The public key of the generated key pair + * returned by the card. + * + * @return SC_ERROR_INVALID_ARGURMENTS: Invalid key length. + * SC_ERROR_OUT_OF_MEMORY + */ +static int +isoApplet_generate_key_rsa(sc_pkcs15_prkey_info_t *key_info, sc_card_t *card, + sc_pkcs15_pubkey_t *pubkey) +{ + int rv; + size_t keybits; + struct sc_cardctl_isoApplet_genkey args; + + LOG_FUNC_CALLED(card->ctx); + + /* Check key size: */ + keybits = key_info->modulus_length; + if (keybits != 2048) + { + rv = SC_ERROR_INVALID_ARGUMENTS; + sc_log(card->ctx, "%s: RSA private key length is unsupported, correct length is 2048", sc_strerror(rv)); + goto err; + } + + /* Generate the key. + * Note: key size is not explicitly passed to the card. + * It assumes 2048 along with the algorithm reference. */ + memset(&args, 0, sizeof(args)); + args.algorithm_ref = SC_ISOAPPLET_ALG_REF_RSA_GEN_2048; + args.priv_key_ref = key_info->key_reference; + + args.pubkey.rsa.modulus.len = keybits / 8; + args.pubkey.rsa.modulus.value = malloc(args.pubkey.rsa.modulus.len); + if (!args.pubkey.rsa.modulus.value) + { + rv = SC_ERROR_OUT_OF_MEMORY; + sc_log(card->ctx, "%s: Unable to allocate public key buffer.", sc_strerror(rv)); + goto err; + } + + args.pubkey.rsa.exponent.len = 3; + args.pubkey.rsa.exponent.value = malloc(args.pubkey.rsa.exponent.len); + if(!args.pubkey.rsa.exponent.value) + { + rv = SC_ERROR_OUT_OF_MEMORY; + sc_log(card->ctx, "%s: Unable to allocate public key exponent buffer.", sc_strerror(rv)); + goto err; + } + + rv = sc_card_ctl(card, SC_CARDCTL_ISOAPPLET_GENERATE_KEY, &args); + if (rv < 0) + { + sc_log(card->ctx, "%s: Error in card_ctl", sc_strerror(rv)); + goto err; + } + + /* extract the public key */ + pubkey->algorithm = SC_ALGORITHM_RSA; + pubkey->u.rsa.modulus.len = args.pubkey.rsa.modulus.len; + pubkey->u.rsa.modulus.data = args.pubkey.rsa.modulus.value; + pubkey->u.rsa.exponent.len = args.pubkey.rsa.exponent.len; + pubkey->u.rsa.exponent.data = args.pubkey.rsa.exponent.value; + rv = SC_SUCCESS; + LOG_FUNC_RETURN(card->ctx, rv); +err: + if (args.pubkey.rsa.modulus.value) + { + free(args.pubkey.rsa.modulus.value); + pubkey->u.rsa.modulus.data = NULL; + pubkey->u.rsa.modulus.len = 0; + } + if (args.pubkey.rsa.exponent.value) + { + free(args.pubkey.rsa.exponent.value); + pubkey->u.rsa.exponent.data = NULL; + pubkey->u.rsa.exponent.len = 0; + } + LOG_FUNC_RETURN(card->ctx, rv); +} + +/* + * @brief Generate a EC private key on the card. + * + * A MANAGE SECURITY ENVIRONMENT apdu must have been sent before. + * This function uses card_ctl to access the card-isoApplet driver. + * + * @param[in] key_info + * @param[in] card + * @param[in/out] pubkey The public key of the generated key pair + * returned by the card. + * + * @return SC_ERROR_INVALID_ARGURMENTS: Invalid key length or curve. + * SC_ERROR_OUT_OF_MEMORY + * SC_ERROR_INCOMPATIBLE_KEY: The data returned by the card + * was unexpected and can not be + * handled. + */ +static int +isoApplet_generate_key_ec(const sc_pkcs15_prkey_info_t *key_info, sc_card_t *card, + sc_pkcs15_pubkey_t *pubkey) +{ + int r; + const struct ec_curve *curve = NULL; + struct sc_ec_parameters *alg_id_params = NULL; + sc_cardctl_isoApplet_genkey_t args; + const struct sc_ec_parameters *info_ecp = + (struct sc_ec_parameters *) key_info->params.data; + + LOG_FUNC_CALLED(card->ctx); + + /* Check key size: */ + if(key_info->field_length == 0) + { + sc_log(card->ctx, "Unknown field length."); + r = SC_ERROR_INVALID_ARGUMENTS; + goto out; + } + + r = isoApplet_get_curve(info_ecp->der.value, info_ecp->der.len, &curve); + if(r < 0) + { + sc_log(card->ctx, "EC key generation failed: Unsupported curve: [%s].", info_ecp->named_curve); + goto out; + } + + /* Generate the key. + * Note: The field size is not explicitly passed to the card. + * As we only support FP curves, the field length can be calculated from any parameter. */ + memset(&args, 0, sizeof(args)); + + args.pubkey.ec.params.prime.value = curve->prime.value; + args.pubkey.ec.params.prime.len = curve->prime.len; + args.pubkey.ec.params.coefficientA.value = curve->coefficientA.value; + args.pubkey.ec.params.coefficientA.len = curve->coefficientA.len; + args.pubkey.ec.params.coefficientB.value = curve->coefficientB.value; + args.pubkey.ec.params.coefficientB.len = curve->coefficientB.len; + args.pubkey.ec.params.basePointG.value = curve->basePointG.value; + args.pubkey.ec.params.basePointG.len = curve->basePointG.len; + args.pubkey.ec.params.order.value = curve->order.value; + args.pubkey.ec.params.order.len = curve->order.len; + args.pubkey.ec.params.coFactor.value = curve->coFactor.value; + args.pubkey.ec.params.coFactor.len = curve->coFactor.len; + /* The length of the public key point will be: + * Uncompressed tag + 2 * field length in bytes. */ + args.pubkey.ec.ecPointQ.len = 1 + 2 * key_info->field_length / 8; + args.pubkey.ec.ecPointQ.value = malloc(args.pubkey.ec.ecPointQ.len); + if(!args.pubkey.ec.ecPointQ.value) + { + r = SC_ERROR_OUT_OF_MEMORY; + goto out; + } + + args.algorithm_ref = SC_ISOAPPLET_ALG_REF_EC_GEN; + args.priv_key_ref = key_info->key_reference; + + /* On-card key generation */ + r = sc_card_ctl(card, SC_CARDCTL_ISOAPPLET_GENERATE_KEY, &args); + if (r < 0) + { + sc_log(card->ctx, "%s: Error in card_ctl.", sc_strerror(r)); + goto out; + } + + /* Extract and compose the public key. */ + pubkey->algorithm = SC_ALGORITHM_EC; + + /* der-encoded parameters */ + alg_id_params = calloc(1, sizeof(*alg_id_params)); + if(!alg_id_params) + { + r = SC_ERROR_OUT_OF_MEMORY; + goto out; + } + alg_id_params->der.len = curve->oid.len; + alg_id_params->der.value = malloc(alg_id_params->der.len); + if(!alg_id_params->der.value) + { + r = SC_ERROR_OUT_OF_MEMORY; + goto out; + } + memcpy(alg_id_params->der.value, curve->oid.value, curve->oid.len); + alg_id_params->type = 1; /* named curve */ + + pubkey->alg_id = malloc(sizeof(*pubkey->alg_id)); + if(!pubkey->alg_id) + { + r = SC_ERROR_OUT_OF_MEMORY; + goto out; + } + pubkey->alg_id->algorithm = SC_ALGORITHM_EC; + pubkey->alg_id->params = alg_id_params; + + /* Extract ecpointQ */ + pubkey->u.ec.ecpointQ.len = args.pubkey.ec.ecPointQ.len; + pubkey->u.ec.ecpointQ.value = malloc(pubkey->u.ec.ecpointQ.len); + if(!pubkey->u.ec.ecpointQ.value) + { + r = SC_ERROR_OUT_OF_MEMORY; + goto out; + } + memcpy(pubkey->u.ec.ecpointQ.value, args.pubkey.ec.ecPointQ.value, args.pubkey.ec.ecPointQ.len); + + /* The OID is also written to the pubkey->u.ec.params */ + pubkey->u.ec.params.der.value = malloc(alg_id_params->der.len); + if(!pubkey->u.ec.params.der.value) + { + r = SC_ERROR_OUT_OF_MEMORY; + goto out; + } + memcpy(pubkey->u.ec.params.der.value, alg_id_params->der.value, alg_id_params->der.len); + pubkey->u.ec.params.der.len = alg_id_params->der.len; + r = sc_pkcs15_fix_ec_parameters(card->ctx, &pubkey->u.ec.params); +out: + if(args.pubkey.ec.ecPointQ.value) + { + free(args.pubkey.ec.ecPointQ.value); + args.pubkey.ec.ecPointQ.value = NULL; + } + if(r < 0 && pubkey) + { + if(pubkey->alg_id) + { + free(pubkey->alg_id); + pubkey->alg_id = NULL; + } + if(pubkey->u.ec.params.der.value) + { + free(pubkey->u.ec.params.der.value); + pubkey->u.ec.params.der.value = NULL; + pubkey->u.ec.params.der.len = 0; + } + if(r < 0 && pubkey->u.ec.ecpointQ.value) + { + free(pubkey->u.ec.ecpointQ.value); + pubkey->u.ec.ecpointQ.value = NULL; + pubkey->u.ec.ecpointQ.len = 0; + } + memset(pubkey, 0, sizeof(sc_pkcs15_pubkey_t)); + } + if(r < 0 && alg_id_params) + { + if(alg_id_params->der.value) + { + free(alg_id_params->der.value); + alg_id_params->der.value = NULL; + } + free(alg_id_params); + pubkey->alg_id->params = NULL; + } + LOG_FUNC_RETURN(card->ctx, r); +} + +static int +isoApplet_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, + sc_pkcs15_object_t *obj, + sc_pkcs15_pubkey_t *pubkey) +{ + int r; + sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data; + sc_file_t *privKeyFile=NULL; + sc_card_t *card = p15card->card; + + LOG_FUNC_CALLED(card->ctx); + + /* Authentication stuff. */ + r = sc_profile_get_file_by_path(profile, &key_info->path, &privKeyFile); + if(!privKeyFile) + { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED); + } + r = sc_pkcs15init_authenticate(profile, p15card, privKeyFile, SC_AC_OP_CREATE_EF); + if(r < 0) + { + sc_file_free(privKeyFile); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED); + } + sc_file_free(privKeyFile); + + /* Generate the key. */ + switch(obj->type) + { + case SC_PKCS15_TYPE_PRKEY_RSA: + r = isoApplet_generate_key_rsa(key_info, card, pubkey); + break; + + case SC_PKCS15_TYPE_PRKEY_EC: + r = isoApplet_generate_key_ec(key_info, card, pubkey); + break; + + default: + r = SC_ERROR_NOT_SUPPORTED; + sc_log(card->ctx, "%s: Key generation failed: Unknown/unsupported key type.", strerror(r)); + } + + LOG_FUNC_RETURN(card->ctx, r); +} + + +/* + * Create a new key file. This is a no-op, because private keys are stored as key objects on the javacard. + */ +static int +isoApplet_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj) +{ + sc_card_t *card = p15card->card; + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +/* + * Select a key reference. + */ +static int +isoApplet_select_key_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card, + sc_pkcs15_prkey_info_t *key_info) +{ + int rv = SC_SUCCESS; + sc_card_t *card = p15card->card; + + LOG_FUNC_CALLED(card->ctx); + + if(key_info->key_reference < ISOAPPLET_KEY_ID_MIN) + { + key_info->key_reference = ISOAPPLET_KEY_ID_MIN; + rv = SC_SUCCESS; + } + if(key_info->key_reference > ISOAPPLET_KEY_ID_MAX) + { + rv = SC_ERROR_TOO_MANY_OBJECTS; + } + LOG_FUNC_RETURN(card->ctx, rv); +} + +/* + * Store a usable private key on the card. + */ +static int +isoApplet_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *object, + sc_pkcs15_prkey_t *key) +{ + sc_card_t *card = p15card->card; + sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) object->data; + sc_file_t *privKeyFile=NULL; + sc_cardctl_isoApplet_import_key_t args; + int r; + + LOG_FUNC_CALLED(card->ctx); + + /* Authentication stuff. */ + r = sc_profile_get_file_by_path(profile, &key_info->path, &privKeyFile); + if(!privKeyFile) + { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED); + } + r = sc_pkcs15init_authenticate(profile, p15card, privKeyFile, SC_AC_OP_CREATE_EF); + if(r < 0) + { + sc_file_free(privKeyFile); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED); + } + sc_file_free(privKeyFile); + + /* Key import. */ + switch(object->type) + { + case SC_PKCS15_TYPE_PRKEY_RSA: + args.algorithm_ref = SC_ISOAPPLET_ALG_REF_RSA_GEN_2048; + if(!key->u.rsa.p.data + ||!key->u.rsa.q.data + ||!key->u.rsa.iqmp.data + ||!key->u.rsa.dmp1.data + ||!key->u.rsa.dmq1.data) + { + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "Only CRT RSA keys may be imported."); + } + args.privkey.rsa.p.value = key->u.rsa.p.data; + args.privkey.rsa.p.len = key->u.rsa.p.len; + args.privkey.rsa.q.value = key->u.rsa.q.data; + args.privkey.rsa.q.len = key->u.rsa.q.len; + args.privkey.rsa.iqmp.value = key->u.rsa.iqmp.data; + args.privkey.rsa.iqmp.len = key->u.rsa.iqmp.len; + args.privkey.rsa.dmp1.value = key->u.rsa.dmp1.data; + args.privkey.rsa.dmp1.len = key->u.rsa.dmp1.len; + args.privkey.rsa.dmq1.value = key->u.rsa.dmq1.data; + args.privkey.rsa.dmq1.len = key->u.rsa.dmq1.len; + break; + + case SC_PKCS15_TYPE_PRKEY_EC: + { + const struct ec_curve *curve = NULL; + + args.algorithm_ref = SC_ISOAPPLET_ALG_REF_EC_GEN; + if(key->u.ec.params.der.len == 0 || key->u.ec.params.der.value == NULL) { + r = sc_pkcs15_fix_ec_parameters(card->ctx, &key->u.ec.params); + LOG_TEST_RET(card->ctx, r, "EC key storing failed: Unkown curve."); + } + r = isoApplet_get_curve(key->u.ec.params.der.value, key->u.ec.params.der.len, &curve); + LOG_TEST_RET(card->ctx, r, "EC key generation failed: Unsupported curve"); + args.privkey.ec.params.prime.value = curve->prime.value; + args.privkey.ec.params.prime.len = curve->prime.len; + args.privkey.ec.params.coefficientA.value = curve->coefficientA.value; + args.privkey.ec.params.coefficientA.len = curve->coefficientA.len; + args.privkey.ec.params.coefficientB.value = curve->coefficientB.value; + args.privkey.ec.params.coefficientB.len = curve->coefficientB.len; + args.privkey.ec.params.basePointG.value = curve->basePointG.value; + args.privkey.ec.params.basePointG.len = curve->basePointG.len; + args.privkey.ec.params.order.value = curve->order.value; + args.privkey.ec.params.order.len = curve->order.len; + args.privkey.ec.params.coFactor.value = curve->coFactor.value; + args.privkey.ec.params.coFactor.len = curve->coFactor.len; + args.privkey.ec.privateD.value = key->u.ec.privateD.data; + args.privkey.ec.privateD.len = key->u.ec.privateD.len; + } + break; + + default: + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + } + args.priv_key_ref = key_info->key_reference; + + r = sc_card_ctl(card, SC_CARDCTL_ISOAPPLET_IMPORT_KEY, &args); + if (r < 0) + { + sc_log(card->ctx, "%s: Error in card_ctl", sc_strerror(r)); + LOG_FUNC_RETURN(card->ctx, r); + } + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static struct sc_pkcs15init_operations sc_pkcs15init_isoApplet_operations = +{ + NULL, /* erase_card */ + NULL, /* init_card */ + isoApplet_create_dir, /* create_dir */ + NULL, /* create_domain */ + isoApplet_select_pin_reference, /* pin_reference*/ + isoApplet_create_pin, /* create_pin */ + isoApplet_select_key_reference, /* key_reference */ + isoApplet_create_key, /* create_key */ + isoApplet_store_key, /* store_key */ + isoApplet_generate_key, /* generate_key */ + NULL, NULL, /* encode private/public key */ + NULL, /* finalize */ + NULL, /* delete_object */ + NULL, NULL, NULL, NULL, NULL, /* pkcs15init emulation */ + NULL, /* sanity_check*/ +}; + +struct +sc_pkcs15init_operations *sc_pkcs15init_get_isoApplet_ops(void) +{ + return &sc_pkcs15init_isoApplet_operations; +} diff --git a/src/pkcs15init/pkcs15-lib.c b/src/pkcs15init/pkcs15-lib.c index 5d554ba4..51a71b7a 100644 --- a/src/pkcs15init/pkcs15-lib.c +++ b/src/pkcs15init/pkcs15-lib.c @@ -154,6 +154,7 @@ static struct profile_operations { { "westcos", (void *) sc_pkcs15init_get_westcos_ops }, { "myeid", (void *) sc_pkcs15init_get_myeid_ops }, { "sc-hsm", (void *) sc_pkcs15init_get_sc_hsm_ops }, + { "isoApplet", (void *) sc_pkcs15init_get_isoApplet_ops }, #ifdef ENABLE_OPENSSL { "authentic", (void *) sc_pkcs15init_get_authentic_ops }, { "iasecc", (void *) sc_pkcs15init_get_iasecc_ops }, diff --git a/win32/OpenSC.wxs.in b/win32/OpenSC.wxs.in index eb793c11..8559708b 100644 --- a/win32/OpenSC.wxs.in +++ b/win32/OpenSC.wxs.in @@ -180,6 +180,9 @@ + + + @@ -245,6 +248,7 @@ +