From 48bd6b09645014365a2be8361d113757a1736814 Mon Sep 17 00:00:00 2001 From: Philip Wendland Date: Thu, 28 Aug 2014 14:29:16 +0200 Subject: [PATCH 01/19] Add support for the IsoApplet (Java Card applet) The IsoApplet can be found here: https://github.com/philipWendland/IsoApplet Add read/write support for this applet, including RSA and ECC support. --- src/libopensc/Makefile.am | 1 + src/libopensc/Makefile.mak | 2 +- src/libopensc/card-isoApplet.c | 1223 +++++++++++++++++++++++++++++ src/libopensc/cardctl.h | 33 +- src/libopensc/cards.h | 7 +- src/libopensc/ctx.c | 1 + src/pkcs15init/Makefile.am | 6 +- src/pkcs15init/Makefile.mak | 3 +- src/pkcs15init/isoApplet.profile | 164 ++++ src/pkcs15init/pkcs15-init.h | 1 + src/pkcs15init/pkcs15-isoApplet.c | 871 ++++++++++++++++++++ src/pkcs15init/pkcs15-lib.c | 1 + 12 files changed, 2307 insertions(+), 6 deletions(-) create mode 100644 src/libopensc/card-isoApplet.c create mode 100644 src/pkcs15init/isoApplet.profile create mode 100644 src/pkcs15init/pkcs15-isoApplet.c 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..06509b59 --- /dev/null +++ b/src/libopensc/card-isoApplet.c @@ -0,0 +1,1223 @@ +/* + * 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 "internal.h" +#include "opensc.h" +#include "cardctl.h" +#include "log.h" +#include "asn1.h" +#include "pkcs15.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 0x03 +#define ISOAPPLET_API_FEATURE_EXT_APDU 0x01 + +#define ISOAPPLET_AID_LEN 12 +static const u8 isoAppletId[] = {0xf2,0x76,0xa2,0x88,0xbc,0xfb,0xa6,0x9d,0x34,0xf3,0x10,0x01}; + +struct isoApplet_drv_data +{ + unsigned int sec_env_alg_ref; +}; +#define DRVDATA(card) ((struct isoApplet_drv_data *) ((card)->drv_data)) + +static struct sc_card_operations isoApplet_ops; +static const struct sc_card_operations *iso_ops = NULL; +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; + u8 aid_wc[SC_MAX_APDU_BUFFER_SIZE]; + + LOG_FUNC_CALLED(card->ctx); + + if(aid_len > SC_MAX_APDU_BUFFER_SIZE) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL); + memcpy(aid_wc, aid, aid_len); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xa4, 0x04, 0x00); + apdu.lc = aid_len; + apdu.data = aid_wc; + 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, isoAppletId, ISOAPPLET_AID_LEN, rbuf, &rlen); + + if(rv != SC_SUCCESS) + { + return 0; + } + + /* 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) +{ + unsigned long flags = 0; + unsigned long ext_flags = 0; + struct isoApplet_drv_data *drvdata; + + LOG_FUNC_CALLED(card->ctx); + + drvdata=malloc(sizeof(struct isoApplet_drv_data)); + if (!drvdata) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + memset(drvdata, 0, sizeof(struct isoApplet_drv_data)); + drvdata->sec_env_alg_ref = 0; + + card->drv_data = drvdata; + card->cla = 0x00; + + /* ECDSA */ + 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; + _sc_card_add_ec_alg(card, 192, flags, ext_flags); + _sc_card_add_ec_alg(card, 256, flags, ext_flags); + + /* RSA */ + flags = 0; + /* Padding schemes: */ + flags |= SC_ALGORITHM_RSA_PAD_PKCS1; + /* Hashes: */ + flags |= SC_ALGORITHM_RSA_HASH_NONE; + flags |= SC_ALGORITHM_RSA_HASH_SHA1; + flags |= SC_ALGORITHM_RSA_HASH_MD5; + flags |= SC_ALGORITHM_RSA_HASH_MD5_SHA1; + flags |= SC_ALGORITHM_RSA_HASH_SHA224; + flags |= SC_ALGORITHM_RSA_HASH_SHA256; + flags |= SC_ALGORITHM_RSA_HASH_SHA384; + flags |= SC_ALGORITHM_RSA_HASH_SHA512; + flags |= SC_ALGORITHM_RSA_HASH_RIPEMD160; + /* 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 +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] = 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); +} + +/* + * Adds 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 return by the applet. + */ +static int +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 = sa_to_acl(file, SC_AC_OP_DELETE_SELF, sa[1]); + LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); + r = sa_to_acl(file, SC_AC_OP_LOCK, sa[2]); + LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); + r = sa_to_acl(file, SC_AC_OP_ACTIVATE, sa[3]); + LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); + r = sa_to_acl(file, SC_AC_OP_DEACTIVATE, sa[4]); + LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); + r = sa_to_acl(file, SC_AC_OP_CREATE_DF, sa[5]); + LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); + r = sa_to_acl(file, SC_AC_OP_CREATE_EF, sa[6]); + LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); + r = 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 = sa_to_acl(file, SC_AC_OP_DELETE_SELF, sa[1]); + LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); + r = sa_to_acl(file, SC_AC_OP_LOCK, sa[2]); + LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); + r = sa_to_acl(file, SC_AC_OP_ACTIVATE, sa[3]); + LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); + r = sa_to_acl(file, SC_AC_OP_DEACTIVATE, sa[4]); + LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); + r = sa_to_acl(file, SC_AC_OP_WRITE, sa[5]); + LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); + r = sa_to_acl(file, SC_AC_OP_UPDATE, sa[6]); + LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); + r = 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); +} + +static int +isoApplet_ctl_generate_key(sc_card_t *card, struct sc_cardctl_isoApplet_genkey *args) +{ + int r, len; + size_t tag_len; + sc_apdu_t apdu; + u8 rbuf[SC_MAX_EXT_APDU_BUFFER_SIZE]; + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; + u8 *p; + const u8 *curr_pos; + + 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); + 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"); + + + /* 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. */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_2, 0x46, 0x42, 0x00); + + apdu.resp = rbuf; + apdu.resplen = SC_MAX_EXT_APDU_BUFFER_SIZE; + 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); + LOG_TEST_RET(card->ctx, r, "Card returned error"); + + /* Parse the public key / response. */ + switch(args->algorithm_ref) + { + + case SC_ISOAPPLET_ALG_REF_RSA_GEN_2048: + /* We expect: + * - Tag: 7F 49 + * - Length: 82 01 09 (265 Bytes) */ + p = rbuf; + if(memcmp(p, "\x7F\x49\x82\x01\x09", 5) != 0) + { + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, + "The data returned by the card is unexpected."); + } + else + { + len = 265; + } + p += 5; /* p points to the value field of the outer (7F 49) tag. + * This value field is a TLV-structure again. */ + + /* Search for the modulus tag (81). */ + curr_pos = sc_asn1_find_tag(card->ctx, p, len, (unsigned int) 0x81, &tag_len); + if(curr_pos == NULL || tag_len != 256) + { + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid modulus."); + } + if(args->pubkey_len < 256) + { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL); + } + args->pubkey_len = tag_len; + memcpy(args->pubkey, curr_pos, args->pubkey_len); + + /* Exponent tag (82) */ + curr_pos = sc_asn1_find_tag(card->ctx, p, len, (unsigned int) 0x82, &tag_len); + if(curr_pos == NULL || tag_len != 3) + { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_DATA); + } + if(args->exponent_len < 3) + { + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid exponent."); + } + if(memcmp(curr_pos, "\x01\x00\x01", 3) != 0) + { + LOG_TEST_RET(card->ctx, SC_ERROR_INCOMPATIBLE_KEY, + "Key generation error: Unexpected public key exponent."); + } + args->exponent_len = 3; + memcpy(args->exponent, curr_pos, args->exponent_len); + p = NULL; + break; + + case SC_ISOAPPLET_ALG_REF_EC_GEN_BRAINPOOLP192R1: + p = rbuf; + if(args->pubkey_len >= apdu.resplen) + { + memcpy(args->pubkey, p, apdu.resplen); + } + else + { + LOG_TEST_RET(card->ctx, SC_ERROR_BUFFER_TOO_SMALL, + "Key generation error: Public key buffer too small."); + } + break; + + case SC_ISOAPPLET_ALG_REF_EC_GEN_PRIME256V1: + p = rbuf; + if(args->pubkey_len >= apdu.resplen) + { + memcpy(args->pubkey, p, apdu.resplen); + } + else + { + LOG_TEST_RET(card->ctx, SC_ERROR_BUFFER_TOO_SMALL, + "Key generation error: Public key buffer too small."); + } + 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 Insert the length field of a TLV entry. + * + * The format is: + * 0..127: 1 byte, 00-7F + * 128..255: 2 bytes, 81; 00-FF + * 256..65535: 3 bytes, 82; 0000-FFFF + * + * @param[out] p The buffer where the length tag should be placed. + * @param[in] p_len The length of p. + * @param[in] len The length to be inserted. + * + * @return positive values: The length of the length field inserted. + * SC_ERROR_INVALID_ARGUMENTS: Incorrect length value or p == null. + * SC_ERROR_BUFFER_TOO_SMALL: The buffer p can not hold the length field. + */ +static int +tlv_insert_len(u8 *p, size_t p_len, size_t len) +{ + if(p == NULL) + return SC_ERROR_INVALID_ARGUMENTS; + + /* Note: len < 0 can not happen as it is size_t */ + if(len <= 127) + { + if(p_len < 1) + return SC_ERROR_BUFFER_TOO_SMALL; + *p++ = len & 0x7F; + return 1; + } + else if(len <= 255) + { + if(p_len < 2) + return SC_ERROR_BUFFER_TOO_SMALL; + *p++ = 0x81; + *p++ = len & 0xFF; + return 2; + } + else if(len <= 65535) + { + if(p_len < 3) + return SC_ERROR_BUFFER_TOO_SMALL; + *p++ = 0x82; + *p++ = (len >> 8) & 0xFF; /* MSB */ + *p++ = len & 0xFF; /* LSB */ + return 3; + } + else + { + return SC_ERROR_INVALID_ARGUMENTS; + } +} + +/* + * @brief Add a TLV-entry to a buffer. + * + * @param[out] buf The buffer at where the TLV entry should be placed. + * @param[in] buf_len The length of buf. + * @param[in] tag The one byte tag of the TLV entry. + * @param[in] tag_data The value field of the TLV entry. + * @param[in] tag_data_len The length of the tag_data. + * + */ +static int +tlv_add_tlv(u8 *buf, const size_t buf_len, const u8 tag, + const u8 *tag_data, const size_t tag_data_len) +{ + size_t l_len; /* Length of the length field itself. */ + int r; + + if(buf == NULL || tag_data == NULL) + return SC_ERROR_INVALID_ARGUMENTS; + + if(tag_data_len <= 127) + l_len = 1; + else if(tag_data_len <= 255) + l_len = 2; + else if(tag_data_len <= 65535) + l_len = 3; + else + return SC_ERROR_INVALID_ARGUMENTS; + + if(1 + l_len + tag_data_len > buf_len) + return SC_ERROR_BUFFER_TOO_SMALL; + + *buf++ = tag; + r = tlv_insert_len(buf, buf_len-1, tag_data_len); + if(r < 0) + return r; + else if((unsigned int)r != l_len) + return SC_ERROR_UNKNOWN; + + buf += l_len; + + memcpy(buf, tag_data, tag_data_len); + return 1 + l_len + tag_data_len; +} + +/* + * @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, struct sc_pkcs15_prkey_rsa *rsa) +{ + sc_apdu_t apdu; + const size_t sbuf_len = SC_MAX_APDU_BUFFER_SIZE; + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; + u8 *p = NULL; + int r; + size_t tags_len; + + LOG_FUNC_CALLED(card->ctx); + + if(!rsa + || !rsa->p.data + || !rsa->q.data + || !rsa->iqmp.data + || !rsa->dmp1.data + || !rsa->dmq1.data) + { + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "Only CRT RSA keys may be imported."); + } + + + p = sbuf; + /* Note: The format is according to ISO 2-byte tag 7F48 */ + *p++ = 0x7F; /* T-L pair to indicate a private key data object */ + *p++ = 0x48; + /* Calculate the length of all inner tag-length-value entries. + * One entry consists of: tag (1 byte) + length (1 byte if < 128, 2 if >= 128) + value (len) + * It may actually happen that a parameter is 127 byte (leading zero) */ + tags_len = 1 + (rsa->p.len < 128 ? 1 : 2) + rsa->p.len + + 1 + (rsa->q.len < 128 ? 1 : 2) + rsa->q.len + + 1 + (rsa->iqmp.len < 128 ? 1 : 2) + rsa->iqmp.len + + 1 + (rsa->dmp1.len < 128 ? 1 : 2) + rsa->dmp1.len + + 1 + (rsa->dmq1.len < 128 ? 1 : 2) + rsa->dmq1.len; + r = tlv_insert_len(p, sbuf_len - (p - sbuf), tags_len); /* Private key data object length */ + LOG_TEST_RET(card->ctx, r, "Error in handling TLV."); + p += r; + + /* p */ + r = tlv_add_tlv(p, sbuf_len - (p - sbuf), 0x92, rsa->p.data, rsa->p.len); + LOG_TEST_RET(card->ctx, r, "Error in handling TLV."); + p += r; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xDB, 0x3F, 0xFF); + apdu.cla |= 0x10; /* Chaining */ + apdu.data = sbuf; + apdu.datalen = p - sbuf; + apdu.lc = p - 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); + 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."); + } + LOG_TEST_RET(card->ctx, r, "Card returned error"); + + /* q */ + p = sbuf; + r = tlv_add_tlv(p, sbuf_len - (p - sbuf), 0x93, rsa->q.data, rsa->q.len); + LOG_TEST_RET(card->ctx, r, "Error in handling TLV."); + p += r; + + apdu.data = sbuf; + apdu.datalen = p - sbuf; + apdu.lc = p - sbuf; + r = sc_check_apdu(card, &apdu); + 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"); + + /* 1/q mod p */ + p = sbuf; + r = tlv_add_tlv(p, sbuf_len - (p - sbuf), 0x94, rsa->iqmp.data, rsa->iqmp.len); + LOG_TEST_RET(card->ctx, r, "Error in handling TLV."); + p += r; + + apdu.data = sbuf; + apdu.datalen = p - sbuf; + apdu.lc = p - 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"); + + /* d mod (p-1) */ + p = sbuf; + r = tlv_add_tlv(p, sbuf_len - (p - sbuf), 0x95, rsa->dmp1.data, rsa->dmp1.len); + LOG_TEST_RET(card->ctx, r, "Error in handling TLV."); + p += r; + + apdu.data = sbuf; + apdu.datalen = p - sbuf; + apdu.lc = p - 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"); + + /* d mod (q-1) */ + p = sbuf; + r = tlv_add_tlv(p, sbuf_len - (p - sbuf), 0x96, rsa->dmq1.data, rsa->dmq1.len); + LOG_TEST_RET(card->ctx, r, "Error in handling TLV."); + p += r; + + apdu.cla = 0x00; /* Last part of the chain. */ + apdu.data = sbuf; + apdu.datalen = p - sbuf; + apdu.lc = p - 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"); + + LOG_FUNC_RETURN(card->ctx, r); +} + +/* + * @brief Use PUT DATA to import a private EC key. + * + * I use a simpler format for EC keys (compared to RSA) + * because the card has all the necessary information except the ecPointQ. + * Only the ecPointQ is sent to the card. It is BER-TLV-encoded. The tag is: + * 0xC1 - Private class, primitive encoding, number one. + * + * @param card + * @param ec The EC 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_ec(sc_card_t *card, struct sc_pkcs15_prkey_ec *ec) +{ + sc_apdu_t apdu; + size_t sbuf_len = SC_MAX_EXT_APDU_BUFFER_SIZE; + u8 sbuf[SC_MAX_EXT_APDU_BUFFER_SIZE]; + int r; + + LOG_FUNC_CALLED(card->ctx); + + if(!ec) + { + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "No EC private key."); + } + + r = tlv_add_tlv(sbuf, sbuf_len, 0xC1, ec->privateD.data, ec->privateD.len); + LOG_TEST_RET(card->ctx, r, "Error in handling TLV."); + + /* Send to card. */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xDB, 0x3F, 0xFF); + 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); + 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."); + } + LOG_TEST_RET(card->ctx, r, "Card returned error"); + + 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); + 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."); + } + 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->prkey->u.rsa); + LOG_TEST_RET(card->ctx, r, "Error in PUT DATA."); + break; + + case SC_ISOAPPLET_ALG_REF_EC_GEN_BRAINPOOLP192R1: + case SC_ISOAPPLET_ALG_REF_EC_GEN_PRIME256V1: + r = isoApplet_put_data_prkey_ec(card, &args->prkey->u.ec); + 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; + size_t xlen, ylen; + size_t i, offset; + + 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, sequence} which is already done by the + * card actually. + * To fix this, I strip the {sequence, sequence} structual information + * so that pkcs11-tool.c can add it again... */ + if(drvdata->sec_env_alg_ref == ISOAPPLET_ALG_REF_ECDSA) + { + /* Outer SEQUENCE tag and first INTEGER tag. */ + offset=0; + if(r < 2 + || out[offset++] != 0x30 + || out[offset++] != r-2 + || out[offset++] != 0x02) + { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); + } + + /* X */ + xlen = out[offset++]; + assert(xlen+4 < outlen); + /* Remove the leading 0 of the coordinate, if present. */ + if(out[offset] == 0x00) + { + offset++; + xlen--; + } + for(i=0; i < xlen; i++) + { + out[i] = out[i+offset]; + } + + /* Y */ + assert(i+offset+3 < outlen); + if(out[i+offset++] != 0x02) + { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); + } + ylen = out[i+offset++]; + /* Remove the leading 0 of the coordinate, if present. */ + if(out[i+offset] == 0x00) + { + offset++; + ylen--; + } + assert(offset+xlen+ylen <= outlen); + for(; i < xlen+ylen; i++) + { + out[i] = out[i+offset]; + } + r = xlen+ylen; + } + 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..98a34f0f 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,30 @@ 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_BRAINPOOLP192R1 0xE0 +#define SC_ISOAPPLET_ALG_REF_EC_GEN_PRIME256V1 0xE1 + +typedef struct sc_cardctl_isoApplet_genkey { + u8 algorithm_ref; /* Algorithm reference sent to card */ + unsigned char *exponent; /* RSA public key exponent */ + unsigned int exponent_len; + unsigned int priv_key_ref; /* Private key refernce sent to card */ + unsigned char *pubkey; /* RSA public key modulus (or EC tlv-encoded public key) */ + unsigned int pubkey_len; +} 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 */ + struct sc_pkcs15_prkey *prkey; +} 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..41410c50 --- /dev/null +++ b/src/pkcs15init/pkcs15-isoApplet.c @@ -0,0 +1,871 @@ +/* + * 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 + + +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[] = +{ + + { + { (unsigned char *) "\x2B\x24\x03\x03\x02\x08\x01\x01\x03", 9}, /* brainpoolP192r1 */ + { (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 *) "\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", 48}, + { (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} + }, + + { + { (unsigned char *) "\x2A\x86\x48\xCE\x3D\x03\x01\x07", 8}, /* secp256r1 aka prime256v1 */ + { (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 *) "\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", 64}, + { (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} + }, + + { + { 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, 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 OID of the curve specified by a curve name. + * + * @param[in] named_curve The name of the curve to search the OID of. + * Supported values are: brainpoolP192r1, prime256v1. + * @param[out] oid The OID of the curve. + * + * @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_oid(const char* named_curve, const struct sc_lv_data **oid) +{ + if(!named_curve) + return SC_ERROR_INVALID_ARGUMENTS; + + if(strncmp(named_curve, "brainpoolP192r1", 15) == 0) + { + *oid = &curves[0].oid; + return SC_SUCCESS; + } + else if(strncmp(named_curve, "prime256v1", 10) == 0) + { + *oid = &curves[1].oid; + return SC_SUCCESS; + } + return SC_ERROR_INVALID_ARGUMENTS; +} + +/* + * @brief Check the consistency of TLV-encoded EC curve parameters. + * + * Check the EC params in buf (length: len) that are structured according + * to ISO 7816-8 table 3 - Public key data objects. + * The params are compared with the ones given in the curve struct. + * + * @param[in] ctx + * @param[in] buf The buffer containing the TLV-encoded (ISO 7816-8 table 3) + * EC parameters. + * @param[in] len The length of buf. + * @param[in] curve An ec_curve struct that should be used to check the + * parameters in buf. + * + * @return SC_SUCCESS: If the EC parameters are consistent. + * SC_ERROR_INCOMPATIBLE_KEY: If the curve is unknown or the EC + * parameters are not consistent. + */ +static int +checkEcParams(sc_context_t* ctx, const u8* buf, size_t len, const struct ec_curve curve) +{ + const u8 *curr_pos = NULL; + size_t tag_len; + int r = SC_SUCCESS; + + LOG_FUNC_CALLED(ctx); + + /* Check the field. */ + curr_pos = sc_asn1_find_tag(ctx, buf, len, (unsigned int) 0x81, &tag_len); + if(curr_pos == NULL || tag_len != curve.prime.len) + { + r = SC_ERROR_INCOMPATIBLE_KEY; + LOG_TEST_RET(ctx, r, + "Could not find any EC field tag in the response template or the length was unexpected."); + } + if(memcmp(curr_pos, curve.prime.value, curve.prime.len) != 0) + { + r = SC_ERROR_INCOMPATIBLE_KEY; + LOG_TEST_RET(ctx, r, + "The EC field by the smartcard was unexpected."); + } + + /* Check the coefficient A. */ + curr_pos = sc_asn1_find_tag(ctx, buf, len, (unsigned int) 0x82, &tag_len); + if(curr_pos == NULL || tag_len != curve.coefficientA.len) + { + r = SC_ERROR_INCOMPATIBLE_KEY; + LOG_TEST_RET(ctx, r, + "Could not find any EC coefficient A tag in the response template or the length was unexpected."); + } + if(memcmp(curr_pos, curve.coefficientA.value, curve.coefficientA.len) != 0) + { + r = SC_ERROR_INCOMPATIBLE_KEY; + LOG_TEST_RET(ctx, r, + "The EC coefficient A returned by the smartcard was unexpected."); + } + + /* Check the coefficient B. */ + curr_pos = sc_asn1_find_tag(ctx, buf, len, (unsigned int) 0x83, &tag_len); + if(curr_pos == NULL || tag_len != curve.coefficientB.len) + { + r = SC_ERROR_INCOMPATIBLE_KEY; + LOG_TEST_RET(ctx, r, + "Could not find any EC coefficient B tag in the response template or the length was unexpected."); + } + if(memcmp(curr_pos, curve.coefficientB.value, curve.coefficientB.len) != 0) + { + r = SC_ERROR_INCOMPATIBLE_KEY; + LOG_TEST_RET(ctx, r, + "The EC coefficient B returned by the smartcard was unexpected."); + } + + /* Check the basepoint G. + * Note: The IsoApplet omits the 0x04 (uncompressed) tag. */ + curr_pos = sc_asn1_find_tag(ctx, buf, len, (unsigned int) 0x84, &tag_len); + if(curr_pos == NULL || tag_len != curve.basePointG.len) + { + r = SC_ERROR_INCOMPATIBLE_KEY; + LOG_TEST_RET(ctx, r, + "Could not find any EC basepoint G tag in the response template or the length was unexpected."); + } + if(memcmp(curr_pos, curve.basePointG.value, curve.basePointG.len) != 0) + { + r = SC_ERROR_INCOMPATIBLE_KEY; + LOG_TEST_RET(ctx, r, + "The EC basepoint G returned by the smartcard was unexpected."); + } + + /* Check the order. */ + curr_pos = sc_asn1_find_tag(ctx, buf, len, (unsigned int) 0x85, &tag_len); + if(curr_pos == NULL || tag_len != curve.order.len) + { + r = SC_ERROR_INCOMPATIBLE_KEY; + LOG_TEST_RET(ctx, r, + "Could not find any EC order tag in the response template or the length was unexpected."); + } + if(memcmp(curr_pos, curve.order.value, curve.order.len) != 0) + { + r = SC_ERROR_INCOMPATIBLE_KEY; + LOG_TEST_RET(ctx, r, + "The EC order returned by the smartcard was unexpected."); + } + + /* Check the coFactor. */ + curr_pos = sc_asn1_find_tag(ctx, buf, len, (unsigned int) 0x87, &tag_len); + if(curr_pos == NULL || tag_len != curve.coFactor.len) + { + r = SC_ERROR_INCOMPATIBLE_KEY; + LOG_TEST_RET(ctx, r, + "Could not find any EC cofactor tag in the response template or the length was unexpected."); + } + if(memcmp(curr_pos, curve.coFactor.value, curve.coFactor.len) != 0) + { + r = SC_ERROR_INCOMPATIBLE_KEY; + LOG_TEST_RET(ctx, r, + "The EC cofactor returned by the smartcards was unexpected."); + } + + LOG_FUNC_RETURN(ctx, r); +} + +/* + * @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 +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: keysize 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_len = keybits / 8; + args.pubkey = malloc(args.pubkey_len); + if (!args.pubkey) + { + rv = SC_ERROR_OUT_OF_MEMORY; + sc_log(card->ctx, "%s: Unable to allocate public key buffer.", sc_strerror(rv)); + goto err; + } + + args.exponent_len = 3; + args.exponent = malloc(args.exponent_len); + if(!args.exponent) + { + 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 public key */ + pubkey->algorithm = SC_ALGORITHM_RSA; + pubkey->u.rsa.modulus.len = args.pubkey_len; + pubkey->u.rsa.modulus.data = args.pubkey; + pubkey->u.rsa.exponent.len = args.exponent_len; + pubkey->u.rsa.exponent.data = args.exponent; + rv = SC_SUCCESS; + LOG_FUNC_RETURN(card->ctx, rv); +err: + if (args.pubkey) + { + free(args.pubkey); + pubkey->u.rsa.modulus.data = NULL; + pubkey->u.rsa.modulus.len = 0; + } + if (args.exponent) + { + free(args.exponent); + 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] 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 +generate_key_ec(const sc_pkcs15_prkey_info_t *key_info, sc_card_t *card, + sc_pkcs15_pubkey_t *pubkey) +{ + int r; + u8* p = NULL; + u8* ecPubKeyPoint = NULL; + size_t tag_len; + size_t all_tags_len; + const u8* curr_pos = NULL; + struct sc_ec_params* ecp = NULL; + const struct sc_lv_data* oid = NULL; + sc_cardctl_isoApplet_genkey_t args; + const struct sc_pkcs15_ec_parameters* info_ecp = + (struct sc_pkcs15_ec_parameters *) key_info->params.data; + + LOG_FUNC_CALLED(card->ctx); + + /* Check key size: */ + if(key_info->field_length != 192 + && key_info->field_length != 256) + { + sc_log(card->ctx, "EC field length is unsupported, length provided was: %d.", key_info->field_length); + r = SC_ERROR_INVALID_ARGUMENTS; + goto err; + } + + if(info_ecp->named_curve && strncmp(info_ecp->named_curve, "brainpoolP192r1", 15) != 0 + && strncmp(info_ecp->named_curve, "prime256v1", 10) != 0) + { + sc_log(card->ctx, "EC key generation failed: Unsupported curve: [%s].", info_ecp->named_curve); + r = SC_ERROR_INVALID_ARGUMENTS; + goto err; + } + + /* Generate the key. + * Note: THe field size is not explicitly passed to the card. + * It assumes it along with the algorithm reference. */ + memset(&args, 0, sizeof(args)); + + args.pubkey_len = 512; + args.pubkey = malloc(args.pubkey_len); + if(!args.pubkey) + { + r = SC_ERROR_OUT_OF_MEMORY; + sc_log(card->ctx, "%s: Unable to allocate public key buffer.", sc_strerror(r)); + goto err; + } + + if(strncmp(info_ecp->named_curve, "brainpoolP192r1", 15) == 0) + { + args.algorithm_ref = SC_ISOAPPLET_ALG_REF_EC_GEN_BRAINPOOLP192R1; + } + else if(strncmp(info_ecp->named_curve, "prime256v1", 10) == 0) + { + args.algorithm_ref = SC_ISOAPPLET_ALG_REF_EC_GEN_PRIME256V1; + } + args.priv_key_ref = key_info->key_reference; + + 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 err; + } + + /* Extract the public key. */ + pubkey->algorithm = SC_ALGORITHM_EC; + + /* Get the curves OID. */ + r = isoApplet_get_curve_oid(info_ecp->named_curve, &oid); + if(r < 0) + { + sc_log(card->ctx, "Error obtaining the curve OID.", sc_strerror(r)); + goto err; + } + + /* der-encoded parameters */ + ecp = calloc(1, sizeof(struct sc_ec_params)); + if(!ecp) + { + r = SC_ERROR_OUT_OF_MEMORY; + goto err; + } + ecp->der_len = oid->len + 2; + ecp->der = calloc(ecp->der_len, 1); + if(!ecp->der) + { + r = SC_ERROR_OUT_OF_MEMORY; + sc_log(card->ctx, "%s: Unable to allocate public key buffer.", sc_strerror(r)); + goto err; + } + ecp->der[0] = 0x06; + ecp->der[1] = (u8)oid->len; + memcpy(ecp->der + 2, oid->value, oid->len); + ecp->type = 1; /* named curve */ + + pubkey->alg_id = (struct sc_algorithm_id *)calloc(1, sizeof(struct sc_algorithm_id)); + if(!pubkey->alg_id) + { + r = SC_ERROR_OUT_OF_MEMORY; + sc_log(card->ctx, "%s: Unable to allocate public key sc_algorithm_id.", sc_strerror(r)); + goto err; + } + pubkey->alg_id->algorithm = SC_ALGORITHM_EC; + pubkey->alg_id->params = ecp; + + p = args.pubkey; + if(memcmp(info_ecp->named_curve, "brainpoolP192r1", 15) == 0) + { + /* The applet returns the public key encoded according to + * ISO 7816-8 table 3 - Public key data objects. This is a + * 2-byte tag. A length of 0xD0 = 208 is expected for BrainpoolP192r1. */ + if(memcmp(p, "\x7F\x49\x81\xD0", 4) != 0) + { + r = SC_ERROR_INCOMPATIBLE_KEY; + sc_log(card->ctx, "%s: Key generation error: Unexpected EC public key received length.", sc_strerror(r)); + goto err; + } + else + { + p += 4; /* p points to the value field of the outer (7F 49) tag. + * This value field is a TLV-structure again. */ + all_tags_len = 208; /* 0xD0 bytes */ + } + + /* Check EC params. */ + r = checkEcParams(card->ctx, p, all_tags_len, curves[0]); + if(r != SC_SUCCESS) + { + goto err; + } + } + else if(memcmp(info_ecp->named_curve, "prime256v1", 10) == 0) + { + /* The applet returns the public key encoded according to + * ISO 7816-8 table 3 - Public key data objects. This is a + * 2-byte tag. A length of 0x011A = 282 is expected for Prime256v1. */ + if(memcmp(p, "\x7F\x49\x82\x01\x1A", 5) != 0) + { + r = SC_ERROR_INCOMPATIBLE_KEY; + sc_log(card->ctx, "%s: Key generation error: Unexpected EC public key parameters.", sc_strerror(r)); + goto err; + } + else + { + p += 5; /* p points to the value field of the outer (7F 49) tag. + * This value field is a TLV-structure again. */ + all_tags_len = 282; /* 0x011A bytes */ + } + + /* Check EC params. */ + r = checkEcParams(card->ctx, p, all_tags_len, curves[1]); + if(r != SC_SUCCESS) + { + goto err; + } + } + + /* Extract ecpointQ */ + curr_pos = sc_asn1_find_tag(card->ctx, p, all_tags_len, (unsigned int) 0x86, &tag_len); + if(curr_pos == NULL || tag_len == 0) + { + r = SC_ERROR_INCOMPATIBLE_KEY; + sc_log(card->ctx, "%s: Could not find any EC pointQ tag in the response template.", sc_strerror(r)); + goto err; + } + ecPubKeyPoint = malloc(tag_len+1); + if(!ecPubKeyPoint) + { + r = SC_ERROR_OUT_OF_MEMORY; + sc_log(card->ctx, "%s: Unable to allocate public key ecpointQ buffer.", sc_strerror(r)); + goto err; + } + *ecPubKeyPoint = 0x04; /* uncompressed */ + memcpy(ecPubKeyPoint+1, curr_pos, tag_len); + pubkey->u.ec.ecpointQ.value = ecPubKeyPoint; + pubkey->u.ec.ecpointQ.len = tag_len+1; + + /* OID for the public key */ + pubkey->u.ec.params.der.value = malloc(ecp->der_len); + if(!pubkey->u.ec.params.der.value) + { + r = SC_ERROR_OUT_OF_MEMORY; + sc_log(card->ctx, "%s: Unable to allocate public key ec params buffer.", sc_strerror(r)); + goto err; + } + memcpy(pubkey->u.ec.params.der.value, ecp->der, ecp->der_len); + pubkey->u.ec.params.der.len = ecp->der_len; + + r = sc_pkcs15_fix_ec_parameters(card->ctx, &pubkey->u.ec.params); + LOG_FUNC_RETURN(card->ctx, r); +err: + if(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; + } + memset(pubkey, 0, sizeof(sc_pkcs15_pubkey_t)); + } + if(args.pubkey) + { + free(args.pubkey); + args.pubkey = NULL; + args.pubkey_len = 0; + } + if(ecPubKeyPoint) + { + free(ecPubKeyPoint); + ecPubKeyPoint = NULL; + } + if(ecp) + { + if(ecp->der) + { + free(ecp->der); + ecp->der = NULL; + } + free(ecp); + ecp = 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 = generate_key_rsa(key_info, card, pubkey); + break; + + case SC_PKCS15_TYPE_PRKEY_EC: + r = 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; + char *p = NULL; + + 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; + break; + + case SC_PKCS15_TYPE_PRKEY_EC: + p = key->u.ec.params.named_curve; + if(!p) + { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + } + + if(strncmp(p, "brainpoolP192r1", 15) == 0) + { + args.algorithm_ref = SC_ISOAPPLET_ALG_REF_EC_GEN_BRAINPOOLP192R1; + } + else if(strncmp(p, "prime256v1", 10) == 0) + { + args.algorithm_ref = SC_ISOAPPLET_ALG_REF_EC_GEN_PRIME256V1; + } + break; + + default: + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + } + args.priv_key_ref = key_info->key_reference; + args.prkey = key; + + 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 cc655f6a..a051201b 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 }, From 6f9e894ebe8189eaf64fc20f644c3683b3f42930 Mon Sep 17 00:00:00 2001 From: Philip Wendland Date: Tue, 21 Oct 2014 16:05:06 +0200 Subject: [PATCH 02/19] IsoApplet: set lock when doing command chaining --- src/libopensc/card-isoApplet.c | 86 +++++++++++++++++++++++++++++----- 1 file changed, 73 insertions(+), 13 deletions(-) diff --git a/src/libopensc/card-isoApplet.c b/src/libopensc/card-isoApplet.c index 06509b59..e43a3ff2 100644 --- a/src/libopensc/card-isoApplet.c +++ b/src/libopensc/card-isoApplet.c @@ -707,6 +707,7 @@ isoApplet_put_data_prkey_rsa(sc_card_t *card, struct sc_pkcs15_prkey_rsa *rsa) u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; u8 *p = NULL; int r; + int locked = 0; size_t tags_len; LOG_FUNC_CALLED(card->ctx); @@ -743,6 +744,10 @@ isoApplet_put_data_prkey_rsa(sc_card_t *card, struct sc_pkcs15_prkey_rsa *rsa) LOG_TEST_RET(card->ctx, r, "Error in handling TLV."); p += r; + r = sc_lock(card); /* We will use several apdus */ + LOG_TEST_RET(card->ctx, r, "sc_lock() failed"); + locked = 1; + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xDB, 0x3F, 0xFF); apdu.cla |= 0x10; /* Chaining */ apdu.data = sbuf; @@ -756,12 +761,20 @@ isoApplet_put_data_prkey_rsa(sc_card_t *card, struct sc_pkcs15_prkey_rsa *rsa) 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."); } - LOG_TEST_RET(card->ctx, r, "Card returned error"); + if(r < 0) + { + sc_log(card->ctx, "Card returned error"); + goto out; + } /* q */ p = sbuf; r = tlv_add_tlv(p, sbuf_len - (p - sbuf), 0x93, rsa->q.data, rsa->q.len); - LOG_TEST_RET(card->ctx, r, "Error in handling TLV."); + if(r < 0) + { + sc_log(card->ctx, "%s: Error in handling TLV.", strerror(r)); + goto out; + } p += r; apdu.data = sbuf; @@ -769,42 +782,78 @@ isoApplet_put_data_prkey_rsa(sc_card_t *card, struct sc_pkcs15_prkey_rsa *rsa) apdu.lc = p - sbuf; r = sc_check_apdu(card, &apdu); r = sc_transmit_apdu(card, &apdu); - LOG_TEST_RET(card->ctx, r, "%s: APDU transmit failed"); + if(r < 0) + { + sc_log(card->ctx, "%s: APDU transmit failed.", strerror(r)); + goto out; + } r = sc_check_sw(card, apdu.sw1, apdu.sw2); - LOG_TEST_RET(card->ctx, r, "Card returned error"); + if(r < 0) + { + sc_log(card->ctx, "%s: Card returned error.", strerror(r)); + goto out; + } /* 1/q mod p */ p = sbuf; r = tlv_add_tlv(p, sbuf_len - (p - sbuf), 0x94, rsa->iqmp.data, rsa->iqmp.len); - LOG_TEST_RET(card->ctx, r, "Error in handling TLV."); + if(r < 0) + { + sc_log(card->ctx, "%s: Error in handling TLV.", strerror(r)); + goto out; + } p += r; apdu.data = sbuf; apdu.datalen = p - sbuf; apdu.lc = p - sbuf; r = sc_transmit_apdu(card, &apdu); - LOG_TEST_RET(card->ctx, r, "%s: APDU transmit failed"); + if(r < 0) + { + sc_log(card->ctx, "%s: APDU transmit failed.", strerror(r)); + goto out; + } r = sc_check_sw(card, apdu.sw1, apdu.sw2); - LOG_TEST_RET(card->ctx, r, "Card returned error"); + if(r < 0) + { + sc_log(card->ctx, "%s: Card returned error.", strerror(r)); + goto out; + } /* d mod (p-1) */ p = sbuf; r = tlv_add_tlv(p, sbuf_len - (p - sbuf), 0x95, rsa->dmp1.data, rsa->dmp1.len); - LOG_TEST_RET(card->ctx, r, "Error in handling TLV."); + if(r < 0) + { + sc_log(card->ctx, "%s: Error in handling TLV.", strerror(r)); + goto out; + } p += r; apdu.data = sbuf; apdu.datalen = p - sbuf; apdu.lc = p - sbuf; r = sc_transmit_apdu(card, &apdu); - LOG_TEST_RET(card->ctx, r, "%s: APDU transmit failed"); + if(r < 0) + { + sc_log(card->ctx, "%s: APDU transmit failed.", strerror(r)); + goto out; + } r = sc_check_sw(card, apdu.sw1, apdu.sw2); - LOG_TEST_RET(card->ctx, r, "Card returned error"); + if(r < 0) + { + sc_log(card->ctx, "%s: Card returned error.", strerror(r)); + goto out; + } /* d mod (q-1) */ p = sbuf; r = tlv_add_tlv(p, sbuf_len - (p - sbuf), 0x96, rsa->dmq1.data, rsa->dmq1.len); - LOG_TEST_RET(card->ctx, r, "Error in handling TLV."); + if(r < 0) + { + sc_log(card->ctx, "%s: Error in handling TLV.", strerror(r)); + goto out; + } p += r; apdu.cla = 0x00; /* Last part of the chain. */ @@ -812,10 +861,21 @@ isoApplet_put_data_prkey_rsa(sc_card_t *card, struct sc_pkcs15_prkey_rsa *rsa) apdu.datalen = p - sbuf; apdu.lc = p - sbuf; r = sc_transmit_apdu(card, &apdu); - LOG_TEST_RET(card->ctx, r, "%s: APDU transmit failed"); + if(r < 0) + { + sc_log(card->ctx, "%s: APDU transmit failed.", strerror(r)); + goto out; + } r = sc_check_sw(card, apdu.sw1, apdu.sw2); - LOG_TEST_RET(card->ctx, r, "Card returned error"); + if(r < 0) + { + sc_log(card->ctx, "%s: Card returned error.", strerror(r)); + goto out; + } +out: + if(locked) + sc_unlock(card); LOG_FUNC_RETURN(card->ctx, r); } From e3cc851b728ce7c5e681f67c58ef9788f74ca2fd Mon Sep 17 00:00:00 2001 From: Philip Wendland Date: Tue, 28 Oct 2014 08:58:30 +0100 Subject: [PATCH 03/19] IsoApplet: EC key-gen rework and refactorings Rework the EC key generation mechanism to send the curve parameters to the card. In earlier versions, the applet had a copy of the curve parameters and there was a different algorithm reference for every curve. This is unfeasible when trying to support a larger number of curves because of size limitations of the applet. This commit additionally includes some refactorings that should not change the functionality. --- src/libopensc/card-isoApplet.c | 599 ++++++++++++++++-------------- src/libopensc/cardctl.h | 47 ++- src/pkcs15init/pkcs15-isoApplet.c | 575 ++++++++++++---------------- 3 files changed, 587 insertions(+), 634 deletions(-) diff --git a/src/libopensc/card-isoApplet.c b/src/libopensc/card-isoApplet.c index e43a3ff2..ea81960f 100644 --- a/src/libopensc/card-isoApplet.c +++ b/src/libopensc/card-isoApplet.c @@ -32,20 +32,31 @@ #define ISOAPPLET_ALG_REF_RSA_PAD_PKCS1 0x11 #define ISOAPPLET_API_VERSION_MAJOR 0x00 -#define ISOAPPLET_API_VERSION_MINOR 0x03 +#define ISOAPPLET_API_VERSION_MINOR 0x04 #define ISOAPPLET_API_FEATURE_EXT_APDU 0x01 #define ISOAPPLET_AID_LEN 12 -static const u8 isoAppletId[] = {0xf2,0x76,0xa2,0x88,0xbc,0xfb,0xa6,0x9d,0x34,0xf3,0x10,0x01}; +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", @@ -71,7 +82,7 @@ static struct sc_card_driver isoApplet_drv = * 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) +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; @@ -103,7 +114,7 @@ isoApplet_select_applet(sc_card_t *card, const u8 aid[], const size_t aid_len, u } static int -isoApplet_finish(sc_card_t * card) +isoApplet_finish(sc_card_t *card) { struct isoApplet_drv_data *drvdata=DRVDATA(card); @@ -117,20 +128,21 @@ isoApplet_finish(sc_card_t * card) } static int -isoApplet_match_card(sc_card_t * card) +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, isoAppletId, ISOAPPLET_AID_LEN, rbuf, &rlen); + rv = isoApplet_select_applet(card, isoApplet_aid, ISOAPPLET_AID_LEN, rbuf, &rlen); if(rv != SC_SUCCESS) { return 0; } - /* If applet does not return API version, versions 0x00 will match */ + /* 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; @@ -170,7 +182,7 @@ isoApplet_match_card(sc_card_t * card) } static int -isoApplet_init(sc_card_t * card) +isoApplet_init(sc_card_t *card) { unsigned long flags = 0; unsigned long ext_flags = 0; @@ -178,10 +190,10 @@ isoApplet_init(sc_card_t * card) LOG_FUNC_CALLED(card->ctx); - drvdata=malloc(sizeof(struct isoApplet_drv_data)); + drvdata=malloc(sizeof(*drvdata)); if (!drvdata) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); - memset(drvdata, 0, sizeof(struct isoApplet_drv_data)); + memset(drvdata, 0, sizeof(*drvdata)); drvdata->sec_env_alg_ref = 0; card->drv_data = drvdata; @@ -194,7 +206,11 @@ isoApplet_init(sc_card_t * card) ext_flags = SC_ALGORITHM_EXT_EC_NAMEDCURVE; ext_flags |= SC_ALGORITHM_EXT_EC_F_P; _sc_card_add_ec_alg(card, 192, flags, ext_flags); + _sc_card_add_ec_alg(card, 224, flags, ext_flags); _sc_card_add_ec_alg(card, 256, flags, ext_flags); + _sc_card_add_ec_alg(card, 320, flags, ext_flags); + _sc_card_add_ec_alg(card, 384, flags, ext_flags); + _sc_card_add_ec_alg(card, 512, flags, ext_flags); /* RSA */ flags = 0; @@ -231,7 +247,7 @@ isoApplet_init(sc_card_t * card) * if unknown operation. */ static u8 -acl_to_security_condition_byte(const sc_acl_entry_t *entry) +isoApplet_acl_to_security_condition_byte(const sc_acl_entry_t *entry) { if(!entry) return 0x00; @@ -320,7 +336,7 @@ isoApplet_create_file(sc_card_t *card, sc_file_t *file) { const sc_acl_entry_t *entry; entry = sc_file_get_acl_entry(file, idx[i]); - access_buf[i] = acl_to_security_condition_byte(entry); + access_buf[i] = isoApplet_acl_to_security_condition_byte(entry); } r = sc_file_set_sec_attr(file, access_buf, 8); @@ -332,18 +348,19 @@ isoApplet_create_file(sc_card_t *card, sc_file_t *file) } /* - * Adds an ACL entry to the OpenSC file struct, according to the operation + * 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 return by the applet. + * @param[in] saByte The security condition byte returned by the applet. */ static int -sa_to_acl(sc_file_t *file, unsigned int operation, u8 saByte) +isoApplet_add_sa_to_acl(sc_file_t *file, unsigned int operation, u8 saByte) { int r; + switch(saByte) { case 0x90: @@ -397,37 +414,37 @@ isoApplet_process_fci(sc_card_t *card, sc_file_t *file, } if(file->type == SC_FILE_TYPE_DF) { - r = sa_to_acl(file, SC_AC_OP_DELETE_SELF, sa[1]); + 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 = sa_to_acl(file, SC_AC_OP_LOCK, sa[2]); + r = isoApplet_add_sa_to_acl(file, SC_AC_OP_LOCK, sa[2]); LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); - r = sa_to_acl(file, SC_AC_OP_ACTIVATE, sa[3]); + r = isoApplet_add_sa_to_acl(file, SC_AC_OP_ACTIVATE, sa[3]); LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); - r = sa_to_acl(file, SC_AC_OP_DEACTIVATE, sa[4]); + r = isoApplet_add_sa_to_acl(file, SC_AC_OP_DEACTIVATE, sa[4]); LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); - r = sa_to_acl(file, SC_AC_OP_CREATE_DF, sa[5]); + 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 = sa_to_acl(file, SC_AC_OP_CREATE_EF, sa[6]); + 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 = sa_to_acl(file, SC_AC_OP_DELETE, sa[7]); + 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 = sa_to_acl(file, SC_AC_OP_DELETE_SELF, sa[1]); + 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 = sa_to_acl(file, SC_AC_OP_LOCK, sa[2]); + r = isoApplet_add_sa_to_acl(file, SC_AC_OP_LOCK, sa[2]); LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); - r = sa_to_acl(file, SC_AC_OP_ACTIVATE, sa[3]); + r = isoApplet_add_sa_to_acl(file, SC_AC_OP_ACTIVATE, sa[3]); LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); - r = sa_to_acl(file, SC_AC_OP_DEACTIVATE, sa[4]); + r = isoApplet_add_sa_to_acl(file, SC_AC_OP_DEACTIVATE, sa[4]); LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); - r = sa_to_acl(file, SC_AC_OP_WRITE, sa[5]); + r = isoApplet_add_sa_to_acl(file, SC_AC_OP_WRITE, sa[5]); LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); - r = sa_to_acl(file, SC_AC_OP_UPDATE, sa[6]); + r = isoApplet_add_sa_to_acl(file, SC_AC_OP_UPDATE, sa[6]); LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); - r = sa_to_acl(file, SC_AC_OP_READ, sa[7]); + r = isoApplet_add_sa_to_acl(file, SC_AC_OP_READ, sa[7]); LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); } @@ -436,16 +453,78 @@ isoApplet_process_fci(sc_card_t *card, sc_file_t *file, 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_ctl_generate_key(sc_card_t *card, struct sc_cardctl_isoApplet_genkey *args) +isoApplet_put_ec_params(sc_card_t *card, sc_cardctl_isoApplet_ec_parameters_t *params, u8 *out, size_t out_len, u8 **ptr) { - int r, len; - size_t tag_len; + 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."); + } + + 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_APDU_BUFFER_SIZE]; u8 *p; - const u8 *curr_pos; + 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); @@ -490,12 +569,30 @@ isoApplet_ctl_generate_key(sc_card_t *card, struct sc_cardctl_isoApplet_genkey * * 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. */ - sc_format_apdu(card, &apdu, SC_APDU_CASE_2, 0x46, 0x42, 0x00); + 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 = SC_MAX_EXT_APDU_BUFFER_SIZE; + apdu.resplen = sizeof(rbuf); apdu.le = 256; - r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); @@ -503,190 +600,99 @@ isoApplet_ctl_generate_key(sc_card_t *card, struct sc_cardctl_isoApplet_genkey * 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: - /* We expect: - * - Tag: 7F 49 - * - Length: 82 01 09 (265 Bytes) */ - p = rbuf; - if(memcmp(p, "\x7F\x49\x82\x01\x09", 5) != 0) - { - LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, - "The data returned by the card is unexpected."); - } - else - { - len = 265; - } - p += 5; /* p points to the value field of the outer (7F 49) tag. - * This value field is a TLV-structure again. */ - /* Search for the modulus tag (81). */ - curr_pos = sc_asn1_find_tag(card->ctx, p, len, (unsigned int) 0x81, &tag_len); - if(curr_pos == NULL || tag_len != 256) + 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(args->pubkey_len < 256) + if(inner_tag_len > args->pubkey.rsa.modulus.len) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL); } - args->pubkey_len = tag_len; - memcpy(args->pubkey, curr_pos, args->pubkey_len); + memcpy(args->pubkey.rsa.modulus.value, inner_tag_value, inner_tag_len); + args->pubkey.rsa.modulus.len = inner_tag_len; /* Exponent tag (82) */ - curr_pos = sc_asn1_find_tag(card->ctx, p, len, (unsigned int) 0x82, &tag_len); - if(curr_pos == NULL || tag_len != 3) - { - LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_DATA); - } - if(args->exponent_len < 3) + 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(memcmp(curr_pos, "\x01\x00\x01", 3) != 0) + 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."); } - args->exponent_len = 3; - memcpy(args->exponent, curr_pos, args->exponent_len); + 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_BRAINPOOLP192R1: - p = rbuf; - if(args->pubkey_len >= apdu.resplen) - { - memcpy(args->pubkey, p, apdu.resplen); - } - else - { - LOG_TEST_RET(card->ctx, SC_ERROR_BUFFER_TOO_SMALL, - "Key generation error: Public key buffer too small."); - } - 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."); - case SC_ISOAPPLET_ALG_REF_EC_GEN_PRIME256V1: - p = rbuf; - if(args->pubkey_len >= apdu.resplen) - { - memcpy(args->pubkey, p, apdu.resplen); - } - else - { - LOG_TEST_RET(card->ctx, SC_ERROR_BUFFER_TOO_SMALL, - "Key generation error: Public key buffer too small."); - } - break; + 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 + }/* switch */ LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } -/* - * @brief Insert the length field of a TLV entry. - * - * The format is: - * 0..127: 1 byte, 00-7F - * 128..255: 2 bytes, 81; 00-FF - * 256..65535: 3 bytes, 82; 0000-FFFF - * - * @param[out] p The buffer where the length tag should be placed. - * @param[in] p_len The length of p. - * @param[in] len The length to be inserted. - * - * @return positive values: The length of the length field inserted. - * SC_ERROR_INVALID_ARGUMENTS: Incorrect length value or p == null. - * SC_ERROR_BUFFER_TOO_SMALL: The buffer p can not hold the length field. - */ -static int -tlv_insert_len(u8 *p, size_t p_len, size_t len) -{ - if(p == NULL) - return SC_ERROR_INVALID_ARGUMENTS; - - /* Note: len < 0 can not happen as it is size_t */ - if(len <= 127) - { - if(p_len < 1) - return SC_ERROR_BUFFER_TOO_SMALL; - *p++ = len & 0x7F; - return 1; - } - else if(len <= 255) - { - if(p_len < 2) - return SC_ERROR_BUFFER_TOO_SMALL; - *p++ = 0x81; - *p++ = len & 0xFF; - return 2; - } - else if(len <= 65535) - { - if(p_len < 3) - return SC_ERROR_BUFFER_TOO_SMALL; - *p++ = 0x82; - *p++ = (len >> 8) & 0xFF; /* MSB */ - *p++ = len & 0xFF; /* LSB */ - return 3; - } - else - { - return SC_ERROR_INVALID_ARGUMENTS; - } -} - -/* - * @brief Add a TLV-entry to a buffer. - * - * @param[out] buf The buffer at where the TLV entry should be placed. - * @param[in] buf_len The length of buf. - * @param[in] tag The one byte tag of the TLV entry. - * @param[in] tag_data The value field of the TLV entry. - * @param[in] tag_data_len The length of the tag_data. - * - */ -static int -tlv_add_tlv(u8 *buf, const size_t buf_len, const u8 tag, - const u8 *tag_data, const size_t tag_data_len) -{ - size_t l_len; /* Length of the length field itself. */ - int r; - - if(buf == NULL || tag_data == NULL) - return SC_ERROR_INVALID_ARGUMENTS; - - if(tag_data_len <= 127) - l_len = 1; - else if(tag_data_len <= 255) - l_len = 2; - else if(tag_data_len <= 65535) - l_len = 3; - else - return SC_ERROR_INVALID_ARGUMENTS; - - if(1 + l_len + tag_data_len > buf_len) - return SC_ERROR_BUFFER_TOO_SMALL; - - *buf++ = tag; - r = tlv_insert_len(buf, buf_len-1, tag_data_len); - if(r < 0) - return r; - else if((unsigned int)r != l_len) - return SC_ERROR_UNKNOWN; - - buf += l_len; - - memcpy(buf, tag_data, tag_data_len); - return 1 + l_len + tag_data_len; -} - /* * @brief Use PUT DATA to import a private RSA key. * @@ -700,10 +706,9 @@ tlv_add_tlv(u8 *buf, const size_t buf_len, const u8 tag, * other errors: Transmit errors / errors returned by card. */ static int -isoApplet_put_data_prkey_rsa(sc_card_t *card, struct sc_pkcs15_prkey_rsa *rsa) +isoApplet_put_data_prkey_rsa(sc_card_t *card, sc_cardctl_isoApplet_import_key_t *args) { sc_apdu_t apdu; - const size_t sbuf_len = SC_MAX_APDU_BUFFER_SIZE; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; u8 *p = NULL; int r; @@ -712,17 +717,15 @@ isoApplet_put_data_prkey_rsa(sc_card_t *card, struct sc_pkcs15_prkey_rsa *rsa) LOG_FUNC_CALLED(card->ctx); - if(!rsa - || !rsa->p.data - || !rsa->q.data - || !rsa->iqmp.data - || !rsa->dmp1.data - || !rsa->dmq1.data) + 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, "Only CRT RSA keys may be imported."); + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "RSA key is missing information."); } - p = sbuf; /* Note: The format is according to ISO 2-byte tag 7F48 */ *p++ = 0x7F; /* T-L pair to indicate a private key data object */ @@ -730,31 +733,34 @@ isoApplet_put_data_prkey_rsa(sc_card_t *card, struct sc_pkcs15_prkey_rsa *rsa) /* Calculate the length of all inner tag-length-value entries. * One entry consists of: tag (1 byte) + length (1 byte if < 128, 2 if >= 128) + value (len) * It may actually happen that a parameter is 127 byte (leading zero) */ - tags_len = 1 + (rsa->p.len < 128 ? 1 : 2) + rsa->p.len + - 1 + (rsa->q.len < 128 ? 1 : 2) + rsa->q.len + - 1 + (rsa->iqmp.len < 128 ? 1 : 2) + rsa->iqmp.len + - 1 + (rsa->dmp1.len < 128 ? 1 : 2) + rsa->dmp1.len + - 1 + (rsa->dmq1.len < 128 ? 1 : 2) + rsa->dmq1.len; - r = tlv_insert_len(p, sbuf_len - (p - sbuf), tags_len); /* Private key data object length */ - LOG_TEST_RET(card->ctx, r, "Error in handling TLV."); - p += r; + tags_len = 1 + (args->privkey.rsa.p.len < 128 ? 1 : 2) + args->privkey.rsa.p.len + + 1 + (args->privkey.rsa.q.len < 128 ? 1 : 2) + args->privkey.rsa.q.len + + 1 + (args->privkey.rsa.iqmp.len < 128 ? 1 : 2) + args->privkey.rsa.iqmp.len + + 1 + (args->privkey.rsa.dmp1.len < 128 ? 1 : 2) + args->privkey.rsa.dmp1.len + + 1 + (args->privkey.rsa.dmq1.len < 128 ? 1 : 2) + args->privkey.rsa.dmq1.len; + *p++ = 0x82; + *p++ = (tags_len >> 8) & 0xFF; /* MSB */ + *p++ = tags_len & 0xFF; /* LSB */ /* p */ - r = tlv_add_tlv(p, sbuf_len - (p - sbuf), 0x92, rsa->p.data, rsa->p.len); - LOG_TEST_RET(card->ctx, r, "Error in handling TLV."); - p += r; - - r = sc_lock(card); /* We will use several apdus */ - LOG_TEST_RET(card->ctx, r, "sc_lock() failed"); - locked = 1; - + r = sc_asn1_put_tag(0x92, args->privkey.rsa.p.value, args->privkey.rsa.p.len, p, sizeof(sbuf) - (p - sbuf), &p); + LOG_TEST_RET(card->ctx, r, "Error handling TLV."); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xDB, 0x3F, 0xFF); apdu.cla |= 0x10; /* Chaining */ apdu.data = sbuf; apdu.datalen = p - sbuf; apdu.lc = p - sbuf; + + r = sc_lock(card); /* We will use several apdus */ + LOG_TEST_RET(card->ctx, r, "sc_lock() failed"); + locked = 1; + r = sc_transmit_apdu(card, &apdu); - LOG_TEST_RET(card->ctx, r, "%s: APDU transmit failed"); + if(r < 0) + { + sc_log(card->ctx, "%s: APDU transmit failed", strerror(r)); + goto out; + } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if(apdu.sw1 == 0x6D && apdu.sw2 == 0x00) { @@ -763,24 +769,21 @@ isoApplet_put_data_prkey_rsa(sc_card_t *card, struct sc_pkcs15_prkey_rsa *rsa) } if(r < 0) { - sc_log(card->ctx, "Card returned error"); + sc_log(card->ctx, "%s: Card returned error", strerror(r)); goto out; } /* q */ p = sbuf; - r = tlv_add_tlv(p, sbuf_len - (p - sbuf), 0x93, rsa->q.data, rsa->q.len); + 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) { sc_log(card->ctx, "%s: Error in handling TLV.", strerror(r)); goto out; } - p += r; - apdu.data = sbuf; apdu.datalen = p - sbuf; apdu.lc = p - sbuf; - r = sc_check_apdu(card, &apdu); r = sc_transmit_apdu(card, &apdu); if(r < 0) { @@ -796,14 +799,12 @@ isoApplet_put_data_prkey_rsa(sc_card_t *card, struct sc_pkcs15_prkey_rsa *rsa) /* 1/q mod p */ p = sbuf; - r = tlv_add_tlv(p, sbuf_len - (p - sbuf), 0x94, rsa->iqmp.data, rsa->iqmp.len); + 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) { sc_log(card->ctx, "%s: Error in handling TLV.", strerror(r)); goto out; } - p += r; - apdu.data = sbuf; apdu.datalen = p - sbuf; apdu.lc = p - sbuf; @@ -822,14 +823,12 @@ isoApplet_put_data_prkey_rsa(sc_card_t *card, struct sc_pkcs15_prkey_rsa *rsa) /* d mod (p-1) */ p = sbuf; - r = tlv_add_tlv(p, sbuf_len - (p - sbuf), 0x95, rsa->dmp1.data, rsa->dmp1.len); + 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) { sc_log(card->ctx, "%s: Error in handling TLV.", strerror(r)); goto out; } - p += r; - apdu.data = sbuf; apdu.datalen = p - sbuf; apdu.lc = p - sbuf; @@ -848,14 +847,12 @@ isoApplet_put_data_prkey_rsa(sc_card_t *card, struct sc_pkcs15_prkey_rsa *rsa) /* d mod (q-1) */ p = sbuf; - r = tlv_add_tlv(p, sbuf_len - (p - sbuf), 0x96, rsa->dmq1.data, rsa->dmq1.len); + 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) { sc_log(card->ctx, "%s: Error in handling TLV.", strerror(r)); goto out; } - p += r; - apdu.cla = 0x00; /* Last part of the chain. */ apdu.data = sbuf; apdu.datalen = p - sbuf; @@ -882,39 +879,77 @@ out: /* * @brief Use PUT DATA to import a private EC key. * - * I use a simpler format for EC keys (compared to RSA) - * because the card has all the necessary information except the ecPointQ. - * Only the ecPointQ is sent to the card. It is BER-TLV-encoded. The tag is: - * 0xC1 - Private class, primitive encoding, number one. + * 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: The RSA key does not contain CRT fields. + * @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, struct sc_pkcs15_prkey_ec *ec) +isoApplet_put_data_prkey_ec(sc_card_t *card, sc_cardctl_isoApplet_import_key_t *args) { sc_apdu_t apdu; - size_t sbuf_len = SC_MAX_EXT_APDU_BUFFER_SIZE; u8 sbuf[SC_MAX_EXT_APDU_BUFFER_SIZE]; int r; + u8 *p; + size_t tags_len; LOG_FUNC_CALLED(card->ctx); - if(!ec) + 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, "No EC private key."); + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "Missing information about EC private key."); } - r = tlv_add_tlv(sbuf, sbuf_len, 0xC1, ec->privateD.data, ec->privateD.len); + p = sbuf; + *p++ = 0xE0; + tags_len = 1 + (args->privkey.ec.params.prime.len < 128 ? 1 : 2) + args->privkey.ec.params.prime.len + + 1 + (args->privkey.ec.params.coefficientA.len < 128 ? 1 : 2) + args->privkey.ec.params.coefficientA.len + + 1 + (args->privkey.ec.params.coefficientB.len < 128 ? 1 : 2) + args->privkey.ec.params.coefficientB.len + + 1 + (args->privkey.ec.params.basePointG.len < 128 ? 1 : 2) + args->privkey.ec.params.basePointG.len + + 1 + (args->privkey.ec.params.order.len < 128 ? 1 : 2) + args->privkey.ec.params.order.len + + 1 + (args->privkey.ec.params.coFactor.len < 128 ? 1 : 2) + args->privkey.ec.params.coFactor.len + + 1 + (args->privkey.ec.privateD.len < 128 ? 1 : 2) + args->privkey.ec.privateD.len; + if(tags_len < 127) + *p++ = (tags_len & 0xFF); + else if (tags_len < 255) + { + *p++ = 0x81; + *p++ = (tags_len & 0xFF); + } + else + { + *p++ = 0x82; + *p++ = (tags_len >> 8) & 0xFF; /* MSB */ + *p++ = tags_len & 0xFF; /* LSB */ + } + r = isoApplet_put_ec_params(card, &args->privkey.ec.params, p, sizeof(sbuf) - (p - sbuf), &p); + LOG_TEST_RET(card->ctx, r, "Error composing EC params."); + r = sc_asn1_put_tag(0x88, args->privkey.ec.privateD.value, args->privkey.ec.privateD.len, p, sizeof(sbuf) - (p - sbuf), &p); LOG_TEST_RET(card->ctx, r, "Error in handling TLV."); /* Send to card. */ - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xDB, 0x3F, 0xFF); - apdu.lc = r; - apdu.datalen = r; + 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); LOG_TEST_RET(card->ctx, r, "%s: APDU transmit failed"); @@ -1000,13 +1035,12 @@ isoApplet_ctl_import_key(sc_card_t *card, sc_cardctl_isoApplet_import_key_t *arg { case SC_ISOAPPLET_ALG_REF_RSA_GEN_2048: - r = isoApplet_put_data_prkey_rsa(card, &args->prkey->u.rsa); + 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_BRAINPOOLP192R1: - case SC_ISOAPPLET_ALG_REF_EC_GEN_PRIME256V1: - r = isoApplet_put_data_prkey_ec(card, &args->prkey->u.ec); + 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; @@ -1173,13 +1207,15 @@ err: static int isoApplet_compute_signature(struct sc_card *card, - const u8 * data, size_t datalen, - u8 * out, size_t outlen) + const u8 *data, size_t datalen, + u8 *out, size_t outlen) { struct isoApplet_drv_data *drvdata = DRVDATA(card); + const u8 * p; int r; - size_t xlen, ylen; - size_t i, offset; + size_t len; + size_t xlen; + size_t ylen; LOG_FUNC_CALLED(card->ctx); @@ -1190,55 +1226,52 @@ isoApplet_compute_signature(struct sc_card *card, } /* If we used ECDSA for the signature op, OpenSC thinks it has to - * convert it to {sequence, sequence} which is already done by the + * convert it to {sequence{integer,integer}} which is already done by the * card actually. - * To fix this, I strip the {sequence, sequence} structual information - * so that pkcs11-tool.c can add it again... */ + * 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) { - /* Outer SEQUENCE tag and first INTEGER tag. */ - offset=0; - if(r < 2 - || out[offset++] != 0x30 - || out[offset++] != r-2 - || out[offset++] != 0x02) + len = r; + p = out; + if(*p++ != (SC_ASN1_TAG_SEQUENCE|SC_ASN1_TAG_CONSTRUCTED)) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); } + len = *p++; + if(len > outlen - (p - out)) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL); /* X */ - xlen = out[offset++]; - assert(xlen+4 < outlen); - /* Remove the leading 0 of the coordinate, if present. */ - if(out[offset] == 0x00) + if(*p++ != SC_ASN1_TAG_INTEGER) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); + xlen = *p++; + if(xlen > outlen - (p - out)) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL); + /* Java Cards might return a leading zero, which needs to be stripped. */ + if(*p == 0x00) { - offset++; + p++; xlen--; } - for(i=0; i < xlen; i++) - { - out[i] = out[i+offset]; - } + memmove(out, p, xlen); + p += xlen; /* Y */ - assert(i+offset+3 < outlen); - if(out[i+offset++] != 0x02) - { + if(*p++ != SC_ASN1_TAG_INTEGER) LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); - } - ylen = out[i+offset++]; - /* Remove the leading 0 of the coordinate, if present. */ - if(out[i+offset] == 0x00) + ylen = *p++; + if(ylen > outlen - (p - out)) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL); + /* Java Cards might return a leading zero, which needs to be stripped. */ + if(*p == 0x00) { - offset++; + p++; ylen--; } - assert(offset+xlen+ylen <= outlen); - for(; i < xlen+ylen; i++) - { - out[i] = out[i+offset]; - } - r = xlen+ylen; + memmove(out+xlen, p, ylen); + + r = xlen + ylen; } LOG_FUNC_RETURN(card->ctx, r); } diff --git a/src/libopensc/cardctl.h b/src/libopensc/cardctl.h index 98a34f0f..edfac57d 100644 --- a/src/libopensc/cardctl.h +++ b/src/libopensc/cardctl.h @@ -969,25 +969,54 @@ typedef struct sc_cardctl_sc_hsm_wrapped_key { */ #define SC_ISOAPPLET_ALG_REF_RSA_GEN_2048 0xF3 -#define SC_ISOAPPLET_ALG_REF_EC_GEN_BRAINPOOLP192R1 0xE0 -#define SC_ISOAPPLET_ALG_REF_EC_GEN_PRIME256V1 0xE1 +#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 char *exponent; /* RSA public key exponent */ - unsigned int exponent_len; unsigned int priv_key_ref; /* Private key refernce sent to card */ - unsigned char *pubkey; /* RSA public key modulus (or EC tlv-encoded public key) */ - unsigned int pubkey_len; + 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 */ + u8 algorithm_ref; /* Algorithm reference sent to card */ unsigned int priv_key_ref; /* Private key refernce sent to card */ - struct sc_pkcs15_prkey *prkey; + 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/pkcs15init/pkcs15-isoApplet.c b/src/pkcs15init/pkcs15-isoApplet.c index 41410c50..971f9847 100644 --- a/src/pkcs15init/pkcs15-isoApplet.c +++ b/src/pkcs15init/pkcs15-isoApplet.c @@ -37,10 +37,10 @@ #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; + 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; @@ -49,29 +49,87 @@ struct ec_curve const struct sc_lv_data coFactor; }; -static struct ec_curve curves[] = +/* OpenSC only works with named curves, but we need the + * explicit parameters for ECC key generation or import. */ +static const struct ec_curve curves[] = { - { - { (unsigned char *) "\x2B\x24\x03\x03\x02\x08\x01\x01\x03", 9}, /* brainpoolP192r1 */ + /* 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 *) "\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", 48}, + { (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} }, { - { (unsigned char *) "\x2A\x86\x48\xCE\x3D\x03\x01\x07", 8}, /* secp256r1 aka prime256v1 */ + /* 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", 1} + }, + + { + /* 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 *) "\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", 64}, + { (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}, @@ -117,7 +175,8 @@ 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, current; + int preferred; + int current; LOG_FUNC_CALLED(card->ctx); @@ -208,155 +267,37 @@ isoApplet_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t } /* - * @brief Get the OID of the curve specified by a curve name. + * @brief Get the curve parameters associated with the curve specified by an OID. * - * @param[in] named_curve The name of the curve to search the OID of. - * Supported values are: brainpoolP192r1, prime256v1. - * @param[out] oid The OID of the curve. + * @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_oid(const char* named_curve, const struct sc_lv_data **oid) +isoApplet_get_curve(u8 *oid, size_t oid_len, const struct ec_curve **curve_out) { - if(!named_curve) + int i; + + if(!oid) return SC_ERROR_INVALID_ARGUMENTS; - if(strncmp(named_curve, "brainpoolP192r1", 15) == 0) + /* Search the curve parameters. */ + for (i = 0; curves[i].oid.value; i++) { - *oid = &curves[0].oid; - return SC_SUCCESS; - } - else if(strncmp(named_curve, "prime256v1", 10) == 0) - { - *oid = &curves[1].oid; - return SC_SUCCESS; + 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 Check the consistency of TLV-encoded EC curve parameters. - * - * Check the EC params in buf (length: len) that are structured according - * to ISO 7816-8 table 3 - Public key data objects. - * The params are compared with the ones given in the curve struct. - * - * @param[in] ctx - * @param[in] buf The buffer containing the TLV-encoded (ISO 7816-8 table 3) - * EC parameters. - * @param[in] len The length of buf. - * @param[in] curve An ec_curve struct that should be used to check the - * parameters in buf. - * - * @return SC_SUCCESS: If the EC parameters are consistent. - * SC_ERROR_INCOMPATIBLE_KEY: If the curve is unknown or the EC - * parameters are not consistent. - */ -static int -checkEcParams(sc_context_t* ctx, const u8* buf, size_t len, const struct ec_curve curve) -{ - const u8 *curr_pos = NULL; - size_t tag_len; - int r = SC_SUCCESS; - - LOG_FUNC_CALLED(ctx); - - /* Check the field. */ - curr_pos = sc_asn1_find_tag(ctx, buf, len, (unsigned int) 0x81, &tag_len); - if(curr_pos == NULL || tag_len != curve.prime.len) - { - r = SC_ERROR_INCOMPATIBLE_KEY; - LOG_TEST_RET(ctx, r, - "Could not find any EC field tag in the response template or the length was unexpected."); - } - if(memcmp(curr_pos, curve.prime.value, curve.prime.len) != 0) - { - r = SC_ERROR_INCOMPATIBLE_KEY; - LOG_TEST_RET(ctx, r, - "The EC field by the smartcard was unexpected."); - } - - /* Check the coefficient A. */ - curr_pos = sc_asn1_find_tag(ctx, buf, len, (unsigned int) 0x82, &tag_len); - if(curr_pos == NULL || tag_len != curve.coefficientA.len) - { - r = SC_ERROR_INCOMPATIBLE_KEY; - LOG_TEST_RET(ctx, r, - "Could not find any EC coefficient A tag in the response template or the length was unexpected."); - } - if(memcmp(curr_pos, curve.coefficientA.value, curve.coefficientA.len) != 0) - { - r = SC_ERROR_INCOMPATIBLE_KEY; - LOG_TEST_RET(ctx, r, - "The EC coefficient A returned by the smartcard was unexpected."); - } - - /* Check the coefficient B. */ - curr_pos = sc_asn1_find_tag(ctx, buf, len, (unsigned int) 0x83, &tag_len); - if(curr_pos == NULL || tag_len != curve.coefficientB.len) - { - r = SC_ERROR_INCOMPATIBLE_KEY; - LOG_TEST_RET(ctx, r, - "Could not find any EC coefficient B tag in the response template or the length was unexpected."); - } - if(memcmp(curr_pos, curve.coefficientB.value, curve.coefficientB.len) != 0) - { - r = SC_ERROR_INCOMPATIBLE_KEY; - LOG_TEST_RET(ctx, r, - "The EC coefficient B returned by the smartcard was unexpected."); - } - - /* Check the basepoint G. - * Note: The IsoApplet omits the 0x04 (uncompressed) tag. */ - curr_pos = sc_asn1_find_tag(ctx, buf, len, (unsigned int) 0x84, &tag_len); - if(curr_pos == NULL || tag_len != curve.basePointG.len) - { - r = SC_ERROR_INCOMPATIBLE_KEY; - LOG_TEST_RET(ctx, r, - "Could not find any EC basepoint G tag in the response template or the length was unexpected."); - } - if(memcmp(curr_pos, curve.basePointG.value, curve.basePointG.len) != 0) - { - r = SC_ERROR_INCOMPATIBLE_KEY; - LOG_TEST_RET(ctx, r, - "The EC basepoint G returned by the smartcard was unexpected."); - } - - /* Check the order. */ - curr_pos = sc_asn1_find_tag(ctx, buf, len, (unsigned int) 0x85, &tag_len); - if(curr_pos == NULL || tag_len != curve.order.len) - { - r = SC_ERROR_INCOMPATIBLE_KEY; - LOG_TEST_RET(ctx, r, - "Could not find any EC order tag in the response template or the length was unexpected."); - } - if(memcmp(curr_pos, curve.order.value, curve.order.len) != 0) - { - r = SC_ERROR_INCOMPATIBLE_KEY; - LOG_TEST_RET(ctx, r, - "The EC order returned by the smartcard was unexpected."); - } - - /* Check the coFactor. */ - curr_pos = sc_asn1_find_tag(ctx, buf, len, (unsigned int) 0x87, &tag_len); - if(curr_pos == NULL || tag_len != curve.coFactor.len) - { - r = SC_ERROR_INCOMPATIBLE_KEY; - LOG_TEST_RET(ctx, r, - "Could not find any EC cofactor tag in the response template or the length was unexpected."); - } - if(memcmp(curr_pos, curve.coFactor.value, curve.coFactor.len) != 0) - { - r = SC_ERROR_INCOMPATIBLE_KEY; - LOG_TEST_RET(ctx, r, - "The EC cofactor returned by the smartcards was unexpected."); - } - - LOG_FUNC_RETURN(ctx, r); -} /* * @brief Generate a RSA private key on the card. @@ -373,8 +314,8 @@ checkEcParams(sc_context_t* ctx, const u8* buf, size_t len, const struct ec_curv * SC_ERROR_OUT_OF_MEMORY */ static int -generate_key_rsa(sc_pkcs15_prkey_info_t *key_info, sc_card_t *card, - sc_pkcs15_pubkey_t *pubkey) +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; @@ -392,23 +333,23 @@ generate_key_rsa(sc_pkcs15_prkey_info_t *key_info, sc_card_t *card, } /* Generate the key. - * Note: keysize is not explicitly passed to the card. It assumes 2048 along with the algorithm reference. */ + * 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_len = keybits / 8; - args.pubkey = malloc(args.pubkey_len); - if (!args.pubkey) + 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.exponent_len = 3; - args.exponent = malloc(args.exponent_len); - if(!args.exponent) + 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)); @@ -422,24 +363,24 @@ generate_key_rsa(sc_pkcs15_prkey_info_t *key_info, sc_card_t *card, goto err; } - /* extract public key */ + /* extract the public key */ pubkey->algorithm = SC_ALGORITHM_RSA; - pubkey->u.rsa.modulus.len = args.pubkey_len; - pubkey->u.rsa.modulus.data = args.pubkey; - pubkey->u.rsa.exponent.len = args.exponent_len; - pubkey->u.rsa.exponent.data = args.exponent; + 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) + if (args.pubkey.rsa.modulus.value) { - free(args.pubkey); + free(args.pubkey.rsa.modulus.value); pubkey->u.rsa.modulus.data = NULL; pubkey->u.rsa.modulus.len = 0; } - if (args.exponent) + if (args.pubkey.rsa.exponent.value) { - free(args.exponent); + free(args.pubkey.rsa.exponent.value); pubkey->u.rsa.exponent.data = NULL; pubkey->u.rsa.exponent.len = 0; } @@ -452,10 +393,10 @@ err: * 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. + * @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 @@ -464,199 +405,127 @@ err: * handled. */ static int -generate_key_ec(const sc_pkcs15_prkey_info_t *key_info, sc_card_t *card, - sc_pkcs15_pubkey_t *pubkey) +isoApplet_generate_key_ec(const sc_pkcs15_prkey_info_t *key_info, sc_card_t *card, + sc_pkcs15_pubkey_t *pubkey) { - int r; - u8* p = NULL; - u8* ecPubKeyPoint = NULL; - size_t tag_len; - size_t all_tags_len; - const u8* curr_pos = NULL; - struct sc_ec_params* ecp = NULL; - const struct sc_lv_data* oid = NULL; - sc_cardctl_isoApplet_genkey_t args; - const struct sc_pkcs15_ec_parameters* info_ecp = + int r; + const struct ec_curve *curve = NULL; + struct sc_ec_params *alg_id_params = NULL; + sc_cardctl_isoApplet_genkey_t args; + const struct sc_pkcs15_ec_parameters *info_ecp = (struct sc_pkcs15_ec_parameters *) key_info->params.data; LOG_FUNC_CALLED(card->ctx); /* Check key size: */ - if(key_info->field_length != 192 - && key_info->field_length != 256) + if(key_info->field_length == 0) { - sc_log(card->ctx, "EC field length is unsupported, length provided was: %d.", key_info->field_length); + sc_log(card->ctx, "Unknown field length."); r = SC_ERROR_INVALID_ARGUMENTS; - goto err; + goto out; } - if(info_ecp->named_curve && strncmp(info_ecp->named_curve, "brainpoolP192r1", 15) != 0 - && strncmp(info_ecp->named_curve, "prime256v1", 10) != 0) + 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); - r = SC_ERROR_INVALID_ARGUMENTS; - goto err; + goto out; } /* Generate the key. - * Note: THe field size is not explicitly passed to the card. - * It assumes it along with the algorithm reference. */ + * 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_len = 512; - args.pubkey = malloc(args.pubkey_len); - if(!args.pubkey) + 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; - sc_log(card->ctx, "%s: Unable to allocate public key buffer.", sc_strerror(r)); - goto err; + goto out; } - if(strncmp(info_ecp->named_curve, "brainpoolP192r1", 15) == 0) - { - args.algorithm_ref = SC_ISOAPPLET_ALG_REF_EC_GEN_BRAINPOOLP192R1; - } - else if(strncmp(info_ecp->named_curve, "prime256v1", 10) == 0) - { - args.algorithm_ref = SC_ISOAPPLET_ALG_REF_EC_GEN_PRIME256V1; - } + 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 err; + goto out; } - /* Extract the public key. */ + /* Extract and compose the public key. */ pubkey->algorithm = SC_ALGORITHM_EC; - /* Get the curves OID. */ - r = isoApplet_get_curve_oid(info_ecp->named_curve, &oid); - if(r < 0) - { - sc_log(card->ctx, "Error obtaining the curve OID.", sc_strerror(r)); - goto err; - } - /* der-encoded parameters */ - ecp = calloc(1, sizeof(struct sc_ec_params)); - if(!ecp) + alg_id_params = calloc(1, sizeof(*alg_id_params)); + if(!alg_id_params) { r = SC_ERROR_OUT_OF_MEMORY; - goto err; + goto out; } - ecp->der_len = oid->len + 2; - ecp->der = calloc(ecp->der_len, 1); - if(!ecp->der) + alg_id_params->der_len = curve->oid.len; + alg_id_params->der = malloc(alg_id_params->der_len); + if(!alg_id_params->der) { r = SC_ERROR_OUT_OF_MEMORY; - sc_log(card->ctx, "%s: Unable to allocate public key buffer.", sc_strerror(r)); - goto err; + goto out; } - ecp->der[0] = 0x06; - ecp->der[1] = (u8)oid->len; - memcpy(ecp->der + 2, oid->value, oid->len); - ecp->type = 1; /* named curve */ + memcpy(alg_id_params->der, curve->oid.value, curve->oid.len); + alg_id_params->type = 1; /* named curve */ - pubkey->alg_id = (struct sc_algorithm_id *)calloc(1, sizeof(struct sc_algorithm_id)); + pubkey->alg_id = malloc(sizeof(*pubkey->alg_id)); if(!pubkey->alg_id) { r = SC_ERROR_OUT_OF_MEMORY; - sc_log(card->ctx, "%s: Unable to allocate public key sc_algorithm_id.", sc_strerror(r)); - goto err; + goto out; } pubkey->alg_id->algorithm = SC_ALGORITHM_EC; - pubkey->alg_id->params = ecp; - - p = args.pubkey; - if(memcmp(info_ecp->named_curve, "brainpoolP192r1", 15) == 0) - { - /* The applet returns the public key encoded according to - * ISO 7816-8 table 3 - Public key data objects. This is a - * 2-byte tag. A length of 0xD0 = 208 is expected for BrainpoolP192r1. */ - if(memcmp(p, "\x7F\x49\x81\xD0", 4) != 0) - { - r = SC_ERROR_INCOMPATIBLE_KEY; - sc_log(card->ctx, "%s: Key generation error: Unexpected EC public key received length.", sc_strerror(r)); - goto err; - } - else - { - p += 4; /* p points to the value field of the outer (7F 49) tag. - * This value field is a TLV-structure again. */ - all_tags_len = 208; /* 0xD0 bytes */ - } - - /* Check EC params. */ - r = checkEcParams(card->ctx, p, all_tags_len, curves[0]); - if(r != SC_SUCCESS) - { - goto err; - } - } - else if(memcmp(info_ecp->named_curve, "prime256v1", 10) == 0) - { - /* The applet returns the public key encoded according to - * ISO 7816-8 table 3 - Public key data objects. This is a - * 2-byte tag. A length of 0x011A = 282 is expected for Prime256v1. */ - if(memcmp(p, "\x7F\x49\x82\x01\x1A", 5) != 0) - { - r = SC_ERROR_INCOMPATIBLE_KEY; - sc_log(card->ctx, "%s: Key generation error: Unexpected EC public key parameters.", sc_strerror(r)); - goto err; - } - else - { - p += 5; /* p points to the value field of the outer (7F 49) tag. - * This value field is a TLV-structure again. */ - all_tags_len = 282; /* 0x011A bytes */ - } - - /* Check EC params. */ - r = checkEcParams(card->ctx, p, all_tags_len, curves[1]); - if(r != SC_SUCCESS) - { - goto err; - } - } + pubkey->alg_id->params = alg_id_params; /* Extract ecpointQ */ - curr_pos = sc_asn1_find_tag(card->ctx, p, all_tags_len, (unsigned int) 0x86, &tag_len); - if(curr_pos == NULL || tag_len == 0) - { - r = SC_ERROR_INCOMPATIBLE_KEY; - sc_log(card->ctx, "%s: Could not find any EC pointQ tag in the response template.", sc_strerror(r)); - goto err; - } - ecPubKeyPoint = malloc(tag_len+1); - if(!ecPubKeyPoint) + 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; - sc_log(card->ctx, "%s: Unable to allocate public key ecpointQ buffer.", sc_strerror(r)); - goto err; + goto out; } - *ecPubKeyPoint = 0x04; /* uncompressed */ - memcpy(ecPubKeyPoint+1, curr_pos, tag_len); - pubkey->u.ec.ecpointQ.value = ecPubKeyPoint; - pubkey->u.ec.ecpointQ.len = tag_len+1; + memcpy(pubkey->u.ec.ecpointQ.value, args.pubkey.ec.ecPointQ.value, args.pubkey.ec.ecPointQ.len); - /* OID for the public key */ - pubkey->u.ec.params.der.value = malloc(ecp->der_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; - sc_log(card->ctx, "%s: Unable to allocate public key ec params buffer.", sc_strerror(r)); - goto err; + goto out; } - memcpy(pubkey->u.ec.params.der.value, ecp->der, ecp->der_len); - pubkey->u.ec.params.der.len = ecp->der_len; - + memcpy(pubkey->u.ec.params.der.value, alg_id_params->der, 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); - LOG_FUNC_RETURN(card->ctx, r); -err: - if(pubkey) +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) { @@ -669,28 +538,23 @@ err: 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(args.pubkey) + if(r < 0 && alg_id_params) { - free(args.pubkey); - args.pubkey = NULL; - args.pubkey_len = 0; - } - if(ecPubKeyPoint) - { - free(ecPubKeyPoint); - ecPubKeyPoint = NULL; - } - if(ecp) - { - if(ecp->der) + if(alg_id_params->der) { - free(ecp->der); - ecp->der = NULL; + free(alg_id_params->der); + alg_id_params->der = NULL; } - free(ecp); - ecp = NULL; + free(alg_id_params); + pubkey->alg_id->params = NULL; } LOG_FUNC_RETURN(card->ctx, r); } @@ -700,10 +564,10 @@ 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; + 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); @@ -725,11 +589,11 @@ isoApplet_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, switch(obj->type) { case SC_PKCS15_TYPE_PRKEY_RSA: - r = generate_key_rsa(key_info, card, pubkey); + r = isoApplet_generate_key_rsa(key_info, card, pubkey); break; case SC_PKCS15_TYPE_PRKEY_EC: - r = generate_key_ec(key_info, card, pubkey); + r = isoApplet_generate_key_ec(key_info, card, pubkey); break; default: @@ -783,11 +647,10 @@ isoApplet_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_ 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_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; - char *p = NULL; LOG_FUNC_CALLED(card->ctx); @@ -810,30 +673,58 @@ isoApplet_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_ { 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: - p = key->u.ec.params.named_curve; - if(!p) - { - LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); - } + { + const struct ec_curve *curve = NULL; - if(strncmp(p, "brainpoolP192r1", 15) == 0) + args.algorithm_ref = SC_ISOAPPLET_ALG_REF_EC_GEN; + if(!key->u.ec.params.named_curve) { - args.algorithm_ref = SC_ISOAPPLET_ALG_REF_EC_GEN_BRAINPOOLP192R1; + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "Unspecified curve / no curve name."); } - else if(strncmp(p, "prime256v1", 10) == 0) - { - args.algorithm_ref = SC_ISOAPPLET_ALG_REF_EC_GEN_PRIME256V1; - } - break; + 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; - args.prkey = key; r = sc_card_ctl(card, SC_CARDCTL_ISOAPPLET_IMPORT_KEY, &args); if (r < 0) From c463f1a7a1e4bfa653c6d34f5ee4f0a2d41883b1 Mon Sep 17 00:00:00 2001 From: Philip Wendland Date: Tue, 28 Oct 2014 11:17:45 +0100 Subject: [PATCH 04/19] IsoApplet: Do not set RSA hashes See: 189e998486a5813bca2f6737a465ad62abb318c7 The IsoApplet requires the host to do the hashes with RSA. OpenSC will add all hashes that are available in software and will not expect the card to do the hashing. --- src/libopensc/card-isoApplet.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/libopensc/card-isoApplet.c b/src/libopensc/card-isoApplet.c index ea81960f..fb1487a8 100644 --- a/src/libopensc/card-isoApplet.c +++ b/src/libopensc/card-isoApplet.c @@ -216,16 +216,8 @@ isoApplet_init(sc_card_t *card) flags = 0; /* Padding schemes: */ flags |= SC_ALGORITHM_RSA_PAD_PKCS1; - /* Hashes: */ + /* Hashes are to be done by the host for RSA */ flags |= SC_ALGORITHM_RSA_HASH_NONE; - flags |= SC_ALGORITHM_RSA_HASH_SHA1; - flags |= SC_ALGORITHM_RSA_HASH_MD5; - flags |= SC_ALGORITHM_RSA_HASH_MD5_SHA1; - flags |= SC_ALGORITHM_RSA_HASH_SHA224; - flags |= SC_ALGORITHM_RSA_HASH_SHA256; - flags |= SC_ALGORITHM_RSA_HASH_SHA384; - flags |= SC_ALGORITHM_RSA_HASH_SHA512; - flags |= SC_ALGORITHM_RSA_HASH_RIPEMD160; /* Key-generation: */ flags |= SC_ALGORITHM_ONBOARD_KEY_GEN; /* Modulus lengths: */ From 09acfd7ec00d6561a9561c243cc75470d414cd2e Mon Sep 17 00:00:00 2001 From: Philip Wendland Date: Fri, 31 Oct 2014 17:17:00 +0100 Subject: [PATCH 05/19] IsoApplet: ECDSA signatures - only strip excess zeroes It is required to strip excessive zeroes returned by some Java Cards when removeing the ASN1 structural information for PKCS#11 so that the x/y can be calculated by dividing the signature length by 2. However, the leading zero may only be stripped if it is excessive (outside the field length). Otherwise generated signatures are wrong in rare cases (1 out of 256). --- src/libopensc/card-isoApplet.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/libopensc/card-isoApplet.c b/src/libopensc/card-isoApplet.c index fb1487a8..105ec2b2 100644 --- a/src/libopensc/card-isoApplet.c +++ b/src/libopensc/card-isoApplet.c @@ -1240,8 +1240,9 @@ isoApplet_compute_signature(struct sc_card *card, xlen = *p++; if(xlen > outlen - (p - out)) LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL); - /* Java Cards might return a leading zero, which needs to be stripped. */ - if(*p == 0x00) + /* Java Cards might return a leading zero, + * which needs to be stripped when not using the ASN1 structural information. */ + if(*p == 0x00 && (xlen == 192/8+1 || xlen == 224/8+1 || xlen == 256/8+1 || xlen == 320/8+1 || xlen == 384/8+1)) { p++; xlen--; @@ -1255,8 +1256,9 @@ isoApplet_compute_signature(struct sc_card *card, ylen = *p++; if(ylen > outlen - (p - out)) LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL); - /* Java Cards might return a leading zero, which needs to be stripped. */ - if(*p == 0x00) + /* Java Cards might return a leading zero, + * which needs to be stripped when not using the ASN1 structural information. */ + if(*p == 0x00 && (ylen == 192/8+1 || ylen == 224/8+1 || ylen == 256/8+1 || ylen == 320/8+1 || ylen == 384/8+1)) { p++; ylen--; From 6cdf6c08a598779903ff5eb81850d8cf574f6ba0 Mon Sep 17 00:00:00 2001 From: Philip Wendland Date: Thu, 13 Nov 2014 13:51:10 +0100 Subject: [PATCH 06/19] IsoApplet: use AID directly when selecting applet Now that apdu.data is const (see ef94c6b875b7927bb80e49139a29a3ce2b4937b7), this can be done without discarding the const qualifier of the aid parameter. --- src/libopensc/card-isoApplet.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/libopensc/card-isoApplet.c b/src/libopensc/card-isoApplet.c index 105ec2b2..46203b2b 100644 --- a/src/libopensc/card-isoApplet.c +++ b/src/libopensc/card-isoApplet.c @@ -82,22 +82,20 @@ static struct sc_card_driver isoApplet_drv = * 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) +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; - u8 aid_wc[SC_MAX_APDU_BUFFER_SIZE]; LOG_FUNC_CALLED(card->ctx); if(aid_len > SC_MAX_APDU_BUFFER_SIZE) LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL); - memcpy(aid_wc, aid, aid_len); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xa4, 0x04, 0x00); apdu.lc = aid_len; - apdu.data = aid_wc; + apdu.data = aid; apdu.datalen = aid_len; apdu.resp = resp; apdu.resplen = *resp_len; From e791948e4230cad5d8def22f5148ceaf9248e837 Mon Sep 17 00:00:00 2001 From: Philip Wendland Date: Wed, 19 Nov 2014 14:04:40 +0100 Subject: [PATCH 07/19] IsoApplet: Simplify the private key import Use the new features of sc_asn1_put_tag introduced in OpenSC/OpenSC#314. Additionally, a RSA private key is sent from one large buffer using either extended APDUs or chaining (in compliance to IsoApplet API version 00.05). --- src/libopensc/card-isoApplet.c | 235 +++++++++++---------------------- 1 file changed, 79 insertions(+), 156 deletions(-) diff --git a/src/libopensc/card-isoApplet.c b/src/libopensc/card-isoApplet.c index 46203b2b..b83adb24 100644 --- a/src/libopensc/card-isoApplet.c +++ b/src/libopensc/card-isoApplet.c @@ -32,7 +32,7 @@ #define ISOAPPLET_ALG_REF_RSA_PAD_PKCS1 0x11 #define ISOAPPLET_API_VERSION_MAJOR 0x00 -#define ISOAPPLET_API_VERSION_MINOR 0x04 +#define ISOAPPLET_API_VERSION_MINOR 0x05 #define ISOAPPLET_API_FEATURE_EXT_APDU 0x01 #define ISOAPPLET_AID_LEN 12 @@ -480,6 +480,11 @@ isoApplet_put_ec_params(sc_card_t *card, sc_cardctl_isoApplet_ec_parameters_t *p 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); @@ -699,10 +704,9 @@ 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_APDU_BUFFER_SIZE]; + u8 sbuf[SC_MAX_EXT_APDU_BUFFER_SIZE]; u8 *p = NULL; int r; - int locked = 0; size_t tags_len; LOG_FUNC_CALLED(card->ctx); @@ -716,154 +720,65 @@ isoApplet_put_data_prkey_rsa(sc_card_t *card, sc_cardctl_isoApplet_import_key_t LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "RSA key is missing information."); } - p = sbuf; - /* Note: The format is according to ISO 2-byte tag 7F48 */ - *p++ = 0x7F; /* T-L pair to indicate a private key data object */ - *p++ = 0x48; - /* Calculate the length of all inner tag-length-value entries. - * One entry consists of: tag (1 byte) + length (1 byte if < 128, 2 if >= 128) + value (len) - * It may actually happen that a parameter is 127 byte (leading zero) */ - tags_len = 1 + (args->privkey.rsa.p.len < 128 ? 1 : 2) + args->privkey.rsa.p.len + - 1 + (args->privkey.rsa.q.len < 128 ? 1 : 2) + args->privkey.rsa.q.len + - 1 + (args->privkey.rsa.iqmp.len < 128 ? 1 : 2) + args->privkey.rsa.iqmp.len + - 1 + (args->privkey.rsa.dmp1.len < 128 ? 1 : 2) + args->privkey.rsa.dmp1.len + - 1 + (args->privkey.rsa.dmq1.len < 128 ? 1 : 2) + args->privkey.rsa.dmq1.len; - *p++ = 0x82; - *p++ = (tags_len >> 8) & 0xFF; /* MSB */ - *p++ = tags_len & 0xFF; /* LSB */ + /* 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); LOG_TEST_RET(card->ctx, r, "Error handling TLV."); - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xDB, 0x3F, 0xFF); - apdu.cla |= 0x10; /* Chaining */ - apdu.data = sbuf; - apdu.datalen = p - sbuf; - apdu.lc = p - sbuf; - - r = sc_lock(card); /* We will use several apdus */ - LOG_TEST_RET(card->ctx, r, "sc_lock() failed"); - locked = 1; - - r = sc_transmit_apdu(card, &apdu); - if(r < 0) - { - sc_log(card->ctx, "%s: APDU transmit failed", strerror(r)); - 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."); - } - if(r < 0) - { - sc_log(card->ctx, "%s: Card returned error", strerror(r)); - goto out; - } - /* q */ - p = sbuf; 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) - { - sc_log(card->ctx, "%s: Error in handling TLV.", strerror(r)); - goto out; - } - apdu.data = sbuf; - apdu.datalen = p - sbuf; - apdu.lc = p - sbuf; - r = sc_transmit_apdu(card, &apdu); - if(r < 0) - { - sc_log(card->ctx, "%s: APDU transmit failed.", strerror(r)); - goto out; - } - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - if(r < 0) - { - sc_log(card->ctx, "%s: Card returned error.", strerror(r)); - goto out; - } - + LOG_TEST_RET(card->ctx, r, "Error handling TLV."); /* 1/q mod p */ - p = sbuf; 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) - { - sc_log(card->ctx, "%s: Error in handling TLV.", strerror(r)); - goto out; - } - apdu.data = sbuf; - apdu.datalen = p - sbuf; - apdu.lc = p - sbuf; - r = sc_transmit_apdu(card, &apdu); - if(r < 0) - { - sc_log(card->ctx, "%s: APDU transmit failed.", strerror(r)); - goto out; - } - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - if(r < 0) - { - sc_log(card->ctx, "%s: Card returned error.", strerror(r)); - goto out; - } - + LOG_TEST_RET(card->ctx, r, "Error handling TLV."); /* d mod (p-1) */ - p = sbuf; 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) - { - sc_log(card->ctx, "%s: Error in handling TLV.", strerror(r)); - goto out; - } - apdu.data = sbuf; - apdu.datalen = p - sbuf; - apdu.lc = p - sbuf; - r = sc_transmit_apdu(card, &apdu); - if(r < 0) - { - sc_log(card->ctx, "%s: APDU transmit failed.", strerror(r)); - goto out; - } - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - if(r < 0) - { - sc_log(card->ctx, "%s: Card returned error.", strerror(r)); - goto out; - } - + LOG_TEST_RET(card->ctx, r, "Error handling TLV."); /* d mod (q-1) */ - p = sbuf; 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) - { - sc_log(card->ctx, "%s: Error in handling TLV.", strerror(r)); - goto out; - } - apdu.cla = 0x00; /* Last part of the chain. */ + LOG_TEST_RET(card->ctx, r, "Error handling TLV."); + + /* 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) - { - sc_log(card->ctx, "%s: APDU transmit failed.", strerror(r)); - goto out; - } + LOG_TEST_RET(card->ctx, r, "APDU transmit failed."); r = sc_check_sw(card, apdu.sw1, apdu.sw2); - if(r < 0) - { - sc_log(card->ctx, "%s: Card returned error.", strerror(r)); - goto out; - } + LOG_TEST_RET(card->ctx, r, "Card returned error."); -out: - if(locked) - sc_unlock(card); - LOG_FUNC_RETURN(card->ctx, r); + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } /* @@ -909,28 +824,36 @@ isoApplet_put_data_prkey_ec(sc_card_t *card, sc_cardctl_isoApplet_import_key_t * 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; - *p++ = 0xE0; - tags_len = 1 + (args->privkey.ec.params.prime.len < 128 ? 1 : 2) + args->privkey.ec.params.prime.len + - 1 + (args->privkey.ec.params.coefficientA.len < 128 ? 1 : 2) + args->privkey.ec.params.coefficientA.len + - 1 + (args->privkey.ec.params.coefficientB.len < 128 ? 1 : 2) + args->privkey.ec.params.coefficientB.len + - 1 + (args->privkey.ec.params.basePointG.len < 128 ? 1 : 2) + args->privkey.ec.params.basePointG.len + - 1 + (args->privkey.ec.params.order.len < 128 ? 1 : 2) + args->privkey.ec.params.order.len + - 1 + (args->privkey.ec.params.coFactor.len < 128 ? 1 : 2) + args->privkey.ec.params.coFactor.len + - 1 + (args->privkey.ec.privateD.len < 128 ? 1 : 2) + args->privkey.ec.privateD.len; - if(tags_len < 127) - *p++ = (tags_len & 0xFF); - else if (tags_len < 255) - { - *p++ = 0x81; - *p++ = (tags_len & 0xFF); - } - else - { - *p++ = 0x82; - *p++ = (tags_len >> 8) & 0xFF; /* MSB */ - *p++ = tags_len & 0xFF; /* LSB */ - } + 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); LOG_TEST_RET(card->ctx, r, "Error composing EC params."); r = sc_asn1_put_tag(0x88, args->privkey.ec.privateD.value, args->privkey.ec.privateD.len, p, sizeof(sbuf) - (p - sbuf), &p); @@ -1238,7 +1161,7 @@ isoApplet_compute_signature(struct sc_card *card, xlen = *p++; if(xlen > outlen - (p - out)) LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL); - /* Java Cards might return a leading zero, + /* Java Cards might return a leading zero, * which needs to be stripped when not using the ASN1 structural information. */ if(*p == 0x00 && (xlen == 192/8+1 || xlen == 224/8+1 || xlen == 256/8+1 || xlen == 320/8+1 || xlen == 384/8+1)) { @@ -1254,7 +1177,7 @@ isoApplet_compute_signature(struct sc_card *card, ylen = *p++; if(ylen > outlen - (p - out)) LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL); - /* Java Cards might return a leading zero, + /* Java Cards might return a leading zero, * which needs to be stripped when not using the ASN1 structural information. */ if(*p == 0x00 && (ylen == 192/8+1 || ylen == 224/8+1 || ylen == 256/8+1 || ylen == 320/8+1 || ylen == 384/8+1)) { From 73b391731b49ad37625ef52ca60f84f56112963a Mon Sep 17 00:00:00 2001 From: Philip Wendland Date: Wed, 19 Nov 2014 21:54:10 +0100 Subject: [PATCH 08/19] IsoApplet: Don't set ECC field length of 512 512 is wrong for EC FP (correct would be 521 bit), and neither of those two are currently supported by OpenSC. --- src/libopensc/card-isoApplet.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libopensc/card-isoApplet.c b/src/libopensc/card-isoApplet.c index b83adb24..de8256cf 100644 --- a/src/libopensc/card-isoApplet.c +++ b/src/libopensc/card-isoApplet.c @@ -208,7 +208,6 @@ isoApplet_init(sc_card_t *card) _sc_card_add_ec_alg(card, 256, flags, ext_flags); _sc_card_add_ec_alg(card, 320, flags, ext_flags); _sc_card_add_ec_alg(card, 384, flags, ext_flags); - _sc_card_add_ec_alg(card, 512, flags, ext_flags); /* RSA */ flags = 0; From 0473decae4dbc3b463028c59015a6086f9e3fa00 Mon Sep 17 00:00:00 2001 From: Philip Wendland Date: Fri, 28 Nov 2014 18:06:13 +0100 Subject: [PATCH 09/19] IsoApplet: clear memory after prkey import. Private key import via plain APDUs is dangerous and not recommended anyway, but clearing the apdu buffer does not hurt anyone. --- src/libopensc/card-isoApplet.c | 51 +++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/src/libopensc/card-isoApplet.c b/src/libopensc/card-isoApplet.c index de8256cf..3fe763a3 100644 --- a/src/libopensc/card-isoApplet.c +++ b/src/libopensc/card-isoApplet.c @@ -748,19 +748,24 @@ isoApplet_put_data_prkey_rsa(sc_card_t *card, sc_cardctl_isoApplet_import_key_t /* 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); - LOG_TEST_RET(card->ctx, r, "Error handling TLV."); + 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); - LOG_TEST_RET(card->ctx, r, "Error handling TLV."); + 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); - LOG_TEST_RET(card->ctx, r, "Error handling TLV."); + 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); - LOG_TEST_RET(card->ctx, r, "Error handling TLV."); + 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); - LOG_TEST_RET(card->ctx, r, "Error handling TLV."); + 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); @@ -773,11 +778,16 @@ isoApplet_put_data_prkey_rsa(sc_card_t *card, sc_cardctl_isoApplet_import_key_t apdu.flags |= SC_APDU_FLAGS_CHAINING; } r = sc_transmit_apdu(card, &apdu); - LOG_TEST_RET(card->ctx, r, "APDU transmit failed."); + if(r < 0) + goto out; r = sc_check_sw(card, apdu.sw1, apdu.sw2); - LOG_TEST_RET(card->ctx, r, "Card returned error."); + if(r < 0) + goto out; - LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); + r = SC_SUCCESS; +out: + sc_mem_clear(sbuf, sizeof(sbuf)); + LOG_FUNC_RETURN(card->ctx, r); } /* @@ -854,9 +864,14 @@ isoApplet_put_data_prkey_ec(sc_card_t *card, sc_cardctl_isoApplet_import_key_t * /* Write inner tags. */ r = isoApplet_put_ec_params(card, &args->privkey.ec.params, p, sizeof(sbuf) - (p - sbuf), &p); - LOG_TEST_RET(card->ctx, r, "Error composing EC params."); + 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); - LOG_TEST_RET(card->ctx, r, "Error in handling TLV."); + if(r < 0) + goto out; /* Send to card. */ sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xDB, 0x3F, 0xFF); @@ -864,7 +879,11 @@ isoApplet_put_data_prkey_ec(sc_card_t *card, sc_cardctl_isoApplet_import_key_t * apdu.datalen = p - sbuf; apdu.data = sbuf; r = sc_transmit_apdu(card, &apdu); - LOG_TEST_RET(card->ctx, r, "%s: APDU transmit failed"); + 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) @@ -872,10 +891,16 @@ isoApplet_put_data_prkey_ec(sc_card_t *card, sc_cardctl_isoApplet_import_key_t * 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."); } - LOG_TEST_RET(card->ctx, r, "Card returned error"); + 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); - } /* From 43fa99c0f2943fc86a241501919bba3491a8c1f7 Mon Sep 17 00:00:00 2001 From: Philip Wendland Date: Fri, 28 Nov 2014 18:14:24 +0100 Subject: [PATCH 10/19] IsoApplet: Move the key gen debug info to the right places --- src/libopensc/card-isoApplet.c | 60 +++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/src/libopensc/card-isoApplet.c b/src/libopensc/card-isoApplet.c index 3fe763a3..1b500007 100644 --- a/src/libopensc/card-isoApplet.c +++ b/src/libopensc/card-isoApplet.c @@ -166,7 +166,7 @@ isoApplet_match_card(sc_card_t *card) 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)." + "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]); } @@ -545,13 +545,6 @@ isoApplet_ctl_generate_key(sc_card_t *card, sc_cardctl_isoApplet_genkey_t *args) 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"); @@ -591,6 +584,13 @@ isoApplet_ctl_generate_key(sc_card_t *card, sc_cardctl_isoApplet_genkey_t *args) 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. */ @@ -781,6 +781,20 @@ isoApplet_put_data_prkey_rsa(sc_card_t *card, sc_cardctl_isoApplet_import_key_t 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; @@ -888,9 +902,23 @@ isoApplet_put_data_prkey_ec(sc_card_t *card, sc_cardctl_isoApplet_import_key_t * 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." + 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"); @@ -950,20 +978,6 @@ isoApplet_ctl_import_key(sc_card_t *card, sc_cardctl_isoApplet_import_key_t *arg LOG_TEST_RET(card->ctx, r, "%s: APDU transmit failed"); 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."); - } LOG_TEST_RET(card->ctx, r, "Card returned error"); From 5628a06353e34c5462b132a9f288a438204d52bd Mon Sep 17 00:00:00 2001 From: Philip Wendland Date: Fri, 28 Nov 2014 19:06:21 +0100 Subject: [PATCH 11/19] IsoApplet: Align comments with spaces instead of tabs Better view with tabstop=8. --- src/libopensc/card-isoApplet.c | 130 +++++++++++++++--------------- src/pkcs15init/pkcs15-isoApplet.c | 39 ++++----- 2 files changed, 85 insertions(+), 84 deletions(-) diff --git a/src/libopensc/card-isoApplet.c b/src/libopensc/card-isoApplet.c index 1b500007..4ed6aced 100644 --- a/src/libopensc/card-isoApplet.c +++ b/src/libopensc/card-isoApplet.c @@ -70,16 +70,16 @@ static struct sc_card_driver isoApplet_drv = * 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. + * @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. + * @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) @@ -232,8 +232,8 @@ isoApplet_init(sc_card_t *card) * * @param entry The OpenSC ACL entry. * - * @return The security condition byte. No restriction (0x00) - * if unknown operation. + * @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) @@ -267,8 +267,8 @@ isoApplet_acl_to_security_condition_byte(const sc_acl_entry_t *entry) * 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 + * 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) @@ -284,34 +284,34 @@ isoApplet_create_file(sc_card_t *card, sc_file_t *file) if(file->type == SC_FILE_TYPE_DF) { - const int df_idx[8] = /* These are the SC operations. */ + 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 + 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 + 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 + 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++) { @@ -341,9 +341,9 @@ isoApplet_create_file(sc_card_t *card, sc_file_t *file) * 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. + * @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) @@ -446,19 +446,19 @@ isoApplet_process_fci(sc_card_t *card, sc_file_t *file, * @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 + * 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). + * @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) @@ -551,11 +551,11 @@ isoApplet_ctl_generate_key(sc_card_t *card, sc_cardctl_isoApplet_genkey_t *args) /* 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. */ + * 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); @@ -694,10 +694,10 @@ isoApplet_ctl_generate_key(sc_card_t *card, sc_cardctl_isoApplet_genkey_t *args) * 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. + * @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. + * @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) @@ -808,21 +808,21 @@ out: * @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) + * 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. + * @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. + * @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) @@ -1088,7 +1088,7 @@ isoApplet_set_security_env(sc_card_t *card, LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "Unsupported algorithm."); } - *p++ = 0x80; /* algorithm reference */ + *p++ = 0x80; /* algorithm reference */ *p++ = 0x01; *p++ = drvdata->sec_env_alg_ref; } diff --git a/src/pkcs15init/pkcs15-isoApplet.c b/src/pkcs15init/pkcs15-isoApplet.c index 971f9847..a4e064d7 100644 --- a/src/pkcs15init/pkcs15-isoApplet.c +++ b/src/pkcs15init/pkcs15-isoApplet.c @@ -269,9 +269,9 @@ isoApplet_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t /* * @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. + * @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 @@ -305,13 +305,13 @@ isoApplet_get_curve(u8 *oid, size_t oid_len, const struct ec_curve **curve_out) * 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. + * @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 + * SC_ERROR_OUT_OF_MEMORY */ static int isoApplet_generate_key_rsa(sc_pkcs15_prkey_info_t *key_info, sc_card_t *card, @@ -333,7 +333,8 @@ isoApplet_generate_key_rsa(sc_pkcs15_prkey_info_t *key_info, sc_card_t *card, } /* Generate the key. - * Note: key size is not explicitly passed to the card. It assumes 2048 along with the algorithm reference. */ + * 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; @@ -393,16 +394,16 @@ err: * 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. + * @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. + * @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, @@ -434,7 +435,7 @@ isoApplet_generate_key_ec(const sc_pkcs15_prkey_info_t *key_info, sc_card_t *car /* 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. */ + * 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; From 44d724b012484b751bb24d40c20196ff29105242 Mon Sep 17 00:00:00 2001 From: Philip Wendland Date: Sat, 29 Nov 2014 15:43:23 +0100 Subject: [PATCH 12/19] IsoApplet: fix more (comment) alignment issues with tabstop=8 --- src/pkcs15init/pkcs15-isoApplet.c | 78 +++++++++++++++---------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/src/pkcs15init/pkcs15-isoApplet.c b/src/pkcs15init/pkcs15-isoApplet.c index a4e064d7..c70a0f17 100644 --- a/src/pkcs15init/pkcs15-isoApplet.c +++ b/src/pkcs15init/pkcs15-isoApplet.c @@ -682,16 +682,16 @@ isoApplet_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_ { 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; + 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: @@ -705,20 +705,20 @@ isoApplet_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_ } 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; + 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; @@ -739,21 +739,21 @@ isoApplet_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_ 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*/ + 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 From da05fa2a473354ab8beb6edff89c9dc84d102148 Mon Sep 17 00:00:00 2001 From: Philip Wendland Date: Tue, 3 Feb 2015 21:59:28 +0100 Subject: [PATCH 13/19] IsoApplet: try to fix EC parameters when importing private keys from file --- src/pkcs15init/pkcs15-isoApplet.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pkcs15init/pkcs15-isoApplet.c b/src/pkcs15init/pkcs15-isoApplet.c index c70a0f17..637ba976 100644 --- a/src/pkcs15init/pkcs15-isoApplet.c +++ b/src/pkcs15init/pkcs15-isoApplet.c @@ -699,9 +699,9 @@ isoApplet_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_ const struct ec_curve *curve = NULL; args.algorithm_ref = SC_ISOAPPLET_ALG_REF_EC_GEN; - if(!key->u.ec.params.named_curve) - { - LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "Unspecified curve / no curve name."); + 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"); From 3312c1e2b9967942e04f498c7a84b939ec5f8b96 Mon Sep 17 00:00:00 2001 From: Philip Wendland Date: Tue, 17 Feb 2015 23:24:42 +0100 Subject: [PATCH 14/19] IsoApplet: install isoApplet.profile on windows --- win32/OpenSC.wxs.in | 4 ++++ 1 file changed, 4 insertions(+) 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 @@ + From a9d43af4bf08d46b369bf4f59ad892005e325085 Mon Sep 17 00:00:00 2001 From: Philip Wendland Date: Thu, 19 Feb 2015 21:10:38 +0100 Subject: [PATCH 15/19] IsoApplet: use a buffer large enough when generating EC keys larger than 320 bit --- src/libopensc/card-isoApplet.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libopensc/card-isoApplet.c b/src/libopensc/card-isoApplet.c index 4ed6aced..1608035e 100644 --- a/src/libopensc/card-isoApplet.c +++ b/src/libopensc/card-isoApplet.c @@ -511,7 +511,7 @@ 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_APDU_BUFFER_SIZE]; + u8 sbuf[SC_MAX_EXT_APDU_BUFFER_SIZE]; u8 *p; const u8 *inner_tag_value; const u8 *outer_tag_value; From 59eeacb74b3971c0f510230feba01516c5e8e9d1 Mon Sep 17 00:00:00 2001 From: Philip Wendland Date: Fri, 20 Feb 2015 22:07:49 +0100 Subject: [PATCH 16/19] IsoApplet: react to removal of sc_pkcs15_ec_parameters in fa923831f8e8073f0b57ea492c9e2e93f661a373 --- src/pkcs15init/pkcs15-isoApplet.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/pkcs15init/pkcs15-isoApplet.c b/src/pkcs15init/pkcs15-isoApplet.c index 637ba976..6f3861e6 100644 --- a/src/pkcs15init/pkcs15-isoApplet.c +++ b/src/pkcs15init/pkcs15-isoApplet.c @@ -411,10 +411,10 @@ isoApplet_generate_key_ec(const sc_pkcs15_prkey_info_t *key_info, sc_card_t *car { int r; const struct ec_curve *curve = NULL; - struct sc_ec_params *alg_id_params = NULL; + struct sc_ec_parameters *alg_id_params = NULL; sc_cardctl_isoApplet_genkey_t args; - const struct sc_pkcs15_ec_parameters *info_ecp = - (struct sc_pkcs15_ec_parameters *) key_info->params.data; + const struct sc_ec_parameters *info_ecp = + (struct sc_ec_parameters *) key_info->params.data; LOG_FUNC_CALLED(card->ctx); @@ -481,14 +481,14 @@ isoApplet_generate_key_ec(const sc_pkcs15_prkey_info_t *key_info, sc_card_t *car r = SC_ERROR_OUT_OF_MEMORY; goto out; } - alg_id_params->der_len = curve->oid.len; - alg_id_params->der = malloc(alg_id_params->der_len); - if(!alg_id_params->der) + 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, curve->oid.value, curve->oid.len); + 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)); @@ -511,14 +511,14 @@ isoApplet_generate_key_ec(const sc_pkcs15_prkey_info_t *key_info, sc_card_t *car 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); + 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, alg_id_params->der_len); - pubkey->u.ec.params.der.len = alg_id_params->der_len; + 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) @@ -549,10 +549,10 @@ out: } if(r < 0 && alg_id_params) { - if(alg_id_params->der) + if(alg_id_params->der.value) { - free(alg_id_params->der); - alg_id_params->der = NULL; + free(alg_id_params->der.value); + alg_id_params->der.value = NULL; } free(alg_id_params); pubkey->alg_id->params = NULL; From 1aeebdaf1c0ad1143329eac62aaf75cf3f0fe055 Mon Sep 17 00:00:00 2001 From: Philip Wendland Date: Fri, 20 Feb 2015 22:56:23 +0100 Subject: [PATCH 17/19] IsoApplet: react to changes of _sc_card_add_ec_alg() in fa923831f8e8073f0b57ea492c9e2e93f661a373 --- src/libopensc/card-isoApplet.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libopensc/card-isoApplet.c b/src/libopensc/card-isoApplet.c index 1608035e..7ac07839 100644 --- a/src/libopensc/card-isoApplet.c +++ b/src/libopensc/card-isoApplet.c @@ -203,11 +203,11 @@ isoApplet_init(sc_card_t *card) flags |= SC_ALGORITHM_ONBOARD_KEY_GEN; ext_flags = SC_ALGORITHM_EXT_EC_NAMEDCURVE; ext_flags |= SC_ALGORITHM_EXT_EC_F_P; - _sc_card_add_ec_alg(card, 192, flags, ext_flags); - _sc_card_add_ec_alg(card, 224, flags, ext_flags); - _sc_card_add_ec_alg(card, 256, flags, ext_flags); - _sc_card_add_ec_alg(card, 320, flags, ext_flags); - _sc_card_add_ec_alg(card, 384, flags, ext_flags); + _sc_card_add_ec_alg(card, 192, flags, ext_flags, NULL); + _sc_card_add_ec_alg(card, 224, flags, ext_flags, NULL); + _sc_card_add_ec_alg(card, 256, flags, ext_flags, NULL); + _sc_card_add_ec_alg(card, 320, flags, ext_flags, NULL); + _sc_card_add_ec_alg(card, 384, flags, ext_flags, NULL); /* RSA */ flags = 0; From 85d16fbc57170bf2177e0d29d7c70993fb01d27c Mon Sep 17 00:00:00 2001 From: Philip Wendland Date: Tue, 17 Feb 2015 20:23:21 +0100 Subject: [PATCH 18/19] IsoApplet: use helper function sc_asn1_sig_value_sequence_to_rs() introduced in #381 --- src/libopensc/card-isoApplet.c | 54 +++++++--------------------------- 1 file changed, 10 insertions(+), 44 deletions(-) diff --git a/src/libopensc/card-isoApplet.c b/src/libopensc/card-isoApplet.c index 7ac07839..1e0694a0 100644 --- a/src/libopensc/card-isoApplet.c +++ b/src/libopensc/card-isoApplet.c @@ -1162,11 +1162,7 @@ isoApplet_compute_signature(struct sc_card *card, u8 *out, size_t outlen) { struct isoApplet_drv_data *drvdata = DRVDATA(card); - const u8 * p; int r; - size_t len; - size_t xlen; - size_t ylen; LOG_FUNC_CALLED(card->ctx); @@ -1183,48 +1179,18 @@ isoApplet_compute_signature(struct sc_card *card, * which will be added again later.*/ if(drvdata->sec_env_alg_ref == ISOAPPLET_ALG_REF_ECDSA) { - len = r; - p = out; - if(*p++ != (SC_ASN1_TAG_SEQUENCE|SC_ASN1_TAG_CONSTRUCTED)) - { - LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); - } - len = *p++; - if(len > outlen - (p - out)) - LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL); + u8* p = NULL; + size_t len; - /* X */ - if(*p++ != SC_ASN1_TAG_INTEGER) - LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); - xlen = *p++; - if(xlen > outlen - (p - out)) - LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL); - /* Java Cards might return a leading zero, - * which needs to be stripped when not using the ASN1 structural information. */ - if(*p == 0x00 && (xlen == 192/8+1 || xlen == 224/8+1 || xlen == 256/8+1 || xlen == 320/8+1 || xlen == 384/8+1)) - { - p++; - xlen--; + 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); } - memmove(out, p, xlen); - p += xlen; - - /* Y */ - if(*p++ != SC_ASN1_TAG_INTEGER) - LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); - ylen = *p++; - if(ylen > outlen - (p - out)) - LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL); - /* Java Cards might return a leading zero, - * which needs to be stripped when not using the ASN1 structural information. */ - if(*p == 0x00 && (ylen == 192/8+1 || ylen == 224/8+1 || ylen == 256/8+1 || ylen == 320/8+1 || ylen == 384/8+1)) - { - p++; - ylen--; - } - memmove(out+xlen, p, ylen); - - r = xlen + ylen; + memcpy(out, p, len); + r = len; + free(p); } LOG_FUNC_RETURN(card->ctx, r); } From 74aeb8c92325ab7730f32712144c79c0ffee98bb Mon Sep 17 00:00:00 2001 From: Philip Wendland Date: Sun, 22 Feb 2015 23:08:18 +0100 Subject: [PATCH 19/19] IsoApplet: register supported EC curve *per curve* --- src/libopensc/card-isoApplet.c | 49 ++++++++++++++++++++++++------- src/pkcs15init/pkcs15-isoApplet.c | 2 +- 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/src/libopensc/card-isoApplet.c b/src/libopensc/card-isoApplet.c index 1e0694a0..131ac08f 100644 --- a/src/libopensc/card-isoApplet.c +++ b/src/libopensc/card-isoApplet.c @@ -21,12 +21,13 @@ #include #include -#include "internal.h" -#include "opensc.h" -#include "cardctl.h" -#include "log.h" #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 @@ -182,9 +183,11 @@ isoApplet_match_card(sc_card_t *card) 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); @@ -197,17 +200,43 @@ isoApplet_init(sc_card_t *card) card->drv_data = drvdata; card->cla = 0x00; - /* ECDSA */ + /* 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; - _sc_card_add_ec_alg(card, 192, flags, ext_flags, NULL); - _sc_card_add_ec_alg(card, 224, flags, ext_flags, NULL); - _sc_card_add_ec_alg(card, 256, flags, ext_flags, NULL); - _sc_card_add_ec_alg(card, 320, flags, ext_flags, NULL); - _sc_card_add_ec_alg(card, 384, flags, ext_flags, NULL); + /* 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; diff --git a/src/pkcs15init/pkcs15-isoApplet.c b/src/pkcs15init/pkcs15-isoApplet.c index 6f3861e6..b4095a85 100644 --- a/src/pkcs15init/pkcs15-isoApplet.c +++ b/src/pkcs15init/pkcs15-isoApplet.c @@ -105,7 +105,7 @@ static const struct ec_curve curves[] = { (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", 1} + { (unsigned char *) "\x00\x01", 2} }, {