diff --git a/src/libopensc/Makefile.am b/src/libopensc/Makefile.am index 01af7503..9b89cc1a 100644 --- a/src/libopensc/Makefile.am +++ b/src/libopensc/Makefile.am @@ -40,7 +40,7 @@ libopensc_la_SOURCES = \ card-oberthur.c card-belpic.c card-atrust-acos.c card-entersafe.c \ card-incrypto34.c card-piv.c card-muscle.c card-acos5.c \ card-asepcos.c card-akis.c card-gemsafeV1.c card-rutoken.c \ - card-rtecp.c card-westcos.c \ + card-rtecp.c card-westcos.c card-myeid.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 9ec97f16..343bcb35 100644 --- a/src/libopensc/Makefile.mak +++ b/src/libopensc/Makefile.mak @@ -29,7 +29,7 @@ OBJECTS = \ card-oberthur.obj card-belpic.obj card-atrust-acos.obj card-entersafe.obj \ card-incrypto34.obj card-piv.obj card-muscle.obj card-acos5.obj \ card-asepcos.obj card-akis.obj card-gemsafeV1.obj card-rutoken.obj \ - card-rtecp.obj \ + card-rtecp.obj card-myeid.obj \ \ p15emu-westcos.obj \ pkcs15-openpgp.obj pkcs15-infocamere.obj pkcs15-starcert.obj \ diff --git a/src/libopensc/card-entersafe.c b/src/libopensc/card-entersafe.c index 49c872d4..96455c8d 100644 --- a/src/libopensc/card-entersafe.c +++ b/src/libopensc/card-entersafe.c @@ -169,7 +169,6 @@ static int entersafe_cipher_apdu(sc_card_t *card, sc_apdu_t *apdu, u8 *buff, size_t buffsize) { EVP_CIPHER_CTX ctx; - int i,r; u8 iv[8]={0}; SC_FUNC_CALLED(card->ctx, 1); @@ -223,7 +222,7 @@ static int entersafe_mac_apdu(sc_card_t *card, sc_apdu_t *apdu, { int r; u8 iv[8]; - u8 *tmp=0,*tmp_rounded; + u8 *tmp=0,*tmp_rounded=NULL; size_t tmpsize=0,tmpsize_rounded=0,outl=0; EVP_CIPHER_CTX ctx; @@ -430,8 +429,6 @@ static int entersafe_process_fci(struct sc_card *card, struct sc_file *file, const u8 *buf, size_t buflen) { int r; - const u8 *tag = NULL, *p = buf; - size_t taglen, len = buflen; assert(file); SC_FUNC_CALLED(card->ctx, 1); @@ -493,7 +490,7 @@ static int entersafe_select_aid(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out) { - int r; + int r = 0; if (card->cache_valid && card->cache.current_path.type == SC_PATH_TYPE_DF_NAME @@ -772,7 +769,6 @@ static int entersafe_create_file(sc_card_t *card, sc_file_t *file) SC_FUNC_CALLED(card->ctx, 1); if (file->type == SC_FILE_TYPE_WORKING_EF) { - int r; sc_entersafe_create_data data; memset(&data,0,sizeof(data)); @@ -1127,7 +1123,6 @@ static int entersafe_write_rsa_key_factor(sc_card_t *card, { int r; sc_apdu_t apdu; - u8 sbuff[SC_MAX_APDU_BUFFER_SIZE]; SC_FUNC_CALLED(card->ctx, 1); @@ -1148,6 +1143,8 @@ static int entersafe_write_rsa_key_factor(sc_card_t *card, } {/* Write 'x'; */ + u8 sbuff[SC_MAX_APDU_BUFFER_SIZE]; + sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0x46,factor,0x00); memcpy(sbuff,data.data,data.len); @@ -1378,6 +1375,7 @@ static int entersafe_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) SC_FUNC_RETURN(card->ctx,4,SC_SUCCESS); } +#if 0 static int entersafe_preinstall_rsa_1024(sc_card_t *card,u8 key_id) { u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; @@ -1440,6 +1438,7 @@ static int entersafe_preinstall_rsa_1024(sc_card_t *card,u8 key_id) SC_FUNC_RETURN(card->ctx,4,SC_SUCCESS); } +#endif static int entersafe_preinstall_rsa_2048(sc_card_t *card,u8 key_id) { @@ -1591,6 +1590,7 @@ static int entersafe_preinstall_keys(sc_card_t *card,int (*install_rsa)(sc_card_ SC_FUNC_RETURN(card->ctx,4,SC_SUCCESS); } +#if 0 static int entersafe_card_ctl_1024(sc_card_t *card, unsigned long cmd, void *ptr) { sc_entersafe_create_data * tmp = (sc_entersafe_create_data *)ptr; @@ -1621,6 +1621,7 @@ static int entersafe_card_ctl_1024(sc_card_t *card, unsigned long cmd, void *ptr return SC_ERROR_NOT_SUPPORTED; } } +#endif static int entersafe_card_ctl_2048(sc_card_t *card, unsigned long cmd, void *ptr) { diff --git a/src/libopensc/card-myeid.c b/src/libopensc/card-myeid.c new file mode 100644 index 00000000..acfa68a3 --- /dev/null +++ b/src/libopensc/card-myeid.c @@ -0,0 +1,981 @@ +/* + * card-myeid.c + * + * Copyright (C) 2008-2009 Aventra Ltd. + * + * 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 "internal.h" +#include "asn1.h" +#include "types.h" +#include "cardctl.h" +#include +#include + + +static enum LOAD_KEY { + LOAD_KEY_MODULUS = 0x80, + LOAD_KEY_PUBLIC_EXPONENT = 0x81, + LOAD_KEY_PRIME_P = 0x83, + LOAD_KEY_PRIME_Q = 0x84, + LOAD_KEY_DP1 = 0x85, + LOAD_KEY_DQ1 = 0x86, + LOAD_KEY_INVQ = 0x87 +}; + +static struct sc_card_operations myeid_ops; +static struct sc_card_driver myeid_drv = { + "MyEID cards with PKCS#15 applet", + "myeid", + &myeid_ops +}; + +static const char *myeid_atrs[] = { + "3B:F5:18:00:FF:81:31:FE:45:4D:79:45:49:44:65", + "3B:F5:18:00:00:81:31:FE:45:4D:79:45:49:44:9A", + NULL +}; + + +static int myeid_finish(struct sc_card *card) +{ + return 0; +} + +static int myeid_match_card(struct sc_card *card) +{ + int i, match = -1; + + for (i = 0; myeid_atrs[i] != NULL; i++) + { + u8 defatr[SC_MAX_ATR_SIZE]; + size_t len = sizeof(defatr); + const char *atrp = myeid_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) + return 0; + + return 1; +} + +static int myeid_init(struct sc_card *card) +{ + unsigned long flags =0; + + 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, 0); + _sc_card_add_rsa_alg(card, 2048, flags, 0); + + /* State that we have an RNG */ + card->caps |= SC_CARD_CAP_RNG; + + return 0; +} + +static const struct sc_card_operations *iso_ops = NULL; + +static int acl_to_byte(const struct sc_acl_entry *e) +{ + switch (e->method) { + case SC_AC_NONE: + return 0x00; + case SC_AC_CHV: + case SC_AC_TERM: + case SC_AC_AUT: + if (e->key_ref == SC_AC_KEY_REF_NONE) + return -1; + if (e->key_ref < 1 || e->key_ref > 14) + return -1; + return e->key_ref; + case SC_AC_NEVER: + return 0x0F; + } + return 0x00; +} + +static void add_acl_entry(struct sc_file *file, int op, u8 byte) +{ + unsigned int method, key_ref = SC_AC_KEY_REF_NONE; + + switch (byte) + { + case 0: + method = SC_AC_NONE; + break; + case 15: + method = SC_AC_NEVER; + break; + default: + method = SC_AC_CHV; + key_ref = byte; + break; + } + sc_file_add_acl_entry(file, op, method, key_ref); +} + +static void parse_sec_attr(struct sc_file *file, const u8 *buf, size_t len) +{ + int i; + const int df_ops[4] = + { SC_AC_OP_CREATE, SC_AC_OP_CREATE, SC_AC_OP_DELETE, -1 }; + const int ef_ops[4] = + { SC_AC_OP_READ, SC_AC_OP_UPDATE, SC_AC_OP_DELETE, -1 }; + const int key_ops[4] = + { SC_AC_OP_CRYPTO, SC_AC_OP_UPDATE, SC_AC_OP_DELETE, SC_AC_OP_CRYPTO }; + + const int *ops; + + if (len < 2) + 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: + ops = key_ops; + break; + } + + for (i = 0; i < 4; i++) + { + if (ops[i] == -1) + continue; + if ((i & 1) == 0) + add_acl_entry(file, ops[i], (u8)(buf[i / 2] >> 4)); + else + add_acl_entry(file, ops[i], (u8)(buf[i / 2] & 0x0F)); + } +} + +static int myeid_select_file(struct sc_card *card, const struct sc_path *in_path, + struct sc_file **file) +{ + int r; + + r = iso_ops->select_file(card, in_path, file); + if (r) + return r; + + if (file != NULL) { + parse_sec_attr(*file, (*file)->sec_attr, (*file)->sec_attr_len); + } + + return 0; +} + +static int myeid_read_binary(struct sc_card *card, unsigned int idx, + u8 * buf, size_t count, unsigned long flags) +{ + return iso_ops->read_binary(card, idx, buf, count, flags); +} + +static int myeid_list_files(struct sc_card *card, u8 *buf, size_t buflen) +{ + struct sc_apdu apdu; + int r,i; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xCA, 0x01, 0xA1); + apdu.resp = buf; + apdu.resplen = buflen; + apdu.le = buflen > 256 ? 256 : buflen; + + r = sc_transmit_apdu(card, &apdu); + + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + if (apdu.resplen == 0) + return sc_check_sw(card, apdu.sw1, apdu.sw2); + return apdu.resplen; +} + +static int myeid_process_fci(struct sc_card *card, struct sc_file *file, + const u8 *buf, size_t buflen) +{ + size_t taglen = 0; + const u8 *tag = NULL; + int r ; + + r = iso_ops->process_fci(card, file, buf, buflen); + if (r < 0) + return r; + + if(file->type == SC_FILE_EF_UNKNOWN) + { + tag = sc_asn1_find_tag(NULL, buf, buflen, 0x82, &taglen); + if (tag != NULL && taglen > 0 && *tag == 17) + { + file->type = SC_FILE_TYPE_INTERNAL_EF; + } + } + + return 0; +} + +static int encode_file_structure(sc_card_t *card, const sc_file_t *file, + u8 *out, size_t *outlen) +{ + u8 buf[40]; + int i; + + /* PrivateKey + * 0E0000019 6217 81020400 820111 83024B01 8603000000 85028000 8A0100 RESULT 6984 + * 6217 81020400 820111 83024B01 8603000000 85021000 8A0100 */ + memset(buf,0x0,sizeof(buf)); + + buf[0] = 0x62; + buf[1] = 0x17; + /* File size */ + buf[2] = (SC_FILE_TYPE_WORKING_EF == file->type ?0x80:0x81); + buf[3] = 0x02; + buf[4] = (file->size >> 8) & 0xFF; + buf[5] = file->size & 0xFF; + + /* File Description tag */ + buf[6] = 0x82; + buf[7] = 0x01; + buf[8] = 0x01; + + /* File Identifier tag */ + buf[9] = 0x83; + buf[10] = 0x02; + buf[11] = (file->id >> 8) & 0xFF; + buf[12] = file->id & 0xFF; + + /* Security Attributes Tag */ + buf[13] = 0x86; + buf[14] = 0x03; + buf[15] = 0x0; + buf[16] = 0x0; + buf[17] = 0x0; + + if (file->sec_attr_len == 3 && file->sec_attr) + { + buf[15] = file->sec_attr[0]; + buf[16] = file->sec_attr[1]; + buf[17] = file->sec_attr[2]; + } + + /* Proprietary Information */ + buf[18] = 0x85; + buf[19] = 0x02; + /* AC right to clear default 0 */ + buf[20] = (SC_FILE_TYPE_INTERNAL_EF == file->type ?0x0:0x80); + buf[21] = 0x0; + + /* Life Cycle Status tag */ + buf[22] = 0x8A; + buf[23] = 0x01; + buf[24] = 0x0; /* RFU */ + + switch (file->type) + { + case SC_FILE_TYPE_WORKING_EF: + break; + + case SC_FILE_TYPE_INTERNAL_EF: + buf[8] = 0x11; + break; + + case SC_FILE_TYPE_DF: + buf[8] = 0x38; + if(file->namelen > 0 && file->namelen <= 16) + { + buf[25] = 0x84; + buf[26] = (u8)file->namelen; + for(i=0;i < (int)file->namelen;i++) + { + buf[i + 26] = file->name[i]; + } + buf[1] = 0x19 + file->namelen + 2; + } + break; + default: + sc_debug(card->ctx, "Unknown file type\n"); + return SC_ERROR_INVALID_ARGUMENTS; + } + + *outlen = buf[1]+2; + memcpy(out, buf, *outlen); + + return 0; +} + +static int myeid_create_file(struct sc_card *card, struct sc_file *file) +{ + sc_apdu_t apdu; + u8 sbuf[32]; + size_t buflen; + int r; + + 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; + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + if (apdu.sw1 == 0x6A && apdu.sw2 == 0x89) + return SC_ERROR_FILE_ALREADY_EXISTS; + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + SC_TEST_RET(card->ctx, r, "Card returned error"); + + return 0; +} + +/* no record oriented file services */ +static int myeid_read_record_unsupp(struct sc_card *card, unsigned int rec_nr, + u8 *buf, size_t count, unsigned long flags) +{ + return SC_ERROR_NOT_SUPPORTED; +} + +static int myeid_wrupd_record_unsupp(struct sc_card *card, unsigned int rec_nr, + const u8 *buf, size_t count, unsigned long flags) +{ + return SC_ERROR_NOT_SUPPORTED; +} + +static int myeid_append_record_unsupp(struct sc_card *card, const u8 *buf, + size_t count, unsigned long flags) +{ + return SC_ERROR_NOT_SUPPORTED; +} + + +static int myeid_write_binary(struct sc_card *card, unsigned int idx, + const u8 *buf, size_t count, unsigned long flags) +{ + return iso_ops->write_binary(card, idx, buf, count, flags); +} + + +static int myeid_update_binary(struct sc_card *card, unsigned int idx, + const u8 *buf, size_t count, unsigned long flags) +{ + return iso_ops->update_binary(card, idx, buf, count, flags); +} + +static int myeid_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) + { + sc_debug(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); + apdu.cla = 0xA0; + + 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 int myeid_set_security_env2(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; + + assert(card != NULL && env != NULL); + + + if (env->flags & SC_SEC_ENV_KEY_REF_ASYMMETRIC) + { + sc_debug(card->ctx, "asymmetric keyref not supported.\n"); + return SC_ERROR_NOT_SUPPORTED; + } + if (se_num > 0) + { + sc_debug(card->ctx, "restore security environment not supported.\n"); + return SC_ERROR_NOT_SUPPORTED; + } + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0, 0); + switch (env->operation) + { + case SC_SEC_OPERATION_DECIPHER: + apdu.p1 = 0x41; + apdu.p2 = 0xB8; + break; + case SC_SEC_OPERATION_SIGN: + apdu.p1 = 0x41; + apdu.p2 = 0xB6; + break; + default: + return SC_ERROR_INVALID_ARGUMENTS; + } + apdu.le = 0; + p = sbuf; + if (env->flags & SC_SEC_ENV_ALG_REF_PRESENT) + { + *p++ = 0x80; /* algorithm reference */ + *p++ = 0x01; + *p++ = env->algorithm_ref & 0xFF; + } + if (env->flags & SC_SEC_ENV_FILE_REF_PRESENT) + { + *p++ = 0x81; + *p++ = 2; + memcpy(p, env->file_ref.value, 2); + p += 2; + } + if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) + { + *p++ = 0x84; + *p++ = 1; + *p++ = 0; + } + r = p - sbuf; + apdu.lc = r; + apdu.datalen = r; + apdu.data = sbuf; + apdu.resplen = 0; + if (se_num > 0) { + r = sc_lock(card); + SC_TEST_RET(card->ctx, r, "sc_lock() failed"); + locked = 1; + } + if (apdu.datalen != 0) + { + r = sc_transmit_apdu(card, &apdu); + if (r) + { + sc_perror(card->ctx, r, "APDU transmit failed"); + goto err; + } + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + if (r) + { + sc_perror(card->ctx, r, "Card returned error"); + goto err; + } + } + if (se_num <= 0) + return 0; + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0xF2, se_num); + r = sc_transmit_apdu(card, &apdu); + sc_unlock(card); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + return sc_check_sw(card, apdu.sw1, apdu.sw2); +err: + if (locked) + sc_unlock(card); + return r; +} + +static int myeid_set_security_env(struct sc_card *card, + const struct sc_security_env *env, int se_num) +{ + if (env->flags & SC_SEC_ENV_ALG_PRESENT) + { + sc_security_env_t tmp; + + tmp = *env; + tmp.flags &= ~SC_SEC_ENV_ALG_PRESENT; + tmp.flags |= SC_SEC_ENV_ALG_REF_PRESENT; + if (tmp.algorithm != SC_ALGORITHM_RSA) + { + sc_debug(card->ctx, "Only RSA algorithm supported.\n"); + return SC_ERROR_NOT_SUPPORTED; + } + + 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 (tmp.algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) + tmp.algorithm_ref |= 0x10; + return myeid_set_security_env2(card, &tmp, se_num); + } + return myeid_set_security_env2(card, env, se_num); +} + +static int myeid_compute_signature(struct sc_card *card, const u8 * data, + size_t datalen, u8 * out, size_t outlen) +{ + int r; + struct sc_apdu apdu; + u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; + + assert(card != NULL && data != NULL && out != NULL); + if (datalen > 256) + SC_FUNC_RETURN(card->ctx, 4, SC_ERROR_INVALID_ARGUMENTS); + + /* 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.resplen = sizeof(rbuf); + apdu.le = 256; + if (datalen == 256) + { + apdu.p2 = data[0]; + memcpy(sbuf, data+1, datalen-1); + apdu.lc = datalen - 1; + apdu.datalen = datalen - 1; + } + else + { + memcpy(sbuf, data, datalen); + apdu.lc = datalen; + apdu.datalen = datalen; + } + + apdu.data = sbuf; + 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) + { + int len = apdu.resplen > outlen ? outlen : apdu.resplen; + + memcpy(out, apdu.resp, len); + SC_FUNC_RETURN(card->ctx, 4, len); + } + + SC_FUNC_RETURN(card->ctx, 4, sc_check_sw(card, apdu.sw1, apdu.sw2)); +} + +static int myeid_decipher(struct sc_card *card, const u8 * crgram, + size_t crgram_len, u8 * out, size_t outlen) +{ + int r; + struct sc_apdu apdu; + u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; + + assert(card != NULL && crgram != NULL && out != NULL); + SC_FUNC_CALLED(card->ctx, 2); + if (crgram_len > 256) + SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_INVALID_ARGUMENTS); + + /* INS: 0x2A PERFORM SECURITY OPERATION + * P1: 0x80 Resp: Plain value + * P2: 0x86 Cmd: Padding indicator byte followed by cryptogram */ + sc_format_apdu(card, &apdu, + (crgram_len < 256) ? SC_APDU_CASE_4_SHORT : SC_APDU_CASE_3_SHORT, + 0x2A, 0x80, 0x86); + + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + apdu.le = crgram_len; + apdu.sensitive = 1; + + if (crgram_len == 256) + { apdu.le = 0; + /* padding indicator byte, 0x81 = first half of 2048 bit cryptogram */ + sbuf[0] = 0x81; + memcpy(sbuf + 1, crgram, crgram_len / 2); + apdu.lc = crgram_len / 2 + 1; + } + else + { + sbuf[0] = 0; /* padding indicator byte, 0x00 = No further indication */ + memcpy(sbuf + 1, crgram, crgram_len); + apdu.lc = crgram_len + 1; + } + + apdu.datalen = apdu.lc; apdu.data = sbuf; + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) + { + if (crgram_len == 256) + { + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, + 0x2A, 0x80, 0x86); + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + apdu.le = crgram_len; + apdu.sensitive = 1; + /* padding indicator byte, + * 0x82 = Second half of 2048 bit cryptogram */ + sbuf[0] = 0x82; + memcpy(sbuf + 1, crgram + crgram_len / 2, crgram_len / 2); + apdu.lc = crgram_len / 2 + 1; + apdu.datalen = apdu.lc; + apdu.data = sbuf; + + r = sc_transmit_apdu(card, &apdu); + + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + + if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) + { + int len = apdu.resplen > outlen ? outlen : apdu.resplen; + memcpy(out, apdu.resp, len); + SC_FUNC_RETURN(card->ctx, 2, len); + } + } + else + { + int len = apdu.resplen > outlen ? outlen : apdu.resplen; + + memcpy(out, apdu.resp, len); + SC_FUNC_RETURN(card->ctx, 2, len); + } + } + SC_FUNC_RETURN(card->ctx, 2, sc_check_sw(card, apdu.sw1, apdu.sw2)); +} + +/* Write internal data, e.g. add default pin-records to pin */ +static int myeid_putdata(struct sc_card *card, struct sc_cardctl_myeid_data_obj* data_obj) +{ + int r; + struct sc_apdu apdu; + + SC_FUNC_CALLED(card->ctx, 1); + + memset(&apdu, 0, sizeof(apdu)); + apdu.cse = SC_APDU_CASE_3_SHORT; + apdu.cla = 0x00; + apdu.ins = 0xDA; + apdu.p1 = data_obj->P1; + apdu.p2 = data_obj->P2; + apdu.lc = data_obj->DataLen; + apdu.datalen = data_obj->DataLen; + apdu.data = data_obj->Data; + + 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, "PUT_DATA returned error"); + + SC_FUNC_RETURN(card->ctx, 1, r); +} + +/* Read internal data, e.g. get RSA public key */ +static int myeid_getdata(struct sc_card *card, struct sc_cardctl_myeid_data_obj* data_obj) +{ + int r; + struct sc_apdu apdu; + + SC_FUNC_CALLED(card->ctx, 1); + + memset(&apdu, 0, sizeof(apdu)); + apdu.cse = SC_APDU_CASE_2_SHORT; + apdu.cla = 0x00; + apdu.ins = 0xCA; /* GET DATA */ + apdu.p1 = data_obj->P1; + apdu.p2 = data_obj->P2; + apdu.lc = 0; + apdu.datalen = 0; + apdu.data = data_obj->Data; + + apdu.le = 256; + apdu.resp = data_obj->Data; + apdu.resplen = data_obj->DataLen; + + 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, "GET_DATA returned error"); + + if (apdu.resplen > data_obj->DataLen) + r = SC_ERROR_WRONG_LENGTH; + else + data_obj->DataLen = apdu.resplen; + + SC_FUNC_RETURN(card->ctx, 1, r); +} + +static int myeid_loadkey(sc_card_t *card, int mode, u8* value, int value_len) +{ + sc_apdu_t apdu; + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; + int r, len; + + len = 0; + if(value_len == 0 || value == NULL) + return 0; + + if(value != NULL && + value[0] != 0x0 && + mode != LOAD_KEY_PUBLIC_EXPONENT) + sbuf[len++] = 0x0; + + SC_FUNC_CALLED(card->ctx, 1); + + if(mode == LOAD_KEY_MODULUS && value_len >= 256) + { + r=0; + if((value_len % 2) > 0 && value[0] == 0x00) + { + value_len--; + memmove(value, value + 1, value_len); + } + mode = 0x88; + len = 128; + memcpy(sbuf,value, 128); + + memset(&apdu, 0, sizeof(apdu)); + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xDA, 0x01, mode); + + apdu.cla = 0x00; + apdu.data = sbuf; + apdu.datalen = len; + apdu.lc = len; + + r = sc_transmit_apdu(card, &apdu); + if(r < 0) + return r; + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + if(r < 0) + return r; + + mode = 0x89; + len = value_len - 128; + memset(&sbuf, 0, SC_MAX_APDU_BUFFER_SIZE); + memcpy(sbuf,value + 128, value_len - 128); + } + else + { + memcpy(sbuf + len, value, value_len); + len += value_len; + } + + memset(&apdu, 0, sizeof(apdu)); + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xDA, 0x01, mode); + + apdu.cla = 0x00; + apdu.data = sbuf; + apdu.datalen = len; + apdu.lc = len; + + 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_FUNC_RETURN(card->ctx, 1, r); + +} + +/* Generate or store a key */ +static int myeid_generate_store_key(struct sc_card *card, + struct sc_cardctl_myeid_gen_store_key_info *data) +{ + struct sc_apdu apdu; + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; + int r,len; + + /* Setup key-generation paramters */ + if (data->op_type == OP_TYPE_GENERATE) + { + len = 0; + + sbuf[len++] = 0x30; + sbuf[len++] = 0x05; + sbuf[len++] = 0x81; + sbuf[len++] = data->pubexp_len; + + memcpy(sbuf + len, data->pubexp, data->pubexp_len); + len += data->pubexp_len; + + memset(&apdu, 0, sizeof(apdu)); + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x46, 0x00, 0x00); + apdu.cla = 0x00; + apdu.data = sbuf; + apdu.datalen = len; + apdu.lc = len; + + 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, "GENERATE_KEY returned error"); + + SC_FUNC_RETURN(card->ctx, 1, r); + } + else + { + if((r=myeid_loadkey(card, LOAD_KEY_PRIME_P, + data->primep, data->primep_len)) >= 0 && + (r=myeid_loadkey(card, LOAD_KEY_PRIME_Q, + data->primeq, data->primeq_len)) >= 0 && + (r=myeid_loadkey(card, LOAD_KEY_DP1, + data->dp1, data->dp1_len)) >= 0 && + (r=myeid_loadkey(card, LOAD_KEY_DQ1, + data->dq1, data->dq1_len)) >= 0 && + (r=myeid_loadkey(card, LOAD_KEY_INVQ, + data->invq, data->invq_len)) >= 0 && + (r=myeid_loadkey(card, LOAD_KEY_MODULUS, + data->mod, data->mod_len)) >= 0 && + (r=myeid_loadkey(card, LOAD_KEY_PUBLIC_EXPONENT, + data->pubexp, data->pubexp_len)) >= 0) + return r; + } + + return r; +} + +static int myeid_activate_card(struct sc_card *card) +{ + int r; + u8 sbuf[] ="\xA0\x00\x00\x00\x63\x50\x4B\x43\x53\x2D\x31\x35"; + sc_apdu_t apdu; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x44, 0x04, 0x00); + apdu.cla = 0x00; + apdu.data = sbuf; + apdu.datalen = 0x0C; + apdu.lc = 0x0C; + + 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, "ACTIVATE_APPLET returned error"); + + SC_FUNC_RETURN(card->ctx, 1, r); +} + +static int myeid_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) +{ + int r; + sc_apdu_t apdu; + u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0xA0); + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + apdu.le = 256; + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + + if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) + return SC_ERROR_INTERNAL; + + if (apdu.resplen != 20) + { + sc_debug(card->ctx, "unexpected response to GET DATA serial number\n"); + return SC_ERROR_INTERNAL; + } + + /* cache serial number */ + memcpy(card->serialnr.value, &rbuf[10], 8); + card->serialnr.len = 8; + + /* copy and return serial number */ + memcpy(serial, &card->serialnr, sizeof(*serial)); + + return SC_SUCCESS; +} + +static int myeid_card_ctl(struct sc_card *card, unsigned long cmd, void *ptr) +{ + switch(cmd) { + case SC_CARDCTL_MYEID_PUTDATA: + return myeid_putdata(card, + (struct sc_cardctl_myeid_data_obj*) ptr); + case SC_CARDCTL_MYEID_GETDATA: + return myeid_getdata(card, + (struct sc_cardctl_myeid_data_obj*) ptr); + case SC_CARDCTL_MYEID_GENERATE_KEY: + return myeid_generate_store_key(card, + (struct sc_cardctl_myeid_gen_store_key_info *) ptr); + case SC_CARDCTL_MYEID_ACTIVATE_CARD: + return myeid_activate_card(card); + case SC_CARDCTL_GET_SERIALNR: + return myeid_get_serialnr(card, (sc_serial_number_t *)ptr); + } + + return SC_ERROR_NOT_SUPPORTED; +} + +/* "The PINs are "global" in a PKCS#15 sense, meaning that they remain valid + * until card reset! Selecting another applet doesn't invalidate the PINs, + * you need to reset the card." - javacard@zurich.ibm.com, when asked about + * how to invalidate logged in pins. +*/ +static int myeid_logout(struct sc_card *card) +{ + return 0; /* Can't */ +} + +static struct sc_card_driver * sc_get_driver(void) +{ + struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); + + myeid_ops = *iso_drv->ops; + myeid_ops.match_card = myeid_match_card; + myeid_ops.init = myeid_init; + myeid_ops.finish = myeid_finish; + if (iso_ops == NULL) + iso_ops = iso_drv->ops; + myeid_ops.read_binary = myeid_read_binary; + myeid_ops.read_record = myeid_read_record_unsupp; + myeid_ops.write_record = myeid_wrupd_record_unsupp; + myeid_ops.append_record = myeid_append_record_unsupp; + myeid_ops.update_record = myeid_wrupd_record_unsupp; + myeid_ops.write_binary = myeid_write_binary; + myeid_ops.update_binary = myeid_update_binary; + myeid_ops.select_file = myeid_select_file; + myeid_ops.create_file = myeid_create_file; + myeid_ops.delete_file = myeid_delete_file; + myeid_ops.list_files = myeid_list_files; + myeid_ops.set_security_env = myeid_set_security_env; + myeid_ops.compute_signature = myeid_compute_signature; + myeid_ops.decipher = myeid_decipher; + myeid_ops.logout = myeid_logout; + myeid_ops.process_fci = myeid_process_fci; + myeid_ops.card_ctl = myeid_card_ctl; + + return &myeid_drv; +} + +struct sc_card_driver * sc_get_myeid_driver(void) +{ + return sc_get_driver(); +} + diff --git a/src/libopensc/card-rtecp.c b/src/libopensc/card-rtecp.c index 79ada5af..65d66495 100644 --- a/src/libopensc/card-rtecp.c +++ b/src/libopensc/card-rtecp.c @@ -639,6 +639,7 @@ static int rtecp_card_ctl(sc_card_t *card, unsigned long request, void *data) apdu.resp = buf; apdu.resplen = sizeof(buf); apdu.le = sizeof(buf) - 2; + serial->len = sizeof(serial->value); break; case SC_CARDCTL_RTECP_GENERATE_KEY: if (!genkey_data) diff --git a/src/libopensc/cardctl.h b/src/libopensc/cardctl.h index 657fa185..409c89b9 100644 --- a/src/libopensc/cardctl.h +++ b/src/libopensc/cardctl.h @@ -188,6 +188,14 @@ enum { SC_CARDCTL_WESTCOS_SET_DEFAULT_KEY, SC_CARDCTL_WESTCOS_LOAD_DATA, + /* + * MyEID specific calls + */ + SC_CARDCTL_MYEID_BASE = _CTL_PREFIX('M', 'Y', 'E'), + SC_CARDCTL_MYEID_PUTDATA, + SC_CARDCTL_MYEID_GETDATA, + SC_CARDCTL_MYEID_GENERATE_KEY, + SC_CARDCTL_MYEID_ACTIVATE_CARD, }; enum { @@ -690,6 +698,35 @@ typedef struct sc_rtecp_genkey_data { size_t modulus_len; } sc_rtecp_genkey_data_t; +/* +* MyEID stuff +*/ + struct sc_cardctl_myeid_data_obj { + int P1; + int P2; + u8 * Data; + size_t DataLen; + int LengthMax; + }; + + struct sc_cardctl_myeid_gen_store_key_info { + int op_type; + unsigned int mod_len; + unsigned char *mod; + unsigned int pubexp_len; + unsigned char *pubexp; + unsigned int primep_len; + unsigned char *primep; + unsigned int primeq_len; + unsigned char *primeq; + unsigned int dp1_len; + unsigned char *dp1; + unsigned int dq1_len; + unsigned char *dq1; + unsigned int invq_len; + unsigned char *invq; +}; + #ifdef __cplusplus } #endif diff --git a/src/libopensc/cards.h b/src/libopensc/cards.h index 465059ba..c1778d2f 100644 --- a/src/libopensc/cards.h +++ b/src/libopensc/cards.h @@ -146,6 +146,10 @@ enum { SC_CARD_TYPE_ENTERSAFE_BASE = 19000, SC_CARD_TYPE_ENTERSAFE_3K, SC_CARD_TYPE_ENTERSAFE_FTCOS_PK_01C, + + /* MyEID cards */ + SC_CARD_TYPE_MYEID_BASE = 20000, + SC_CARD_TYPE_MYEID_GENERIC, }; extern sc_card_driver_t *sc_get_default_driver(void); @@ -175,6 +179,7 @@ extern sc_card_driver_t *sc_get_entersafe_driver(void); extern sc_card_driver_t *sc_get_rutoken_driver(void); extern sc_card_driver_t *sc_get_rtecp_driver(void); extern sc_card_driver_t *sc_get_westcos_driver(void); +extern sc_card_driver_t *sc_get_myeid_driver(void); #ifdef __cplusplus } diff --git a/src/libopensc/ctx.c b/src/libopensc/ctx.c index efde1157..e9832b17 100644 --- a/src/libopensc/ctx.c +++ b/src/libopensc/ctx.c @@ -85,6 +85,7 @@ static const struct _sc_driver_entry internal_card_drivers[] = { { "rutoken", (void *(*)(void)) sc_get_rutoken_driver }, { "rutoken_ecp",(void *(*)(void)) sc_get_rtecp_driver }, { "westcos", (void *(*)(void)) sc_get_westcos_driver }, + { "myeid", (void *(*)(void)) sc_get_myeid_driver }, /* emv is not really implemented */ { "emv", (void *(*)(void)) sc_get_emv_driver }, /* The default driver should be last, as it handles all the diff --git a/src/pkcs15init/Makefile.am b/src/pkcs15init/Makefile.am index 7f41e86f..bd0ee28f 100644 --- a/src/pkcs15init/Makefile.am +++ b/src/pkcs15init/Makefile.am @@ -25,7 +25,8 @@ dist_pkgdata_DATA = \ asepcos.profile \ entersafe.profile \ rutoken_ecp.profile \ - westcos.profile + westcos.profile \ + myeid.profile AM_CPPFLAGS = -DSC_PKCS15_PROFILE_DIRECTORY=\"$(pkgdatadir)\" AM_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) $(LTLIB_CFLAGS) @@ -38,7 +39,7 @@ libpkcs15init_la_SOURCES = \ pkcs15-cardos.c pkcs15-jcop.c pkcs15-starcos.c \ pkcs15-oberthur.c pkcs15-setcos.c pkcs15-incrypto34.c \ pkcs15-muscle.c pkcs15-asepcos.c pkcs15-rutoken.c \ - pkcs15-entersafe.c pkcs15-rtecp.c \ + pkcs15-entersafe.c pkcs15-rtecp.c pkcs15-myeid.c \ pkcs15init.exports if WIN32 libpkcs15init_la_SOURCES += versioninfo.rc diff --git a/src/pkcs15init/Makefile.mak b/src/pkcs15init/Makefile.mak index ca23a37f..60f45d47 100644 --- a/src/pkcs15init/Makefile.mak +++ b/src/pkcs15init/Makefile.mak @@ -11,6 +11,7 @@ OBJECTS = pkcs15-lib.obj profile.obj keycache.obj \ pkcs15-oberthur.obj pkcs15-setcos.obj pkcs15-incrypto34.obj \ pkcs15-muscle.obj pkcs15-asepcos.obj pkcs15-rutoken.obj \ pkcs15-entersafe.obj pkcs15-rtecp.obj pkcs15-westcos.obj \ + pkcs15-myeid.obj \ versioninfo.res all: install-headers $(TARGET) diff --git a/src/pkcs15init/keycache.c b/src/pkcs15init/keycache.c index 017042eb..4c8eb135 100644 --- a/src/pkcs15init/keycache.c +++ b/src/pkcs15init/keycache.c @@ -264,6 +264,10 @@ sc_keycache_set_pin_name(const sc_path_t *path, int ref, int name) s = new_entry(path, SC_AC_CHV, ref); if (s == NULL) return SC_ERROR_OUT_OF_MEMORY; + + s->len = sc_keycache_get_key(path, SC_AC_CHV, -1, s->value, MAX_SECRET); + if(s->len < 0) + return SC_ERROR_OBJECT_NOT_FOUND; } /* Set the pin name */ diff --git a/src/pkcs15init/myeid.profile b/src/pkcs15init/myeid.profile new file mode 100644 index 00000000..063553d5 --- /dev/null +++ b/src/pkcs15init/myeid.profile @@ -0,0 +1,53 @@ +# +# PKCS15 r/w profile for MyEID cards +# +cardinfo { + max-pin-length = 8; + pin-encoding = ascii-numeric; + pin-pad-char = 0x00; +} + +# Define reasonable limits for PINs and PUK +# Note that we do not set a file path or reference +# here; that is done dynamically. +PIN user-pin { + attempts = 3; +} +PIN user-puk { + attempts = 10; +} + +# Additional filesystem info. +# This is added to the file system info specified in the +# main profile. +filesystem { + DF MF { + DF PKCS15-AppDF { + EF template-private-key { + type = internal-ef; + file-id = 4B01; # This is the base FileID + size = 266; # 266 is enough for 1024-bit keys + ACL = *=NEVER, CRYPTO=$PIN, UPDATE=$PIN; + } + EF template-public-key { + file-id = 5501; + ACL = *=NEVER, READ=NONE, UPDATE=$PIN; + } + EF template-certificate { + file-id = 4301; + ACL = *=NEVER, READ=NONE, UPDATE=$PIN; + } + EF template-extractable-key { + file-id = 7000; + ACL = *=NEVER, READ=$PIN, UPDATE=$PIN; + } + } + } +} + +# Define an SO pin +# This PIN is not used yet. +#PIN sopin { +# file = sopinfile; +# reference = 0; +#} diff --git a/src/pkcs15init/pkcs15-init.h b/src/pkcs15init/pkcs15-init.h index 8c37a186..2319d1a4 100644 --- a/src/pkcs15init/pkcs15-init.h +++ b/src/pkcs15init/pkcs15-init.h @@ -405,6 +405,7 @@ extern struct sc_pkcs15init_operations *sc_pkcs15init_get_rutoken_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_entersafe_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_rtecp_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_westcos_ops(void); +extern struct sc_pkcs15init_operations *sc_pkcs15init_get_myeid_ops(void); #ifdef __cplusplus } diff --git a/src/pkcs15init/pkcs15-lib.c b/src/pkcs15init/pkcs15-lib.c index 8322a631..87e3f53f 100644 --- a/src/pkcs15init/pkcs15-lib.c +++ b/src/pkcs15init/pkcs15-lib.c @@ -166,6 +166,7 @@ static struct profile_operations { { "entersafe",(void*) sc_pkcs15init_get_entersafe_ops }, { "rutoken_ecp", (void *) sc_pkcs15init_get_rtecp_ops }, { "westcos", (void *) sc_pkcs15init_get_westcos_ops }, + { "myeid", (void *) sc_pkcs15init_get_myeid_ops }, { NULL, NULL }, }; diff --git a/src/pkcs15init/pkcs15-myeid.c b/src/pkcs15init/pkcs15-myeid.c new file mode 100644 index 00000000..83f39ef1 --- /dev/null +++ b/src/pkcs15init/pkcs15-myeid.c @@ -0,0 +1,426 @@ +/* + * MyEID specific operations for PKCS15 initialization + * + * Copyright (C) 2008-2009 Aventra Ltd. + * + * 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 +#include +#include +#include "pkcs15-init.h" +#include "keycache.h" +#include "profile.h" + +#define MYEID_MAX_PINS 5 + +unsigned char MYEID_DEFAULT_PUBKEY[] = {0x01, 0x00, 0x01}; +#define MYEID_DEFAULT_PUBKEY_LEN sizeof(MYEID_DEFAULT_PUBKEY) + +static int myeid_generate_store_key( sc_profile_t *, sc_card_t *, + unsigned int, unsigned int, sc_pkcs15_pubkey_t *, + sc_pkcs15_prkey_t *, sc_pkcs15_prkey_info_t *); + +static int myeid_create_pin_internal(sc_profile_t *, sc_card_t *, + int, sc_pkcs15_pin_info_t *, const u8 *, size_t, + const u8 *, size_t); + +static int myeid_puk_retries(sc_profile_t *, int); + +/* + * Erase the card. + */ +static int myeid_erase_card(sc_profile_t *profile, sc_card_t *card) +{ + struct sc_cardctl_myeid_data_obj data_obj; + sc_pkcs15_pin_info_t pin_info; + u8 data[8]; + int r; + + /* Just delete the entire MF */ + + /* The SO pin has pin reference 1 -- not that it matters much + * because pkcs15-init will ask to enter all pins, even if we + * did a --so-pin on the command line. */ + sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &pin_info); + sc_keycache_set_pin_name(NULL, pin_info.reference, SC_PKCS15INIT_SO_PIN); + + /* Select parent DF and verify PINs/key as necessary */ + r = sc_pkcs15init_authenticate(profile, card, profile->mf_info->file, SC_AC_OP_DELETE); + if (r < 0) + return r == SC_ERROR_FILE_NOT_FOUND ? 0 : r; + + data[0]= 0xFF; + data[1]= 0xFF; + data[2]= 0x33; + data[3]= 0x3F; + data[4]= 0xFF; + data[5]= 0x33; + data[6]= 0x3F; + data[7]= 0xFF; + + data_obj.P1 = 0x01; + data_obj.P2 = 0xE0; + data_obj.Data = data; + data_obj.DataLen = 0x08; + + r = sc_card_ctl(card, SC_CARDCTL_MYEID_PUTDATA, &data_obj); + + return r; +} + +/* + * Select the PIN reference + */ +static int myeid_select_pin_reference(sc_profile_t *profile, sc_card_t *card, + sc_pkcs15_pin_info_t *pin_info) +{ + sc_pkcs15_pin_info_t pin_info_prof; + + pin_info_prof.reference = 1; /* Default SO PIN ref. */ + sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &pin_info_prof); + + /* For the SO pin, we take the first available pin reference = 1 */ + if (pin_info->flags & SC_PKCS15_PIN_FLAG_SO_PIN) + pin_info->reference = pin_info_prof.reference; + /* sc_pkcs15init_create_pin() starts checking if 0 is an acceptable + * pin reference, which isn't for the myeid cards. And since the + * value 1 has been assigned to the SO pin, we'll jump to 2. */ + else if (pin_info->reference == 0) + pin_info->reference = pin_info_prof.reference + 1; + + return 0; +} + +/* + * Create a new PIN + */ +static int myeid_create_pin(sc_profile_t *profile, sc_card_t *card, + sc_file_t *df, sc_pkcs15_object_t *pin_obj, + const u8 *pin, size_t pin_len, + const u8 *puk, size_t puk_len) +{ + return myeid_create_pin_internal(profile, card, + 0, (sc_pkcs15_pin_info_t *) pin_obj->data, + pin, pin_len, + puk, puk_len); +} + +/* + * Setup file struct & path: get correct template from the profile, construct full path + * num = number of objects of this type already on the card + */ +static int myeid_new_file(sc_profile_t *profile, sc_card_t *card, + unsigned int type, unsigned int num, + sc_file_t **out) +{ + sc_file_t *file; + sc_path_t *p; + char name[64], *tag; + int r; + + if (type == SC_PKCS15_TYPE_PRKEY_RSA) + tag = "private-key"; + else if (type == SC_PKCS15_TYPE_PUBKEY_RSA) + tag = "public-key"; + else if ((type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_CERT) + tag = "certificate"; + else if ((type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_DATA_OBJECT) + tag = "data"; + else + { + sc_debug(card->ctx, "Unsupported file type"); + return SC_ERROR_INVALID_ARGUMENTS; + } + + /* Get template from profile */ + snprintf(name, sizeof(name), "template-%s", tag); + if (sc_profile_get_file(profile, name, &file) < 0) + { + sc_debug(card->ctx, "Profile doesn't define %s", name); + return SC_ERROR_NOT_SUPPORTED; + } + + /* Auto-increment FID for next object */ + file->id += num; + p = &file->path; + *p = profile->df_info->file->path; + p->value[p->len++] = (u8) (file->id / 256); + p->value[p->len++] = (u8) (file->id % 256); + + /* Increment FID until there's no file with such path */ + r = sc_select_file(card, p, NULL); + while(r == 0) + { + file->id++; + p->value[p->len - 2] = (u8) (file->id / 256); + p->value[p->len - 1] = (u8) (file->id % 256); + r = sc_select_file(card, p, NULL); + } + + *out = file; + return 0; +} + +static int myeid_encode_private_key(sc_profile_t *profile, sc_card_t *card, + struct sc_pkcs15_prkey_rsa *rsa, u8 *key, + size_t *keysize, int key_ref) +{ + return 0; +} + +static int myeid_encode_public_key(sc_profile_t *profile, sc_card_t *card, + struct sc_pkcs15_prkey_rsa *rsa, u8 *key, + size_t *keysize, int key_ref) +{ + return 0; +} + +/* + * Generate RSA key + */ +static int myeid_generate_key(sc_profile_t *profile, sc_card_t *card, + unsigned int index, /* keyref: 0 for 1st key, ... */ + unsigned int keybits, + sc_pkcs15_pubkey_t *pubkey, + struct sc_pkcs15_prkey_info *info) +{ + return myeid_generate_store_key(profile, card, index, keybits, + pubkey, NULL, info); +} + +/* + * Store RSA key + */ +static int myeid_new_key(sc_profile_t *profile, sc_card_t *card, + struct sc_pkcs15_prkey *key, unsigned int index, + struct sc_pkcs15_prkey_info *info) +{ + return myeid_generate_store_key(profile, card, index, + key->u.rsa.modulus.len * 8, NULL, key, info); +} + +/* + * Common code for generating or storing a private key. + * If pubkey == NULL and prkey != NULL, we have to store a private key + * In the oposite case, we have to generate a private key + */ +static int myeid_generate_store_key(sc_profile_t *profile, sc_card_t *card, + unsigned int index, /* keynumber: 0 for 1st priv key, ... */ + unsigned int keybits, + sc_pkcs15_pubkey_t *pubkey, + sc_pkcs15_prkey_t *prkey, + sc_pkcs15_prkey_info_t *info) +{ + struct sc_cardctl_myeid_gen_store_key_info args; + struct sc_cardctl_myeid_data_obj data_obj; + unsigned char raw_pubkey[256]; + int r; + unsigned int mod_len; + sc_file_t *prkf = NULL; + + /* Parameter check */ + if ( (keybits < 1024) || (keybits > 2048) || (keybits & 0X7)) { + sc_debug(card->ctx, + "Unsupported key size [%u]: 1024-2048 bit + 8-multiple\n", keybits); + return SC_ERROR_INVALID_ARGUMENTS; + } + + /* Get the private key file */ + r = myeid_new_file(profile, card, SC_PKCS15_TYPE_PRKEY_RSA, index, &prkf); + if (r < 0) + goto done; + + /* Take enough room for a 1024 bit key */ + if (prkf->size < 1024) + prkf->size = 1024; + + /* Now create the key file */ + r = sc_pkcs15init_create_file(profile, card, prkf); + if (r < 0) + goto done; + + /* Fill in data structure */ + memset(&args, 0, sizeof(args)); + args.mod_len = keybits; + if (prkey == NULL) + { + args.op_type = OP_TYPE_GENERATE; + args.pubexp_len = MYEID_DEFAULT_PUBKEY_LEN; + args.pubexp = MYEID_DEFAULT_PUBKEY; + } + else + { + args.op_type = OP_TYPE_STORE; + args.pubexp_len = prkey->u.rsa.exponent.len; + args.pubexp = prkey->u.rsa.exponent.data; + args.primep_len = prkey->u.rsa.p.len; + args.primep = prkey->u.rsa.p.data; + args.primeq_len = prkey->u.rsa.q.len; + args.primeq = prkey->u.rsa.q.data; + + args.dp1_len = prkey->u.rsa.dmp1.len; + args.dp1 = prkey->u.rsa.dmp1.data; + args.dq1_len = prkey->u.rsa.dmq1.len; + args.dq1 = prkey->u.rsa.dmq1.data; + args.invq_len = prkey->u.rsa.iqmp.len; + args.invq = prkey->u.rsa.iqmp.data; + + args.mod_len = prkey->u.rsa.modulus.len; + args.mod = prkey->u.rsa.modulus.data; + } + + /* Authenticate */ + r = sc_pkcs15init_authenticate(profile, card, prkf, SC_AC_OP_UPDATE); + if (r < 0) + goto done; + + /* Generate/store rsa key */ + r = sc_card_ctl(card, SC_CARDCTL_MYEID_GENERATE_KEY, &args); + if (r < 0) + goto done; + + info->key_reference = 0; + info->path = prkf->path; + +done: + if (prkf) + sc_file_free(prkf); + + return r; +} + +/* + * Create a new PIN + */ +static int myeid_create_pin_internal(sc_profile_t *profile, sc_card_t *card, + int ignore_ac, sc_pkcs15_pin_info_t *pin_info, + const u8 *pin, size_t pin_len, + const u8 *puk, size_t puk_len) +{ + u8 data[19]; + int so_pin_ref; + int r; + struct sc_cardctl_myeid_data_obj data_obj; + sc_file_t *pinfile = NULL; + + if (pin_info->reference >= MYEID_MAX_PINS) + return SC_ERROR_INVALID_ARGUMENTS; + if (pin == NULL || puk == NULL || pin_len < 4 || puk_len < 4) + return SC_ERROR_INVALID_PIN_LENGTH; + + /* Verify required access rights if needed (i.e. if the + * pin file isn't in the CREATE life cycle state). */ + if (!ignore_ac) + { + /* Re-ink the SO pin to the MF because there is the pin file */ + so_pin_ref = sc_keycache_find_named_pin(&profile->df_info->file->path, + SC_PKCS15INIT_SO_PIN); + if (so_pin_ref >= 0) + sc_keycache_set_pin_name(&profile->mf_info->file->path, + so_pin_ref, SC_PKCS15INIT_SO_PIN); + + r = sc_profile_get_file(profile, "pinfile", &pinfile); + if (r >= 0) + r = sc_pkcs15init_authenticate(profile, card, pinfile, SC_AC_OP_UPDATE); + sc_file_free(pinfile); + if (r < 0) + return r; + } + + /* Make command to add a pin-record */ + data_obj.P1 = 01; + data_obj.P2 = pin_info->reference; /* myeid pin number */ + + memcpy(&data[0], (u8 *)pin, pin_len); /* copy pin*/ + memcpy(&data[8], (u8 *)puk, puk_len); /* copy puk */ + + + /* Optional PIN locking + * data[17] = pin_info->tries_left & 0x0F; + * data[18] = myeid_puk_retries(profile, pin_info->reference) & 0x0F; + */ + + data[17] = 0x00; + data[18] = 0x00; + data[19] = 0x00; + + data_obj.Data = data; + data_obj.DataLen = 0x10; + + r = sc_card_ctl(card, SC_CARDCTL_MYEID_PUTDATA, &data_obj); + + return r; +} + +static int myeid_puk_retries(sc_profile_t *profile, int pin_ref) +{ + sc_pkcs15_pin_info_t pin_info; + + pin_info.reference = 1; /* Default SO PIN ref. */ + sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &pin_info); + + /* If pin_ref is the SO PIN, get the SO PUK info, otherwise the User PUK info */ + sc_profile_get_pin_info(profile, + pin_ref == pin_info.reference ? + SC_PKCS15INIT_SO_PUK : SC_PKCS15INIT_USER_PUK, + &pin_info); + + if ((pin_info.tries_left < 0) || (pin_info.tries_left > 15)) + return 3; /* Little extra safety */ + return pin_info.tries_left; +} + +/* For Myeid, all objects are files that can be deleted in any order */ +static int myeid_delete_object(struct sc_profile *profile, + struct sc_card *card, unsigned int type, + const void *data, const sc_path_t *path) +{ + return sc_pkcs15init_delete_by_path(profile, card, path); +} + +static struct sc_pkcs15init_operations sc_pkcs15init_myeid_operations = { + myeid_erase_card, + NULL, /* init_card */ + NULL, /* create_dir */ + NULL, /* create_domain */ + myeid_select_pin_reference, + myeid_create_pin, + NULL, /* select_key_reference */ + NULL, /* create_key */ + NULL, /* store_key */ + NULL, /* generate_key */ + myeid_encode_private_key, + myeid_encode_public_key, + NULL, /* finalize_card */ + NULL, + NULL, /* style api */ + myeid_new_key, + myeid_new_file, + myeid_generate_key, + myeid_delete_object +}; + +struct sc_pkcs15init_operations *sc_pkcs15init_get_myeid_ops(void) +{ + return &sc_pkcs15init_myeid_operations; +} diff --git a/src/scconf/README.scconf b/src/scconf/README.scconf index 5ce86fb7..f30e3823 100644 --- a/src/scconf/README.scconf +++ b/src/scconf/README.scconf @@ -18,7 +18,7 @@ It isn't It doesn't have - anything else but data. No locking, no threads etc. -It has heirarchical data blocks, it has lists. +It has heirarchical data blocks, it has lists. Similar, but different: - .ini files. scconf is block structured, has lists and arrays @@ -216,8 +216,8 @@ typedef struct _scconf_entry { * Look for blocks with this key, or check if this * block has an item with this key. Run the block * or blocks found against the rest of this entry - * Stop after the first one, unless - * SCCONF_ALL_BLOCKS is set in flags + * Stop after the first one, unless + * SCCONF_ALL_BLOCKS is set in flags unsigned int type; * SCCONF_CALLBACK * parm contains a function ptr of type @@ -226,7 +226,7 @@ typedef struct _scconf_entry { * scconf_entry* entry, * int depth); * run the callback with the block found - * + * * SCCONF_BLOCK * param contains a pointer to another entry table * use the found block against every entry @@ -269,7 +269,7 @@ typedef struct _scconf_entry { * where a pointer to a copy of list * can be stored * - * + * unsigned int flags; * SCCONF_PRESENT * This bit is or'ed in when found @@ -298,8 +298,8 @@ Sub-blocks can be added, and callbacks can be issued. This is a handy method for adding scconf data from within a program. typedef struct _scconf_entry { - const char *name; - * key value for blocks and items * + const char *name; + * key value for blocks and items * unsigned int type; * SCCONF_CALLBACK * parm contains a function ptr of type @@ -307,12 +307,12 @@ typedef struct _scconf_entry { * scconf_block* block, * scconf_entry* entry, * int depth); - * + * * SCCONF_BLOCK * param contains a pointer to another entry table * the entry table is added as a block to the * current block, with name as the key, and - * arg is a list of names + * arg is a list of names * * SCCONF_LIST * SCCONF_BOOLEAN @@ -320,7 +320,7 @@ typedef struct _scconf_entry { * SCCONF_STRING * these add key=value pairs to the current * block. The value is in parm. - * + * unsigned int flags; * SCCONF_PRESENT * This bit is or'ed in when item added