From 7d2171c1d25d6f19e3ce737076b28c9f168ac6d1 Mon Sep 17 00:00:00 2001 From: jey Date: Sat, 9 Mar 2002 15:11:46 +0000 Subject: [PATCH] - Improved support for MioCOS cards - Removed PKCS #15 creation from cryptoflex-tool - Added PIN pad character option to profile.c git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@282 c6295689-39f2-0310-b995-f0e70906c6a9 --- src/libopensc/card-flex.c | 58 ++- src/libopensc/card-miocos.c | 298 +++++++++++----- src/libopensc/opensc.h | 3 +- src/tools/Makefile.am | 2 +- src/tools/cryptoflex-tool.c | 177 +--------- src/tools/flex.profile | 51 +++ src/tools/miocos-rw.profile | 97 ----- src/tools/miocos.profile | 57 +++ src/tools/opensc-explorer.c | 6 +- src/tools/pkcs15-cflex.c | 684 ++++++++++++++++++++++++++++++++++++ src/tools/pkcs15-init.c | 2 + src/tools/pkcs15-init.h | 1 + src/tools/profile.c | 9 +- 13 files changed, 1071 insertions(+), 374 deletions(-) create mode 100644 src/tools/flex.profile delete mode 100644 src/tools/miocos-rw.profile create mode 100644 src/tools/miocos.profile create mode 100644 src/tools/pkcs15-cflex.c diff --git a/src/libopensc/card-flex.c b/src/libopensc/card-flex.c index 601d4f9b..30e9a94f 100644 --- a/src/libopensc/card-flex.c +++ b/src/libopensc/card-flex.c @@ -36,7 +36,7 @@ struct flex_private_data { static struct sc_card_operations flex_ops; static const struct sc_card_driver flex_drv = { "Schlumberger Multiflex/Cryptoflex", - "slb", + "flex", &flex_ops }; @@ -81,7 +81,7 @@ static int flex_init(struct sc_card *card) } static void add_acl_entry(struct sc_file *file, unsigned int op, - u8 nibble) + u8 nibble, int is_mf) { switch (nibble) { case 0: @@ -97,7 +97,10 @@ static void add_acl_entry(struct sc_file *file, unsigned int op, sc_file_add_acl_entry(file, op, SC_AC_PRO, SC_AC_KEY_REF_NONE); break; case 4: - sc_file_add_acl_entry(file, op, SC_AC_AUT, SC_AC_KEY_REF_NONE); + if (is_mf) + sc_file_add_acl_entry(file, op, SC_AC_AUT, 1); + else + sc_file_add_acl_entry(file, op, SC_AC_AUT, SC_AC_KEY_REF_NONE); break; case 6: sc_file_add_acl_entry(file, op, SC_AC_CHV, 1); @@ -129,7 +132,7 @@ static int parse_flex_sf_reply(struct sc_context *ctx, const u8 *buf, int buflen { const u8 *p = buf + 2; u8 b1, b2; - int left; + int left, is_mf = 0; if (buflen < 14) return -1; @@ -139,6 +142,8 @@ static int parse_flex_sf_reply(struct sc_context *ctx, const u8 *buf, int buflen b1 = *p++; b2 = *p++; file->id = (b1 << 8) + b2; + if (file->id == 0x3F00) + is_mf = 1; switch (*p) { case 0x01: file->type = SC_FILE_TYPE_WORKING_EF; @@ -165,18 +170,18 @@ static int parse_flex_sf_reply(struct sc_context *ctx, const u8 *buf, int buflen } p += 2; if (file->type == SC_FILE_TYPE_DF) { - add_acl_entry(file, SC_AC_OP_LIST_FILES, p[0] >> 4); - add_acl_entry(file, SC_AC_OP_DELETE, p[1] >> 4); - add_acl_entry(file, SC_AC_OP_CREATE, p[1] & 0x0F); + add_acl_entry(file, SC_AC_OP_LIST_FILES, p[0] >> 4, is_mf); + add_acl_entry(file, SC_AC_OP_DELETE, p[1] >> 4, is_mf); + add_acl_entry(file, SC_AC_OP_CREATE, p[1] & 0x0F, is_mf); } else { /* EF */ - add_acl_entry(file, SC_AC_OP_READ, p[0] >> 4); + add_acl_entry(file, SC_AC_OP_READ, p[0] >> 4, 0); switch (file->ef_structure) { case SC_FILE_EF_TRANSPARENT: - add_acl_entry(file, SC_AC_OP_UPDATE, p[0] & 0x0F); + add_acl_entry(file, SC_AC_OP_UPDATE, p[0] & 0x0F, 0); break; case SC_FILE_EF_LINEAR_FIXED: case SC_FILE_EF_LINEAR_VARIABLE: - add_acl_entry(file, SC_AC_OP_UPDATE, p[0] & 0x0F); + add_acl_entry(file, SC_AC_OP_UPDATE, p[0] & 0x0F, 0); break; case SC_FILE_EF_CYCLIC: #if 0 @@ -186,8 +191,8 @@ static int parse_flex_sf_reply(struct sc_context *ctx, const u8 *buf, int buflen break; } } - add_acl_entry(file, SC_AC_OP_REHABILITATE, p[2] >> 4); - add_acl_entry(file, SC_AC_OP_INVALIDATE, p[2] & 0x0F); + add_acl_entry(file, SC_AC_OP_REHABILITATE, p[2] >> 4, is_mf); + add_acl_entry(file, SC_AC_OP_INVALIDATE, p[2] & 0x0F, is_mf); p += 3; if (*p++) file->status = SC_FILE_STATUS_ACTIVATED; @@ -268,6 +273,27 @@ void cache_path(struct sc_card *card, const struct sc_path *path) } } +static int get_flex_ac_keys(struct sc_card *card, struct sc_file *file) +{ +#if 0 + struct sc_apdu apdu; + u8 rbuf[3]; + int r; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xC4, 0x00, 0x00); + apdu.cla = 0xF0; + apdu.le = 3; + apdu.resplen = 3; + apdu.resp = rbuf; + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + if (apdu.sw1 != 0x90 && apdu.sw2 != 0x00) + return 0; + debug(card->ctx, "AC Keys: %02X %02X %02X\n", rbuf[0], rbuf[1], rbuf[2]); +#endif + return 0; +} + static int select_file_id(struct sc_card *card, const u8 *buf, size_t buflen, u8 p1, struct sc_file **file_out) { @@ -308,6 +334,8 @@ static int select_file_id(struct sc_card *card, const u8 *buf, size_t buflen, sc_file_free(file); return r; } + r = get_flex_ac_keys(card, file); + *file_out = file; return 0; } @@ -513,13 +541,15 @@ static int encode_file_structure(struct sc_card *card, const struct sc_file *fil p[8] = p[9] = p[10] = 0; p[13] = p[14] = p[15] = 0; /* Key numbers */ for (i = 0; i < 6; i++) { + const struct sc_acl_entry *entry; if (ops[i] == -1) continue; - r = acl_to_ac_nibble(file->acl[ops[i]]); + entry = sc_file_get_acl_entry(file, ops[i]); + r = acl_to_ac_nibble(entry); SC_TEST_RET(card->ctx, r, "Invalid ACL value"); /* Do some magic to get the nibbles right */ p[8 + i/2] |= (r & 0x0F) << (((i+1) % 2) * 4); - r = acl_to_keynum_nibble(file->acl[ops[i]]); + r = acl_to_keynum_nibble(entry); p[13 + i/2] |= (r & 0x0F) << (((i+1) % 2) * 4); } p[11] = (file->status & SC_FILE_STATUS_INVALIDATED) ? 0x00 : 0x01; diff --git a/src/libopensc/card-miocos.c b/src/libopensc/card-miocos.c index 0f533111..9a4729d9 100644 --- a/src/libopensc/card-miocos.c +++ b/src/libopensc/card-miocos.c @@ -1,5 +1,5 @@ /* - * card-miocos.c: Support for MioCOS cards by Miotec + * card-miocos.c: Support for PKI cards by Miotec * * Copyright (C) 2002 Juha Yrjölä * @@ -20,13 +20,20 @@ #include "sc-internal.h" #include "sc-log.h" +#include -static const char *miocos_atrs[] = { - /* MioCOS 1.1 Test Card */ - "3B:9D:94:40:23:00:68:10:11:4D:69:6F:43:4F:53:00:90:00", - NULL +static struct sc_atr_table miocos_atrs[] = { + { "\x3B\x9D\x94\x40\x23\x00\x68\x10\x11\x4D\x69\x6F\x43\x4F" + "\x53\x00\x90\x00", 18 }, + { NULL } }; +struct miocos_priv_data { + int type; +}; + +#define DRVDATA(card) ((struct miocos_priv_data *) ((card)->drv_data)) + static struct sc_card_operations miocos_ops; static const struct sc_card_driver miocos_drv = { "MioCOS 1.1 cards", @@ -41,32 +48,35 @@ static int miocos_finish(struct sc_card *card) static int miocos_match_card(struct sc_card *card) { - int i, match = -1; + int i; - for (i = 0; miocos_atrs[i] != NULL; i++) { - u8 defatr[SC_MAX_ATR_SIZE]; - size_t len = sizeof(defatr); - const char *atrp = miocos_atrs[i]; - - if (sc_hex_to_bin(atrp, defatr, &len)) - continue; - if (len != card->atr_len) - continue; - if (memcmp(card->atr, defatr, len) != 0) - continue; - match = i; - break; - } - if (match == -1) + i = _sc_match_atr(card, miocos_atrs, NULL); + if (i < 0) return 0; - return 1; } static int miocos_init(struct sc_card *card) { - card->drv_data = NULL; + int i, id; + struct miocos_priv_data *priv = NULL; + + i = _sc_match_atr(card, miocos_atrs, &id); + if (i < 0) + return 0; + priv = malloc(sizeof(struct miocos_priv_data)); + if (priv == NULL) + return SC_ERROR_OUT_OF_MEMORY; + card->drv_data = priv; card->cla = 0x00; + if (1) { + unsigned long flags; + + flags = SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_PAD_PKCS1; + flags |= SC_ALGORITHM_RSA_HASH_NONE | SC_ALGORITHM_RSA_HASH_SHA1; + + _sc_card_add_rsa_alg(card, 1024, flags, -1); + } return 0; } @@ -98,41 +108,128 @@ static u8 acl_to_byte(const struct sc_acl_entry *e) return 0x00; } +static int encode_file_structure(struct sc_card *card, const struct sc_file *file, + u8 *buf, size_t *buflen) +{ + u8 *p = buf; + const int df_ops[8] = { + SC_AC_OP_DELETE, SC_AC_OP_CREATE, + -1, /* CREATE AC */ -1, /* UPDATE AC */ -1, -1, -1, -1 + }; + const int ef_ops[8] = { + SC_AC_OP_DELETE, -1, SC_AC_OP_READ, + SC_AC_OP_UPDATE, -1, -1, SC_AC_OP_INVALIDATE, + SC_AC_OP_REHABILITATE + }; + const int key_ops[8] = { + SC_AC_OP_DELETE, -1, -1, + SC_AC_OP_UPDATE, SC_AC_OP_CRYPTO, -1, SC_AC_OP_INVALIDATE, + SC_AC_OP_REHABILITATE + }; + const int *ops; + int i; + + *p++ = file->id >> 8; + *p++ = file->id & 0xFF; + switch (file->type) { + case SC_FILE_TYPE_DF: + *p++ = 0x20; + break; + case SC_FILE_TYPE_WORKING_EF: + switch (file->ef_structure) { + case SC_FILE_EF_TRANSPARENT: + *p++ = 0x40; + break; + case SC_FILE_EF_LINEAR_FIXED: + *p++ = 0x41; + break; + case SC_FILE_EF_CYCLIC: + *p++ = 0x43; + break; + default: + error(card->ctx, "Invalid EF structure\n"); + return SC_ERROR_INVALID_ARGUMENTS; + } + break; + case SC_FILE_TYPE_INTERNAL_EF: + *p++ = 0x44; + break; + default: + error(card->ctx, "Unknown file type\n"); + return SC_ERROR_INVALID_ARGUMENTS; + } + if (file->type == SC_FILE_TYPE_DF) { + *p++ = 0; + *p++ = 0; + } else { + *p++ = file->size >> 8; + *p++ = file->size & 0xFF; + } + switch (file->type) { + case SC_FILE_TYPE_WORKING_EF: + ops = ef_ops; + break; + case SC_FILE_TYPE_INTERNAL_EF: + ops = key_ops; + break; + case SC_FILE_TYPE_DF: + ops = df_ops; + break; + default: + return SC_ERROR_INVALID_ARGUMENTS; + } + for (i = 0; i < 8; i++) { + u8 nibble; + + if (ops[i] == -1) + nibble = 0x00; + else + nibble = acl_to_byte(sc_file_get_acl_entry(file, ops[i])); + if ((i & 1) == 0) + *p = nibble << 4; + else + *p++ = nibble & 0x0F; + } + if (file->type == SC_FILE_TYPE_WORKING_EF && + file->ef_structure != SC_FILE_EF_TRANSPARENT) + *p++ = file->record_length; + else + *p++ = 0; + if (file->status & SC_FILE_STATUS_INVALIDATED) + *p++ = 0; + else + *p++ = 0x01; + if (file->type == SC_FILE_TYPE_DF && file->namelen) { + assert(file->namelen <= 16); + memcpy(p, file->name, file->namelen); + p += file->namelen; + } + *buflen = p - buf; + + return 0; +} + static int miocos_create_file(struct sc_card *card, struct sc_file *file) { - if (file->prop_attr_len == 0) { - memcpy(file->prop_attr, "\x03\x00\x00", 3); - file->prop_attr_len = 3; - } - if (file->sec_attr_len == 0) { - int idx[6], i; - u8 buf[6]; + struct sc_apdu apdu; + u8 sbuf[32]; + size_t buflen; + int r; - if (file->type == SC_FILE_TYPE_DF) { - const int df_idx[6] = { - SC_AC_OP_SELECT, SC_AC_OP_LOCK, SC_AC_OP_DELETE, - SC_AC_OP_CREATE, SC_AC_OP_REHABILITATE, - SC_AC_OP_INVALIDATE - }; - for (i = 0; i < 6; i++) - idx[i] = df_idx[i]; - } else { - const int ef_idx[6] = { - SC_AC_OP_READ, SC_AC_OP_UPDATE, SC_AC_OP_WRITE, - SC_AC_OP_ERASE, SC_AC_OP_REHABILITATE, - SC_AC_OP_INVALIDATE - }; - for (i = 0; i < 6; i++) - idx[i] = ef_idx[i]; - } - for (i = 0; i < 6; i++) - buf[i] = acl_to_byte(file->acl[idx[i]]); + r = encode_file_structure(card, file, sbuf, &buflen); + if (r) + return r; + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x00, 0x00); + apdu.data = sbuf; + apdu.datalen = buflen; + apdu.lc = buflen; - memcpy(file->sec_attr, buf, 6); - file->sec_attr_len = 6; - } + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + SC_TEST_RET(card->ctx, r, "Card returned error"); - return iso_ops->create_file(card, file); + return 0; } static int miocos_set_security_env(struct sc_card *card, @@ -143,21 +240,21 @@ static int miocos_set_security_env(struct sc_card *card, struct sc_security_env tmp; tmp = *env; - tmp.flags &= ~SC_SEC_ENV_ALG_PRESENT; + tmp.flags &= ~SC_SEC_ENV_ALG_PRESENT; tmp.flags |= SC_SEC_ENV_ALG_REF_PRESENT; if (tmp.algorithm != SC_ALGORITHM_RSA) { error(card->ctx, "Only RSA algorithm supported.\n"); return SC_ERROR_NOT_SUPPORTED; } - tmp.algorithm_ref = 0x00; - if (tmp.algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) + tmp.algorithm_ref = 0x00; + /* potential FIXME: return an error, if an unsupported + * pad or hash was requested, although this shouldn't happen. + */ + if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) tmp.algorithm_ref = 0x02; -#if 0 if (tmp.algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) - tmp.algorithm_ref |= 0x10; -#endif - return iso_ops->set_security_env(card, &tmp, se_num); - + tmp.algorithm_ref |= 0x10; + return iso_ops->set_security_env(card, &tmp, se_num); } return iso_ops->set_security_env(card, env, se_num); } @@ -194,29 +291,45 @@ static void add_acl_entry(struct sc_file *file, int op, u8 byte) static void parse_sec_attr(struct sc_file *file, const u8 *buf, size_t len) { int i; - int idx[6]; + const int df_ops[8] = { + SC_AC_OP_DELETE, SC_AC_OP_CREATE, + -1, /* CREATE AC */ -1, /* UPDATE AC */ -1, -1, -1, -1 + }; + const int ef_ops[8] = { + SC_AC_OP_DELETE, -1, SC_AC_OP_READ, + SC_AC_OP_UPDATE, -1, -1, SC_AC_OP_INVALIDATE, + SC_AC_OP_REHABILITATE + }; + const int key_ops[8] = { + SC_AC_OP_DELETE, -1, -1, + SC_AC_OP_UPDATE, SC_AC_OP_CRYPTO, -1, SC_AC_OP_INVALIDATE, + SC_AC_OP_REHABILITATE + }; + const int *ops; - if (len < 6) - return; - if (file->type == SC_FILE_TYPE_DF) { - const int df_idx[6] = { - SC_AC_OP_SELECT, SC_AC_OP_LOCK, SC_AC_OP_DELETE, - SC_AC_OP_CREATE, SC_AC_OP_REHABILITATE, - SC_AC_OP_INVALIDATE - }; - for (i = 0; i < 6; i++) - idx[i] = df_idx[i]; - } else { - const int ef_idx[6] = { - SC_AC_OP_READ, SC_AC_OP_UPDATE, SC_AC_OP_WRITE, - SC_AC_OP_ERASE, SC_AC_OP_REHABILITATE, - SC_AC_OP_INVALIDATE - }; - for (i = 0; i < 6; i++) - idx[i] = ef_idx[i]; + if (len < 4) + return; + switch (file->type) { + case SC_FILE_TYPE_WORKING_EF: + ops = ef_ops; + break; + case SC_FILE_TYPE_INTERNAL_EF: + ops = key_ops; + break; + case SC_FILE_TYPE_DF: + ops = df_ops; + break; + default: + return; + } + for (i = 0; i < 8; i++) { + if (ops[i] == -1) + continue; + if ((i & 1) == 0) + add_acl_entry(file, ops[i], buf[i / 2] >> 4); + else + add_acl_entry(file, ops[i], buf[i / 2] & 0x0F); } - for (i = 0; i < 6; i++) - add_acl_entry(file, idx[i], buf[i]); } static int miocos_select_file(struct sc_card *card, @@ -249,6 +362,26 @@ static int miocos_list_files(struct sc_card *card, u8 *buf, size_t buflen) return apdu.resplen; } +static int miocos_delete_file(struct sc_card *card, const struct sc_path *path) +{ + int r; + struct sc_apdu apdu; + + SC_FUNC_CALLED(card->ctx, 1); + if (path->type != SC_PATH_TYPE_FILE_ID && path->len != 2) { + error(card->ctx, "File type has to be SC_PATH_TYPE_FILE_ID\n"); + SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INVALID_ARGUMENTS); + } + r = sc_select_file(card, path, NULL); + SC_TEST_RET(card->ctx, r, "Unable to select file to be deleted"); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xE4, 0x00, 0x00); + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + return sc_check_sw(card, apdu.sw1, apdu.sw2); +} + static const struct sc_card_driver * sc_get_driver(void) { const struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); @@ -263,6 +396,7 @@ static const struct sc_card_driver * sc_get_driver(void) miocos_ops.set_security_env = miocos_set_security_env; miocos_ops.select_file = miocos_select_file; miocos_ops.list_files = miocos_list_files; + miocos_ops.delete_file = miocos_delete_file; return &miocos_drv; } diff --git a/src/libopensc/opensc.h b/src/libopensc/opensc.h index 762b9411..6c21dbb3 100644 --- a/src/libopensc/opensc.h +++ b/src/libopensc/opensc.h @@ -129,6 +129,7 @@ extern "C" { #define SC_AC_OP_REHABILITATE 4 #define SC_AC_OP_INVALIDATE 5 #define SC_AC_OP_LIST_FILES 6 +#define SC_AC_OP_CRYPTO 7 /* Operations relating to access control (in case of EF) */ #define SC_AC_OP_READ 0 @@ -137,7 +138,7 @@ extern "C" { #define SC_AC_OP_ERASE 3 /* rehab and invalidate are the same as in DF case */ -#define SC_MAX_AC_OPS 7 +#define SC_MAX_AC_OPS 8 /* sc_*_record() flags */ #define SC_RECORD_EF_ID_MASK 0x0001F diff --git a/src/tools/Makefile.am b/src/tools/Makefile.am index 4165b7c2..ca4e5a38 100644 --- a/src/tools/Makefile.am +++ b/src/tools/Makefile.am @@ -23,6 +23,6 @@ pkcs15_crypt_LDADD = @GETOPTSRC@ cryptoflex_tool_SOURCES = cryptoflex-tool.c util.c cryptoflex_tool_LDADD = @GETOPTSRC@ @LIBCRYPTO@ pkcs15_init_SOURCES = pkcs15-init.c profile.c util.c \ - pkcs15-gpk.c pkcs15-miocos.c + pkcs15-gpk.c pkcs15-miocos.c pkcs15-cflex.c pkcs15_init_LDADD = @GETOPTSRC@ @LIBCRYPTO@ noinst_HEADERS = util.h profile.h pkcs15-init.h diff --git a/src/tools/cryptoflex-tool.c b/src/tools/cryptoflex-tool.c index 4e5e8a20..fb07f8f3 100644 --- a/src/tools/cryptoflex-tool.c +++ b/src/tools/cryptoflex-tool.c @@ -42,7 +42,6 @@ const char *opt_appdf = NULL, *opt_prkeyf = NULL, *opt_pubkeyf = NULL; u8 *pincode = NULL; const struct option options[] = { - { "create-pkcs15", 0, 0, 'C' }, { "list-keys", 0, 0, 'l' }, { "create-key-files", 1, 0, 'c' }, { "create-pin-file", 1, 0, 'P' }, @@ -62,7 +61,6 @@ const struct option options[] = { }; const char *option_help[] = { - "Creates a new PKCS #15 structure", "Lists all keys in a public key file", "Creates new RSA key files for keys", "Creates a new CHV file", @@ -1066,169 +1064,6 @@ int create_pin_file(const struct sc_path *inpath, int chv, const char *key_id) return 0; } -int add_object(struct sc_pkcs15_card *p15card, - struct sc_pkcs15_df *df, int file_nr, - unsigned int type, void *data, size_t datalen) -{ - struct sc_pkcs15_object *obj; - - obj = malloc(sizeof(*obj)); - if (obj == NULL) - return -1; - obj->type = type; - obj->data = malloc(datalen); - if (obj->data == NULL) { - free(obj); - return -1; - } - memcpy(obj->data, data, datalen); - return sc_pkcs15_add_object(p15card, df, file_nr, obj); -} - -int create_pkcs15() -{ -#if 0 - struct sc_pkcs15_card *p15card; - struct sc_file *file; - struct sc_path path; - struct sc_pkcs15_object obj; - struct sc_pkcs15_cert_info cert; - struct sc_pkcs15_pin_info pin; - struct sc_pkcs15_prkey_info prkey; - int r, file_no; - - p15card = sc_pkcs15_card_new(); - if (p15card == NULL) - return 1; - p15card->label = strdup("OpenSC Test Card"); - p15card->manufacturer_id = strdup("OpenSC Project"); - p15card->serial_number = strdup("1234"); - p15card->flags = SC_PKCS15_CARD_FLAG_EID_COMPLIANT; - p15card->version = 1; - p15card->file_app = sc_file_new(); - sc_format_path("3F005015", &p15card->file_app->path); - p15card->card = card; - - file = sc_file_new(); - file->size = 32; /* reserve 32 additional bytes in each DF */ - - sc_format_path("3F0050155032", &file->path); - sc_file_dup(&p15card->file_tokeninfo, file); - - sc_format_path("3F0050155031", &file->path); - sc_file_dup(&p15card->file_odf, file); - - sc_format_path("3F0050154403", &file->path); - file_no = new_pkcs15_df(p15card, SC_PKCS15_CDF, file); - if (file_no < 0) - return 1; - - sc_format_path("3F0050154402", &file->path); - file_no = new_pkcs15_df(p15card, SC_PKCS15_PRKDF, file); - if (file_no < 0) - return 1; - - sc_format_path("3F0050154401", &file->path); - file_no = new_pkcs15_df(p15card, SC_PKCS15_AODF, file); - if (file_no < 0) - return 1; - - sc_file_free(file); - file = NULL; - - memset(&cert, 0, sizeof(cert)); - strcpy(cert.com_attr.label, "Authentication certificate"); - sc_pkcs15_format_id("45", &cert.id); - sc_format_path("3F0050154301", &cert.path); - add_object(p15card, &p15card->df[SC_PKCS15_CDF], file_no, - SC_PKCS15_TYPE_CERT_X509, &cert, sizeof(cert)), - - strcpy(cert.com_attr.label, "Non-repudiation certificate"); - sc_pkcs15_format_id("46", &cert.id); - sc_format_path("3F0050154302", &cert.path); - add_object(p15card, &p15card->df[SC_PKCS15_CDF], file_no, - SC_PKCS15_TYPE_CERT_X509, &cert, sizeof(cert)), - - memset(&prkey, 0, sizeof(prkey)); - prkey.modulus_length = opt_mod_length; - prkey.com_attr.flags = 1; - prkey.native = 1; - - strcpy(prkey.com_attr.label, "Authentication key"); - sc_pkcs15_format_id("45", &prkey.id); - sc_pkcs15_format_id("01", &prkey.com_attr.auth_id); - sc_format_path("0012", &prkey.path); - prkey.key_reference = 0; - prkey.usage = SC_PKCS15_PRKEY_USAGE_SIGN; - prkey.access_flags = 0x1D; - add_object(p15card, &p15card->df[SC_PKCS15_PRKDF], file_no, - SC_PKCS15_TYPE_PRKEY_RSA, &prkey, sizeof(prkey)), - - strcpy(prkey.com_attr.label, "Non-repudiation key"); - sc_pkcs15_format_id("46", &prkey.id); - sc_pkcs15_format_id("02", &prkey.com_attr.auth_id); - sc_format_path("3F004B020012", &prkey.path); - prkey.key_reference = 0; - prkey.usage = SC_PKCS15_PRKEY_USAGE_NONREPUDIATION; - prkey.access_flags = 0x1D; - add_object(p15card, &p15card->df[SC_PKCS15_PRKDF], file_no, - SC_PKCS15_TYPE_PRKEY_RSA, &prkey, sizeof(prkey)), - - memset(&pin, 0, sizeof(pin)); - pin.com_attr.flags = 0x03; - pin.magic = SC_PKCS15_PIN_MAGIC; - - strcpy(pin.com_attr.label, "Authentication PIN"); - sc_pkcs15_format_id("01", &pin.auth_id); - sc_format_path("3F005015", &pin.path); - pin.reference = 1; - pin.flags = 0x32; - pin.min_length = 4; - pin.stored_length = 8; - pin.pad_char = 0x00; - pin.type = 1; - add_object(p15card, &p15card->df[SC_PKCS15_AODF], file_no, - SC_PKCS15_TYPE_AUTH_PIN, &pin, sizeof(pin)), - - strcpy(pin.com_attr.label, "Non-repuditiation PIN"); - sc_pkcs15_format_id("02", &pin.auth_id); - sc_format_path("3F004B02", &pin.path); - pin.reference = 1; - pin.flags = 0x32; - pin.min_length = 4; - pin.stored_length = 8; - pin.pad_char = 0x00; - pin.type = 1; - add_object(p15card, &p15card->df[SC_PKCS15_AODF], file_no, - SC_PKCS15_TYPE_AUTH_PIN, &pin, sizeof(pin)), - - r = create_app_df(&p15card->file_app->path, 5000); - if (r) { - fprintf(stderr, "Unable to create app DF: %s\n", sc_strerror(r)); - return 1; - } - r = create_pin_file(&p15card->file_app->path, 1, " (key 1)"); - if (r) - return 1; - sc_format_path("3F004B02", &path); - r = create_app_df(&path, 1000); - if (r) { - fprintf(stderr, "Unable to create DF for key 2: %s\n", sc_strerror(r)); - return 1; - } - r = create_pin_file(&path, 1, " (key 2)"); - if (r) - return 1; - r = sc_pkcs15_create(p15card, card); - sc_pkcs15_card_free(p15card); - if (r) { - fprintf(stderr, "PKCS #15 structure creation failed: %s\n", sc_strerror(r)); - return 1; - } -#endif - return 0; -} - int create_pin() { struct sc_path path; @@ -1256,19 +1091,14 @@ int main(int argc, char * const argv[]) int do_list_keys = 0; int do_store_key = 0; int do_create_pin_file = 0; - int do_create_pkcs15 = 0; while (1) { - c = getopt_long(argc, argv, "P:Cvslgc:Rk:r:p:u:e:m:dqa:", options, &long_optind); + c = getopt_long(argc, argv, "P:vslgc:Rk:r:p:u:e:m:dqa:", options, &long_optind); if (c == -1) break; if (c == '?') print_usage_and_die("cryptoflex-tool"); switch (c) { - case 'C': - do_create_pkcs15 = 1; - action_count++; - break; case 'l': do_list_keys = 1; action_count++; @@ -1366,11 +1196,6 @@ int main(int argc, char * const argv[]) err = 1; goto end; } - if (do_create_pkcs15) { - if ((err = create_pkcs15()) != 0) - goto end; - action_count--; - } if (do_create_pin_file) { if ((err = create_pin()) != 0) goto end; diff --git a/src/tools/flex.profile b/src/tools/flex.profile new file mode 100644 index 00000000..50e04598 --- /dev/null +++ b/src/tools/flex.profile @@ -0,0 +1,51 @@ +# +# PKCS #15 r/w profile for GPK cards +# +CardInfo + Label "OpenSC Card" + Manufacturer "OpenSC Project" + MinPinLength 1 + MaxPinLength 8 + PinEncoding ascii-numeric + PinPadChar 0x2d # '-' + PrKeyAccessFlags RSA 0x1D + + # This is the AAK (ie. transport key) required for + # creating files in the MF + Key AUT1 0x0001 "=Muscle00" + +EF chv1file + FileID 0000 + Path 50154B01 + Structure transparent + Size 23 + ACL *=NEVER UPDATE=AUT1 + +EF chv2file + FileID 0000 + Path 50154B02 + Structure transparent + Size 23 + ACL *=NEVER UPDATE=AUT1 + +EF template-private-key + FileID 0012 # This is the base FileID + Structure transparent + ACL *=NEVER UPDATE=AUT1 + +EF template-public-key + FileID 1012 + Structure transparent + ACL *=NONE + +# CVH1. 3 attempts for the PIN, and 10 for the PUK +PIN CHV1 + File chv1file + Reference 0x1 + Attempts 3 10 + +# CVH2. 3 attempts for the PIN, and 10 for the PUK +PIN CHV2 + File chv2file + Reference 0x1 + Attempts 3 10 diff --git a/src/tools/miocos-rw.profile b/src/tools/miocos-rw.profile deleted file mode 100644 index f0ef090b..00000000 --- a/src/tools/miocos-rw.profile +++ /dev/null @@ -1,97 +0,0 @@ -# -# PKCS 15 r/w profile for GPK cards -# -CardInfo - Label "OpenSC Card" - Manufacturer "OpenSC Project" - MinPinLength 0 - MaxPinLength 8 - #PinEncoding - - # This is the secure messaging key required for - # creating files in the MF - #Key PRO 0x0001 "=TEST KEYTEST KEY" - -# This is the application DF -DF - Path 3F005015 - AID A0:00:00:00:63:50:4B:43:53:2D:31:35 - ACL *=NONE - -EF pinfile - Path 3F0050150000 - Structure 0x21 # GPK specific - RecordLength 8 - Size 32 - ACL *=NEVER - -EF PKCS15-DIR - Path 3F002F00 - ACL *=NONE - -EF PKCS15-ODF - Path 3F0050155031 - ACL *=NONE - -EF PKCS15-TokenInfo - Path 3F0050155032 - ACL *=NONE - -EF PKCS15-AODF - Path 3F0050154401 - ACL *=NEVER READ=NONE UPDATE=CHV2 - -EF PKCS15-PrKDF - Path 3F0050154402 - ACL *=NEVER READ=NONE UPDATE=CHV2 - -EF PKCS15-CDF - Path 3F0050154403 - ACL *=NEVER READ=NONE UPDATE=CHV2 - -# Private key files. -# GPK private key files will never let you read the private key -# part, so it's okay to set READ=NONE. What's more, we need -# read access so we're able to update the file. -EF pk1 - Path 3F005015000E - Structure 0x2C # GPK specific - ACL *=NEVER READ=NEVER UPDATE=CHV2 WRITE=CHV2 - -EF pk2 - Path 3F005015000F - Structure 0x2C # GPK specific - ACL *=NEVER READ=NEVER UPDATE=CHV2 WRITE=CHV2 - -# CVH1. 7 attempts for the PIN, and 3 for the PUK -# Reference 0x8 means "PIN0 in the local EFsc" in GPK parlance -PIN CHV1 - File pinfile - Label "Authentication PIN" - Reference 0x8 - Attempts 7 3 - AuthID 01 - -# CVH2. 7 attempts for the PIN, and 3 for the PUK -# Reference 0xA means "PIN2 in the local EFsc" in GPK parlance -PIN CHV2 - File pinfile - Label "Non-repudiation PIN" - Reference 0xA - Attempts 7 3 - Offset 16 - AuthID 02 - -PrivateKey AuthKey - Label "Authentication Key" - File pk1 - ID 45 - AuthID 01 # Requires CHV1 - KeyUsage sign - -PrivateKey SignKey - Label "Non-repudiation Key" - File pk2 - ID 46 - AuthID 02 # Requires CHV2 - KeyUsage NonRepudiation diff --git a/src/tools/miocos.profile b/src/tools/miocos.profile new file mode 100644 index 00000000..3fe9ba61 --- /dev/null +++ b/src/tools/miocos.profile @@ -0,0 +1,57 @@ +# +# PKCS 15 r/w profile for MioCOS cards +# +CardInfo + Label "OpenSC Card" + Manufacturer "OpenSC Project" + MinPinLength 4 + MaxPinLength 8 + PinEncoding ascii-numeric + PrKeyAccessFlags RSA 0x1D + PrKeyAccessFlags DSA 0x12 + + # This is the secure messaging key required for + # creating files in the MF + # Key PRO 0x0001 "=TEST KEYTEST KEY" + +# Note: many commands use the short file ID (i.e. the lower 5 bits +# of the FID) so you must be careful when picking FIDs for the +# public key and PIN files. + +# Currently we do not support PIN files that can be updated +# by CHV2. Far too messy. +EF pinfile + FileID 0000 + Structure 0x21 # GPK specific + RecordLength 8 + Size 32 + ACL *=NEVER + +# Private key files. +# GPK private key files will never let you read the private key +# part, so it's okay to set READ=NONE. What's more, we need +# read access so we're able to update the file. +EF template-private-key + FileID 0006 # This is the base FileID + Structure 0x2C # GPK specific + ACL *=NEVER READ=NONE UPDATE=CHV2 WRITE=CHV2 + +EF template-public-key + FileID 8000 + Structure transparent + ACL *=NONE + +# CVH1. 7 attempts for the PIN, and 3 for the PUK +# Reference 0x8 means "PIN0 in the local EFsc" in GPK parlance +PIN CHV1 + File pinfile + Reference 0x8 + Attempts 7 3 + +# CVH2. 7 attempts for the PIN, and 3 for the PUK +# Reference 0xA means "PIN2 in the local EFsc" in GPK parlance +PIN CHV2 + File pinfile + Reference 0xA + Attempts 7 3 + Offset 16 diff --git a/src/tools/opensc-explorer.c b/src/tools/opensc-explorer.c index 8c37f8ea..c52bfaae 100644 --- a/src/tools/opensc-explorer.c +++ b/src/tools/opensc-explorer.c @@ -465,7 +465,7 @@ int do_create(const char *arg, const char *arg2) struct sc_path path; struct sc_file *file; unsigned int size; - int r; + int r, i; if (arg_to_path(arg, &path, 1) != 0) goto usage; @@ -478,7 +478,9 @@ int do_create(const char *arg, const char *arg2) file->ef_structure = SC_FILE_EF_TRANSPARENT; file->size = (size_t) size; file->status = SC_FILE_STATUS_ACTIVATED; - + for (i = 0; i < SC_MAX_AC_OPS; i++) + sc_file_add_acl_entry(file, i, SC_AC_NONE, SC_AC_KEY_REF_NONE); + r = create_file(file); sc_file_free(file); return r; diff --git a/src/tools/pkcs15-cflex.c b/src/tools/pkcs15-cflex.c new file mode 100644 index 00000000..8ae5d33b --- /dev/null +++ b/src/tools/pkcs15-cflex.c @@ -0,0 +1,684 @@ +/* + * Cryptoflex specific operation for PKCS #15 initialization + * + * Copyright (C) 2002 Juha Yrjölä + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include +#include "opensc.h" +#include "cardctl.h" +#include "pkcs15-init.h" +#include "util.h" + +/* + * Update the contents of a PIN file + */ +static int cflex_update_pin(struct sc_card *card, struct pin_info *info) +{ + u8 buffer[23], *p = buffer; + int r; + size_t len; + + if (!info->attempt[1]) { + error("Cryptoflex needs a PUK code"); + return SC_ERROR_INVALID_ARGUMENTS; + } + + memset(p, 0xFF, 3); + p += 3; + memset(p, '-', 8); + strncpy(p, info->secret[0], 8); + p += 8; + *p++ = info->attempt[0]; + *p++ = info->attempt[0]; + memset(p, '-', 8); + strncpy(p, info->secret[0], 8); + p += 8; + *p++ = info->attempt[1]; + *p++ = info->attempt[1]; + len = 23; + + r = sc_update_binary(card, 0, buffer, len, 0); + if (r < 0) + return r; + return 0; +} + +/* + * Create the PIN file and write the PINs + */ +static int cflex_store_pin(struct sc_profile *profile, struct sc_card *card, + struct pin_info *info) +{ + struct sc_file *pinfile; + int r; + + sc_file_dup(&pinfile, info->file->file); + + /* Now create the file */ + if ((r = sc_pkcs15init_create_file(profile, pinfile)) < 0) + goto out; + + /* If messing with the PIN file requires any sort of + * authentication, send it to the card now */ + if ((r = sc_select_file(card, &pinfile->path, NULL)) < 0 + || (r = sc_pkcs15init_authenticate(profile, pinfile, SC_AC_OP_UPDATE)) < 0) + goto out; + + r = cflex_update_pin(card, info); + +out: sc_file_free(pinfile); + return r; +} + +/* + * Initialize the Application DF and store the PINs + * + */ +static int cflex_init_app(struct sc_profile *profile, struct sc_card *card) +{ + struct pin_info *pin1, *pin2; + + pin1 = sc_profile_find_pin(profile, "CHV1"); + pin2 = sc_profile_find_pin(profile, "CHV2"); + if (pin1 == NULL) { + fprintf(stderr, "No CHV1 defined\n"); + return 1; + } + + /* XXX TODO: + * if the CHV2 pin file is required to create files + * in the application DF, create that file first */ + + /* Create the application DF */ + if (sc_pkcs15init_create_file(profile, profile->df_info.file)) + return 1; + + /* Store CHV2 */ + if (pin2) { + if (cflex_store_pin(profile, card, pin2)) + return 1; + } + + /* Store CHV1 */ + if (cflex_store_pin(profile, card, pin1)) + return 1; + + return 0; +} + +/* + * Allocate a file + */ +static int cflex_allocate_file(struct sc_profile *profile, struct sc_card *card, + unsigned int type, unsigned int num, + struct sc_file **out) +{ + struct file_info *templ; + struct sc_file *file; + struct sc_path *p; + char name[64], *tag, *desc; + + desc = tag = NULL; + while (1) { + switch (type) { + case SC_PKCS15_TYPE_PRKEY_RSA: + desc = "RSA private key"; + tag = "private-key"; + break; + case SC_PKCS15_TYPE_PUBKEY_RSA: + desc = "RSA public key"; + tag = "public-key"; + break; + case SC_PKCS15_TYPE_CERT: + desc = "certificate"; + tag = "data"; + break; + case SC_PKCS15_TYPE_DATA_OBJECT: + desc = "data object"; + tag = "data"; + break; + } + if (tag) + break; + /* If this is a specific type such as + * SC_PKCS15_TYPE_CERT_FOOBAR, fall back to + * the generic class (SC_PKCS15_TYPE_CERT) + */ + if (!(type & ~SC_PKCS15_TYPE_CLASS_MASK)) { + error("File type not supported by card driver"); + return SC_ERROR_INVALID_ARGUMENTS; + } + type &= SC_PKCS15_TYPE_CLASS_MASK; + } + + snprintf(name, sizeof(name), "template-%s", tag); + if (!(templ = sc_profile_find_file(profile, name))) { + error("Profile doesn't define %s template (%s)\n", + desc, name); + return SC_ERROR_NOT_SUPPORTED; + } + + /* Now construct file from template */ + sc_file_dup(&file, templ->file); + file->id += num; + + p = &file->path; + *p = profile->df_info.file->path; + p->value[p->len++] = file->id >> 8; + p->value[p->len++] = file->id; + + *out = file; + return 0; +} + +#if 0 +/* + * Create the PK file + */ +static int gpk_pkfile_create(struct sc_profile *profile, struct sc_card *card, + struct sc_file *file) +{ + struct sc_file *found = NULL; + int r; + + card->ctx->log_errors = 0; + r = sc_select_file(card, &file->path, &found); + card->ctx->log_errors = 1; + if (r == SC_ERROR_FILE_NOT_FOUND) { + r = sc_pkcs15init_create_file(profile, file); + if (r >= 0) + r = sc_select_file(card, &file->path, &found); + } else { + /* XXX: make sure the file has correct type and size? */ + } + + if (r >= 0) + r = sc_pkcs15init_authenticate(profile, file, SC_AC_OP_UPDATE); + if (found) + sc_file_free(found); + + return r; +} + +/* + * Set up the public key record for a signature only public key + */ +static int +gpk_pkfile_init_public(struct sc_card *card, struct sc_file *file, + unsigned int algo, unsigned int bits, + unsigned int usage, struct sc_acl_entry *acl) +{ + u8 sysrec[7], buffer[256]; + unsigned int npins, n; + int r; + + /* Set up the system record */ + memset(sysrec, 0, sizeof(sysrec)); + + /* XXX: How to map keyUsage to sysrec[2]? + * 0x00 sign & unwrap + * 0x10 sign only + * 0x20 unwrap only + * 0x30 CA key + * Which PKCS15 key usage values map to which flag? + */ + sysrec[2] = 0x00; /* no restriction for now */ + + /* Set the key type and algorithm */ + if ((r = gpk_pkfile_keybits(bits, &sysrec[1])) < 0 + || (r = gpk_pkfile_keyalgo(algo, &sysrec[5])) < 0) + return r; + + /* Set PIN protection if requested. */ + for (npins = 0; acl; acl = acl->next) { + if (acl->method == SC_AC_NONE + || acl->method == SC_AC_NEVER) + continue; + if (acl->method == SC_AC_CHV) { + if (++npins >= 2) { + error("Too many pins for PrKEY file!\n"); + return SC_ERROR_NOT_SUPPORTED; + } + sysrec[2] += 0x40; + sysrec[3] >>= 4; + sysrec[3] |= acl->key_ref << 4; + } + } + + /* compute checksum - yet another slightly different + * checksum algorithm courtesy of Gemplus */ + /* XXX: This is different from what the GPK reference + * manual says which tells you to start with 0xA5 -- but + * maybe that's just for the GPK8000 */ + for (sysrec[6] = 0xFF, n = 0; n < 6; n++) + sysrec[6] ^= sysrec[n]; + + card->ctx->log_errors = 0; + r = sc_read_record(card, 1, buffer, sizeof(buffer), + SC_RECORD_BY_REC_NR); + card->ctx->log_errors = 1; + if (r >= 0) { + if (r != 7 || buffer[0] != 0) { + error("first record of public key file is not Lsys0"); + return SC_ERROR_OBJECT_NOT_VALID; + } + + r = sc_update_record(card, 1, sysrec, sizeof(sysrec), + SC_RECORD_BY_REC_NR); + } else { + r = sc_append_record(card, sysrec, sizeof(sysrec), 0); + } + return r; +} + +static int +gpk_pkfile_update_public(struct sc_card *card, struct pkpart *part) +{ + struct pkcomp *pe; + unsigned char buffer[256]; + unsigned int m, n, tag; + int r = 0, found; + + if (card->ctx->debug > 1) + printf("Updating public key elements\n"); + + /* If we've been given a key with public parts, write them now */ + for (n = 2; n < 256; n++) { + card->ctx->log_errors = 0; + r = sc_read_record(card, n, buffer, sizeof(buffer), + SC_RECORD_BY_REC_NR); + card->ctx->log_errors = 1; + if (r < 0) { + r = 0; + break; + } + + /* Check for bad record */ + if (r < 2) { + error("key file format error: " + "record %u too small (%u bytes)\n", + n, r); + return SC_ERROR_OBJECT_NOT_VALID; + } + + tag = buffer[0]; + + for (m = 0, found = 0; m < part->count; m++) { + pe = part->components + m; + if (pe->tag == tag) { + r = sc_update_record(card, n, + pe->data, pe->size, + SC_RECORD_BY_REC_NR); + if (r < 0) + return r; + pe->tag = 0; /* mark as stored */ + found++; + break; + } + } + + if (!found && card->ctx->debug) + printf("GPK unknown PK tag %u\n", tag); + } + + /* Write all remaining elements */ + for (m = 0; r >= 0 && m < part->count; m++) { + pe = part->components + m; + if (pe->tag != 0) + r = sc_append_record(card, pe->data, pe->size, 0); + } + + return r; +} + +static int +gpk_pkfile_init_private(struct sc_card *card, + struct sc_file *file, unsigned int privlen) +{ + struct sc_cardctl_gpk_pkinit args; + + if (card->ctx->debug > 1) + printf("Initializing private key portion of file\n"); + args.file = file; + args.privlen = privlen; + return sc_card_ctl(card, SC_CARDCTL_GPK_PKINIT, &args); +} + +static int +gpk_pkfile_load_private(struct sc_card *card, struct sc_file *file, + u8 *data, unsigned int len, unsigned int datalen) +{ + struct sc_cardctl_gpk_pkload args; + + args.file = file; + args.data = data; + args.len = len; + args.datalen = datalen; + return sc_card_ctl(card, SC_CARDCTL_GPK_PKLOAD, &args); +} + +static int +gpk_pkfile_update_private(struct sc_profile *profile, + struct sc_card *card, struct sc_file *file, + struct pkpart *part) +{ + struct auth_info *sm; + unsigned int m, size, nb, cks; + struct pkcomp *pe; + u8 data[256]; + int r = 0; + + if (card->ctx->debug > 1) + printf("Updating private key elements\n"); + + /* We must set a secure messaging key before each Load Private Key + * command. Any key will do... + * The GPK _is_ weird. */ + sm = sc_profile_find_key(profile, SC_AC_PRO, -1); + if (sm == NULL) { + error("No secure messaging key defined by profile"); + return SC_ERROR_SECURITY_STATUS_NOT_SATISFIED; + } + + for (m = 0; m < part->count; m++) { + pe = part->components + m; + + if (pe->size + 8 > sizeof(data)) + return SC_ERROR_BUFFER_TOO_SMALL; + memcpy(data, pe->data, pe->size); + size = pe->size; + + r = sc_verify(card, SC_AC_PRO, + sm->ref, sm->key, sm->key_len, NULL); + if (r < 0) + break; + + /* Pad out data to a multiple of 8 and checksum. + * The GPK manual is a bit unclear about whether you + * checksum first and then pad, or vice versa. + * The following code does seem to work though: */ + for (nb = 0, cks = 0xff; nb < size; nb++) + cks ^= data[nb]; + data[nb++] = cks; + while (nb & 7) + data[nb++] = 0; + + r = gpk_pkfile_load_private(card, file, data, size, nb); + if (r < 0) + break; + pe++; + } + return r; +} + +/* Sum up the size of the public key elements + * Each element is type + tag + bignum + */ +static void +gpk_compute_publen(struct pkpart *part) +{ + unsigned int n, publen = 8; /* length of sysrec0 */ + + for (n = 0; n < part->count; n++) + publen += 2 + part->components[n].size; + part->size = (publen + 3) & ~3UL; +} + +/* Sum up the size of the private key elements + * Each element is type + tag + bignum + checksum, padded to a multiple + * of eight + */ +static void +gpk_compute_privlen(struct pkpart *part) +{ + unsigned int n, privlen = 8; + + for (n = 0; n < part->count; n++) + privlen += (3 + part->components[n].size + 7) & ~7UL; + part->size = privlen; +} + +/* + * Convert BIGNUM to GPK representation, optionally zero padding to size. + * Note OpenSSL stores BIGNUMs big endian while the GPK wants them + * little endian + */ +static void +gpk_bn2bin(const BIGNUM *bn, unsigned char *dest, unsigned int size) +{ + u8 temp[256], *src; + unsigned int n, len; + + assert(BN_num_bytes(bn) <= sizeof(temp)); + len = BN_bn2bin(bn, temp); + + assert(len <= size); + for (n = 0, src = temp + len - 1; n < len; n++) + dest[n] = *src--; + for (; n < size; n++) + dest[n] = '\0'; +} + +/* + * Add a BIGNUM component, optionally padding out the number to size bytes + */ +static void +gpk_add_bignum(struct pkpart *part, unsigned int tag, BIGNUM *bn, size_t size) +{ + struct pkcomp *comp; + + if (size == 0) + size = BN_num_bytes(bn); + + comp = &part->components[part->count++]; + memset(comp, 0, sizeof(*comp)); + comp->tag = tag; + comp->size = size + 1; + comp->data = malloc(size + 1); + + /* Add the tag */ + comp->data[0] = tag; + + /* Add the BIGNUM */ + gpk_bn2bin(bn, comp->data + 1, size); + + /* printf("TAG 0x%02x, len=%u\n", tag, comp->size); */ +} + +int +gpk_encode_rsa_key(RSA *rsa, struct pkdata *p, unsigned int usage) +{ + if (!rsa->n || !rsa->e) { + error("incomplete RSA public key"); + return SC_ERROR_INVALID_ARGUMENTS; + } + + /* Make sure the exponent is 0x10001 because that's + * the only exponent supported by GPK4000 and GPK8000 */ + if (!BN_is_word(rsa->e, RSA_F4)) { + error("unsupported RSA exponent"); + return SC_ERROR_INVALID_ARGUMENTS; + } + + memset(p, 0, sizeof(*p)); + p->algo = SC_ALGORITHM_RSA; + p->usage = usage; + p->bits = BN_num_bits(rsa->n); + p->bytes = BN_num_bytes(rsa->n); + + /* Set up the list of public elements */ + gpk_add_bignum(&p->public, 0x01, rsa->n, 0); + gpk_add_bignum(&p->public, 0x07, rsa->e, 0); + + /* Set up the list of private elements */ + if (!rsa->p || !rsa->q || !rsa->dmp1 || !rsa->dmq1 || !rsa->iqmp) { + /* No or incomplete CRT information */ + if (!rsa->d) { + error("incomplete RSA private key"); + return SC_ERROR_INVALID_ARGUMENTS; + } + gpk_add_bignum(&p->private, 0x04, rsa->d, 0); + } else if (5 * (p->bytes / 2) < 256) { + /* All CRT elements are stored in one record */ + struct pkcomp *comp; + unsigned int K = p->bytes / 2; + u8 *crtbuf; + + crtbuf = malloc(5 * K + 1); + + crtbuf[0] = 0x05; + gpk_bn2bin(rsa->p, crtbuf + 1, K); + gpk_bn2bin(rsa->q, crtbuf + 1 + 1 * K, K); + gpk_bn2bin(rsa->iqmp, crtbuf + 1 + 2 * K, K); + gpk_bn2bin(rsa->dmp1, crtbuf + 1 + 3 * K, K); + gpk_bn2bin(rsa->dmq1, crtbuf + 1 + 4 * K, K); + + comp = &p->private.components[p->private.count++]; + comp->tag = 0x05; + comp->size = 5 * K + 1; + comp->data = crtbuf; + } else { + /* CRT elements stored in individual records. + * Make sure they're all fixed length even if they're + * shorter */ + gpk_add_bignum(&p->private, 0x51, rsa->p, p->bytes/2); + gpk_add_bignum(&p->private, 0x52, rsa->q, p->bytes/2); + gpk_add_bignum(&p->private, 0x53, rsa->iqmp, p->bytes/2); + gpk_add_bignum(&p->private, 0x54, rsa->dmp1, p->bytes/2); + gpk_add_bignum(&p->private, 0x55, rsa->dmq1, p->bytes/2); + } + + return 0; +} + +/* + * Encode a DSA key. + * Confusingly, the GPK manual says that the GPK8000 can handle + * DSA with 512 as well as 1024 bits, but all byte sizes shown + * in the tables are 512 bits only... + */ +int +gpk_encode_dsa_key(DSA *dsa, struct pkdata *p, unsigned int usage) +{ + if (!dsa->p || !dsa->q || !dsa->g || !dsa->pub_key || !dsa->priv_key) { + error("incomplete DSA public key"); + return SC_ERROR_INVALID_ARGUMENTS; + } + + memset(p, 0, sizeof(*p)); + p->algo = SC_ALGORITHM_RSA; + p->usage = usage; + p->bits = BN_num_bits(dsa->p); + p->bytes = BN_num_bytes(dsa->p); + + /* Make sure the key is either 512 or 1024 bits */ + if (p->bytes <= 64) { + p->bits = 512; + p->bytes = 64; + } else if (p->bytes <= 128) { + p->bits = 1024; + p->bytes = 128; + } else { + error("incompatible DSA key size (%u bits)", p->bits); + return SC_ERROR_INVALID_ARGUMENTS; + } + + /* Set up the list of public elements */ + gpk_add_bignum(&p->public, 0x09, dsa->p, 0); + gpk_add_bignum(&p->public, 0x0a, dsa->q, 0); + gpk_add_bignum(&p->public, 0x0b, dsa->g, 0); + gpk_add_bignum(&p->public, 0x0c, dsa->pub_key, 0); + + /* Set up the list of private elements */ + gpk_add_bignum(&p->private, 0x0d, dsa->priv_key, 0); + + return 0; +} + +static int +gpk_store_pk(struct sc_profile *profile, struct sc_card *card, + struct sc_file *file, struct pkdata *p, + struct sc_acl_entry *key_acl) +{ + int r; + + /* Compute length of private/public key parts */ + gpk_compute_publen(&p->public); + gpk_compute_privlen(&p->private); + + if (card->ctx->debug) + printf("Storing pk: %u bits, pub %u bytes, priv %u bytes\n", + p->bits, p->bytes, p->private.size); + + /* Strange, strange, strange... when I create the public part with + * the exact size of 8 + PK elements, the card refuses to store + * the last record even though there's enough room in the file. + * XXX: Check why */ + file->size = p->public.size + 8 + p->private.size + 8; + r = gpk_pkfile_create(profile, card, file); + if (r < 0) + return r; + + /* Put the system record */ + r = gpk_pkfile_init_public(card, file, p->algo, + p->bits, p->usage, key_acl); + if (r < 0) + return r; + + /* Put the public key elements */ + r = gpk_pkfile_update_public(card, &p->public); + if (r < 0) + return r; + + /* Create the private key part */ + r = gpk_pkfile_init_private(card, file, p->private.size); + if (r < 0) + return r; + + /* Now store the private key elements */ + r = gpk_pkfile_update_private(profile, card, file, &p->private); + + return r; +} + +/* + * Store a RSA key on the card + */ +static int gpk_store_rsa_key(struct sc_profile *profile, struct sc_card *card, + struct sc_key_template *info, RSA *rsa) +{ + struct pkdata data; + int r; + + if ((r = gpk_encode_rsa_key(rsa, &data, info->pkcs15.priv.usage)) < 0) + return r; + return gpk_store_pk(profile, card, info->file, &data, info->key_acl); +} +#endif + +void bind_cflex_operations(struct pkcs15_init_operations *ops) +{ + ops->init_app = cflex_init_app; + ops->allocate_file = cflex_allocate_file; +// ops->store_rsa = gpk_store_rsa_key; +} diff --git a/src/tools/pkcs15-init.c b/src/tools/pkcs15-init.c index 1351470f..64aa9b6b 100644 --- a/src/tools/pkcs15-init.c +++ b/src/tools/pkcs15-init.c @@ -272,6 +272,8 @@ bind_operations(struct pkcs15_init_operations *ops, const char *driver) bind_gpk_operations(ops); else if (!strcasecmp(driver, "MioCOS")) bind_miocos_operations(ops); + else if (!strcasecmp(driver, "flex")) + bind_cflex_operations(ops); else fatal("Don't know how to handle %s cards", driver); } diff --git a/src/tools/pkcs15-init.h b/src/tools/pkcs15-init.h index dfcc70e4..cdfe2ad7 100644 --- a/src/tools/pkcs15-init.h +++ b/src/tools/pkcs15-init.h @@ -49,5 +49,6 @@ extern int sc_pkcs15init_authenticate(struct sc_profile *, /* Card specific stuff */ extern void bind_gpk_operations(struct pkcs15_init_operations *); extern void bind_miocos_operations(struct pkcs15_init_operations *); +extern void bind_cflex_operations(struct pkcs15_init_operations *); #endif /* PKCS15_INIT_H */ diff --git a/src/tools/profile.c b/src/tools/profile.c index d2f4586f..c0b676c5 100644 --- a/src/tools/profile.c +++ b/src/tools/profile.c @@ -506,6 +506,12 @@ do_default_pin_type(int argc, char **argv) &parser.profile->pin_encoding, pinTypeNames); } +static int +do_pin_pad_char(int argc, char **argv) +{ + return get_uint(argv[0], &parser.profile->pin_pad_char); +} + static int do_card_label(int argc, char **argv) { @@ -533,7 +539,7 @@ do_default_access_flags(int argc, char **argv) if (!strcasecmp(argv[0], "DSA")) { flags = &parser.profile->dsa_access_flags; } else { - parse_error("Unknown alforithm \"%s\"", argv[0]); + parse_error("Unknown algorithm \"%s\"", argv[0]); return 1; } argc--, argv++; @@ -1136,6 +1142,7 @@ static struct command commands[] = { { "MaxPinLength", PARSE_CARDINFO, 1, 1, do_maxpinlength }, { "MinPinLength", PARSE_CARDINFO, 1, 1, do_minpinlength }, { "PinEncoding", PARSE_CARDINFO, 1, 1, do_default_pin_type }, + { "PinPadChar", PARSE_CARDINFO, 1, 1, do_pin_pad_char }, { "Key", PARSE_CARDINFO, 3, 3, do_key }, { "Label", PARSE_CARDINFO, 1, 1, do_card_label }, { "Manufacturer", PARSE_CARDINFO, 1, 1, do_card_manufacturer},