From f5902e4f0c03ad542f039f6fe312b4b8f9e30fd6 Mon Sep 17 00:00:00 2001 From: nils Date: Wed, 28 Dec 2005 19:38:55 +0000 Subject: [PATCH] add support for cardos m4.2 (still experimental) git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@2793 c6295689-39f2-0310-b995-f0e70906c6a9 --- src/libopensc/card-etoken.c | 488 +++++++++++++++++++++------------ src/libopensc/cards.h | 1 + src/pkcs15init/pkcs15-etoken.c | 312 +++++++++++++++------ 3 files changed, 547 insertions(+), 254 deletions(-) diff --git a/src/libopensc/card-etoken.c b/src/libopensc/card-etoken.c index 5728007f..70ec1443 100644 --- a/src/libopensc/card-etoken.c +++ b/src/libopensc/card-etoken.c @@ -2,6 +2,7 @@ * card-etoken.c: Support for Siemens CardOS based cards and tokens * (for example Aladdin eToken PRO, Eutron CryptoIdentity IT-SEC) * + * Copyright (c) 2005 Nils Larsch * Copyright (C) 2002 Andreas Jellinghaus * Copyright (C) 2001 Juha Yrjölä * @@ -25,6 +26,8 @@ #include #include +#include + /* andreas says: hm, my card only works for small payloads */ /* comment by okir: one of the examples in the developer guide * also talks about copying data in chunks of 128. @@ -48,8 +51,8 @@ static struct sc_atr_table etoken_atrs[] = { { "3b:f2:98:00:ff:c1:10:31:fe:55:c8:03:15", NULL, NULL, SC_CARD_TYPE_ETOKEN_GENERIC, 0, NULL }, /* 4.01a */ { "3b:f2:98:00:ff:c1:10:31:fe:55:c8:04:12", NULL, NULL, SC_CARD_TYPE_ETOKEN_GENERIC, 0, NULL }, - /* aladdin etoken pro 64 - aj: not compatible :/ */ - /* { "3b f2 18 00 ff c1 0a 31 fe 55 c8 06 8a", NULL, NULL, SC_CARD_TYPE_ETOKEN_GENERIC, 0, NULL }, */ + /* M4.2 */ + { "3b:f2:18:00:ff:c1:0a:31:fe:55:c8:06:8a", NULL, NULL, SC_CARD_TYPE_CARDOS_M4_2, 0, NULL }, /* Italian eID card, postecert */ { "3b:e9:00:ff:c1:10:31:fe:55:00:64:05:00:c8:02:31:80:00:47", NULL, NULL, SC_CARD_TYPE_ETOKEN_GENERIC, 0, NULL }, /* Italian eID card, infocamere */ @@ -75,6 +78,42 @@ static int etoken_match_card(sc_card_t *card) return 1; } +static int cardos_have_2048bit_package(sc_card_t *card) +{ + sc_apdu_t apdu; + u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; + int r; + const u8 *p = rbuf, *q; + size_t len, tlen = 0, ilen = 0; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x88); + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + apdu.lc = 0; + apdu.le = 256; + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + + if ((len = apdu.resplen) == 0) + /* looks like no package has been installed */ + return 0; + + while (len != 0) { + p = sc_asn1_find_tag(card->ctx, p, len, 0xe1, &tlen); + if (p == NULL) + return 0; + q = sc_asn1_find_tag(card->ctx, p, tlen, 0x01, &ilen); + if (q == NULL || ilen != 4) + return 0; + if (q[0] == 0x1c) + return 1; + p += tlen; + len -= tlen + 2; + } + + return 0; +} + static int etoken_init(sc_card_t *card) { unsigned long flags; @@ -92,6 +131,22 @@ static int etoken_init(sc_card_t *card) _sc_card_add_rsa_alg(card, 768, flags, 0); _sc_card_add_rsa_alg(card, 1024, flags, 0); + if (card->type == SC_CARD_TYPE_CARDOS_M4_2) { + int r = cardos_have_2048bit_package(card); + if (r < 0) + return r; + if (r == 1) + card->caps |= SC_CARD_CAP_RSA_2048; + card->caps |= SC_CARD_CAP_APDU_EXT; + } + + if (card->caps & SC_CARD_CAP_RSA_2048) { + _sc_card_add_rsa_alg(card, 1280, flags, 0); + _sc_card_add_rsa_alg(card, 1536, flags, 0); + _sc_card_add_rsa_alg(card, 1792, flags, 0); + _sc_card_add_rsa_alg(card, 2048, flags, 0); + } + return 0; } @@ -174,91 +229,16 @@ static int etoken_check_sw(sc_card_t *card, unsigned int sw1, unsigned int sw2) return SC_ERROR_CARD_CMD_FAILED; } -static u8 etoken_extract_offset(u8 *buf, int buflen) { - int i; - int mode; - u8 tag,len; - - tag=0; len=0; - mode = 0; - - for (i=0; i < buflen;) { - if (mode == 0) { - tag = buf[i++]; - mode=1; - continue; - } - if (mode == 1) { - len=buf[i++]; - mode=2; - continue; - } - if (len == 0) { - mode=0; - continue; - } - if (tag == 0x8a && len == 1) { - return buf[i]; - } - - i+=len; - mode=0; - } - - return 0; -} - -static u8* etoken_extract_fid(u8 *buf, int buflen) { - int i; - int mode; - u8 tag,len; - - mode = 0; - tag = 0; - len = 0; - - - for (i=0; i < buflen;) { - if (mode == 0) { - tag = buf[i++]; - mode=1; - continue; - } - if (mode == 1) { - len=buf[i++]; - mode=2; - continue; - } - if (len == 0) { - mode=0; - continue; - } - if ((tag == 0x86) && (len == 2) && (i+1 < buflen)) { - return &buf[i]; - } - - i+=len; - mode=0; - } - - return NULL; -} - static int etoken_list_files(sc_card_t *card, u8 *buf, size_t buflen) { sc_apdu_t apdu; - u8 rbuf[256]; - int r; - int len; - size_t i, fids; - u8 offset; - u8 *fid; + u8 rbuf[256], offset = 0; + const u8 *p = rbuf, *q; + int r; + size_t fids = 0, len; SC_FUNC_CALLED(card->ctx, 1); - fids=0; - offset=0; - /* 0x16: DIRECTORY */ /* 0x02: list both DF and EF */ @@ -278,34 +258,41 @@ get_next_part: sc_error(card->ctx, "directory listing > 256 bytes, cutting"); r = 256; } - for (i=0; i < apdu.resplen;) { + + len = apdu.resplen; + while (len != 0) { + size_t tlen = 0, ilen = 0; /* is there a file informatin block (0x6f) ? */ - if (rbuf[i] != 0x6f) { - sc_error(card->ctx, "directory listing not parseable"); + p = sc_asn1_find_tag(card->ctx, p, len, 0x6f, &tlen); + if (p == NULL) { + sc_error(card->ctx, "directory tag missing"); + return SC_ERROR_INTERNAL; + } + if (tlen == 0) + /* empty directory */ break; + q = sc_asn1_find_tag(card->ctx, p, tlen, 0x86, &ilen); + if (q == NULL || ilen != 2) { + sc_error(card->ctx, "error parsing file id TLV object"); + return SC_ERROR_INTERNAL; } - if (i+1 > apdu.resplen) { - sc_error(card->ctx, "directory listing short"); + /* put file id in buf */ + if (buflen >= 2) { + buf[fids++] = q[0]; + buf[fids++] = q[1]; + buflen -= 2; + } else + /* not enought space left in buffer => break */ break; + /* extract next offset */ + q = sc_asn1_find_tag(card->ctx, p, tlen, 0x8a, &ilen); + if (q != NULL && ilen == 1) { + offset = (u8)ilen; + if (offset != 0) + goto get_next_part; } - len = rbuf[i+1]; - if (i + 1 + len > apdu.resplen) { - sc_error(card->ctx, "directory listing short"); - break; - } - fid = etoken_extract_fid(&rbuf[i+2], len); - - if (fid) { - if (fids + 2 >= buflen) - break; - buf[fids++] = fid[0]; - buf[fids++] = fid[1]; - } - - offset = etoken_extract_offset(&rbuf[i+2], len); - if (offset) - goto get_next_part; - i += len+2; + len -= tlen + 2; + p += tlen; } r = fids; @@ -377,7 +364,7 @@ static const int ef_acl[9] = { SC_AC_OP_INVALIDATE, /* EF */ SC_AC_OP_REHABILITATE, /* EF */ - SC_AC_OP_ERASE, /* (delete) EF */ + SC_AC_OP_DELETE, /* (delete) EF */ /* XXX: ADMIN should be an ACL type of its own, or mapped * to erase */ @@ -412,47 +399,56 @@ static int etoken_select_file(sc_card_t *card, SC_FUNC_RETURN(card->ctx, 1, r); } -static int etoken_create_file(sc_card_t *card, sc_file_t *file) +static int cardos_acl_to_bytes(sc_card_t *card, const sc_file_t *file, + u8 *buf, size_t *outlen) { - int r, i, byte; + int i, byte; const int *idx; - u8 acl[9], type[3], status[3]; - if (card->ctx->debug >= 1) { - char pbuf[128+1]; - size_t n; + if (buf == NULL || *outlen < 9) + return SC_ERROR_INVALID_ARGUMENTS; - for (n = 0; n < file->path.len; n++) { - snprintf(pbuf + 2 * n, sizeof(pbuf) - 2 * n, - "%02X", file->path.value[n]); + idx = (file->type == SC_FILE_TYPE_DF) ? df_acl : ef_acl; + for (i = 0; i < 9; i++) { + if (idx[i] < 0) + byte = 0x00; + else + byte = acl_to_byte(sc_file_get_acl_entry(file, idx[i])); + if (byte < 0) { + sc_error(card->ctx, "Invalid ACL\n"); + return SC_ERROR_INVALID_ARGUMENTS; } - - sc_debug(card->ctx, "etoken_create_file(%s)\n", pbuf); + buf[i] = byte; } + *outlen = 9; + + return SC_SUCCESS; +} + +static int cardos_set_file_attributes(sc_card_t *card, sc_file_t *file) +{ + int r; if (file->type_attr_len == 0) { + u8 type[3]; + memset(type, 0, sizeof(type)); type[0] = 0x00; switch (file->type) { case SC_FILE_TYPE_WORKING_EF: break; - case SC_FILE_TYPE_INTERNAL_EF: - type[0] = 0x08; - break; case SC_FILE_TYPE_DF: type[0] = 0x38; break; default: - r = SC_ERROR_NOT_SUPPORTED; - goto out; + return SC_ERROR_NOT_SUPPORTED; } if (file->type != SC_FILE_TYPE_DF) { switch (file->ef_structure) { case SC_FILE_EF_LINEAR_FIXED_TLV: case SC_FILE_EF_LINEAR_VARIABLE: case SC_FILE_EF_CYCLIC_TLV: - r = SC_ERROR_NOT_SUPPORTED; - goto out; + return SC_ERROR_NOT_SUPPORTED; /* No idea what this means, but it * seems to be required for key * generation. */ @@ -464,10 +460,12 @@ static int etoken_create_file(sc_card_t *card, sc_file_t *file) } } r = sc_file_set_type_attr(file, type, sizeof(type)); - if (r) - goto out; + if (r != SC_SUCCESS) + return r; } if (file->prop_attr_len == 0) { + u8 status[3]; + status[0] = 0x01; if (file->type == SC_FILE_TYPE_DF) { status[1] = file->size >> 8; @@ -476,34 +474,164 @@ static int etoken_create_file(sc_card_t *card, sc_file_t *file) status[1] = status[2] = 0x00; /* not used */ } r = sc_file_set_prop_attr(file, status, sizeof(status)); - if (r) - goto out; + if (r != SC_SUCCESS) + return r; } if (file->sec_attr_len == 0) { - idx = (file->type == SC_FILE_TYPE_DF) ? df_acl : ef_acl; - for (i = 0; i < 9; i++) { - if (idx[i] < 0) - byte = 0x00; - else - byte = acl_to_byte( - sc_file_get_acl_entry(file, idx[i])); - if (byte < 0) { - sc_error(card->ctx, "Invalid ACL\n"); - r = SC_ERROR_INVALID_ARGUMENTS; - goto out; - } - acl[i] = byte; - } - r = sc_file_set_sec_attr(file, acl, sizeof(acl)); - if (r) - goto out; + u8 acl[9]; + size_t blen = sizeof(acl); + + r = cardos_acl_to_bytes(card, file, acl, &blen); + if (r != SC_SUCCESS) + return r; + r = sc_file_set_sec_attr(file, acl, blen); + if (r != SC_SUCCESS) + return r; } - r = iso_ops->create_file(card, file); + return SC_SUCCESS; +} - /* FIXME: if this is a DF and there's an AID, set it here - * using PUT_DATA_FCI */ +/* newer versions of cardos seems to prefer the FCP */ +static int cardos_construct_fcp(sc_card_t *card, const sc_file_t *file, + u8 *out, size_t *outlen) +{ + u8 buf[64], *p = out; + size_t inlen = *outlen, len; + int r; -out: SC_FUNC_RETURN(card->ctx, 1, r); + SC_FUNC_CALLED(card->ctx, 2); + + if (out == NULL || inlen < 64) + return SC_ERROR_INVALID_ARGUMENTS; + /* add FCP tag */ + *p++ = 0x62; + /* we will add the length later */ + p++; + + /* set the length */ + buf[0] = (file->size >> 8) & 0xff; + buf[1] = file->size & 0xff; + if (file->type == SC_FILE_TYPE_DF) + r = sc_asn1_put_tag(0x81, buf, 2, p, 4, &p); + else + r = sc_asn1_put_tag(0x80, buf, 2, p, 4, &p); + if (r != SC_SUCCESS) + return r; + /* set file type */ + if (file->shareable != 0) + buf[0] = 0x40; + else + buf[0] = 0x00; + if (file->type == SC_FILE_TYPE_WORKING_EF) { + switch (file->ef_structure) { + case SC_FILE_EF_TRANSPARENT: + buf[0] |= 0x01; + break; + case SC_FILE_EF_LINEAR_VARIABLE_TLV: + buf[0] |= 0x05; + break; + case SC_FILE_EF_LINEAR_FIXED: + buf[0] |= 0x02; + buf[1] |= 0x21; + buf[2] |= 0x00; + buf[3] |= (u8) file->record_length; + buf[4] |= (u8) file->record_count; + break; + case SC_FILE_EF_CYCLIC: + buf[0] |= 0x06; + buf[1] |= 0x21; + buf[2] |= 0x00; + buf[3] |= (u8) file->record_length; + buf[4] |= (u8) file->record_count; + break; + default: + sc_error(card->ctx, "unknown EF type: %u", file->type); + return SC_ERROR_INVALID_ARGUMENTS; + } + if (file->ef_structure == SC_FILE_EF_CYCLIC || + file->ef_structure == SC_FILE_EF_LINEAR_FIXED) + r = sc_asn1_put_tag(0x82, buf, 5, p, 8, &p); + else + r = sc_asn1_put_tag(0x82, buf, 1, p, 8, &p); + } else if (file->type == SC_FILE_TYPE_DF) { + buf[0] |= 0x38; + r = sc_asn1_put_tag(0x82, buf, 1, p, 8, &p); + } else + return SC_ERROR_NOT_SUPPORTED; + if (r != SC_SUCCESS) + return r; + /* set file id */ + buf[0] = (file->id >> 8) & 0xff; + buf[1] = file->id & 0xff; + r = sc_asn1_put_tag(0x83, buf, 2, p, 8, &p); + if (r != SC_SUCCESS) + return r; + /* set aid (for DF only) */ + if (file->type == SC_FILE_TYPE_DF && file->namelen != 0) { + r = sc_asn1_put_tag(0x84, file->name, file->namelen, p, 20, &p); + if (r != SC_SUCCESS) + return r; + } + /* set proprietary file attributes */ + buf[0] = 0x00; /* use default values */ + if (file->type == SC_FILE_TYPE_DF) + r = sc_asn1_put_tag(0x85, buf, 1, p, 8, &p); + else { + buf[1] = 0x00; + buf[2] = 0x00; + r = sc_asn1_put_tag(0x85, buf, 1, p, 8, &p); + } + if (r != SC_SUCCESS) + return r; + /* set ACs */ + len = 9; + r = cardos_acl_to_bytes(card, file, buf, &len); + if (r != SC_SUCCESS) + return r; + r = sc_asn1_put_tag(0x86, buf, len, p, 18, &p); + if (r != SC_SUCCESS) + return r; + /* finally set the length of the FCP */ + out[1] = p - out - 2; + + *outlen = p - out; + + return SC_SUCCESS; +} + +static int cardos_create_file(sc_card_t *card, sc_file_t *file) +{ + int r; + + SC_FUNC_CALLED(card->ctx, 1); + + if (card->type == SC_CARD_TYPE_ETOKEN_GENERIC) { + r = cardos_set_file_attributes(card, file); + if (r != SC_SUCCESS) + return r; + return iso_ops->create_file(card, file); + } else if (card->type == SC_CARD_TYPE_CARDOS_M4_2) { + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; + size_t len = sizeof(sbuf); + sc_apdu_t apdu; + + r = cardos_construct_fcp(card, file, sbuf, &len); + if (r < 0) { + sc_error(card->ctx, "unable to create FCP"); + return r; + } + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x00, 0x00); + apdu.lc = len; + apdu.datalen = len; + apdu.data = sbuf; + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + + return sc_check_sw(card, apdu.sw1, apdu.sw2); + } else + return SC_ERROR_NOT_SUPPORTED; } /* @@ -594,34 +722,26 @@ do_compute_signature(sc_card_t *card, const u8 *data, size_t datalen, { int r; sc_apdu_t apdu; - u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; - u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; - - if (datalen > SC_MAX_APDU_BUFFER_SIZE || - outlen > SC_MAX_APDU_BUFFER_SIZE) - return SC_ERROR_INTERNAL; /* INS: 0x2A PERFORM SECURITY OPERATION * P1: 0x9E Resp: Digital Signature * P2: 0x9A Cmd: Input for Digital Signature */ - sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x9E, 0x9A); - apdu.resp = rbuf; - apdu.le = outlen; - apdu.resplen = sizeof(rbuf); + sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x2A, 0x9E, 0x9A); + apdu.resp = out; + apdu.le = outlen; + apdu.resplen = outlen; - memcpy(sbuf, data, datalen); - apdu.data = sbuf; - apdu.lc = datalen; + apdu.data = data; + apdu.lc = datalen; apdu.datalen = datalen; apdu.sensitive = 1; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, r, "APDU transmit failed"); - if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { - memcpy(out, rbuf, outlen); - SC_FUNC_RETURN(card->ctx, 4, apdu.resplen); - } - SC_FUNC_RETURN(card->ctx, 4, sc_check_sw(card, apdu.sw1, apdu.sw2)); + if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) + SC_FUNC_RETURN(card->ctx, 4, apdu.resplen) + else + SC_FUNC_RETURN(card->ctx, 4, sc_check_sw(card, apdu.sw1, apdu.sw2)) } static int @@ -637,7 +757,7 @@ etoken_compute_signature(sc_card_t *card, const u8 *data, size_t datalen, ctx = card->ctx; SC_FUNC_CALLED(ctx, 1); - if (datalen > 255) + if (datalen > SC_MAX_APDU_BUFFER_SIZE) SC_FUNC_RETURN(card->ctx, 4, SC_ERROR_INVALID_ARGUMENTS); if (outlen < datalen) SC_FUNC_RETURN(card->ctx, 4, SC_ERROR_BUFFER_TOO_SMALL); @@ -688,7 +808,7 @@ etoken_lifecycle_get(sc_card_t *card, int *mode) SC_FUNC_CALLED(card->ctx, 1); - sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 01, 0x83); + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x83); apdu.cla = 0x00; apdu.le = 256; apdu.resplen = sizeof(rbuf); @@ -835,7 +955,6 @@ etoken_generate_key(sc_card_t *card, apdu.cla = 0x00; apdu.ins = 0x46; apdu.p1 = 0x00; - apdu.p2 = args->key_id;/* doc is not clear, it just says "ID" */ apdu.p2 = 0x00; apdu.data= data; apdu.datalen = apdu.lc = sizeof(data); @@ -848,7 +967,7 @@ etoken_generate_key(sc_card_t *card, return r; } -static int etoken_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) +static int cardos_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) { int r; sc_apdu_t apdu; @@ -897,7 +1016,7 @@ etoken_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) case SC_CARDCTL_LIFECYCLE_SET: return etoken_lifecycle_set(card, (int *) ptr); case SC_CARDCTL_GET_SERIALNR: - return etoken_get_serialnr(card, (sc_serial_number_t *)ptr); + return cardos_get_serialnr(card, (sc_serial_number_t *)ptr); } return SC_ERROR_NOT_SUPPORTED; } @@ -922,6 +1041,30 @@ etoken_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, return iso_ops->pin_cmd(card, data, tries_left); } +static int cardos_logout(sc_card_t *card) +{ + if (card->type == SC_CARD_TYPE_CARDOS_M4_2) { + sc_apdu_t apdu; + int r; + sc_path_t path; + + sc_format_path("3F00", &path); + r = sc_select_file(card, &path, NULL); + if (r != SC_SUCCESS) + return r; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xEA, 0x00, 0x00); + apdu.cla = 0x80; + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + + return sc_check_sw(card, apdu.sw1, apdu.sw2); + } else + return iso_ops->logout(card); +} + + /* eToken R2 supports WRITE_BINARY, PRO Tokens support UPDATE_BINARY */ @@ -934,7 +1077,7 @@ static struct sc_card_driver * sc_get_driver(void) etoken_ops.init = etoken_init; etoken_ops.finish = etoken_finish; etoken_ops.select_file = etoken_select_file; - etoken_ops.create_file = etoken_create_file; + etoken_ops.create_file = cardos_create_file; etoken_ops.set_security_env = etoken_set_security_env; etoken_ops.restore_security_env = etoken_restore_security_env; etoken_ops.compute_signature = etoken_compute_signature; @@ -943,6 +1086,7 @@ static struct sc_card_driver * sc_get_driver(void) etoken_ops.check_sw = etoken_check_sw; etoken_ops.card_ctl = etoken_card_ctl; etoken_ops.pin_cmd = etoken_pin_cmd; + etoken_ops.logout = cardos_logout; return &etoken_drv; } diff --git a/src/libopensc/cards.h b/src/libopensc/cards.h index a3c69fbf..e58ae754 100644 --- a/src/libopensc/cards.h +++ b/src/libopensc/cards.h @@ -39,6 +39,7 @@ enum { /* etoken driver */ SC_CARD_TYPE_ETOKEN_BASE = 1000, SC_CARD_TYPE_ETOKEN_GENERIC, + SC_CARD_TYPE_CARDOS_M4_2, /* flex/cyberflex drivers */ SC_CARD_TYPE_FLEX_BASE = 2000, diff --git a/src/pkcs15init/pkcs15-etoken.c b/src/pkcs15init/pkcs15-etoken.c index efe55393..7a5af66c 100644 --- a/src/pkcs15init/pkcs15-etoken.c +++ b/src/pkcs15init/pkcs15-etoken.c @@ -1,6 +1,7 @@ /* * CardOS specific operation for PKCS15 initialization * + * Copyright (C) 2005 Nils Larsch * Copyright (C) 2002 Olaf Kirch * * This library is free software; you can redistribute it and/or @@ -29,6 +30,8 @@ #include #include #include +#include +#include #include "pkcs15-init.h" #include "profile.h" @@ -42,14 +45,6 @@ struct tlv { unsigned char * current; unsigned char * next; }; -#define RSAKEY_MAX_BITS 1024 -#define RSAKEY_MAX_SIZE (RSAKEY_MAX_BITS/8) -struct rsakey { - struct bignum { - size_t len; - u8 data[RSAKEY_MAX_SIZE]; - } n, d; -}; /* * Local functions @@ -59,12 +54,14 @@ static int etoken_store_pin(sc_profile_t *profile, sc_card_t *card, const u8 *pin, size_t pin_len); static int etoken_create_sec_env(sc_profile_t *, sc_card_t *, unsigned int, unsigned int); -static int etoken_put_key(struct sc_profile *, sc_card_t *, +static int cardos_put_key(struct sc_profile *, sc_card_t *, int, sc_pkcs15_prkey_info_t *, struct sc_pkcs15_prkey_rsa *); -static int etoken_key_algorithm(unsigned int, int *); -static int etoken_extract_pubkey(sc_card_t *, int, - u8, sc_pkcs15_bignum_t *); +static int cardos_key_algorithm(unsigned int, size_t, int *); +static int cardos_extract_pubkey(sc_card_t *, sc_pkcs15_pubkey_t *, + sc_file_t *, int); +static int etoken_extract_pubkey(sc_card_t *card, int nr, u8 tag, + sc_pkcs15_bignum_t *bn); /* Object IDs for PIN objects. * SO PIN = 0x01, SO PUK = 0x02 @@ -82,8 +79,8 @@ static int etoken_extract_pubkey(sc_card_t *, int, #define ETOKEN_ALGO_RSA_PURE_SIG 0x8C #define ETOKEN_ALGO_RSA_SIG_SHA1 0xC8 #define ETOKEN_ALGO_RSA_PURE_SIG_SHA1 0xCC -#define ETOKEN_SIGN_RSA ETOKEN_ALGO_RSA_PURE_SIG -#define ETOKEN_DECIPHER_RSA ETOKEN_ALGO_RSA_PURE +#define CARDOS_ALGO_EXT_RSA_PURE 0x0a +#define CARDOS_ALGO_EXT_RSA_SIG_PURE 0x8a #define ETOKEN_ALGO_PIN 0x87 static inline void @@ -215,8 +212,7 @@ etoken_create_pin(sc_profile_t *profile, sc_card_t *card, sc_file_t *df, if (r >= 0) { r = etoken_store_pin(profile, card, - pin_info, puk_id, - pin, pin_len); + pin_info, puk_id, pin, pin_len); } return r; @@ -267,17 +263,39 @@ etoken_store_key(sc_profile_t *profile, sc_card_t *card, return SC_ERROR_NOT_SUPPORTED; } - if (etoken_key_algorithm(key_info->usage, &algorithm) < 0) { + if (cardos_key_algorithm(key_info->usage, key_info->modulus_length, &algorithm) < 0) { sc_error(card->ctx, "CardOS does not support keys " "that can both sign _and_ decrypt."); return SC_ERROR_NOT_SUPPORTED; } - r = etoken_put_key(profile, card, algorithm, key_info, &key->u.rsa); + r = cardos_put_key(profile, card, algorithm, key_info, &key->u.rsa); return r; } +static void init_key_object(struct sc_pkcs15_prkey_rsa *key, + u8 *data, size_t len) +{ + /* Create a key object, initializing components to 0xff */ + memset(key, 0x00, sizeof(*key)); + memset(data, 0xff, len); + key->modulus.data = data; + key->modulus.len = len; + key->d.data = data; + key->d.len = len; + key->p.len = len >> 1; + key->p.data = data; + key->q.len = len >> 1; + key->q.data = data; + key->iqmp.len = len >> 1; + key->iqmp.data = data; + key->dmp1.len = len >> 1; + key->dmp1.data = data; + key->dmq1.len = len >> 1; + key->dmq1.data = data; +} + /* * Key generation */ @@ -290,47 +308,46 @@ etoken_generate_key(sc_profile_t *profile, sc_card_t *card, struct sc_pkcs15_prkey_rsa key_obj; struct sc_cardctl_etoken_genkey_info args; struct sc_file *temp; - u8 abignum[RSAKEY_MAX_SIZE]; - unsigned int keybits; - int algorithm, r, delete_it = 0; + u8 abignum[256]; + int algorithm, r, delete_it = 0, use_ext_rsa = 0; + size_t keybits, rsa_max_size; - if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { - sc_error(card->ctx, "CardOS supports only RSA keys."); + if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) return SC_ERROR_NOT_SUPPORTED; + + rsa_max_size = (card->caps & SC_CARD_CAP_RSA_2048) ? 2048 : 1024; + keybits = key_info->modulus_length & ~7UL; + if (keybits > rsa_max_size) { + sc_error(card->ctx, "Unable to generate key, max size is %lu", rsa_max_size); + return SC_ERROR_INVALID_ARGUMENTS; } - if (etoken_key_algorithm(key_info->usage, &algorithm) < 0) { + if (keybits > 1024) + use_ext_rsa = 1; + + if (cardos_key_algorithm(key_info->usage, keybits, &algorithm) < 0) { sc_error(card->ctx, "CardOS does not support keys " "that can both sign _and_ decrypt."); return SC_ERROR_NOT_SUPPORTED; } - keybits = key_info->modulus_length & ~7UL; - if (keybits > RSAKEY_MAX_BITS) { - sc_error(card->ctx, "Unable to generate key, max size is %d", - RSAKEY_MAX_BITS); - return SC_ERROR_INVALID_ARGUMENTS; - } - if (sc_profile_get_file(profile, "tempfile", &temp) < 0) { sc_error(card->ctx, "Profile doesn't define temporary file " "for key generation."); return SC_ERROR_NOT_SUPPORTED; } - memset(pubkey, 0, sizeof(*pubkey)); + if (use_ext_rsa == 0) + temp->ef_structure = SC_FILE_EF_LINEAR_VARIABLE_TLV; + else + temp->ef_structure = SC_FILE_EF_TRANSPARENT; if ((r = sc_pkcs15init_create_file(profile, card, temp)) < 0) goto out; delete_it = 1; - /* Create a key object, initializing components to 0xff */ - memset(&key_obj, 0, sizeof(key_obj)); - memset(abignum, 0xFF, sizeof(abignum)); - key_obj.modulus.data = abignum; - key_obj.modulus.len = keybits >> 3; - key_obj.d.data = abignum; - key_obj.d.len = keybits >> 3; - r = etoken_put_key(profile, card, algorithm, key_info, &key_obj); + init_key_object(&key_obj, abignum, keybits >> 3); + + r = cardos_put_key(profile, card, algorithm, key_info, &key_obj); if (r < 0) goto out; @@ -342,21 +359,12 @@ etoken_generate_key(sc_profile_t *profile, sc_card_t *card, if (r < 0) goto out; - /* extract public key from file and delete it */ - if ((r = sc_select_file(card, &temp->path, NULL)) < 0) - goto out; - r = etoken_extract_pubkey(card, 1, 0x10, &pubkey->u.rsa.modulus); - if (r < 0) - goto out; - r = etoken_extract_pubkey(card, 2, 0x11, &pubkey->u.rsa.exponent); - if (r < 0) - goto out; - pubkey->algorithm = SC_ALGORITHM_RSA; - -out: if (delete_it) { + r = cardos_extract_pubkey(card, pubkey, temp, use_ext_rsa); +out: + if (delete_it != 0) sc_pkcs15init_rmdir(card, profile, temp); - } sc_file_free(temp); + if (r < 0) { if (pubkey->u.rsa.modulus.data) free (pubkey->u.rsa.modulus.data); @@ -379,13 +387,17 @@ etoken_store_pin(sc_profile_t *profile, sc_card_t *card, unsigned char pinpadded[16]; struct tlv tlv; unsigned int attempts, minlen, maxlen; + int r; /* We need to do padding because pkcs15-lib.c does it. * Would be nice to have a flag in the profile that says * "no padding required". */ maxlen = MIN(profile->pin_maxlen, sizeof(pinpadded)); - if (pin_len > maxlen) - pin_len = maxlen; + if (pin_len > maxlen) { + sc_error(card->ctx, "invalid pin length: %u (max %u)\n", + pin_len, maxlen); + return SC_ERROR_INVALID_ARGUMENTS; + } memcpy(pinpadded, pin, pin_len); while (pin_len < maxlen) pinpadded[pin_len++] = profile->pin_pad_char; @@ -416,9 +428,12 @@ etoken_store_pin(sc_profile_t *profile, sc_card_t *card, tlv_add(&tlv, 0xff); /* DEK: not documented, no idea what it means */ - tlv_add(&tlv, 0x00); + tlv_add(&tlv, 0xff); - /* ARA counter: Nils says this is the userConsent field */ + /* ARA counter: number of times the test object can be used before + * another verification is required (~ user consent) + * (0x00 unlimited usage) + */ tlv_add(&tlv, 0x00); tlv_add(&tlv, minlen); /* minlen */ @@ -437,6 +452,11 @@ etoken_store_pin(sc_profile_t *profile, sc_card_t *card, args.data = buffer; args.len = tlv_len(&tlv); + /* ensure we are in the correct lifecycle */ + r = sc_pkcs15init_set_lifecycle(card, SC_CARDCTRL_LIFECYCLE_ADMIN); + if (r < 0 && r != SC_ERROR_NOT_SUPPORTED) + return r; + return sc_card_ctl(card, SC_CARDCTL_ETOKEN_PUT_DATA_OCI, &args); } @@ -450,6 +470,7 @@ etoken_create_sec_env(struct sc_profile *profile, sc_card_t *card, struct sc_cardctl_etoken_obj_info args; struct tlv tlv; unsigned char buffer[64]; + int r; tlv_init(&tlv, buffer, sizeof(buffer)); tlv_next(&tlv, 0x83); @@ -469,6 +490,12 @@ etoken_create_sec_env(struct sc_profile *profile, sc_card_t *card, args.data = buffer; args.len = tlv_len(&tlv); + + /* ensure we are in the correct lifecycle */ + r = sc_pkcs15init_set_lifecycle(card, SC_CARDCTRL_LIFECYCLE_ADMIN); + if (r < 0 && r != SC_ERROR_NOT_SUPPORTED) + return r; + return sc_card_ctl(card, SC_CARDCTL_ETOKEN_PUT_DATA_SECI, &args); } @@ -482,17 +509,22 @@ etoken_create_sec_env(struct sc_profile *profile, sc_card_t *card, #define USAGE_ANY_DECIPHER (SC_PKCS15_PRKEY_USAGE_DECRYPT|\ SC_PKCS15_PRKEY_USAGE_UNWRAP) -static int -etoken_key_algorithm(unsigned int usage, int *algop) +static int cardos_key_algorithm(unsigned int usage, size_t keylen, int *algop) { int sign = 0, decipher = 0; if (usage & USAGE_ANY_SIGN) { - *algop = ETOKEN_SIGN_RSA; + if (keylen <= 1024) + *algop = ETOKEN_ALGO_RSA_PURE_SIG; + else + *algop = CARDOS_ALGO_EXT_RSA_SIG_PURE; sign = 1; } if (usage & USAGE_ANY_DECIPHER) { - *algop = ETOKEN_DECIPHER_RSA; + if (keylen <= 1024) + *algop = ETOKEN_ALGO_RSA_PURE; + else + *algop = CARDOS_ALGO_EXT_RSA_PURE; decipher = 1; } return (sign == decipher)? -1 : 0; @@ -509,12 +541,15 @@ etoken_store_key_component(sc_card_t *card, unsigned int key_id, unsigned int pin_id, unsigned int num, const u8 *data, size_t len, - int last) + int last, int use_prefix) { struct sc_cardctl_etoken_obj_info args; struct tlv tlv; unsigned char buffer[256]; +#if SET_SM_BYTES unsigned int n; +#endif + int r; /* Initialize the TLV encoder */ tlv_init(&tlv, buffer, sizeof(buffer)); @@ -540,35 +575,46 @@ etoken_store_key_component(sc_card_t *card, tlv_add(&tlv, pin_id); /* AC USE */ tlv_add(&tlv, pin_id); /* AC CHANGE */ tlv_add(&tlv, pin_id); /* UNKNOWN */ - /* The next 4 AC bytes are sent by the eToken run-time - * as well, but aren't documented anywhere. - * Key generation won't work without them, however. */ - tlv_add(&tlv, 0); - tlv_add(&tlv, 0); - tlv_add(&tlv, 0); + tlv_add(&tlv, 0); /* rfu */ + tlv_add(&tlv, 0); /* rfu */ + tlv_add(&tlv, 0); /* rfu */ +#if 0 + tlv_add(&tlv, pin_id); /* AC GENKEY */ +#else tlv_add(&tlv, 0); +#endif +#if SET_SM_BYTES + /* it shouldn't be necessary to set the default value */ /* SM bytes */ tlv_next(&tlv, 0x8B); for (n = 0; n < 16; n++) tlv_add(&tlv, 0xFF); +#endif /* key component */ tlv_next(&tlv, 0x8f); - tlv_add(&tlv, len+1); - tlv_add(&tlv, 0); + if (use_prefix != 0) { + tlv_add(&tlv, len+1); + tlv_add(&tlv, 0); + } while (len--) tlv_add(&tlv, *data++); args.data = buffer; args.len = tlv_len(&tlv); + + /* ensure we are in the correct lifecycle */ + r = sc_pkcs15init_set_lifecycle(card, SC_CARDCTRL_LIFECYCLE_ADMIN); + if (r < 0 && r != SC_ERROR_NOT_SUPPORTED) + return r; + return sc_card_ctl(card, SC_CARDCTL_ETOKEN_PUT_DATA_OCI, &args); } -static int -etoken_put_key(sc_profile_t *profile, sc_card_t *card, - int algorithm, sc_pkcs15_prkey_info_t *key_info, - struct sc_pkcs15_prkey_rsa *key) +static int cardos_put_key(sc_profile_t *profile, sc_card_t *card, + int algorithm, sc_pkcs15_prkey_info_t *key_info, + struct sc_pkcs15_prkey_rsa *key) { int r, key_id, pin_id; @@ -577,12 +623,33 @@ etoken_put_key(sc_profile_t *profile, sc_card_t *card, if (pin_id < 0) pin_id = 0; - r = etoken_store_key_component(card, algorithm, key_id, pin_id, 0, - key->modulus.data, key->modulus.len, 0); - if (r < 0) - return r; - r = etoken_store_key_component(card, algorithm, key_id, pin_id, 1, - key->d.data, key->d.len, 1); + if (key_info->modulus_length > 1024 && card->type == SC_CARD_TYPE_CARDOS_M4_2) { + r = etoken_store_key_component(card, algorithm, key_id, pin_id, 0, + key->p.data, key->p.len, 0, 0); + if (r != SC_SUCCESS) + return r; + r = etoken_store_key_component(card, algorithm, key_id, pin_id, 1, + key->q.data, key->q.len, 0, 0); + if (r != SC_SUCCESS) + return r; + r = etoken_store_key_component(card, algorithm, key_id, pin_id, 2, + key->dmp1.data, key->dmp1.len, 0, 0); + if (r != SC_SUCCESS) + return r; + r = etoken_store_key_component(card, algorithm, key_id, pin_id, 3, + key->dmq1.data, key->dmq1.len, 0, 0); + if (r != SC_SUCCESS) + return r; + r = etoken_store_key_component(card, algorithm, key_id, pin_id, 4, + key->iqmp.data, key->iqmp.len, 1, 0); + } else { + r = etoken_store_key_component(card, algorithm, key_id, pin_id, 0, + key->modulus.data, key->modulus.len, 0, 1); + if (r != SC_SUCCESS) + return r; + r = etoken_store_key_component(card, algorithm, key_id, pin_id, 1, + key->d.data, key->d.len, 1, 1); + } return r; } @@ -591,6 +658,52 @@ etoken_put_key(sc_profile_t *profile, sc_card_t *card, * Extract a key component from the public key file populated by * GENERATE KEY PAIR */ +static int parse_ext_pubkey_file(sc_card_t *card, const u8 *data, size_t len, + sc_pkcs15_pubkey_t *pubkey) +{ + const u8 *p; + size_t ilen = 0, tlen = 0, i; + + if (data == NULL || len < 32) + return SC_ERROR_INVALID_ARGUMENTS; + if (*data++ != 0x7f || *data++ != 0x49) { + sc_error(card->ctx, "invalid public key data: missing tag"); + return SC_ERROR_INVALID_ARGUMENTS; + } + len -= 2; + tlen = *data++ & 0x7f; + if (tlen > 3) { + sc_error(card->ctx, "invalid public key data: invalid tag length"); + return SC_ERROR_INVALID_ARGUMENTS; + } + for (i = 0; i < tlen; i++) + ilen += (*data++) << (8 * (tlen - i - 1)); + + p = sc_asn1_find_tag(card->ctx, data, ilen, 0x81, &tlen); + if (p == NULL) { + sc_error(card->ctx, "invalid public key data: missing modulus"); + return SC_ERROR_INTERNAL; + } + pubkey->u.rsa.modulus.len = tlen; + pubkey->u.rsa.modulus.data = malloc(tlen); + if (pubkey->u.rsa.modulus.data == NULL) + return SC_ERROR_OUT_OF_MEMORY; + memcpy(pubkey->u.rsa.modulus.data, p, tlen); + + p = sc_asn1_find_tag(card->ctx, data, ilen, 0x82, &tlen); + if (p == NULL) { + sc_error(card->ctx, "invalid public key data: missing exponent"); + return SC_ERROR_INTERNAL; + } + pubkey->u.rsa.exponent.len = tlen; + pubkey->u.rsa.exponent.data = malloc(tlen); + if (pubkey->u.rsa.exponent.data == NULL) + return SC_ERROR_OUT_OF_MEMORY; + memcpy(pubkey->u.rsa.exponent.data, p, tlen); + + return SC_SUCCESS; +} + static int etoken_extract_pubkey(sc_card_t *card, int nr, u8 tag, sc_pkcs15_bignum_t *bn) @@ -603,12 +716,47 @@ etoken_extract_pubkey(sc_card_t *card, int nr, u8 tag, return r; count = r - 4; if (count <= 0 || buf[0] != tag || buf[1] != count + 2 - || buf[2] != count + 1 || buf[3] != 0) + || buf[2] != count + 1 || buf[3] != 0) return SC_ERROR_INTERNAL; bn->len = count; bn->data = (u8 *) malloc(count); + if (bn->data == NULL) + return SC_ERROR_OUT_OF_MEMORY; memcpy(bn->data, buf + 4, count); - return 0; + return SC_SUCCESS; +} + +static int cardos_extract_pubkey(sc_card_t *card, sc_pkcs15_pubkey_t *pubkey, + sc_file_t *tfile, int use_ext_rsa) +{ + int r; + + memset(pubkey, 0, sizeof(*pubkey)); + + r = sc_select_file(card, &tfile->path, NULL); + if (r != SC_SUCCESS) + return r; + + if (use_ext_rsa == 0) { + r = etoken_extract_pubkey(card, 1, 0x10, &pubkey->u.rsa.modulus); + if (r != SC_SUCCESS) + return r; + r = etoken_extract_pubkey(card, 2, 0x11, &pubkey->u.rsa.exponent); + } else { + u8 *buf; + + buf = malloc(tfile->size); + if (buf == NULL) + return SC_ERROR_OUT_OF_MEMORY; + r = sc_read_binary(card, 0, buf, tfile->size, 0); + if (r > 0) + r = parse_ext_pubkey_file(card, buf, (size_t)r, pubkey); + free(buf); + } + + pubkey->algorithm = SC_ALGORITHM_RSA; + + return r; } static struct sc_pkcs15init_operations sc_pkcs15init_etoken_operations = {