From 27d00f9265e053c574690d9cf27d4d579d29a09d Mon Sep 17 00:00:00 2001 From: aj Date: Wed, 20 Aug 2008 05:41:20 +0000 Subject: [PATCH] Add new entersafe driver for ePass 3000 tokens. git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@3556 c6295689-39f2-0310-b995-f0e70906c6a9 --- src/libopensc/Makefile.am | 4 +- src/libopensc/Makefile.mak | 4 +- src/libopensc/card-entersafe.c | 1588 +++++++++++++++++++++++++++++ src/libopensc/cardctl.h | 93 +- src/libopensc/cards.h | 6 +- src/libopensc/ctx.c | 3 + src/libopensc/opensc.h | 1 + src/libopensc/pkcs15-esinit.c | 89 ++ src/libopensc/pkcs15-syn.c | 3 + src/pkcs15init/Makefile.am | 4 +- src/pkcs15init/Makefile.mak | 1 + src/pkcs15init/entersafe.profile | 181 ++++ src/pkcs15init/pkcs15-entersafe.c | 416 ++++++++ src/pkcs15init/pkcs15-init.h | 1 + src/pkcs15init/pkcs15-lib.c | 17 +- 15 files changed, 2403 insertions(+), 8 deletions(-) create mode 100644 src/libopensc/card-entersafe.c create mode 100644 src/libopensc/pkcs15-esinit.c create mode 100644 src/pkcs15init/entersafe.profile create mode 100644 src/pkcs15init/pkcs15-entersafe.c diff --git a/src/libopensc/Makefile.am b/src/libopensc/Makefile.am index 9ce04c8e..e3ba715e 100644 --- a/src/libopensc/Makefile.am +++ b/src/libopensc/Makefile.am @@ -35,14 +35,14 @@ libopensc_la_SOURCES = \ card-setcos.c card-miocos.c card-flex.c card-gpk.c \ card-cardos.c card-tcos.c card-emv.c card-default.c \ card-mcrd.c card-starcos.c card-openpgp.c card-jcop.c \ - card-oberthur.c card-belpic.c card-atrust-acos.c \ + 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\ \ pkcs15-openpgp.c pkcs15-infocamere.c pkcs15-starcert.c \ pkcs15-tcos.c pkcs15-esteid.c pkcs15-postecert.c pkcs15-gemsafeGPK.c \ pkcs15-actalis.c pkcs15-atrust-acos.c pkcs15-tccardos.c pkcs15-piv.c \ - pkcs15-rutoken.c \ + pkcs15-rutoken.c pkcs15-esinit.c \ compression.c p15card-helper.c \ \ libopensc.exports diff --git a/src/libopensc/Makefile.mak b/src/libopensc/Makefile.mak index a617c9e6..a17e3855 100644 --- a/src/libopensc/Makefile.mak +++ b/src/libopensc/Makefile.mak @@ -25,14 +25,14 @@ OBJECTS = \ card-setcos.obj card-miocos.obj card-flex.obj card-gpk.obj \ card-cardos.obj card-tcos.obj card-emv.obj card-default.obj \ card-mcrd.obj card-starcos.obj card-openpgp.obj card-jcop.obj \ - card-oberthur.obj card-belpic.obj card-atrust-acos.obj \ + 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 \ \ pkcs15-openpgp.obj pkcs15-infocamere.obj pkcs15-starcert.obj \ pkcs15-tcos.obj pkcs15-esteid.obj pkcs15-postecert.obj pkcs15-gemsafeGPK.obj \ pkcs15-actalis.obj pkcs15-atrust-acos.obj pkcs15-tccardos.obj pkcs15-piv.obj \ - pkcs15-rutoken.obj \ + pkcs15-rutoken.obj pkcs15-esinit.obj \ compression.obj p15card-helper.obj \ versioninfo.res diff --git a/src/libopensc/card-entersafe.c b/src/libopensc/card-entersafe.c new file mode 100644 index 00000000..53da8529 --- /dev/null +++ b/src/libopensc/card-entersafe.c @@ -0,0 +1,1588 @@ +/* + * 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 + */ + +/* Initially written by Weitao Sun (weitao@ftsafe.com) 2008 */ + +#include "internal.h" +#include "asn1.h" +#include "cardctl.h" +#include +#include + +#ifdef ENABLE_OPENSSL +#include + +static struct sc_atr_table entersafe_atrs[] = { + { + "3b:0f:00:65:46:53:05:19:05:71:df:00:00:00:00:00:00", + "ff:ff:ff:ff:ff:ff:ff:00:ff:ff:ff:00:00:00:00:00:00", + "ePass3000", SC_CARD_TYPE_ENTERSAFE_3K, 0, NULL }, + { NULL, NULL, NULL, 0, 0, NULL } +}; + +static struct sc_card_operations entersafe_ops; +static struct sc_card_operations *iso_ops = NULL; + +static struct sc_card_driver entersafe_drv = { + "entersafe", + "entersafe", + &entersafe_ops, + NULL, 0, NULL +}; + +static u8 trans_code[] = +{ + 0x01,0x02,0x03,0x04, + 0x05,0x06,0x07,0x08, +}; + +static u8 init_key[] = +{ + 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12, + 13, 14, 15, 16, +}; + +static u8 key_maintain[] = +{ + 0x12, 0x34, 0x56, 0x78, + 0x21, 0x43, 0x65, 0x87, + 0x11, 0x22, 0xaa, 0xbb, + 0x33, 0x44, 0xcd, 0xef +}; + +static void entersafe_reverse_buffer(u8* buff,size_t size) +{ + u8 t; + u8 * end=buff+size-1; + + while(buffctx, 1); + + i = _sc_match_atr(card, entersafe_atrs, &card->type); + if (i < 0) + return 0; + + return 1; +} + +static int entersafe_init(sc_card_t *card) +{ + unsigned int flags; + + SC_FUNC_CALLED(card->ctx, 1); + + card->name = "entersafe"; + card->cla = 0x00; + /*card->drv_data = NULL;*/ + + flags =SC_ALGORITHM_ONBOARD_KEY_GEN + | SC_ALGORITHM_RSA_RAW + | SC_ALGORITHM_RSA_HASH_NONE; + + + + _sc_card_add_rsa_alg(card, 512, flags, 0x10001); + _sc_card_add_rsa_alg(card, 768, flags, 0x10001); + _sc_card_add_rsa_alg(card,1024, flags, 0x10001); + _sc_card_add_rsa_alg(card,2048, flags, 0x10001); + + card->caps = SC_CARD_CAP_RNG; + card->drv_data = 0; + + /* we need read_binary&friends with max 224 bytes per read */ + if (card->max_send_size > 0xE0) + card->max_send_size = 0xE0; + if (card->max_recv_size > 0xE0) + card->max_recv_size = 0xE0; + SC_FUNC_RETURN(card->ctx,4,SC_SUCCESS); +} + +static int entersafe_gen_radom(sc_card_t *card,u8 *buff,size_t size) +{ + int r=SC_SUCCESS; + u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]={0}; + sc_apdu_t apdu; + + SC_FUNC_CALLED(card->ctx, 1); + + sc_format_apdu(card,&apdu,SC_APDU_CASE_2_SHORT,0x84,0x00,0x00); + apdu.resp=rbuf; + apdu.le=size; + apdu.resplen=sizeof(rbuf); + + r=sc_transmit_apdu(card,&apdu); + SC_TEST_RET(card->ctx, r, "entersafe gen random failed"); + + if(apdu.resplen!=size) + SC_FUNC_RETURN(card->ctx,1,SC_ERROR_INTERNAL); + memcpy(buff,rbuf,size); + + SC_FUNC_RETURN(card->ctx,1,r); +} + +static int entersafe_cipher_apdu(sc_card_t *card, sc_apdu_t *apdu, + u8 *key, size_t keylen, + u8 *buff, size_t buffsize) +{ + EVP_CIPHER_CTX ctx; + int i,r; + u8 iv[8]={0}; + + SC_FUNC_CALLED(card->ctx, 1); + + assert(card); + assert(apdu); + assert(key); + assert(buff); + + /* padding as 0x80 0x00 0x00...... */ + memset(buff,0,buffsize); + buff[0]=apdu->lc; + memcpy(buff+1,apdu->data,apdu->lc); + buff[apdu->lc+1]=0x80; + + EVP_CIPHER_CTX_init(&ctx); + EVP_CIPHER_CTX_set_padding(&ctx,0); + + if(keylen == 8) + EVP_EncryptInit_ex(&ctx, EVP_des_ecb(), NULL, key, iv); + else if (keylen == 16) + EVP_EncryptInit_ex(&ctx, EVP_des_ede(), NULL, key, iv); + else + SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INTERNAL); + + if(!EVP_EncryptUpdate(&ctx,buff,&apdu->lc,buff,buffsize)){ + sc_error(card->ctx, "entersafe encryption error."); + SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INTERNAL); + } + + if (!EVP_CIPHER_CTX_cleanup(&ctx)){ + sc_error(card->ctx, "entersafe encryption error."); + SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INTERNAL); + } + + if(apdu->lc!=buffsize) + { + sc_error(card->ctx, "entersafe build cipher apdu failed."); + SC_FUNC_RETURN(card->ctx, 3, SC_ERROR_INTERNAL); + } + + apdu->data=buff; + apdu->datalen=apdu->lc; + + SC_FUNC_RETURN(card->ctx, 3, SC_SUCCESS); +} + +static int entersafe_mac_apdu(sc_card_t *card, sc_apdu_t *apdu, + u8 * key,size_t keylen, + u8 * buff,size_t buffsize) +{ + int r; + u8 iv[8]; + u8 *tmp=0,*tmp_rounded; + size_t tmpsize=0,tmpsize_rounded=0,outl=0; + EVP_CIPHER_CTX ctx; + + SC_FUNC_CALLED(card->ctx, 1); + + assert(card); + assert(apdu); + assert(key); + assert(buff); + + if(apdu->cse != SC_APDU_CASE_3_SHORT) + return SC_ERROR_INTERNAL; + if(keylen!=8 && keylen!=16) + return SC_ERROR_INTERNAL; + + r=entersafe_gen_radom(card,iv,sizeof(iv)); + SC_TEST_RET(card->ctx,r,"entersafe gen radom failed"); + + /* encode the APDU in the buffer */ + if ((r=sc_apdu_get_octets(card->ctx, apdu, &tmp, &tmpsize,SC_PROTO_RAW)) != SC_SUCCESS) + goto out; + + /* round to 8 */ + tmpsize_rounded=(tmpsize/8+1)*8; + + tmp_rounded = malloc(tmpsize_rounded); + if (tmp_rounded == NULL) + { + r = SC_ERROR_MEMORY_FAILURE; + goto out; + } + + /*build content and padded buffer by 0x80 0x00 0x00..... */ + memset(tmp_rounded,0,tmpsize_rounded); + memcpy(tmp_rounded,tmp,tmpsize); + tmp_rounded[4]+=4; + tmp_rounded[tmpsize]=0x80; + + /* block_size-1 blocks*/ + EVP_CIPHER_CTX_init(&ctx); + EVP_CIPHER_CTX_set_padding(&ctx,0); + EVP_EncryptInit_ex(&ctx, EVP_des_cbc(), NULL, key, iv); + + if(tmpsize_rounded>8){ + if(!EVP_EncryptUpdate(&ctx,tmp_rounded,&outl,tmp_rounded,tmpsize_rounded-8)){ + r = SC_ERROR_INTERNAL; + goto out; + } + } + /* last block */ + if(keylen==8) + { + if(!EVP_EncryptUpdate(&ctx,tmp_rounded+outl,&outl,tmp_rounded+outl,8)){ + r = SC_ERROR_INTERNAL; + goto out; + } + } + else + { + EVP_EncryptInit_ex(&ctx, EVP_des_ede_cbc(), NULL, key,tmp_rounded+outl-8); + if(!EVP_EncryptUpdate(&ctx,tmp_rounded+outl,&outl,tmp_rounded+outl,8)){ + r = SC_ERROR_INTERNAL; + goto out; + } + } + + if (!EVP_CIPHER_CTX_cleanup(&ctx)){ + r = SC_ERROR_INTERNAL; + goto out; + } + + memcpy(buff,apdu->data,apdu->lc); + /* use first 4 bytes of last block as mac value*/ + memcpy(buff+apdu->lc,tmp_rounded+tmpsize_rounded-8,4); + apdu->data=buff; + apdu->lc+=4; + apdu->datalen=apdu->lc; + +out: + if(tmp) + free(tmp); + if(tmp_rounded) + free(tmp_rounded); + + SC_FUNC_RETURN(card->ctx, 3, r); +} + +static int entersafe_transmit_apdu(sc_card_t *card, sc_apdu_t *apdu, + u8 * key, size_t keylen, + int cipher,int mac) +{ + u8 *cipher_data=0,*mac_data=0; + size_t cipher_data_size,mac_data_size; + int blocks; + int r=SC_SUCCESS; + + SC_FUNC_CALLED(card->ctx, 1); + + assert(card); + assert(apdu); + + if((cipher||mac) && (!key||(keylen!=8 && keylen!=16))) + SC_FUNC_RETURN(card->ctx, 3, SC_ERROR_INVALID_ARGUMENTS); + + if (card->ctx->debug >= 6) + { + u8 *sbuf=NULL; + size_t ssize=0; + r = sc_apdu_get_octets(card->ctx, apdu, &sbuf, &ssize, SC_PROTO_RAW); + if (r == SC_SUCCESS) + sc_apdu_log(card->ctx, sbuf, ssize, 1); + free(sbuf); + } + + if(cipher) + { + blocks=(apdu->lc+2)/8+1; + cipher_data_size=blocks*8; + cipher_data=malloc(cipher_data_size); + if(!cipher) + { + r = SC_ERROR_OUT_OF_MEMORY; + goto out; + } + + if((r = entersafe_cipher_apdu(card,apdu,key,keylen,cipher_data,cipher_data_size))<0) + goto out; + } + if(mac) + { + mac_data_size=apdu->lc+4; + mac_data=malloc(mac_data_size); + r = entersafe_mac_apdu(card,apdu,key,keylen,mac_data,mac_data_size); + if(r<0) + goto out; + } + + r = sc_transmit_apdu(card,apdu); + +out: + if(cipher_data) + free(cipher_data); + if(mac_data) + free(mac_data); + + SC_FUNC_RETURN(card->ctx, 3, r); +} + +static int entersafe_read_binary(sc_card_t *card, + unsigned int idx, u8 *buf, size_t count, + unsigned long flags) +{ + sc_apdu_t apdu; + u8 recvbuf[SC_MAX_APDU_BUFFER_SIZE]; + int r; + + SC_FUNC_CALLED(card->ctx, 1); + + assert(count <= card->max_recv_size); + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xB0, + (idx >> 8) & 0xFF, idx & 0xFF); + + apdu.cla=idx > 0x7fff ? 0x80:0x00; + apdu.le = count; + apdu.resplen = count; + apdu.resp = recvbuf; + + r = entersafe_transmit_apdu(card, &apdu,0,0,0,0); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + if (apdu.resplen == 0) + SC_FUNC_RETURN(card->ctx, 2, sc_check_sw(card, apdu.sw1, apdu.sw2)); + memcpy(buf, recvbuf, apdu.resplen); + + SC_FUNC_RETURN(card->ctx, 3, apdu.resplen); +} + +static int entersafe_update_binary(sc_card_t *card, + unsigned int idx, const u8 *buf, + size_t count, unsigned long flags) +{ + sc_apdu_t apdu; + int r; + + SC_FUNC_CALLED(card->ctx, 1); + + assert(count <= card->max_send_size); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xD6, + (idx >> 8) & 0xFF, idx & 0xFF); + apdu.cla=idx > 0x7fff ? 0x80:0x00; + apdu.lc = count; + apdu.datalen = count; + apdu.data = buf; + + r = entersafe_transmit_apdu(card, &apdu,0,0,0,0); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + SC_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), + "Card returned error"); + SC_FUNC_RETURN(card->ctx, 3, count); +} + + +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); + + r = iso_ops->process_fci(card,file,buf,buflen); + SC_TEST_RET(card->ctx, r, "Process fci failed"); + + if(file->namelen) + { + file->type = SC_FILE_TYPE_DF; + file->ef_structure = SC_FILE_EF_UNKNOWN; + } + else + { + file->type = SC_FILE_TYPE_WORKING_EF; + file->ef_structure = SC_FILE_EF_TRANSPARENT; + } + + SC_FUNC_RETURN(card->ctx, 4, r); +} + +static int entersafe_select_fid(sc_card_t *card, + unsigned int id_hi, unsigned int id_lo, + sc_file_t **file_out) +{ + int r; + sc_file_t *file=0; + sc_path_t path; + + path.type=SC_PATH_TYPE_FILE_ID; + path.value[0]=id_hi; + path.value[1]=id_lo; + path.len=2; + + r = iso_ops->select_file(card,&path,&file); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + + /* update cache */ + if (file->type == SC_FILE_TYPE_DF) { + card->cache.current_path.type = SC_PATH_TYPE_PATH; + card->cache.current_path.value[0] = 0x3f; + card->cache.current_path.value[1] = 0x00; + if (id_hi == 0x3f && id_lo == 0x00){ + card->cache.current_path.len = 2; + }else{ + card->cache.current_path.len = 4; + card->cache.current_path.value[2] = id_hi; + card->cache.current_path.value[3] = id_lo; + } + } + + if (file_out) + *file_out = file; + + SC_FUNC_RETURN(card->ctx, 2, SC_SUCCESS); +} + +static int entersafe_select_aid(sc_card_t *card, + const sc_path_t *in_path, + sc_file_t **file_out) +{ + int r; + + if (card->cache_valid + && card->cache.current_path.type == SC_PATH_TYPE_DF_NAME + && card->cache.current_path.len == in_path->len + && memcmp(card->cache.current_path.value, in_path->value, in_path->len)==0 ) + { + if(file_out) + { + *file_out = sc_file_new(); + if(!file_out) + SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_OUT_OF_MEMORY); + } + } + else + { + r = iso_ops->select_file(card,in_path,file_out); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + + /* update cache */ + card->cache.current_path.type = SC_PATH_TYPE_DF_NAME; + card->cache.current_path.len = in_path->len; + memcpy(card->cache.current_path.value,in_path->value,in_path->len); + } + if (file_out) { + sc_file_t *file = *file_out; + assert(file); + + file->type = SC_FILE_TYPE_DF; + file->ef_structure = SC_FILE_EF_UNKNOWN; + file->path.len = 0; + file->size = 0; + /* AID */ + memcpy(file->name,in_path->value,in_path->len); + file->namelen = in_path->len; + file->id = 0x0000; + } + SC_FUNC_RETURN(card->ctx, 2, r); +} + +static int entersafe_select_path(sc_card_t *card, + const u8 pathbuf[16], const size_t len, + sc_file_t **file_out) +{ + u8 n_pathbuf[SC_MAX_PATH_SIZE]; + const u8 *path=pathbuf; + size_t pathlen=len; + int bMatch = -1; + int i; + int r; + + if (pathlen%2 != 0 || pathlen > 6 || pathlen <= 0) + SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_INVALID_ARGUMENTS); + + /* if pathlen == 6 then the first FID must be MF (== 3F00) */ + if (pathlen == 6 && ( path[0] != 0x3f || path[1] != 0x00 )) + SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_INVALID_ARGUMENTS); + + /* unify path (the first FID should be MF) */ + if (path[0] != 0x3f || path[1] != 0x00) + { + n_pathbuf[0] = 0x3f; + n_pathbuf[1] = 0x00; + for (i=0; i< pathlen; i++) + n_pathbuf[i+2] = pathbuf[i]; + path = n_pathbuf; + pathlen += 2; + } + + /* check current working directory */ + if (card->cache_valid + && card->cache.current_path.type == SC_PATH_TYPE_PATH + && card->cache.current_path.len >= 2 + && card->cache.current_path.len <= pathlen ) + { + bMatch = 0; + for (i=0; i < card->cache.current_path.len; i+=2) + if (card->cache.current_path.value[i] == path[i] + && card->cache.current_path.value[i+1] == path[i+1] ) + bMatch += 2; + } + + if ( card->cache_valid && bMatch > 2 ) + { + if ( pathlen - bMatch == 2 ) + { + /* we are in the rigth directory */ + return entersafe_select_fid(card, path[bMatch], path[bMatch+1], file_out); + } + else if ( pathlen - bMatch > 2 ) + { + /* two more steps to go */ + sc_path_t new_path; + + /* first step: change directory */ + r = entersafe_select_fid(card, path[bMatch], path[bMatch+1], NULL); + SC_TEST_RET(card->ctx, r, "SELECT FILE (DF-ID) failed"); + + new_path.type = SC_PATH_TYPE_PATH; + new_path.len = pathlen - bMatch-2; + memcpy(new_path.value, &(path[bMatch+2]), new_path.len); + /* final step: select file */ + return entersafe_select_file(card, &new_path, file_out); + } + else /* if (bMatch - pathlen == 0) */ + { + /* done: we are already in the + * requested directory */ + if ( card->ctx->debug >= 4 ) + sc_debug(card->ctx, "cache hit\n"); + /* copy file info (if necessary) */ + if (file_out) { + sc_file_t *file = sc_file_new(); + if (!file) + SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_OUT_OF_MEMORY); + file->id = (path[pathlen-2] << 8) + + path[pathlen-1]; + file->path = card->cache.current_path; + file->type = SC_FILE_TYPE_DF; + file->ef_structure = SC_FILE_EF_UNKNOWN; + file->size = 0; + file->namelen = 0; + file->magic = SC_FILE_MAGIC; + *file_out = file; + } + /* nothing left to do */ + return SC_SUCCESS; + } + } + else + { + /* no usable cache */ + for ( i=0; ictx, r, "SELECT FILE (DF-ID) failed"); + } + return entersafe_select_fid(card, path[pathlen-2], path[pathlen-1], file_out); + } +} + +static int entersafe_select_file(sc_card_t *card, + const sc_path_t *in_path, + sc_file_t **file_out) +{ + int r; + assert(card); + assert(in_path); + SC_FUNC_CALLED(card->ctx, 1); + + if (card->ctx->debug >= 4) { + char pbuf[SC_MAX_PATH_STRING_SIZE]; + + r = sc_path_print(pbuf, sizeof(pbuf), &card->cache.current_path); + if (r != SC_SUCCESS) + pbuf[0] = '\0'; + + sc_debug(card->ctx, "current path (%s, %s): %s (len: %u)\n", + (card->cache.current_path.type==SC_PATH_TYPE_DF_NAME?"aid":"path"), + (card->cache_valid?"valid":"invalid"), pbuf, + card->cache.current_path.len); + } + + + switch(in_path->type) + { + case SC_PATH_TYPE_FILE_ID: + if (in_path->len != 2) + SC_FUNC_RETURN(card->ctx,2,SC_ERROR_INVALID_ARGUMENTS); + return entersafe_select_fid(card,in_path->value[0],in_path->value[1], file_out); + case SC_PATH_TYPE_DF_NAME: + return entersafe_select_aid(card,in_path,file_out); + case SC_PATH_TYPE_PATH: + return entersafe_select_path(card,in_path->value,in_path->len,file_out); + default: + SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_INVALID_ARGUMENTS); + } +} + +static int entersafe_create_mf(sc_card_t *card, sc_entersafe_create_data * data) +{ + int r; + sc_apdu_t apdu; + + SC_FUNC_CALLED(card->ctx, 1); + + memcpy(data->data.mf.init_key, init_key, sizeof(init_key)); + + sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0xE0,0x00,0x00); + apdu.cla=0x84; + apdu.data=(u8*)&data->data.mf; + apdu.datalen=apdu.lc=sizeof(data->data.mf); + + r = entersafe_transmit_apdu(card, &apdu,trans_code,sizeof(trans_code),0,1); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + return sc_check_sw(card, apdu.sw1, apdu.sw2); +} +static int entersafe_create_df(sc_card_t *card, sc_entersafe_create_data * data) +{ + int r; + sc_apdu_t apdu; + + SC_FUNC_CALLED(card->ctx, 1); + + memcpy(data->data.df.init_key, init_key, sizeof(init_key)); + + sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0xE0,0x01,0x00); + apdu.cla=0x84; + apdu.data=(u8*)&data->data.df; + apdu.lc=apdu.datalen=sizeof(data->data.df); + + r = entersafe_transmit_apdu(card, &apdu,init_key,sizeof(init_key),0,1); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + return sc_check_sw(card, apdu.sw1, apdu.sw2); +} + +static int entersafe_create_ef(sc_card_t *card, sc_entersafe_create_data * data) +{ + int r; + sc_apdu_t apdu; + + SC_FUNC_CALLED(card->ctx, 1); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x02, 0x00); + apdu.cla = 0x84; + apdu.data = (u8*)&data->data.ef; + apdu.lc = apdu.datalen = sizeof(data->data.ef); + + r = entersafe_transmit_apdu(card, &apdu,init_key,sizeof(init_key),0,1); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + return sc_check_sw(card, apdu.sw1, apdu.sw2); +} + +static u8 process_acl_entry(sc_file_t *in, unsigned int method, unsigned int in_def) +{ + u8 def = (u8)in_def; + const sc_acl_entry_t *entry = sc_file_get_acl_entry(in, method); + if (!entry) + { + return def; + } + else if (entry->method & SC_AC_CHV) + { + unsigned int key_ref = entry->key_ref; + if (key_ref == SC_AC_KEY_REF_NONE) + return def; + else + return ENTERSAFE_AC_ALWAYS&0x04; + } + else if (entry->method & SC_AC_NEVER) + { + return ENTERSAFE_AC_NEVER; + } + else + { + return def; + } +} + +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)); + + data.data.ef.file_id[0] = (file->id>>8)&0xFF; + data.data.ef.file_id[1] = file->id&0xFF; + data.data.ef.size[0] = (file->size>>8)&0xFF; + data.data.ef.size[1] = file->size&0xFF; + memset(data.data.ef.ac,ENTERSAFE_AC_ALWAYS,sizeof(data.data.ef.ac)); + data.data.ef.ac[0] = process_acl_entry(file,SC_AC_OP_READ,ENTERSAFE_AC_ALWAYS); + data.data.ef.ac[1] = process_acl_entry(file,SC_AC_OP_UPDATE,ENTERSAFE_AC_ALWAYS); + + return entersafe_create_ef(card, &data); + } else + return SC_ERROR_INVALID_ARGUMENTS; +} + +static int entersafe_internal_set_security_env(sc_card_t *card, + const sc_security_env_t *env, + u8 ** data,size_t* size) +{ + sc_apdu_t apdu; + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; + u8 *p=sbuf; + int r; + + SC_FUNC_CALLED(card->ctx, 1); + + assert(card != NULL && env != NULL); + + switch (env->operation) { + case SC_SEC_OPERATION_DECIPHER: + case SC_SEC_OPERATION_SIGN: + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0, 0); + apdu.p1 = 0x41; + apdu.p2 = 0xB8; + *p++ = 0x80; + *p++ = 0x01; + *p++ = 0x80; + *p++ = 0x83; + *p++ = 0x02; + *p++ = env->key_ref[0]; + *p++ = 0x22; + if(*size>1024/8) + { + if(*size == 2048/8) + { + *p++ = 0x89; + *p++ = 0x40; + memcpy(p,*data,0x40); + p+=0x40; + *data+=0x40; + *size-=0x40; + } + else + { + SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_INVALID_ARGUMENTS); + } + } + break; + default: + SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_INVALID_ARGUMENTS); + } + + apdu.le = 0; + apdu.lc = apdu.datalen = p - sbuf; + apdu.data = sbuf; + apdu.resplen = 0; + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + return sc_check_sw(card, apdu.sw1, apdu.sw2); +} + +/** + * We don't really set the security envirment,but cache it.It will be set when + * security operation is performed later.Because we may transport partial of + * the sign/decipher data within the security envirment apdu. + */ +static int entersafe_set_security_env(sc_card_t *card, + const sc_security_env_t *env, + int se_num) +{ + assert(card); + assert(env); + + SC_FUNC_CALLED(card->ctx, 1); + + if(card->drv_data){ + free(card->drv_data); + card->drv_data=0; + } + + card->drv_data = calloc(1,sizeof(*env)); + if(!card->drv_data) + SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_OUT_OF_MEMORY); + + memcpy(card->drv_data,env,sizeof(*env)); + SC_FUNC_RETURN(card->ctx, 2, SC_NO_ERROR); +} + +static int entersafe_restore_security_env(sc_card_t *card, int se_num) +{ + SC_FUNC_CALLED(card->ctx, 1); + return SC_NO_ERROR; +} + + +static int entersafe_compute_with_prkey(sc_card_t *card, + const u8 * data, size_t datalen, + u8 * out, size_t outlen) +{ + int r; + sc_apdu_t apdu; + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; + u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; + u8* p=sbuf; + size_t size = datalen; + + SC_FUNC_CALLED(card->ctx, 1); + + if(!data) + SC_FUNC_RETURN(card->ctx, 4,SC_ERROR_INVALID_ARGUMENTS); + + memcpy(p,data,size); + + if(!card->drv_data) + SC_FUNC_RETURN(card->ctx, 4,SC_ERROR_INTERNAL); + + r = entersafe_internal_set_security_env(card,card->drv_data,&p,&size); + SC_TEST_RET(card->ctx, r, "internal set security env failed"); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x86,0x80); + apdu.data=p; + apdu.lc = size; + apdu.datalen = size; + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + apdu.le = 256; + apdu.sensitive = 1; + + r = entersafe_transmit_apdu(card, &apdu,0,0,0,0); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + + if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { + size_t 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 entersafe_compute_signature(sc_card_t *card, + const u8 * data, size_t datalen, + u8 * out, size_t outlen) +{ + SC_FUNC_CALLED(card->ctx, 1); + return entersafe_compute_with_prkey(card,data,datalen,out,outlen); +} + +static int entersafe_decipher(sc_card_t *card, + const u8 * crgram, size_t crgram_len, + u8 * out, size_t outlen) +{ + SC_FUNC_CALLED(card->ctx, 1); + return entersafe_compute_with_prkey(card,crgram,crgram_len,out,outlen); +} + +static int entersafe_init_pin_info(struct sc_pin_cmd_pin *pin, unsigned int num) +{ + pin->encoding = SC_PIN_ENCODING_ASCII; + pin->min_length = 4; + pin->max_length = 16; + pin->pad_length = 16; + pin->offset = 5 + num * 16; + pin->pad_char=0x00; +} + +static int entersafe_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, + int *tries_left) +{ + int r; + SC_FUNC_CALLED(card->ctx, 1); + entersafe_init_pin_info(&data->pin1,0); + entersafe_init_pin_info(&data->pin2,1); + data->flags |= SC_PIN_CMD_NEED_PADDING; + + r = iso_ops->pin_cmd(card,data,tries_left); + SC_FUNC_RETURN(card->ctx, 4, r); +} + +static int entersafe_erase_card(sc_card_t *card) +{ + int r; + u8 sbuf[2]; + sc_apdu_t apdu; + + SC_FUNC_CALLED(card->ctx, 1); + + sbuf[0] = 0x3f; + sbuf[1] = 0x00; + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0x00, 0x00); + apdu.lc = 2; + apdu.datalen = 2; + apdu.data = sbuf; + + r = entersafe_transmit_apdu(card, &apdu,0,0,0,0); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + /* invalidate cache */ + card->cache_valid = 0; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xEE, 0x00, 0x00); + apdu.cla=0x84; + apdu.lc=2; + apdu.datalen=2; + apdu.data=sbuf; + r = entersafe_transmit_apdu(card, &apdu,trans_code,sizeof(trans_code),0,1); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + SC_FUNC_RETURN(card->ctx, 4, sc_check_sw(card, apdu.sw1, apdu.sw2)); +} + +static void entersafe_encode_bignum(u8 tag,sc_pkcs15_bignum_t bignum,u8** ptr) +{ + u8 *p=*ptr; + + *p++=tag; + assert(0); + if(bignum.len<256) + { + *p++=(u8)bignum.len; + } + else + { + u8 bytes=0; + size_t len=bignum.len; + while(len) + { + len=len>>8; + ++bytes; + } + bytes&=0x0F; + *p++=0x80|bytes; + while(bytes) + { + *p++=bignum.len>>((bytes-1)*8); + --bytes; + } + } + memcpy(p,bignum.data,bignum.len); + entersafe_reverse_buffer(p,bignum.len); + p+=bignum.len; +} + +static int entersafe_write_small_rsa_key(sc_card_t *card,u8 key_id,struct sc_pkcs15_prkey_rsa *rsa) +{ + sc_apdu_t apdu; + u8 sbuff[SC_MAX_APDU_BUFFER_SIZE]; + int r; + u8 *p=sbuff; + + SC_FUNC_CALLED(card->ctx, 1); + + {/* write prkey */ + *p++=0x00; /* EC */ + *p++=0x00; /* ver */ + entersafe_encode_bignum('E',rsa->exponent,&p); + entersafe_encode_bignum('D',rsa->d,&p); + + sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0xF4,0x22,key_id); + apdu.cla=0x84; + apdu.data=sbuff; + apdu.lc=apdu.datalen=p-sbuff; + + r=entersafe_transmit_apdu(card,&apdu,key_maintain,sizeof(key_maintain),1,1); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + SC_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2),"Write prkey failed"); + } + + p=sbuff; + {/* write pukey */ + *p++=0x00; /* EC */ + *p++=0x00; /* ver */ + entersafe_encode_bignum('E',rsa->exponent,&p); + entersafe_encode_bignum('N',rsa->modulus,&p); + + sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0xF4,0x2A,key_id); + apdu.cla=0x84; + apdu.data=sbuff; + apdu.lc=apdu.datalen=p-sbuff; + + r=entersafe_transmit_apdu(card,&apdu,key_maintain,sizeof(key_maintain),1,1); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + SC_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2),"Write pukey failed"); + } + + SC_FUNC_RETURN(card->ctx,4,SC_SUCCESS); +} + +static int entersafe_write_rsa_key_factor(sc_card_t *card, + u8 key_id,u8 usage, + u8 factor, + sc_pkcs15_bignum_t data) +{ + int r; + sc_apdu_t apdu; + u8 sbuff[SC_MAX_APDU_BUFFER_SIZE]; + + SC_FUNC_CALLED(card->ctx, 1); + + {/* MSE */ + u8 sbuff[4]; + sbuff[0]=0x84; + sbuff[1]=0x02; + sbuff[2]=key_id; + sbuff[3]=usage; + + sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0x22,0x01,0xB8); + apdu.data=sbuff; + apdu.lc=apdu.datalen=4; + + r=entersafe_transmit_apdu(card,&apdu,0,0,0,0); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + SC_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2),"Write prkey factor failed(MSE)"); + } + + {/* Write 'x'; */ + sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0x46,factor,0x00); + + memcpy(sbuff,data.data,data.len); + entersafe_reverse_buffer(sbuff,data.len); + apdu.data=sbuff; + apdu.lc=apdu.datalen=data.len; + + r = entersafe_transmit_apdu(card,&apdu,0,0,0,0); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + SC_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2),"Write prkey factor failed"); + } + SC_FUNC_RETURN(card->ctx,4,SC_SUCCESS); +} + +static int entersafe_write_large_rsa_key(sc_card_t *card,u8 key_id,struct sc_pkcs15_prkey_rsa *rsa) +{ + int r; + + SC_FUNC_CALLED(card->ctx, 1); + + {/* write prkey */ + r = entersafe_write_rsa_key_factor(card,key_id,0x22,0x01,rsa->p); + SC_TEST_RET(card->ctx, r, "write p failed"); + r = entersafe_write_rsa_key_factor(card,key_id,0x22,0x02,rsa->q); + SC_TEST_RET(card->ctx, r, "write q failed"); + r = entersafe_write_rsa_key_factor(card,key_id,0x22,0x03,rsa->dmp1); + SC_TEST_RET(card->ctx, r, "write dmp1 failed"); + r = entersafe_write_rsa_key_factor(card,key_id,0x22,0x04,rsa->dmq1); + SC_TEST_RET(card->ctx, r, "write dmq1 failed"); + r = entersafe_write_rsa_key_factor(card,key_id,0x22,0x05,rsa->iqmp); + SC_TEST_RET(card->ctx, r, "write iqmp failed"); + } + + {/* write pukey */ + u8 sbuff[SC_MAX_APDU_BUFFER_SIZE]; + sc_apdu_t apdu; + + /* first 64(0x40) bytes of N */ + sbuff[0]=0x83; + sbuff[1]=0x02; + sbuff[2]=key_id; + sbuff[3]=0xA2; + sbuff[4]=0x89; + sbuff[5]=0x40; + memcpy(sbuff+6,rsa->modulus.data,0x40); + + sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0x22,0x01,0xB8); + apdu.data=sbuff; + apdu.lc=apdu.datalen=0x46; + + r=entersafe_transmit_apdu(card,&apdu,0,0,0,0); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + SC_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2),"Write pukey N(1) failed"); + + /* left 192(0xC0) bytes of N */ + sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0x46,0x0B,0x00); + apdu.data=rsa->modulus.data+0x40; + apdu.lc=apdu.datalen=0xC0; + + r=entersafe_transmit_apdu(card,&apdu,0,0,0,0); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + SC_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2),"Write pukey N(2) failed"); + + /* E */ + r = entersafe_write_rsa_key_factor(card,key_id,0x2A,0x0D,rsa->exponent); + SC_TEST_RET(card->ctx, r, "write exponent failed"); + } + SC_FUNC_RETURN(card->ctx,4,SC_SUCCESS); +} + +static int entersafe_write_symmetric_key(sc_card_t *card, + u8 key_id,u8 usage, + u8 EC,u8 ver, + u8 *data,size_t len) +{ + sc_apdu_t apdu; + u8 sbuff[SC_MAX_APDU_BUFFER_SIZE]={0}; + int r; + + SC_FUNC_CALLED(card->ctx, 1); + + if(len>240) + SC_FUNC_RETURN(card->ctx,4,SC_ERROR_INCORRECT_PARAMETERS); + + sbuff[0]=EC; + sbuff[1]=ver; + memcpy(&sbuff[2],data,len); + + sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0xF4,usage,key_id); + apdu.cla=0x84; + apdu.data=sbuff; + apdu.lc=apdu.datalen=len+2; + + r=entersafe_transmit_apdu(card,&apdu,key_maintain,sizeof(key_maintain),1,1); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + SC_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2),"Write prkey failed"); + SC_FUNC_RETURN(card->ctx,4,r); +} + +static int entersafe_write_key(sc_card_t *card, sc_entersafe_wkey_data *data) +{ + struct sc_pkcs15_prkey_rsa* rsa=data->key_data.rsa; + + SC_FUNC_CALLED(card->ctx, 1); + + switch(data->usage) + { + case 0x22: + if(rsa->modulus.len<=1024) + return entersafe_write_small_rsa_key(card,data->key_id,rsa); + else + return entersafe_write_large_rsa_key(card,data->key_id,rsa); + break; + case 0x2A: + SC_FUNC_RETURN(card->ctx,4,SC_ERROR_NOT_SUPPORTED); + break; + default: + return entersafe_write_symmetric_key(card,data->key_id,data->usage, + data->key_data.symmetric.EC, + data->key_data.symmetric.ver, + data->key_data.symmetric.key_val, + data->key_data.symmetric.key_len); + break; + } + SC_FUNC_RETURN(card->ctx,4,SC_SUCCESS); +} + +static int entersafe_gen_key(sc_card_t *card, sc_entersafe_gen_key_data *data) +{ + int r; + size_t len = data->key_length >> 3; + sc_apdu_t apdu; + u8 rbuf[300]; + u8 sbuf[4],*p; + + SC_FUNC_CALLED(card->ctx, 1); + + /* MSE */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x01, 0xB8); + apdu.lc=0x04; + sbuf[0]=0x83; + sbuf[1]=0x02; + sbuf[2]=data->key_id; + sbuf[3]=0x2A; + apdu.data = sbuf; + apdu.datalen=4; + apdu.lc=4; + apdu.le=0; + + r=entersafe_transmit_apdu(card, &apdu, 0,0,0,0); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + SC_TEST_RET(card->ctx, sc_check_sw(card,apdu.sw1,apdu.sw2),"EnterSafe set MSE failed"); + + /* generate key */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x46, 0x00, 0x00); + apdu.le = 0; + sbuf[0] = (u8)(data->key_length >> 8); + sbuf[1] = (u8)(data->key_length); + apdu.data = sbuf; + apdu.lc = 2; + apdu.datalen = 2; + + r = entersafe_transmit_apdu(card, &apdu,0,0,0,0); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + SC_TEST_RET(card->ctx, sc_check_sw(card,apdu.sw1,apdu.sw2),"EnterSafe generate keypair failed"); + + /* read public key via READ PUBLIC KEY */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xE6, 0x2A, data->key_id); + apdu.cla = 0x80; + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + apdu.le = 256; + r = entersafe_transmit_apdu(card, &apdu,0,0,0,0); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + SC_TEST_RET(card->ctx, sc_check_sw(card,apdu.sw1,apdu.sw2),"EnterSafe get pukey failed"); + + data->modulus = (u8 *) malloc(len); + if (!data->modulus) + SC_FUNC_RETURN(card->ctx,4,SC_ERROR_OUT_OF_MEMORY); + + p=rbuf; + assert(*p=='E'); + p+=2+p[1]; + /* N */ + assert(*p=='N'); + ++p; + if(*p++>0x80) + { + u8 len_bytes=(*(p-1))&0x0f; + size_t module_len=0; + while(len_bytes!=0) + { + module_len=module_len<<8; + module_len+=*p++; + --len_bytes; + } + } + + entersafe_reverse_buffer(p,len); + memcpy(data->modulus,p,len); + + SC_FUNC_RETURN(card->ctx,4,SC_SUCCESS); +} + +static int entersafe_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_FUNC_CALLED(card->ctx, 1); + assert(serial); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT,0xEA,0x00,0x00); + apdu.cla=0x80; + apdu.resp=rbuf; + apdu.resplen=sizeof(rbuf); + apdu.le=0x08; + + r=entersafe_transmit_apdu(card, &apdu,0,0,0,0); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + SC_TEST_RET(card->ctx, sc_check_sw(card,apdu.sw1,apdu.sw2),"EnterSafe get SN failed"); + + card->serialnr.len=serial->len=8; + memcpy(card->serialnr.value,rbuf,8); + memcpy(serial->value,rbuf,8); + + SC_FUNC_RETURN(card->ctx,4,SC_SUCCESS); +} + +static int entersafe_preinstall_rsa_1024(sc_card_t *card,u8 key_id) +{ + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; + sc_apdu_t apdu; + int ret=0; + static u8 const rsa_key_e[] = + { + 'E', 0x04, 0x01, 0x00, 0x01, 0x00 + }; + + SC_FUNC_CALLED(card->ctx, 1); + + /* create rsa item in IKF */ + sbuf[0] = 0x00;/* key len extern */ + sbuf[1] = 0x8a;/* key len */ + sbuf[2] = 0x22; /* USAGE */ + sbuf[3] = 0x34; /* user ac */ + sbuf[4] = 0x04; /* change ac */ + sbuf[5] = 0x34; /* UPDATE AC */ + sbuf[6] = 0x40; /* ALGO */ + sbuf[7] = 0x00; /* EC */ + sbuf[8] = 0x00; /* VER */ + memcpy(&sbuf[9], rsa_key_e, sizeof(rsa_key_e)); + sbuf[9 + sizeof(rsa_key_e) + 0] = 'D'; + sbuf[9 + sizeof(rsa_key_e) + 1] = 0x82; + sbuf[9 + sizeof(rsa_key_e) + 2] = 0x00; + sbuf[9 + sizeof(rsa_key_e) + 3] = 0x80; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT,0xF0,0x00,key_id); + apdu.cla=0x84; + apdu.data=sbuf; + apdu.lc=apdu.datalen=9 + sizeof(rsa_key_e) + 4; + + ret = entersafe_transmit_apdu(card,&apdu,init_key,sizeof(init_key),0,1); + SC_TEST_RET(card->ctx, ret, "Preinstall rsa failed"); + + /* create rsa item in PKF */ + sbuf[0] = 0x01; /* key len extern */ + sbuf[1] = 0x0A; /* key len */ + sbuf[2] = 0x2A; /* USAGE */ + sbuf[3] = ENTERSAFE_AC_ALWAYS; /* user ac */ + sbuf[4] = 0x04; /* change ac */ + sbuf[5] = ENTERSAFE_AC_ALWAYS; /* UPDATE AC */ + sbuf[6] = 0x40; /* ALGO */ + sbuf[7] = 0x00; /* EC */ + sbuf[8] = 0x00; /* VER */ + memcpy(&sbuf[9], rsa_key_e, sizeof(rsa_key_e)); + sbuf[9 + sizeof(rsa_key_e) + 0] = 'N'; + sbuf[9 + sizeof(rsa_key_e) + 1] = 0x82; + sbuf[9 + sizeof(rsa_key_e) + 2] = 0x01; + sbuf[9 + sizeof(rsa_key_e) + 3] = 0x00; + + sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0xF0,0x00,key_id); + apdu.cla=0x84; + apdu.data=sbuf; + apdu.lc=apdu.datalen=9 + sizeof(rsa_key_e) + 4; + + ret=entersafe_transmit_apdu(card,&apdu,init_key,sizeof(init_key),0,1); + SC_TEST_RET(card->ctx, ret, "Preinstall rsa failed"); + + SC_FUNC_RETURN(card->ctx,4,SC_SUCCESS); +} + +static int entersafe_preinstall_rsa_2048(sc_card_t *card,u8 key_id) +{ + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; + sc_apdu_t apdu; + int ret=0; + static u8 const rsa_key_e[] = + { + 'E', 0x04, 0x01, 0x00, 0x01, 0x00 + }; + + SC_FUNC_CALLED(card->ctx, 1); + + /* create rsa item in IKF */ + sbuf[0] = 0x04; /* key len extern */ + sbuf[1] = 0x0a; /* key len */ + sbuf[2] = 0x22; /* USAGE */ + sbuf[3] = 0x34; /* user ac */ + sbuf[4] = 0x04; /* change ac */ + sbuf[5] = 0x34; /* UPDATE AC */ + sbuf[6] = 0x40; /* ALGO */ + sbuf[7] = 0x00; /* EC */ + sbuf[8] = 0x00; /* VER */ + memcpy(&sbuf[9], rsa_key_e, sizeof(rsa_key_e)); + sbuf[9 + sizeof(rsa_key_e) + 0] = 'C'+'R'+'T'; + sbuf[9 + sizeof(rsa_key_e) + 1] = 0x82; + sbuf[9 + sizeof(rsa_key_e) + 2] = 0x04; + sbuf[9 + sizeof(rsa_key_e) + 3] = 0x00; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT,0xF0,0x00,key_id); + apdu.cla=0x84; + apdu.data=sbuf; + apdu.lc=apdu.datalen=9 + sizeof(rsa_key_e) + 4; + + ret = entersafe_transmit_apdu(card,&apdu,init_key,sizeof(init_key),0,1); + SC_TEST_RET(card->ctx, ret, "Preinstall rsa failed"); + + /* create rsa item in PKF */ + sbuf[0] = 0x01; /* key len extern */ + sbuf[1] = 0x0A; /* key len */ + sbuf[2] = 0x2A; /* USAGE */ + sbuf[3] = ENTERSAFE_AC_ALWAYS; /* user ac */ + sbuf[4] = 0x04; /* change ac */ + sbuf[5] = ENTERSAFE_AC_ALWAYS; /* UPDATE AC */ + sbuf[6] = 0x40; /* ALGO */ + sbuf[7] = 0x00; /* EC */ + sbuf[8] = 0x00; /* VER */ + memcpy(&sbuf[9], rsa_key_e, sizeof(rsa_key_e)); + sbuf[9 + sizeof(rsa_key_e) + 0] = 'N'; + sbuf[9 + sizeof(rsa_key_e) + 1] = 0x82; + sbuf[9 + sizeof(rsa_key_e) + 2] = 0x01; + sbuf[9 + sizeof(rsa_key_e) + 3] = 0x00; + + sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0xF0,0x00,key_id); + apdu.cla=0x84; + apdu.data=sbuf; + apdu.lc=apdu.datalen=9 + sizeof(rsa_key_e) + 4; + + ret=entersafe_transmit_apdu(card,&apdu,init_key,sizeof(init_key),0,1); + SC_TEST_RET(card->ctx, ret, "Preinstall rsa failed"); + + SC_FUNC_RETURN(card->ctx,4,SC_SUCCESS); +} + +static int entersafe_preinstall_keys(sc_card_t *card,int (*install_rsa)(sc_card_t *,u8)) +{ + int r; + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; + sc_apdu_t apdu; + + SC_FUNC_CALLED(card->ctx, 1); + + {/* RSA */ + u8 rsa_index; + for(rsa_index=ENTERSAFE_MIN_KEY_ID; + rsa_index<=ENTERSAFE_MAX_KEY_ID; + ++rsa_index) + { + r=install_rsa(card,rsa_index); + SC_TEST_RET(card->ctx, r, "Preinstall rsa key failed"); + } + } + + {/* key maintain */ + /* create key maintain*/ + sbuf[0] = 0; /* key len extern */ + sbuf[1] = sizeof(key_maintain); /* key len */ + sbuf[2] = 0x03; /* USAGE */ + sbuf[3] = ENTERSAFE_AC_ALWAYS; /* use AC */ + sbuf[4] = ENTERSAFE_AC_ALWAYS; /* CHANGE AC */ + sbuf[5] = ENTERSAFE_AC_NEVER; /* UPDATE AC */ + sbuf[6] = 0x01; /* ALGO */ + sbuf[7] = 0x00; /* EC */ + sbuf[8] = 0x00; /* VER */ + memcpy(&sbuf[9], key_maintain, sizeof(key_maintain)); + + sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0xF0,0x00,0x00); + apdu.cla=0x84; + apdu.data=sbuf; + apdu.lc=apdu.datalen=0x19; + + r = entersafe_transmit_apdu(card,&apdu,init_key,sizeof(init_key),0,1); + SC_TEST_RET(card->ctx, r, "Preinstall key maintain failed"); + } + + {/* user PIN */ + memset(sbuf,0,sizeof(sbuf)); + sbuf[0] = 0; /* key len extern */ + sbuf[1] = 16; /* key len */ + sbuf[2] = 0x0b; /* USAGE */ + sbuf[3] = ENTERSAFE_AC_ALWAYS; /* use AC */ + sbuf[4] = 0X04; /* CHANGE AC */ + sbuf[5] = 0x14; /* UPDATE AC */ + sbuf[6] = 0x01; /* ALGO */ + sbuf[7] = 0xFF; /* EC */ + sbuf[8] = 0x00; /* VER */ + + sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0xF0,0x00,ENTERSAFE_USER_PIN_ID); + apdu.cla=0x84; + apdu.data=sbuf; + apdu.lc=apdu.datalen=0x19; + + r = entersafe_transmit_apdu(card,&apdu,init_key,sizeof(init_key),0,1); + SC_TEST_RET(card->ctx, r, "Preinstall user PIN failed"); + } + + SC_FUNC_RETURN(card->ctx,4,SC_SUCCESS); +} + +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; + SC_FUNC_CALLED(card->ctx, 1); + + switch (cmd) + { + case SC_CARDCTL_ENTERSAFE_CREATE_FILE: + if (tmp->type == SC_ENTERSAFE_MF_DATA) + return entersafe_create_mf(card, tmp); + else if (tmp->type == SC_ENTERSAFE_DF_DATA) + return entersafe_create_df(card, tmp); + else if (tmp->type == SC_ENTERSAFE_EF_DATA) + return entersafe_create_ef(card, tmp); + else + return SC_ERROR_INTERNAL; + case SC_CARDCTL_ENTERSAFE_WRITE_KEY: + return entersafe_write_key(card, (sc_entersafe_wkey_data *)ptr); + case SC_CARDCTL_ENTERSAFE_GENERATE_KEY: + return entersafe_gen_key(card, (sc_entersafe_gen_key_data *)ptr); + case SC_CARDCTL_ERASE_CARD: + return entersafe_erase_card(card); + case SC_CARDCTL_GET_SERIALNR: + return entersafe_get_serialnr(card, (sc_serial_number_t *)ptr); + case SC_CARDCTL_ENTERSAFE_PREINSTALL_KEYS: + return entersafe_preinstall_keys(card,entersafe_preinstall_rsa_1024); + default: + return SC_ERROR_NOT_SUPPORTED; + } +} + +static int entersafe_card_ctl_2048(sc_card_t *card, unsigned long cmd, void *ptr) +{ + sc_entersafe_create_data *tmp = (sc_entersafe_create_data *)ptr; + + SC_FUNC_CALLED(card->ctx, 1); + + switch (cmd) + { + case SC_CARDCTL_ENTERSAFE_CREATE_FILE: + if (tmp->type == SC_ENTERSAFE_MF_DATA) + return entersafe_create_mf(card, tmp); + else if (tmp->type == SC_ENTERSAFE_DF_DATA) + return entersafe_create_df(card, tmp); + else if (tmp->type == SC_ENTERSAFE_EF_DATA) + return entersafe_create_ef(card, tmp); + else + return SC_ERROR_INTERNAL; + case SC_CARDCTL_ENTERSAFE_WRITE_KEY: + return entersafe_write_key(card, (sc_entersafe_wkey_data *)ptr); + case SC_CARDCTL_ENTERSAFE_GENERATE_KEY: + return entersafe_gen_key(card, (sc_entersafe_gen_key_data *)ptr); + case SC_CARDCTL_ERASE_CARD: + return entersafe_erase_card(card); + case SC_CARDCTL_GET_SERIALNR: + return entersafe_get_serialnr(card, (sc_serial_number_t *)ptr); + case SC_CARDCTL_ENTERSAFE_PREINSTALL_KEYS: + return entersafe_preinstall_keys(card,entersafe_preinstall_rsa_2048); + default: + return SC_ERROR_NOT_SUPPORTED; + } +} + +static struct sc_card_driver * sc_get_driver(void) +{ + struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); + + if (iso_ops == NULL) + iso_ops = iso_drv->ops; + + entersafe_ops = *iso_drv->ops; + entersafe_ops.match_card = entersafe_match_card; + entersafe_ops.init = entersafe_init; + entersafe_ops.read_binary = entersafe_read_binary; + entersafe_ops.write_binary = NULL; + entersafe_ops.update_binary = entersafe_update_binary; + entersafe_ops.select_file = entersafe_select_file; + entersafe_ops.restore_security_env = entersafe_restore_security_env; + entersafe_ops.set_security_env = entersafe_set_security_env; + entersafe_ops.decipher = entersafe_decipher; + entersafe_ops.compute_signature = entersafe_compute_signature; + entersafe_ops.create_file = entersafe_create_file; + entersafe_ops.delete_file = NULL; + entersafe_ops.pin_cmd = entersafe_pin_cmd; + entersafe_ops.card_ctl = entersafe_card_ctl_2048; + entersafe_ops.process_fci = entersafe_process_fci; + return &entersafe_drv; +} + +struct sc_card_driver * sc_get_entersafe_driver(void) +{ + return sc_get_driver(); +} +#endif diff --git a/src/libopensc/cardctl.h b/src/libopensc/cardctl.h index 2bba7f07..a1064226 100644 --- a/src/libopensc/cardctl.h +++ b/src/libopensc/cardctl.h @@ -156,7 +156,17 @@ enum { SC_CARDCTL_RUTOKEN_GOST_ENCIPHER, SC_CARDCTL_RUTOKEN_GOST_DECIPHER, SC_CARDCTL_RUTOKEN_FORMAT_INIT, - SC_CARDCTL_RUTOKEN_FORMAT_END + SC_CARDCTL_RUTOKEN_FORMAT_END, + + /* + * EnterSafe specific calls + */ + SC_CARDCTL_ENTERSAFE_BASE = _CTL_PREFIX('E', 'S', 'F'), + SC_CARDCTL_ENTERSAFE_CREATE_FILE, + SC_CARDCTL_ENTERSAFE_CREATE_END, + SC_CARDCTL_ENTERSAFE_WRITE_KEY, + SC_CARDCTL_ENTERSAFE_GENERATE_KEY, + SC_CARDCTL_ENTERSAFE_PREINSTALL_KEYS, }; enum { @@ -551,6 +561,87 @@ struct sc_rutoken_decipherinfo { u8 *outbuf; size_t outlen; }; + +/* + * EnterSafe stuff + * + */ + +#define SC_ENTERSAFE_MF_DATA 0x01 +#define SC_ENTERSAFE_DF_DATA 0x02 +#define SC_ENTERSAFE_EF_DATA 0x04 + +#define ENTERSAFE_USER_PIN_ID 0x01 +#define ENTERSAFE_SO_PIN_ID 0x02 +#define ENTERSAFE_MIN_KEY_ID 0x01 +#define ENTERSAFE_MAX_KEY_ID 0x09 + +#define ENTERSAFE_AC_EVERYONE 0x00 +#define ENTERSAFE_AC_USER 0x04 +#define ENTERSAFE_AC_USER_ 0x08 + + +#define ENTERSAFE_AC_NEVER 0xC0 +#define ENTERSAFE_AC_ALWAYS 0x10 +#define ENTERSAFE_AC_CHV 0x30 + + +typedef struct sc_entersafe_create_data_st { + unsigned int type; + union { + struct { + u8 file_id[2]; + u8 file_count; + u8 flag; + u8 ikf_size[2]; + u8 create_ac; + u8 append_ac; + u8 lock_ac; + u8 aid[16]; + u8 init_key[16]; + } mf; + struct { + u8 file_id[2]; + u8 file_count; + u8 flag; + u8 ikf_size[2]; + u8 create_ac; + u8 append_ac; + u8 lock_ac; + u8 aid[16]; + u8 init_key[16]; + } df; + struct { + u8 file_id[2]; + u8 size[2]; + u8 attr[2]; + u8 name; + u8 ac[10]; + u8 sm[2]; + } ef; + } data; +} sc_entersafe_create_data; + +typedef struct sc_entersafe_wkey_data_st { + u8 key_id; + u8 usage; + union{ + struct sc_pkcs15_prkey_rsa* rsa; + struct{ + u8 EC; + u8 ver; + u8 key_val[256]; + size_t key_len; + } symmetric; + }key_data; +} sc_entersafe_wkey_data; + +typedef struct sc_entersafe_gen_key_data_st { + u8 key_id; + size_t key_length; + u8 *modulus; +} sc_entersafe_gen_key_data; + #pragma pack(pop) #ifdef __cplusplus diff --git a/src/libopensc/cards.h b/src/libopensc/cards.h index 53a6817d..9376c038 100644 --- a/src/libopensc/cards.h +++ b/src/libopensc/cards.h @@ -138,7 +138,11 @@ enum { /* TUBITAK UEKAE cards */ SC_CARD_TYPE_AKIS_BASE = 18000, - SC_CARD_TYPE_AKIS_GENERIC + SC_CARD_TYPE_AKIS_GENERIC, + + /* EnterSafe cards */ + SC_CARD_TYPE_ENTERSAFE_BASE = 19000, + SC_CARD_TYPE_ENTERSAFE_3K, }; #ifdef __cplusplus diff --git a/src/libopensc/ctx.c b/src/libopensc/ctx.c index bde57744..b545acf1 100644 --- a/src/libopensc/ctx.c +++ b/src/libopensc/ctx.c @@ -81,6 +81,9 @@ static const struct _sc_driver_entry internal_card_drivers[] = { #endif { "acos5", (void *(*)(void)) sc_get_acos5_driver }, { "akis", (void *(*)(void)) sc_get_akis_driver }, +#ifdef ENABLE_OPENSSL + { "entersafe",(void *(*)(void)) sc_get_entersafe_driver }, +#endif /* The default driver should be last, as it handles all the * unrecognized cards. */ { "default", (void *(*)(void)) sc_get_default_driver }, diff --git a/src/libopensc/opensc.h b/src/libopensc/opensc.h index b2804c4b..0609ea9d 100644 --- a/src/libopensc/opensc.h +++ b/src/libopensc/opensc.h @@ -1199,6 +1199,7 @@ extern sc_card_driver_t *sc_get_muscle_driver(void); extern sc_card_driver_t *sc_get_acos5_driver(void); extern sc_card_driver_t *sc_get_asepcos_driver(void); extern sc_card_driver_t *sc_get_akis_driver(void); +extern sc_card_driver_t *sc_get_entersafe_driver(void); #ifdef __cplusplus } diff --git a/src/libopensc/pkcs15-esinit.c b/src/libopensc/pkcs15-esinit.c new file mode 100644 index 00000000..c7d7c342 --- /dev/null +++ b/src/libopensc/pkcs15-esinit.c @@ -0,0 +1,89 @@ +/* + * 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 + */ +/* Initially written by Weitao Sun (weitao@ftsafe.com) 2008*/ + +#include "internal.h" +#include "pkcs15.h" +#include "cardctl.h" + +#include +#include +#include + + +#define MANU_ID "entersafe" + +static int entersafe_detect_card( sc_pkcs15_card_t *p15card) +{ + sc_card_t *card = p15card->card; + + SC_FUNC_CALLED(card->ctx, 1); + + /* check if we have the correct card OS */ + if (strcmp(card->name, "entersafe")) + return SC_ERROR_WRONG_CARD; + + return SC_SUCCESS; +} + +static int sc_pkcs15emu_entersafe_init( sc_pkcs15_card_t *p15card) +{ + int r, i; + char buf[256]; + sc_path_t path; + sc_file_t *file = NULL; + sc_card_t *card = p15card->card; + sc_serial_number_t serial; + + SC_FUNC_CALLED(card->ctx, 1); + + /* get serial number */ + r = sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serial); + r = sc_bin_to_hex(serial.value, serial.len, buf, sizeof(buf), 0); + if (r != SC_SUCCESS) + return SC_ERROR_INTERNAL; + if (p15card->serial_number) + free(p15card->serial_number); + p15card->serial_number = (char *) malloc(strlen(buf) + 1); + if (!p15card->serial_number) + return SC_ERROR_INTERNAL; + strcpy(p15card->serial_number, buf); + + /* the manufacturer ID, in this case Giesecke & Devrient GmbH */ + if (p15card->manufacturer_id) + free(p15card->manufacturer_id); + p15card->manufacturer_id = (char *) malloc(strlen(MANU_ID) + 1); + if (!p15card->manufacturer_id) + return SC_ERROR_INTERNAL; + strcpy(p15card->manufacturer_id, MANU_ID); + + return SC_SUCCESS; +} + +int sc_pkcs15emu_entersafe_init_ex(sc_pkcs15_card_t *p15card, + sc_pkcs15emu_opt_t *opts) +{ + SC_FUNC_CALLED(p15card->card->ctx, 1); + + if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) + return sc_pkcs15emu_entersafe_init(p15card); + else { + int r = entersafe_detect_card(p15card); + if (r) + return SC_ERROR_WRONG_CARD; + return sc_pkcs15emu_entersafe_init(p15card); + } +} diff --git a/src/libopensc/pkcs15-syn.c b/src/libopensc/pkcs15-syn.c index 0cdd8020..0d19fa2a 100644 --- a/src/libopensc/pkcs15-syn.c +++ b/src/libopensc/pkcs15-syn.c @@ -54,6 +54,8 @@ extern int sc_pkcs15emu_tccardos_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t extern int sc_pkcs15emu_rutoken_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); +extern int sc_pkcs15emu_entersafe_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); + static struct { const char * name; int (*handler)(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); @@ -71,6 +73,7 @@ static struct { { "atrust-acos",sc_pkcs15emu_atrust_acos_init_ex}, { "tccardos", sc_pkcs15emu_tccardos_init_ex }, { "rutoken", sc_pkcs15emu_rutoken_init_ex }, + { "entersafe", sc_pkcs15emu_entersafe_init_ex }, { NULL, NULL } }; diff --git a/src/pkcs15init/Makefile.am b/src/pkcs15init/Makefile.am index 407cef9e..a01d8129 100644 --- a/src/pkcs15init/Makefile.am +++ b/src/pkcs15init/Makefile.am @@ -22,7 +22,8 @@ dist_pkgdata_DATA = \ pkcs15.profile \ muscle.profile \ rutoken.profile \ - asepcos.profile + asepcos.profile \ + entersafe.profile AM_CPPFLAGS = -DSC_PKCS15_PROFILE_DIRECTORY=\"$(pkgdatadir)\" AM_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) $(LTLIB_CFLAGS) @@ -34,6 +35,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 \ pkcs15init.exports if WIN32 libpkcs15init_la_SOURCES += versioninfo.rc diff --git a/src/pkcs15init/Makefile.mak b/src/pkcs15init/Makefile.mak index acbb131d..2b93d9ed 100644 --- a/src/pkcs15init/Makefile.mak +++ b/src/pkcs15init/Makefile.mak @@ -10,6 +10,7 @@ OBJECTS = pkcs15-lib.obj profile.obj keycache.obj \ pkcs15-cardos.obj pkcs15-jcop.obj pkcs15-starcos.obj \ pkcs15-oberthur.obj pkcs15-setcos.obj pkcs15-incrypto34.obj \ pkcs15-muscle.obj pkcs15-asepcos.obj pkcs15-rutoken.obj \ + pkcs15-entersafe.obj \ versioninfo.res all: install-headers $(TARGET) diff --git a/src/pkcs15init/entersafe.profile b/src/pkcs15init/entersafe.profile new file mode 100644 index 00000000..c66d42c1 --- /dev/null +++ b/src/pkcs15init/entersafe.profile @@ -0,0 +1,181 @@ +# +# pkcs15 profile for entersafe +# +cardinfo { + manufacturer = "EnterSafe"; + min-pin-length = 4; + max-pin-length = 16; + pin-encoding = ascii-numeric; + pin-pad-char = 0x00; +} + +option default { + macros { + pin-flags = initialized, needs-padding; + df_acl = *=NEVER; + protected = *=$PIN,READ=NONE; + dir-size = 128; + tinfo-size = 128; + unusedspace-size = 128; + odf-size = 256; + aodf-size = 256; + cdf-size = 512; + prkdf-size = 256; + pukdf-size = 256; + dodf-size = 256; + info-size = 128; + } +} + +option onepin { + macros { + pin-flags = initialized, needs-padding; + df_acl = *=$PIN; + protected = *=$PIN,READ=NONE; + dir-size = 128; + tinfo-size = 128; + unusedspace-size = 128; + odf-size = 256; + aodf-size = 256; + cdf-size = 512; + prkdf-size = 256; + pukdf-size = 256; + dodf-size = 256; + info-size = 128; + } +} + +PIN so-pin { + reference = 1; + attempts = 3; + flags = $pin-flags; +} +PIN so-puk { + reference = 1; + attempts = 3; + flags = $pin-flags; +} +PIN user-pin { + reference = 1; + attempts = 3; + flags = $pin-flags; +} +PIN user-puk { + reference = 1; + attempts = 3; + flags = $pin-flags; +} + +# Additional filesystem info. +# This is added to the file system info specified in the +# main profile. +filesystem { + DF MF { + ACL = $df_acl; + size = 768; + + EF dir { + type = EF; + size = $dir-size; + ACL = $protected; + file-id = 2F00; + structure = transparent; + } + + DF PKCS15-AppDF { + ACL = $df_acl; + size = 16000; + + # INTERNAL SECRET KEY file of the application DF + # Note: if the WRITE ACL is commented out or no + # sopin is specified the ACs must be activated via + # 'pkcs15-init --finalize' (in this case the + # AC WRITE is NEVER as the required state can't + # be reached). + EF p15_gpkf { + file-id = FFFD; + structure = transparent; + size = 2560; + ACL = $df_acl; + } + + EF PKCS15-ODF { + size = $odf-size; + ACL = $protected; + } + + EF PKCS15-TokenInfo { + size = $tinfo-size; + ACL = $protected; + } + + EF PKCS15-UnusedSpace { + size = $unusedspace-size; + ACL = $protected; + } + + EF PKCS15-AODF { + size = $aodf-size; + ACL = $protected; + } + + EF PKCS15-PrKDF { + size = $prkdf-size; + ACL = $protected; + } + + EF PKCS15-PuKDF { + size = $pukdf-size; + ACL = $protected; + } + + EF PKCS15-CDF { + size = $cdf-size; + ACL = $protected; + } + + EF PKCS15-DODF { + size = $dodf-size; + ACL = $protected; + } + + template key-domain { + # This is a dummy entry - pkcs15-init insists that + # this is present + EF private-key { + file-id = FFFF; + } + EF public-key { + file-id = 3003; + size = 320; + structure = transparent; + ACL = *=NEVER,READ=NONE,UPDATE=$PIN; + } + + # Certificate template + EF certificate { + file-id = 3104; + structure = transparent; + ACL = *=NEVER,READ=NONE,UPDATE=$PIN; + } + + # Extractable private keys are stored in transparent EFs. + # Encryption of the content is performed by libopensc. + EF extractable-key { + file-id = 3201; + structure = transparent; + ACL = *=NEVER,READ=NONE,UPDATE=$PIN; + } + + # data objects are stored in transparent EFs. + EF data { + file-id = 3301; + structure = transparent; + ACL = *=NEVER,READ=NONE,UPDATE=$PIN; + } + + } + + } + } +} diff --git a/src/pkcs15init/pkcs15-entersafe.c b/src/pkcs15init/pkcs15-entersafe.c new file mode 100644 index 00000000..d86a90d3 --- /dev/null +++ b/src/pkcs15init/pkcs15-entersafe.c @@ -0,0 +1,416 @@ +/* + * 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 + */ +/* Initially written by Weitao Sun (weitao@ftsafe.com) 2008*/ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include "pkcs15-init.h" +#include "profile.h" + +static u8 process_acl_entry(sc_file_t *in, unsigned int method, unsigned int in_def) +{ + u8 def = (u8)in_def; + const sc_acl_entry_t *entry = sc_file_get_acl_entry(in, method); + if (!entry) + { + return def; + } + else if (entry->method == SC_AC_CHV) + { + unsigned int key_ref = entry->key_ref; + if (key_ref == SC_AC_KEY_REF_NONE) + return def; + else + return ENTERSAFE_AC_ALWAYS&0x04; + } + else if (entry->method == SC_AC_SYMBOLIC) + { + return ENTERSAFE_AC_ALWAYS&0x04; + } + else if (entry->method == SC_AC_NEVER) + { + return ENTERSAFE_AC_NEVER; + } + else + { + return def; + } +} + +static int entersafe_erase_card(struct sc_profile *profile, sc_card_t *card) +{ + SC_FUNC_CALLED(card->ctx, 1); + return sc_card_ctl(card,SC_CARDCTL_ERASE_CARD,0); +} + +static int entersafe_init_card(sc_profile_t *profile, sc_card_t *card) +{ + int ret; + + {/* MF */ + sc_file_t *mf_file; + sc_entersafe_create_data mf_data; + + SC_FUNC_CALLED(card->ctx, 1); + + ret = sc_profile_get_file(profile, "MF", &mf_file); + SC_TEST_RET(card->ctx,ret,"Get MF info failed"); + + mf_data.type = SC_ENTERSAFE_MF_DATA; + mf_data.data.df.file_id[0]=0x3F; + mf_data.data.df.file_id[1]=0x00; + mf_data.data.df.file_count=0x04; + mf_data.data.df.flag=0x11; + mf_data.data.df.ikf_size[0]=(mf_file->size>>8)&0xFF; + mf_data.data.df.ikf_size[1]=mf_file->size&0xFF; + mf_data.data.df.create_ac=0x10; + mf_data.data.df.append_ac=0xC0; + mf_data.data.df.lock_ac=0x10; + memcpy(mf_data.data.df.aid,mf_file->name,mf_file->namelen); + sc_file_free(mf_file); + + ret = sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_CREATE_FILE, &mf_data); + SC_TEST_RET(card->ctx,ret,"Create MF failed"); + } + + {/* EF(DIR) */ + sc_file_t *dir_file; + size_t fid,size; + sc_entersafe_create_data ef_data; + u8 *buff=0; + + /* get dir profile */ + ret = sc_profile_get_file(profile, "dir", &dir_file); + SC_TEST_RET(card->ctx,ret,"Get EF(DIR) info failed"); + fid=dir_file->id; + size=dir_file->size; + sc_file_free(dir_file); + + ef_data.type=SC_ENTERSAFE_EF_DATA; + ef_data.data.ef.file_id[0]=(fid>>8)&0xFF; + ef_data.data.ef.file_id[1]=fid&0xFF; + ef_data.data.ef.size[0]=(size>>8)&0xFF; + ef_data.data.ef.size[1]=size&0xFF; + ef_data.data.ef.attr[0]=0x00; + ef_data.data.ef.attr[1]=0x00; + ef_data.data.ef.name=0x00; + memset(ef_data.data.ef.ac,0x10,sizeof(ef_data.data.ef.ac)); + memset(ef_data.data.ef.sm,0x00,sizeof(ef_data.data.ef.sm)); + + ret = sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_CREATE_FILE, &ef_data); + SC_TEST_RET(card->ctx,ret,"Create EF(DIR) failed"); + + + /* fill file by 0 */ + buff = calloc(1,size); + if(!buff) + SC_FUNC_RETURN(card->ctx,4,SC_SUCCESS); + memset(buff,0,size); + + ret = sc_update_binary(card,0,buff,size,0); + free(buff); + SC_TEST_RET(card->ctx,ret,"Initialize EF(DIR) failed"); + } + + SC_FUNC_RETURN(card->ctx,4,SC_SUCCESS); + +} + +static int entersafe_create_dir(sc_profile_t *profile, sc_card_t *card, + sc_file_t *df) +{ + int ret; + + SC_FUNC_CALLED(card->ctx, 1); + + {/* df */ + sc_entersafe_create_data df_data; + + df_data.type = SC_ENTERSAFE_DF_DATA; + df_data.data.df.file_id[0]=(df->id >> 8) & 0xFF; + df_data.data.df.file_id[1]=df->id & 0xFF; + df_data.data.df.file_count=0x0F; + df_data.data.df.flag=0x01; + df_data.data.df.ikf_size[0]=(df->size>>8)&0xFF; + df_data.data.df.ikf_size[1]=df->size&0xFF; + df_data.data.df.create_ac=0x10; + df_data.data.df.append_ac=0xC0; + df_data.data.df.lock_ac=0x10; + memcpy(df_data.data.df.aid,df->name,df->namelen); + + ret = sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_CREATE_FILE, &df_data); + SC_TEST_RET(card->ctx,ret,"Crate DF failed"); + } + + {/* GPKF */ + sc_file_t *gpkf_file; + sc_entersafe_create_data ef_data; + + /* get p15_gpkf profile */ + ret = sc_profile_get_file(profile, "p15_gpkf", &gpkf_file); + SC_TEST_RET(card->ctx,ret,"Get GPKF info failed"); + + ef_data.type=SC_ENTERSAFE_EF_DATA; + ef_data.data.ef.file_id[0]=(gpkf_file->id>>8)&0xFF; + ef_data.data.ef.file_id[1]=gpkf_file->id&0xFF; + ef_data.data.ef.size[0]=(gpkf_file->size>>8)&0xFF; + ef_data.data.ef.size[1]=gpkf_file->size&0xFF; + ef_data.data.ef.attr[0]=0x15; + ef_data.data.ef.attr[1]=0x80; + ef_data.data.ef.name=0x00; + memset(ef_data.data.ef.ac,0x10,sizeof(ef_data.data.ef.ac)); + memset(ef_data.data.ef.sm,0x00,sizeof(ef_data.data.ef.sm)); + + sc_file_free(gpkf_file); + + ret = sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_CREATE_FILE, &ef_data); + SC_TEST_RET(card->ctx,ret,"Create GPKF failed"); + } + + {/* p15 efs */ + char* create_efs[]={ + "PKCS15-ODF", + "PKCS15-TokenInfo", + "PKCS15-UnusedSpace", + "PKCS15-AODF", + "PKCS15-PrKDF", + "PKCS15-PuKDF", + "PKCS15-CDF", + "PKCS15-DODF", + NULL, + }; + int i; + sc_file_t *file=0; + sc_entersafe_create_data tmp; + + for(i = 0; create_efs[i]; ++i) { + if (sc_profile_get_file(profile, create_efs[i], &file)) { + sc_error(card->ctx, "Inconsistent profile: cannot find %s", create_efs[i]); + SC_FUNC_RETURN(card->ctx,4,SC_ERROR_INCONSISTENT_PROFILE); + } + + tmp.type=SC_ENTERSAFE_EF_DATA; + tmp.data.ef.file_id[0]=(file->id>>8)&0xFF; + tmp.data.ef.file_id[1]=file->id&0xFF; + tmp.data.ef.size[0]=(file->size>>8)&0xFF; + tmp.data.ef.size[1]=file->size&0xFF; + tmp.data.ef.attr[0]=0x00; + tmp.data.ef.attr[1]=0x00; + tmp.data.ef.name=0x00; + memset(tmp.data.ef.ac,ENTERSAFE_AC_ALWAYS,sizeof(tmp.data.ef.ac)); + tmp.data.ef.ac[0]=process_acl_entry(file,SC_AC_OP_READ,ENTERSAFE_AC_ALWAYS); /* read */ + tmp.data.ef.ac[1]=process_acl_entry(file,SC_AC_OP_UPDATE,ENTERSAFE_AC_ALWAYS); /* update */ + memset(tmp.data.ef.sm,0x00,sizeof(tmp.data.ef.sm)); + + sc_file_free(file); + + ret = sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_CREATE_FILE, &tmp); + SC_TEST_RET(card->ctx,ret,"Create pkcs15 file failed"); + } + } + + {/* Preinstall keys */ + ret = sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_PREINSTALL_KEYS, 0); + SC_TEST_RET(card->ctx,ret,"Preinstall keys failed"); + } + + SC_FUNC_RETURN(card->ctx,4,ret); +} + +static int entersafe_pin_reference(sc_profile_t *profile, sc_card_t *card, + sc_pkcs15_pin_info_t *pin_info) +{ + SC_FUNC_CALLED(card->ctx, 1); + + if (pin_info->reference < ENTERSAFE_USER_PIN_ID) + pin_info->reference = ENTERSAFE_USER_PIN_ID; + if(pin_info->reference>ENTERSAFE_USER_PIN_ID) + return SC_ERROR_TOO_MANY_OBJECTS; + SC_FUNC_RETURN(card->ctx,4,SC_SUCCESS); +} + +static int entersafe_create_pin(sc_profile_t *profile, sc_card_t *card, + sc_file_t *df, sc_pkcs15_object_t *pin_obj, + const unsigned char *pin, size_t pin_len, + const unsigned char *puk, size_t puk_len) +{ + int r; + sc_pkcs15_pin_info_t *pin_info = (sc_pkcs15_pin_info_t *) pin_obj->data; + sc_entersafe_wkey_data data; + + SC_FUNC_CALLED(card->ctx, 1); + + if (!pin || !pin_len || pin_len > 16) + return SC_ERROR_INVALID_ARGUMENTS; + + data.key_id=pin_info->reference; + data.usage=0x0B; + data.key_data.symmetric.EC=0x33; + data.key_data.symmetric.ver=0x00; + /* pad pin with 0 */ + memset(data.key_data.symmetric.key_val, 0, sizeof(data.key_data.symmetric.key_val)); + memcpy(data.key_data.symmetric.key_val, pin, pin_len); + data.key_data.symmetric.key_len=16; + + r = sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_WRITE_KEY, &data); + + SC_FUNC_RETURN(card->ctx,4,r); +} + +static int entersafe_key_reference(sc_profile_t *profile, sc_card_t *card, + sc_pkcs15_prkey_info_t *prkey) +{ + struct sc_file *df = profile->df_info->file; + + SC_FUNC_CALLED(card->ctx, 1); + + if (prkey->key_reference < ENTERSAFE_MIN_KEY_ID) + prkey->key_reference = ENTERSAFE_MIN_KEY_ID; + if (prkey->key_reference > ENTERSAFE_MAX_KEY_ID) + return SC_ERROR_TOO_MANY_OBJECTS; + + prkey->path = df->path; + SC_FUNC_RETURN(card->ctx,4,SC_SUCCESS); +} + +static int entersafe_create_key(sc_profile_t *profile, sc_card_t *card, + sc_pkcs15_object_t *obj) +{ + SC_FUNC_CALLED(card->ctx, 1); + SC_FUNC_RETURN(card->ctx,4,SC_SUCCESS); +} + +static int entersafe_store_key(sc_profile_t *profile, sc_card_t *card, + sc_pkcs15_object_t *obj, sc_pkcs15_prkey_t *key) +{ + sc_pkcs15_prkey_info_t *kinfo = (sc_pkcs15_prkey_info_t *) obj->data; + sc_entersafe_wkey_data data; + sc_file_t *tfile; + const sc_acl_entry_t *acl_entry; + int r; + + SC_FUNC_CALLED(card->ctx, 1); + + if (key->algorithm != SC_ALGORITHM_RSA) + /* ignore DSA keys */ + SC_FUNC_RETURN(card->ctx,4,SC_ERROR_INVALID_ARGUMENTS); + + r = sc_profile_get_file(profile, "PKCS15-AODF", &tfile); + if (r < 0) + return r; + acl_entry = sc_file_get_acl_entry(tfile, SC_AC_OP_UPDATE); + if (acl_entry->method != SC_AC_NONE) { + r = sc_pkcs15init_authenticate(profile, card, tfile, SC_AC_OP_UPDATE); + if(r<0) + r = SC_ERROR_SECURITY_STATUS_NOT_SATISFIED; + } + sc_file_free(tfile); + SC_TEST_RET(card->ctx, r, "cant verify pin"); + + data.key_id = (u8) kinfo->key_reference; + data.usage=0x22; + data.key_data.rsa=&key->u.rsa; + return sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_WRITE_KEY, &data); +} + +static int entersafe_generate_key(sc_profile_t *profile, sc_card_t *card, + sc_pkcs15_object_t *obj, sc_pkcs15_pubkey_t *pubkey) +{ + int r; + sc_entersafe_gen_key_data gendat; + sc_pkcs15_prkey_info_t *kinfo = (sc_pkcs15_prkey_info_t *) obj->data; + sc_file_t *tfile; + const sc_acl_entry_t *acl_entry; + + SC_FUNC_CALLED(card->ctx, 1); + + if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) + return SC_ERROR_NOT_SUPPORTED; + + r = sc_profile_get_file(profile, "PKCS15-AODF", &tfile); + if (r < 0) + return r; + acl_entry = sc_file_get_acl_entry(tfile, SC_AC_OP_UPDATE); + if (acl_entry->method != SC_AC_NONE) { + r = sc_pkcs15init_authenticate(profile, card, tfile, SC_AC_OP_UPDATE); + if(r<0) + r = SC_ERROR_SECURITY_STATUS_NOT_SATISFIED; + } + sc_file_free(tfile); + SC_TEST_RET(card->ctx, r, "cant verify pin"); + + /* generate key pair */ + gendat.key_id = (u8) kinfo->key_reference; + gendat.key_length = (size_t) kinfo->modulus_length; + gendat.modulus = NULL; + r = sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_GENERATE_KEY, &gendat); + SC_TEST_RET(card->ctx, r, "EnterSafe generate RSA key pair failed"); + + /* get the modulus via READ PUBLIC KEY */ + if (pubkey) { + u8 *buf; + struct sc_pkcs15_pubkey_rsa *rsa = &pubkey->u.rsa; + /* set the modulus */ + rsa->modulus.data = gendat.modulus; + rsa->modulus.len = kinfo->modulus_length >> 3; + /* set the exponent (always 0x10001) */ + buf = (u8 *) malloc(3); + if (!buf) + return SC_ERROR_OUT_OF_MEMORY; + buf[0] = 0x01; + buf[1] = 0x00; + buf[2] = 0x01; + rsa->exponent.data = buf; + rsa->exponent.len = 3; + + pubkey->algorithm = SC_ALGORITHM_RSA; + } else + /* free public key */ + free(gendat.modulus); + + SC_FUNC_RETURN(card->ctx,4,SC_SUCCESS); +} + +static struct sc_pkcs15init_operations sc_pkcs15init_entersafe_operations = { + entersafe_erase_card, + entersafe_init_card, + entersafe_create_dir, + NULL, /* create_domain */ + entersafe_pin_reference, + entersafe_create_pin, + entersafe_key_reference, + entersafe_create_key, + entersafe_store_key, + entersafe_generate_key, + NULL, NULL, /* encode private/public key */ + NULL, /* finalize */ + NULL, NULL, NULL, NULL, NULL, /* old style api */ + NULL /* delete_object */ +}; + +struct sc_pkcs15init_operations *sc_pkcs15init_get_entersafe_ops(void) +{ + return &sc_pkcs15init_entersafe_operations; +} diff --git a/src/pkcs15init/pkcs15-init.h b/src/pkcs15init/pkcs15-init.h index f6bfed4c..b9ff5d3b 100644 --- a/src/pkcs15init/pkcs15-init.h +++ b/src/pkcs15init/pkcs15-init.h @@ -402,6 +402,7 @@ extern struct sc_pkcs15init_operations *sc_pkcs15init_get_incrypto34_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_muscle_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_asepcos_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_rutoken_ops(void); +extern struct sc_pkcs15init_operations *sc_pkcs15init_get_entersafe_ops(void); #ifdef __cplusplus } diff --git a/src/pkcs15init/pkcs15-lib.c b/src/pkcs15init/pkcs15-lib.c index b1f74722..c1260a7b 100644 --- a/src/pkcs15init/pkcs15-lib.c +++ b/src/pkcs15init/pkcs15-lib.c @@ -163,6 +163,7 @@ static struct profile_operations { { "incrypto34", (void *) sc_pkcs15init_get_incrypto34_ops }, { "muscle", (void*) sc_pkcs15init_get_muscle_ops }, { "asepcos", (void*) sc_pkcs15init_get_asepcos_ops }, + { "entersafe",(void*) sc_pkcs15init_get_entersafe_ops }, { NULL, NULL }, }; @@ -2793,9 +2794,13 @@ sc_pkcs15init_change_attrib(struct sc_pkcs15_card *p15card, r = sc_pkcs15_encode_df(card->ctx, p15card, df, &buf, &bufsize); if (r >= 0) { + sc_file_t *file; + r = sc_profile_get_file_by_path(profile, &df->path, &file); + if(r<0) return r; r = sc_pkcs15init_update_file(profile, card, - df->file, buf, bufsize); + file, buf, bufsize); free(buf); + sc_file_free(file); } return r < 0 ? r : 0; @@ -3269,15 +3274,25 @@ sc_pkcs15init_authenticate(struct sc_profile *pro, sc_card_t *card, else acl = sc_file_get_acl_entry(file, op); + sc_debug(card->ctx, "r:[0x%08x]\n",r); + sc_debug(card->ctx, "acl:[0x%08x]\n",acl); + for (; r == 0 && acl; acl = acl->next) { if (acl->method == SC_AC_NEVER) + { + sc_debug(card->ctx, "never\n"); return SC_ERROR_SECURITY_STATUS_NOT_SATISFIED; + } if (acl->method == SC_AC_NONE) + { + sc_debug(card->ctx, "none\n"); break; + } if (acl->method == SC_AC_UNKNOWN) { sc_debug(card->ctx, "unknown acl method\n"); break; } + sc_debug(card->ctx, "verify\n"); r = do_verify_pin(pro, card, file_tmp ? file_tmp : file, acl->method, acl->key_ref); }