diff --git a/src/libopensc/Makefile.am b/src/libopensc/Makefile.am index fe3c9cd0..cc3e9aba 100644 --- a/src/libopensc/Makefile.am +++ b/src/libopensc/Makefile.am @@ -32,7 +32,8 @@ libopensc_la_SOURCES = \ card-setcos.c card-miocos.c card-flex.c card-gpk.c \ card-cardos.c card-tcos.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-entersafe.c \ + card-oberthur.c card-belpic.c card-atrust-acos.c \ + card-entersafe.c card-epass2003.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-myeid.c card-ias.c \ diff --git a/src/libopensc/Makefile.mak b/src/libopensc/Makefile.mak index bc7fbf40..92135963 100644 --- a/src/libopensc/Makefile.mak +++ b/src/libopensc/Makefile.mak @@ -16,7 +16,8 @@ OBJECTS = \ card-setcos.obj card-miocos.obj card-flex.obj card-gpk.obj \ card-cardos.obj card-tcos.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-entersafe.obj \ + card-oberthur.obj card-belpic.obj card-atrust-acos.obj \ + card-entersafe.obj card-epass2003.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-westcos.obj card-myeid.obj card-ias.obj \ diff --git a/src/libopensc/card-epass2003.c b/src/libopensc/card-epass2003.c new file mode 100644 index 00000000..3578d171 --- /dev/null +++ b/src/libopensc/card-epass2003.c @@ -0,0 +1,2392 @@ +/* + * Support for ePass2003 smart cards + * + * Copyright (C) 2008, Weitao Sun + * Copyright (C) 2011, Xiaoshuo Wu + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" +#ifdef ENABLE_SM /* empty file without SM enabled */ +#ifdef ENABLE_OPENSSL /* empty file without openssl */ + +#include +#include +#include + +#include +#include + +#include "internal.h" +#include "asn1.h" + +#include +#include +#include + +#include +#include + +#include "internal.h" +#include "asn1.h" +#include "cardctl.h" + +static struct sc_atr_table epass2003_atrs[] = { + /* This is a FIPS certified card using SCP01 security messaging. */ + {"3B:9F:95:81:31:FE:9F:00:66:46:53:05:10:00:11:71:df:00:00:00:6a:82:5e", + "FF:FF:FF:FF:FF:00:FF:FF:FF:FF:FF:FF:00:00:00:ff:00:ff:ff:00:00:00:00", + "FTCOS/ePass2003", SC_CARD_TYPE_ENTERSAFE_FTCOS_EPASS2003, 0, NULL }, + {NULL, NULL, NULL, 0, 0, NULL} +}; + +static struct sc_card_operations *iso_ops = NULL; +static struct sc_card_operations epass2003_ops; + +static struct sc_card_driver epass2003_drv = { + "epass2003", + "epass2003", + &epass2003_ops, + NULL, 0, NULL +}; + +#define KEY_TYPE_AES 0x01 /* FIPS mode */ +#define KEY_TYPE_DES 0x02 /* Non-FIPS mode */ +static unsigned char g_smtype; /* sm cryption algorithm type */ + +#define KEY_LEN_AES 16 +#define KEY_LEN_DES 8 +#define KEY_LEN_DES3 24 +#define HASH_LEN 24 + +static unsigned char PIN_ID[2] = { ENTERSAFE_USER_PIN_ID, ENTERSAFE_SO_PIN_ID }; + +/*0x00:plain; 0x01:scp01 sm*/ +#define SM_PLAIN 0x00 +#define SM_SCP01 0x01 +static unsigned char g_sm; /* if perform sm or not */ + +static unsigned char g_init_key_enc[16] = { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, + 0x0D, 0x0E, 0x0F, 0x10 +}; + +static unsigned char g_init_key_mac[16] = { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, + 0x0D, 0x0E, 0x0F, 0x10 +}; + +static unsigned char g_random[8] = { + 0xBF, 0xC3, 0x29, 0x11, 0xC7, 0x18, 0xC3, 0x40 +}; + +static unsigned char g_sk_enc[16] = { 0 }; /* encrypt session key */ +static unsigned char g_sk_mac[16] = { 0 }; /* mac session key */ +static unsigned char g_icv_mac[16] = { 0 }; /* instruction counter vector(for sm) */ + +#define REVERSE_ORDER4(x) ( \ + ((unsigned long)x & 0xFF000000)>> 24 | \ + ((unsigned long)x & 0x00FF0000)>> 8 | \ + ((unsigned long)x & 0x0000FF00)<< 8 | \ + ((unsigned long)x & 0x000000FF)<< 24) + +static int epass2003_transmit_apdu(struct sc_card *card, struct sc_apdu *apdu); +static int epass2003_select_file(struct sc_card *card, const sc_path_t * in_path, sc_file_t ** file_out); + +static int +openssl_enc(const EVP_CIPHER * cipher, const unsigned char *key, const unsigned char *iv, + const unsigned char *input, size_t length, unsigned char *output) +{ + int r = SC_ERROR_INTERNAL; + EVP_CIPHER_CTX ctx; + int outl = 0; + int outl_tmp = 0; + unsigned char iv_tmp[EVP_MAX_IV_LENGTH] = { 0 }; + + memcpy(iv_tmp, iv, EVP_MAX_IV_LENGTH); + EVP_CIPHER_CTX_init(&ctx); + EVP_CIPHER_CTX_set_padding(&ctx, 0); + EVP_EncryptInit_ex(&ctx, cipher, NULL, key, iv_tmp); + + if (!EVP_EncryptUpdate(&ctx, output, &outl, input, length)) + goto out; + + if (!EVP_EncryptFinal_ex(&ctx, output + outl, &outl_tmp)) + goto out; + + if (!EVP_CIPHER_CTX_cleanup(&ctx)) + goto out; + + r = SC_SUCCESS; +out: + return r; +} + +static int +openssl_dec(const EVP_CIPHER * cipher, const unsigned char *key, const unsigned char *iv, + const unsigned char *input, size_t length, unsigned char *output) +{ + int r = SC_ERROR_INTERNAL; + EVP_CIPHER_CTX ctx; + int outl = 0; + int outl_tmp = 0; + unsigned char iv_tmp[EVP_MAX_IV_LENGTH] = { 0 }; + + memcpy(iv_tmp, iv, EVP_MAX_IV_LENGTH); + EVP_CIPHER_CTX_init(&ctx); + EVP_CIPHER_CTX_set_padding(&ctx, 0); + EVP_DecryptInit_ex(&ctx, cipher, NULL, key, iv_tmp); + + if (!EVP_DecryptUpdate(&ctx, output, &outl, input, length)) + goto out; + + if (!EVP_DecryptFinal_ex(&ctx, output + outl, &outl_tmp)) + goto out; + + if (!EVP_CIPHER_CTX_cleanup(&ctx)) + goto out; + + r = SC_SUCCESS; +out: + return r; +} + + +static int +aes128_encrypt_ecb(const unsigned char *key, int keysize, + const unsigned char *input, size_t length, unsigned char *output) +{ + unsigned char iv[EVP_MAX_IV_LENGTH] = { 0 }; + return openssl_enc(EVP_aes_128_ecb(), key, iv, input, length, output); +} + + +static int +aes128_encrypt_cbc(const unsigned char *key, int keysize, unsigned char iv[16], + const unsigned char *input, size_t length, unsigned char *output) +{ + return openssl_enc(EVP_aes_128_cbc(), key, iv, input, length, output); +} + + +static int +aes128_decrypt_cbc(const unsigned char *key, int keysize, unsigned char iv[16], + const unsigned char *input, size_t length, unsigned char *output) +{ + return openssl_dec(EVP_aes_128_cbc(), key, iv, input, length, output); +} + + +static int +des3_encrypt_ecb(const unsigned char *key, int keysize, + const unsigned char *input, int length, unsigned char *output) +{ + unsigned char iv[EVP_MAX_IV_LENGTH] = { 0 }; + unsigned char bKey[24] = { 0 }; + + if (keysize == 16) { + memcpy(&bKey[0], key, 16); + memcpy(&bKey[16], key, 8); + } + else { + memcpy(&bKey[0], key, 24); + } + + return openssl_enc(EVP_des_ede3(), bKey, iv, input, length, output); +} + + +static int +des3_encrypt_cbc(const unsigned char *key, int keysize, unsigned char iv[8], + const unsigned char *input, size_t length, unsigned char *output) +{ + unsigned char bKey[24] = { 0 }; + + if (keysize == 16) { + memcpy(&bKey[0], key, 16); + memcpy(&bKey[16], key, 8); + } + else { + memcpy(&bKey[0], key, 24); + } + + return openssl_enc(EVP_des_ede3_cbc(), bKey, iv, input, length, output); +} + + +static int +des3_decrypt_cbc(const unsigned char *key, int keysize, unsigned char iv[8], + const unsigned char *input, size_t length, unsigned char *output) +{ + unsigned char bKey[24] = { 0 }; + if (keysize == 16) { + memcpy(&bKey[0], key, 16); + memcpy(&bKey[16], key, 8); + } + else { + memcpy(&bKey[0], key, 24); + } + + return openssl_dec(EVP_des_ede3_cbc(), bKey, iv, input, length, output); +} + + +static int +des_encrypt_cbc(const unsigned char *key, int keysize, unsigned char iv[8], + const unsigned char *input, size_t length, unsigned char *output) +{ + return openssl_enc(EVP_des_cbc(), key, iv, input, length, output); +} + + +static int +des_decrypt_cbc(const unsigned char *key, int keysize, unsigned char iv[8], + const unsigned char *input, size_t length, unsigned char *output) +{ + return openssl_dec(EVP_des_cbc(), key, iv, input, length, output); +} + + +static int +openssl_dig(const EVP_MD * digest, const unsigned char *input, size_t length, + unsigned char *output) +{ + EVP_MD_CTX ctx; + unsigned outl = 0; + + EVP_MD_CTX_init(&ctx); + EVP_DigestInit_ex(&ctx, digest, NULL); + if (!EVP_DigestUpdate(&ctx, input, length)) + return SC_ERROR_INTERNAL; + + if (!EVP_DigestFinal_ex(&ctx, output, &outl)) + return SC_ERROR_INTERNAL; + + if (!EVP_MD_CTX_cleanup(&ctx)) + return SC_ERROR_INTERNAL; + + return SC_SUCCESS; +} + + +static int +sha1_digest(const unsigned char *input, size_t length, unsigned char *output) +{ + return openssl_dig(EVP_sha1(), input, length, output); +} + + +static int +gen_init_key(struct sc_card *card, unsigned char *key_enc, unsigned char *key_mac, + unsigned char *result, unsigned char key_type) +{ + int r; + struct sc_apdu apdu; + unsigned char data[256] = { 0 }; + unsigned char tmp_sm; + unsigned long blocksize = 0; + unsigned char cryptogram[256] = { 0 }; /* host cryptogram */ + unsigned char iv[16] = { 0 }; + + LOG_FUNC_CALLED(card->ctx); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x50, 0x00, 0x00); + apdu.cla = 0x80; + apdu.lc = apdu.datalen = sizeof(g_random); + apdu.data = g_random; /* host random */ + apdu.le = apdu.resplen = 28; + apdu.resp = result; /* card random is result[12~19] */ + + tmp_sm = g_sm; + g_sm = SM_PLAIN; + r = epass2003_transmit_apdu(card, &apdu); + g_sm = tmp_sm; + LOG_TEST_RET(card->ctx, r, "APDU gen_init_key failed"); + + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(card->ctx, r, "gen_init_key failed"); + + /* Step 1 - Generate Derivation data */ + memcpy(data, &result[16], 4); + memcpy(&data[4], g_random, 4); + memcpy(&data[8], &result[12], 4); + memcpy(&data[12], &g_random[4], 4); + + /* Step 2,3 - Create S-ENC/S-MAC Session Key */ + if (KEY_TYPE_AES == key_type) { + aes128_encrypt_ecb(key_enc, 16, data, 16, g_sk_enc); + aes128_encrypt_ecb(key_mac, 16, data, 16, g_sk_mac); + } + else { + des3_encrypt_ecb(key_enc, 16, data, 16, g_sk_enc); + des3_encrypt_ecb(key_mac, 16, data, 16, g_sk_mac); + } + + memcpy(data, g_random, 8); + memcpy(&data[8], &result[12], 8); + data[16] = 0x80; + blocksize = (key_type == KEY_TYPE_AES ? 16 : 8); + memset(&data[17], 0x00, blocksize - 1); + + /* calculate host cryptogram */ + if (KEY_TYPE_AES == key_type) + aes128_encrypt_cbc(g_sk_enc, 16, iv, data, 16 + blocksize, cryptogram); + else + des3_encrypt_cbc(g_sk_enc, 16, iv, data, 16 + blocksize, cryptogram); + + /* verify card cryptogram */ + if (0 != memcmp(&cryptogram[16], &result[20], 8)) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_CARD_CMD_FAILED); + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + + +static int +verify_init_key(struct sc_card *card, unsigned char *ran_key, unsigned char key_type) +{ + int r; + struct sc_apdu apdu; + unsigned long blocksize = (key_type == KEY_TYPE_AES ? 16 : 8); + unsigned char data[256] = { 0 }; + unsigned char cryptogram[256] = { 0 }; /* host cryptogram */ + unsigned char iv[16] = { 0 }; + unsigned char mac[256] = { 0 }; + unsigned long i; + unsigned char tmp_sm; + + LOG_FUNC_CALLED(card->ctx); + + memcpy(data, ran_key, 8); + memcpy(&data[8], g_random, 8); + data[16] = 0x80; + memset(&data[17], 0x00, blocksize - 1); + memset(iv, 0, 16); + + /* calculate host cryptogram */ + if (KEY_TYPE_AES == key_type) { + aes128_encrypt_cbc(g_sk_enc, 16, iv, data, 16 + blocksize, + cryptogram); + } else { + des3_encrypt_cbc(g_sk_enc, 16, iv, data, 16 + blocksize, + cryptogram); + } + + memset(data, 0, sizeof(data)); + memcpy(data, "\x84\x82\x03\x00\x10", 5); + memcpy(&data[5], &cryptogram[16], 8); + memcpy(&data[13], "\x80\x00\x00", 3); + + /* calculate mac icv */ + memset(iv, 0x00, 16); + if (KEY_TYPE_AES == key_type) { + aes128_encrypt_cbc(g_sk_mac, 16, iv, data, 16, mac); + i = 0; + } else { + des3_encrypt_cbc(g_sk_mac, 16, iv, data, 16, mac); + i = 8; + } + /* save mac icv */ + memset(g_icv_mac, 0x00, 16); + memcpy(g_icv_mac, &mac[i], 8); + + /* verify host cryptogram */ + memcpy(data, &cryptogram[16], 8); + memcpy(&data[8], &mac[i], 8); + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x82, 0x03, 0x00); + apdu.cla = 0x84; + apdu.lc = apdu.datalen = 16; + apdu.data = data; + tmp_sm = g_sm; + g_sm = SM_PLAIN; + r = epass2003_transmit_apdu(card, &apdu); + g_sm = tmp_sm; + LOG_TEST_RET(card->ctx, r, + "APDU verify_init_key failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(card->ctx, r, + "verify_init_key failed"); + return r; +} + + +static int +mutual_auth(struct sc_card *card, unsigned char *key_enc, + unsigned char *key_mac) +{ + struct sc_context *ctx = card->ctx; + int r; + unsigned char result[256] = { 0 }; + unsigned char ran_key[8] = { 0 }; + + LOG_FUNC_CALLED(ctx); + + r = gen_init_key(card, key_enc, key_mac, result, g_smtype); + LOG_TEST_RET(ctx, r, "gen_init_key failed"); + memcpy(ran_key, &result[12], 8); + + r = verify_init_key(card, ran_key, g_smtype); + LOG_TEST_RET(ctx, r, "verify_init_key failed"); + + LOG_FUNC_RETURN(ctx, r); +} + + +int +epass2003_refresh(struct sc_card *card) +{ + int r = SC_SUCCESS; + + if (g_sm) { + r = mutual_auth(card, g_init_key_enc, g_init_key_mac); + LOG_TEST_RET(card->ctx, r, "mutual_auth failed"); + } + + return r; +} + + +/* Data(TLV)=0x87|L|0x01+Cipher */ +static int +construct_data_tlv(struct sc_apdu *apdu, unsigned char *apdu_buf, + unsigned char *data_tlv, size_t * data_tlv_len, const unsigned char key_type) +{ + size_t block_size = (KEY_TYPE_AES == key_type ? 16 : 8); + unsigned char pad[4096] = { 0 }; + size_t pad_len; + size_t tlv_more; /* increased tlv length */ + unsigned char iv[16] = { 0 }; + + /* padding */ + apdu_buf[block_size] = 0x87; + memcpy(pad, apdu->data, apdu->lc); + pad[apdu->lc] = 0x80; + if ((apdu->lc + 1) % block_size) + pad_len = ((apdu->lc + 1) / block_size + 1) * block_size; + else + pad_len = apdu->lc + 1; + + /* encode Lc' */ + if (pad_len > 0x7E) { + /* Lc' > 0x7E, use extended APDU */ + apdu_buf[block_size + 1] = 0x82; + apdu_buf[block_size + 2] = (unsigned char)((pad_len + 1) / 0x100); + apdu_buf[block_size + 3] = (unsigned char)((pad_len + 1) % 0x100); + apdu_buf[block_size + 4] = 0x01; + tlv_more = 5; + } + else { + apdu_buf[block_size + 1] = (unsigned char)pad_len + 1; + apdu_buf[block_size + 2] = 0x01; + tlv_more = 3; + } + memcpy(data_tlv, &apdu_buf[block_size], tlv_more); + + /* encrypt Data */ + if (KEY_TYPE_AES == key_type) + aes128_encrypt_cbc(g_sk_enc, 16, iv, pad, pad_len, apdu_buf + block_size + tlv_more); + else + des3_encrypt_cbc(g_sk_enc, 16, iv, pad, pad_len, apdu_buf + block_size + tlv_more); + + memcpy(data_tlv + tlv_more, apdu_buf + block_size + tlv_more, pad_len); + *data_tlv_len = tlv_more + pad_len; + return 0; +} + + +/* Le(TLV)=0x97|L|Le */ +static int +construct_le_tlv(struct sc_apdu *apdu, unsigned char *apdu_buf, size_t data_tlv_len, + unsigned char *le_tlv, size_t * le_tlv_len, const unsigned char key_type) +{ + size_t block_size = (KEY_TYPE_AES == key_type ? 16 : 8); + + *(apdu_buf + block_size + data_tlv_len) = 0x97; + if (apdu->le > 0x7F) { + /* Le' > 0x7E, use extended APDU */ + *(apdu_buf + block_size + data_tlv_len + 1) = 2; + *(apdu_buf + block_size + data_tlv_len + 2) = (unsigned char)(apdu->le / 0x100); + *(apdu_buf + block_size + data_tlv_len + 3) = (unsigned char)(apdu->le % 0x100); + memcpy(le_tlv, apdu_buf + block_size + data_tlv_len, 4); + *le_tlv_len = 4; + } + else { + *(apdu_buf + block_size + data_tlv_len + 1) = 1; + *(apdu_buf + block_size + data_tlv_len + 2) = (unsigned char)apdu->le; + memcpy(le_tlv, apdu_buf + block_size + data_tlv_len, 3); + *le_tlv_len = 3; + } + return 0; +} + + +/* MAC(TLV)=0x8e|0x08|MAC */ +static int +construct_mac_tlv(unsigned char *apdu_buf, size_t data_tlv_len, size_t le_tlv_len, + unsigned char *mac_tlv, size_t * mac_tlv_len, const unsigned char key_type) +{ + size_t block_size = (KEY_TYPE_AES == key_type ? 16 : 8); + unsigned char mac[4096] = { 0 }; + size_t mac_len; + unsigned char icv[16] = { 0 }; + int i = (KEY_TYPE_AES == key_type ? 15 : 7); + + if (0 == data_tlv_len && 0 == le_tlv_len) { + mac_len = block_size; + } + else { + /* padding */ + *(apdu_buf + block_size + data_tlv_len + le_tlv_len) = 0x80; + if ((data_tlv_len + le_tlv_len + 1) % block_size) + mac_len = (((data_tlv_len + le_tlv_len + 1) / block_size) + + 1) * block_size + block_size; + + else + mac_len = data_tlv_len + le_tlv_len + 1 + block_size; + + memset((apdu_buf + block_size + data_tlv_len + le_tlv_len + 1), + 0, (mac_len - (data_tlv_len + le_tlv_len + 1))); + } + + /* increase icv */ + for (; i >= 0; i--) { + if (g_icv_mac[i] == 0xff) { + g_icv_mac[i] = 0; + } + else { + g_icv_mac[i]++; + break; + } + } + + /* calculate MAC */ + memset(icv, 0, sizeof(icv)); + memcpy(icv, g_icv_mac, 16); + if (KEY_TYPE_AES == key_type) { + aes128_encrypt_cbc(g_sk_mac, 16, icv, apdu_buf, mac_len, mac); + memcpy(mac_tlv + 2, &mac[mac_len - 16], 8); + } + else { + unsigned char iv[8] = { 0 }; + unsigned char tmp[8] = { 0 }; + des_encrypt_cbc(g_sk_mac, 8, icv, apdu_buf, mac_len, mac); + des_decrypt_cbc(&g_sk_mac[8], 8, iv, &mac[mac_len - 8], 8, tmp); + memset(iv, 0x00, 8); + des_encrypt_cbc(g_sk_mac, 8, iv, tmp, 8, mac_tlv + 2); + } + + *mac_tlv_len = 2 + 8; + return 0; +} + +#if 0 +static size_t calc_le(size_t le) +{ + size_t le_new = 0; + size_t resp_len = 0; + size_t sw_len = 4; /* T 1 L 1 V 2 */ + size_t mac_len = 10; /* T 1 L 1 V 8 */ + size_t mod = 16; + /* padding first */ + resp_len = 1 + ((le + (mod - 1)) / mod) * mod; + + if (0x7f < resp_len) { + resp_len += 0; + + } else if (0x7f <= resp_len && resp_len < 0xff) { + resp_len += 1; + } else if (0xff <= resp_len) { + resp_len += 2; + } + resp_len += 2; /* +T+L */ + le_new = resp_len + sw_len + mac_len; + return le_new; +} +#endif + + +/* According to GlobalPlatform Card Specification's SCP01 + * encode APDU from + * CLA INS P1 P2 [Lc] Data [Le] + * to + * CLA INS P1 P2 Lc' Data' [Le] + * where + * Data'=Data(TLV)+Le(TLV)+MAC(TLV) */ +static int +encode_apdu(struct sc_apdu *plain, struct sc_apdu *sm, + unsigned char *apdu_buf, size_t * apdu_buf_len) +{ + size_t block_size = (KEY_TYPE_DES == g_smtype ? 16 : 8); + unsigned char dataTLV[4096] = { 0 }; + size_t data_tlv_len = 0; + unsigned char le_tlv[256] = { 0 }; + size_t le_tlv_len = 0; + size_t mac_tlv_len = 10; + size_t tmp_lc = 0; + size_t tmp_le = 0; + unsigned char mac_tlv[256] = { 0 }; + + mac_tlv[0] = 0x8E; + mac_tlv[1] = 8; + /* size_t plain_le = 0; */ + + sm->cse = SC_APDU_CASE_4_SHORT; + apdu_buf[0] = (unsigned char)plain->cla; + apdu_buf[1] = (unsigned char)plain->ins; + apdu_buf[2] = (unsigned char)plain->p1; + apdu_buf[3] = (unsigned char)plain->p2; + + /* plain_le = plain->le; */ + + /* padding */ + apdu_buf[4] = 0x80; + memset(&apdu_buf[5], 0x00, block_size - 5); + + /* Data -> Data' */ + if (plain->lc != 0) + if (0 != construct_data_tlv(plain, apdu_buf, dataTLV, &data_tlv_len, g_smtype)) + return -1; + + if (plain->le != 0 || (plain->le == 0 && plain->resplen != 0)) + if (0 != construct_le_tlv(plain, apdu_buf, data_tlv_len, le_tlv, + &le_tlv_len, g_smtype)) + return -1; + + if (0 != construct_mac_tlv(apdu_buf, data_tlv_len, le_tlv_len, mac_tlv, &mac_tlv_len, g_smtype)) + return -1; + + memset(apdu_buf + 4, 0, *apdu_buf_len - 4); + sm->lc = sm->datalen = data_tlv_len + le_tlv_len + mac_tlv_len; + if (sm->lc > 0xFF) { + sm->cse = SC_APDU_CASE_4_EXT; + apdu_buf[4] = (unsigned char)((sm->lc) / 0x10000); + apdu_buf[5] = (unsigned char)(((sm->lc) / 0x100) % 0x100); + apdu_buf[6] = (unsigned char)((sm->lc) % 0x100); + tmp_lc = 3; + } + else { + apdu_buf[4] = (unsigned char)sm->lc; + tmp_lc = 1; + } + + memcpy(apdu_buf + 4 + tmp_lc, dataTLV, data_tlv_len); + memcpy(apdu_buf + 4 + tmp_lc + data_tlv_len, le_tlv, le_tlv_len); + memcpy(apdu_buf + 4 + tmp_lc + data_tlv_len + le_tlv_len, mac_tlv, mac_tlv_len); + memcpy((unsigned char *)sm->data, apdu_buf + 4 + tmp_lc, sm->datalen); + *apdu_buf_len = 0; + + if (4 == le_tlv_len) { + sm->cse = SC_APDU_CASE_4_EXT; + *(apdu_buf + 4 + tmp_lc + sm->lc) = (unsigned char)(plain->le / 0x100); + *(apdu_buf + 4 + tmp_lc + sm->lc + 1) = (unsigned char)(plain->le % 0x100); + tmp_le = 2; + } + else if (3 == le_tlv_len) { + *(apdu_buf + 4 + tmp_lc + sm->lc) = (unsigned char)plain->le; + tmp_le = 1; + } + + *apdu_buf_len += 4 + tmp_lc + data_tlv_len + le_tlv_len + mac_tlv_len + tmp_le; + /* sm->le = calc_le(plain_le); */ + return 0; +} + + +static int +epass2003_sm_wrap_apdu(struct sc_card *card, struct sc_apdu *plain, struct sc_apdu *sm) +{ + unsigned char buf[4096] = { 0 }; /* APDU buffer */ + size_t buf_len = sizeof(buf); + + LOG_FUNC_CALLED(card->ctx); + + if (g_sm) + plain->cla |= 0x0C; + + sm->cse = plain->cse; + sm->cla = plain->cla; + sm->ins = plain->ins; + sm->p1 = plain->p1; + sm->p2 = plain->p2; + sm->lc = plain->lc; + sm->le = plain->le; + sm->control = plain->control; + sm->flags = plain->flags; + + switch (sm->cla & 0x0C) { + case 0x00: + case 0x04: + sm->datalen = plain->datalen; + sm->data = plain->data; + sm->resplen = plain->resplen; + sm->resp = plain->resp; + break; + case 0x0C: + memset(buf, 0, sizeof(buf)); + if (0 != encode_apdu(plain, sm, buf, &buf_len)) + return SC_ERROR_CARD_CMD_FAILED; + break; + default: + return SC_ERROR_INCORRECT_PARAMETERS; + } + + return SC_SUCCESS; +} + + +/* According to GlobalPlatform Card Specification's SCP01 + * decrypt APDU response from + * ResponseData' SW1 SW2 + * to + * ResponseData SW1 SW2 + * where + * ResponseData'=Data(TLV)+SW12(TLV)+MAC(TLV) + * where + * Data(TLV)=0x87|L|Cipher + * SW12(TLV)=0x99|0x02|SW1+SW2 + * MAC(TLV)=0x8e|0x08|MAC */ +static int +decrypt_response(unsigned char *in, unsigned char *out, size_t * out_len) +{ + size_t in_len; + size_t i; + unsigned char iv[16] = { 0 }; + unsigned char plaintext[4096] = { 0 }; + + /* no cipher */ + if (in[0] == 0x99) + return 0; + + /* parse cipher length */ + if (0x01 == in[2] && 0x82 != in[1]) { + in_len = in[1]; + i = 3; + } + else if (0x01 == in[3] && 0x81 == in[1]) { + in_len = in[2]; + i = 4; + } + else if (0x01 == in[4] && 0x82 == in[1]) { + in_len = in[2] * 0x100; + in_len += in[3]; + i = 5; + } + else { + return -1; + } + + /* decrypt */ + if (KEY_TYPE_AES == g_smtype) + aes128_decrypt_cbc(g_sk_enc, 16, iv, &in[i], in_len - 1, plaintext); + else + des3_decrypt_cbc(g_sk_enc, 16, iv, &in[i], in_len - 1, plaintext); + + /* unpadding */ + while (0x80 != plaintext[in_len - 2] && (in_len - 2 > 0)) + in_len--; + + if (2 == in_len) + return -1; + + memcpy(out, plaintext, in_len - 2); + *out_len = in_len - 2; + return 0; +} + + +static int +epass2003_sm_unwrap_apdu(struct sc_card *card, struct sc_apdu *sm, struct sc_apdu *plain) +{ + int r; + size_t len = 0; + + LOG_FUNC_CALLED(card->ctx); + + r = sc_check_sw(card, sm->sw1, sm->sw2); + if (r == SC_SUCCESS) { + if (g_sm) { + if (0 != decrypt_response(sm->resp, plain->resp, &len)) + return SC_ERROR_CARD_CMD_FAILED; + } + else { + memcpy(plain->resp, sm->resp, sm->resplen); + len = sm->resplen; + } + } + + plain->resplen = len; + plain->sw1 = sm->sw1; + plain->sw2 = sm->sw2; + + sc_log(card->ctx, "unwrapped APDU: resplen %i, SW %02X%02X", plain->resplen, plain->sw1, plain->sw2); + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + + +static int +epass2003_sm_free_wrapped_apdu(struct sc_card *card, + struct sc_apdu *plain, struct sc_apdu **sm_apdu) +{ + struct sc_context *ctx = card->ctx; + int rv = SC_SUCCESS; + + LOG_FUNC_CALLED(ctx); + if (!sm_apdu) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + if (!(*sm_apdu)) + LOG_FUNC_RETURN(ctx, SC_SUCCESS); + + if (plain) + rv = epass2003_sm_unwrap_apdu(card, *sm_apdu, plain); + + if ((*sm_apdu)->data) + free((*sm_apdu)->data); + if ((*sm_apdu)->resp) + free((*sm_apdu)->resp); + free(*sm_apdu); + *sm_apdu = NULL; + + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static int +epass2003_sm_get_wrapped_apdu(struct sc_card *card, + struct sc_apdu *plain, struct sc_apdu **sm_apdu) +{ + struct sc_context *ctx = card->ctx; + struct sc_apdu *apdu = NULL; + int rv; + + LOG_FUNC_CALLED(ctx); + if (!plain || !sm_apdu) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + + *sm_apdu = NULL; + //construct new SM apdu from original apdu + apdu = calloc(1, sizeof(struct sc_apdu)); + if (!apdu) + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + apdu->data = calloc (1, SC_MAX_EXT_APDU_BUFFER_SIZE); + if (!apdu->data) + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + apdu->resp = calloc (1, SC_MAX_EXT_APDU_BUFFER_SIZE); + if (!apdu->resp) + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + apdu->datalen = SC_MAX_EXT_APDU_BUFFER_SIZE; + apdu->resplen = SC_MAX_EXT_APDU_BUFFER_SIZE; + + rv = epass2003_sm_wrap_apdu(card, plain, apdu); + if (rv) { + rv = epass2003_sm_free_wrapped_apdu(card, NULL, &apdu); + LOG_FUNC_RETURN(ctx, rv); + } + + *sm_apdu = apdu; + LOG_FUNC_RETURN(ctx, rv); +} + + +static int +epass2003_transmit_apdu(struct sc_card *card, struct sc_apdu *apdu) +{ + int r; + + LOG_FUNC_CALLED(card->ctx); + + r = sc_transmit_apdu(card, apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + + return r; +} + + +static int +get_data(struct sc_card *card, unsigned char type, unsigned char *data, size_t datalen) +{ + int r; + struct sc_apdu apdu; + unsigned char resp[SC_MAX_APDU_BUFFER_SIZE] = { 0 }; + size_t resplen = SC_MAX_APDU_BUFFER_SIZE; + + LOG_FUNC_CALLED(card->ctx); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, type); + apdu.resp = resp; + apdu.le = 0; + apdu.resplen = resplen; + if (0x86 == type) { + /* No SM temporarily */ + unsigned char tmp_sm = g_sm; + g_sm = SM_PLAIN; + r = sc_transmit_apdu(card, &apdu); + g_sm = tmp_sm; + } + else { + r = sc_transmit_apdu(card, &apdu); + } + LOG_TEST_RET(card->ctx, r, "APDU get_data failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(card->ctx, r, "get_data failed"); + + memcpy(data, resp, datalen); + return r; +} + +/* card driver functions */ + +static int epass2003_match_card(struct sc_card *card) +{ + int r; + + LOG_FUNC_CALLED(card->ctx); + r = _sc_match_atr(card, epass2003_atrs, &card->type); + if (r < 0) + return 0; + + return 1; +} + + +static int +epass2003_init(struct sc_card *card) +{ + unsigned int flags; + unsigned char data[SC_MAX_APDU_BUFFER_SIZE] = { 0 }; + size_t datalen = SC_MAX_APDU_BUFFER_SIZE; + + LOG_FUNC_CALLED(card->ctx); + + card->name = "epass2003"; + card->cla = 0x00; + card->drv_data = NULL; +/* VT + card->ctx->use_sm = 1; +*/ + + g_sm = SM_SCP01; + /* g_sm = SM_PLAIN; */ + + /* decide FIPS/Non-FIPS mode */ + if (SC_SUCCESS != get_data(card, 0x86, data, datalen)) + return SC_ERROR_CARD_CMD_FAILED; + + if (0x01 == data[2]) + g_smtype = KEY_TYPE_AES; + else + g_smtype = KEY_TYPE_DES; + + /* mutual authentication */ + card->max_recv_size = 0xD8; + card->max_send_size = 0xE8; + + card->sm_ctx.ops.open = epass2003_refresh; + card->sm_ctx.ops.get_sm_apdu = epass2003_sm_get_wrapped_apdu; + card->sm_ctx.ops.free_sm_apdu = epass2003_sm_free_wrapped_apdu; + + /* FIXME (VT): rather then set/unset 'g_sm', better to implement filter for APDUs to be wrapped */ + epass2003_refresh(card); + + card->sm_ctx.sm_mode = SM_MODE_TRANSMIT; + + 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 | SC_CARD_CAP_APDU_EXT; + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + + +/* COS implement SFI as lower 5 bits of FID, and not allow same SFI at the + * same DF, so use hook functions to increase/decrease FID by 0x20 */ +static int +epass2003_hook_path(struct sc_path *path, int inc) +{ + u8 fid_h = path->value[path->len - 2]; + u8 fid_l = path->value[path->len - 1]; + + switch (fid_h) { + case 0x29: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + if (inc) + fid_l = fid_l * FID_STEP; + else + fid_l = fid_l / FID_STEP; + path->value[path->len - 1] = fid_l; + return 1; + default: + break; + } + return 0; +} + + +static void +epass2003_hook_file(struct sc_file *file, int inc) +{ + int fidl = file->id & 0xff; + int fidh = file->id & 0xff00; + if (epass2003_hook_path(&file->path, inc)) { + if (inc) + file->id = fidh + fidl * FID_STEP; + else + file->id = fidh + fidl / FID_STEP; + } +} + + +static int +epass2003_select_fid_(struct sc_card *card, sc_path_t * in_path, sc_file_t ** file_out) +{ + sc_context_t *ctx; + struct sc_apdu apdu; + u8 buf[SC_MAX_APDU_BUFFER_SIZE] = { 0 }; + u8 pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf; + int r, pathlen; + sc_file_t *file = NULL; + + ctx = card->ctx; + epass2003_hook_path(in_path, 1); + memcpy(path, in_path->value, in_path->len); + pathlen = in_path->len; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0x00, 0x00); + + switch (in_path->type) { + case SC_PATH_TYPE_FILE_ID: + apdu.p1 = 0; + if (pathlen != 2) + return SC_ERROR_INVALID_ARGUMENTS; + break; + default: + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + } + apdu.p2 = 0; /* first record, return FCI */ + apdu.lc = pathlen; + apdu.data = path; + apdu.datalen = pathlen; + + if (file_out != NULL) { + apdu.resp = buf; + apdu.resplen = sizeof(buf); + apdu.le = 0; + } + else { + apdu.cse = (apdu.lc == 0) ? SC_APDU_CASE_1 : SC_APDU_CASE_3_SHORT; + } + + if (path[0] == 0x29) { /* TODO:0x29 accords with FID prefix in profile */ + /* Not allowed to select prvate key file, so fake fci. */ + /* 62 16 82 02 11 00 83 02 29 00 85 02 08 00 86 08 FF 90 90 90 FF FF FF FF */ + apdu.resplen = 0x18; + memcpy(apdu.resp, + "\x6f\x16\x82\x02\x11\x00\x83\x02\x29\x00\x85\x02\x08\x00\x86\x08\xff\x90\x90\x90\xff\xff\xff\xff", + apdu.resplen); + apdu.resp[9] = path[1]; + apdu.sw1 = 0x90; + apdu.sw2 = 0x00; + } + else { + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + } + + if (file_out == NULL) { + if (apdu.sw1 == 0x61) + LOG_FUNC_RETURN(card->ctx, 0); + LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2)); + } + + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + if (r) + LOG_FUNC_RETURN(card->ctx, r); + if (apdu.resplen < 2) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); + + switch (apdu.resp[0]) { + case 0x6F: + file = sc_file_new(); + if (file == NULL) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + file->path = *in_path; + if (card->ops->process_fci == NULL) { + sc_file_free(file); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + } + + if ((size_t) apdu.resp[1] + 2 <= apdu.resplen) + card->ops->process_fci(card, file, apdu.resp + 2, apdu.resp[1]); + epass2003_hook_file(file, 0); + *file_out = file; + break; + case 0x00: /* proprietary coding */ + LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); + break; + default: + LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); + } + return 0; +} + + +static int +epass2003_select_fid(struct sc_card *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; + + memset(&path, 0, sizeof(path)); + path.type = SC_PATH_TYPE_FILE_ID; + path.value[0] = id_hi; + path.value[1] = id_lo; + path.len = 2; + + r = epass2003_select_fid_(card, &path, &file); + LOG_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; + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + + +static int +epass2003_select_aid(struct sc_card *card, const sc_path_t * in_path, sc_file_t ** file_out) +{ + int r = 0; + + 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) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + } + } + else { + r = iso_ops->select_file(card, in_path, file_out); + LOG_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; + + 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; + } + + LOG_FUNC_RETURN(card->ctx, r); +} + + +static int +epass2003_select_path(struct sc_card *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; + unsigned int i; + int r; + + if (pathlen % 2 != 0 || pathlen > 6 || pathlen <= 0) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + + /* if pathlen == 6 then the first FID must be MF (== 3F00) */ + if (pathlen == 6 && (path[0] != 0x3f || path[1] != 0x00)) + LOG_FUNC_RETURN(card->ctx, 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 epass2003_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 = epass2003_select_fid(card, path[bMatch], path[bMatch + 1], NULL); + LOG_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 epass2003_select_file(card, &new_path, file_out); + } + else { /* if (bMatch - pathlen == 0) */ + + /* done: we are already in the + * requested directory */ + sc_log(card->ctx, "cache hit\n"); + /* copy file info (if necessary) */ + if (file_out) { + sc_file_t *file = sc_file_new(); + if (!file) + LOG_FUNC_RETURN(card->ctx, 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; i < pathlen - 2; i += 2) { + r = epass2003_select_fid(card, path[i], path[i + 1], NULL); + LOG_TEST_RET(card->ctx, r, "SELECT FILE (DF-ID) failed"); + } + + return epass2003_select_fid(card, path[pathlen - 2], path[pathlen - 1], file_out); + } +} + + +static int +epass2003_select_file(struct sc_card *card, const sc_path_t * in_path, + sc_file_t ** file_out) +{ + int r; + char pbuf[SC_MAX_PATH_STRING_SIZE]; + + LOG_FUNC_CALLED(card->ctx); + + r = sc_path_print(pbuf, sizeof(pbuf), &card->cache.current_path); + if (r != SC_SUCCESS) + pbuf[0] = '\0'; + + sc_log(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) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + return epass2003_select_fid(card, in_path->value[0], in_path->value[1], file_out); + case SC_PATH_TYPE_DF_NAME: + return epass2003_select_aid(card, in_path, file_out); + case SC_PATH_TYPE_PATH: + return epass2003_select_path(card, in_path->value, in_path->len, file_out); + default: + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + } +} + + +static int +epass2003_set_security_env(struct sc_card *card, const sc_security_env_t * env, int se_num) +{ + struct sc_apdu apdu; + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE] = { 0 }; + u8 *p; + unsigned short fid = 0; + int r, locked = 0; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0); + switch (env->operation) { + case SC_SEC_OPERATION_DECIPHER: + apdu.p2 = 0xB8; + break; + case SC_SEC_OPERATION_SIGN: + apdu.p2 = 0xB8; + break; + default: + return SC_ERROR_INVALID_ARGUMENTS; + } + + p = sbuf; + *p++ = 0x80; /* algorithm reference */ + *p++ = 0x01; + *p++ = 0x84; + + *p++ = 0x81; + *p++ = 0x02; + + fid = 0x2900; + fid += (unsigned short)(0x20 * (env->key_ref[0] & 0xff)); + *p++ = fid >> 8; + *p++ = fid & 0xff; + r = p - sbuf; + apdu.lc = r; + apdu.datalen = r; + apdu.data = sbuf; + if (se_num > 0) { + r = sc_lock(card); + LOG_TEST_RET(card->ctx, r, "sc_lock() failed"); + locked = 1; + } + if (apdu.datalen != 0) { + r = sc_transmit_apdu(card, &apdu); + if (r) { + sc_log(card->ctx, "%s: APDU transmit failed", sc_strerror(r)); + goto err; + } + + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + if (r) { + sc_log(card->ctx, "%s: Card returned error", sc_strerror(r)); + goto err; + } + } + if (se_num <= 0) + 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); + + LOG_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 +epass2003_restore_security_env(struct sc_card *card, int se_num) +{ + LOG_FUNC_CALLED(card->ctx); + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + + +static int epass2003_decipher(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] = { 0 }; + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE] = { 0 }; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_EXT, 0x2A, 0x80, 0x86); + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + apdu.le = 256; + + memcpy(sbuf, data, datalen); + apdu.data = sbuf; + apdu.lc = datalen; + apdu.datalen = datalen; + + r = sc_transmit_apdu(card, &apdu); + LOG_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); + LOG_FUNC_RETURN(card->ctx, len); + } + + LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2)); +} + + +static int +acl_to_ac_byte(struct sc_card *card, const struct sc_acl_entry *e) +{ + if (e == NULL) + return SC_ERROR_OBJECT_NOT_FOUND; + + switch (e->method) { + case SC_AC_NONE: + LOG_FUNC_RETURN(card->ctx, EPASS2003_AC_MAC_NOLESS | EPASS2003_AC_EVERYONE); + case SC_AC_NEVER: + LOG_FUNC_RETURN(card->ctx, EPASS2003_AC_MAC_NOLESS | EPASS2003_AC_NOONE); + default: + LOG_FUNC_RETURN(card->ctx, EPASS2003_AC_MAC_NOLESS | EPASS2003_AC_USER); + } + + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INCORRECT_PARAMETERS); +} + + +static int +epass2003_process_fci(struct sc_card *card, sc_file_t * file, const u8 * buf, size_t buflen) +{ + sc_context_t *ctx = card->ctx; + size_t taglen, len = buflen; + const u8 *tag = NULL, *p = buf; + + sc_log(ctx, "processing FCI bytes"); + tag = sc_asn1_find_tag(ctx, p, len, 0x83, &taglen); + if (tag != NULL && taglen == 2) { + file->id = (tag[0] << 8) | tag[1]; + sc_log(ctx, " file identifier: 0x%02X%02X", tag[0], tag[1]); + } + + tag = sc_asn1_find_tag(ctx, p, len, 0x80, &taglen); + if (tag != NULL && taglen > 0 && taglen < 3) { + file->size = tag[0]; + if (taglen == 2) + file->size = (file->size << 8) + tag[1]; + sc_log(ctx, " bytes in file: %d", file->size); + } + + if (tag == NULL) { + tag = sc_asn1_find_tag(ctx, p, len, 0x81, &taglen); + if (tag != NULL && taglen >= 2) { + int bytes = (tag[0] << 8) + tag[1]; + + sc_log(ctx, " bytes in file: %d", bytes); + file->size = bytes; + } + } + + tag = sc_asn1_find_tag(ctx, p, len, 0x82, &taglen); + if (tag != NULL) { + if (taglen > 0) { + unsigned char byte = tag[0]; + const char *type; + + if (byte == 0x38) { + type = "DF"; + file->type = SC_FILE_TYPE_DF; + } + else if (0x01 <= byte && byte <= 0x07) { + type = "working EF"; + file->type = SC_FILE_TYPE_WORKING_EF; + switch (byte) { + case 0x01: + file->ef_structure = SC_FILE_EF_TRANSPARENT; + break; + case 0x02: + file->ef_structure = SC_FILE_EF_LINEAR_FIXED; + break; + case 0x04: + file->ef_structure = SC_FILE_EF_LINEAR_FIXED; + break; + case 0x03: + case 0x05: + case 0x06: + case 0x07: + break; + default: + break; + } + + } + else if (0x10 == byte) { + type = "BSO"; + file->type = SC_FILE_TYPE_BSO; + } + else if (0x11 <= byte) { + type = "internal EF"; + file->type = SC_FILE_TYPE_INTERNAL_EF; + switch (byte) { + case 0x11: + break; + case 0x12: + break; + default: + break; + } + } + else { + type = "unknown"; + file->type = SC_FILE_TYPE_INTERNAL_EF; + + } + sc_log(ctx, "type %s, EF structure %d", type, byte); + } + } + + tag = sc_asn1_find_tag(ctx, p, len, 0x84, &taglen); + if (tag != NULL && taglen > 0 && taglen <= 16) { + char tbuf[128]; + memcpy(file->name, tag, taglen); + file->namelen = taglen; + + sc_hex_dump(ctx, SC_LOG_DEBUG_NORMAL, file->name, file->namelen, tbuf, sizeof(tbuf)); + sc_log(ctx, "File name: %s", tbuf); + if (!file->type) + file->type = SC_FILE_TYPE_DF; + } + + tag = sc_asn1_find_tag(ctx, p, len, 0x85, &taglen); + if (tag != NULL && taglen) + sc_file_set_prop_attr(file, tag, taglen); + else + file->prop_attr_len = 0; + + tag = sc_asn1_find_tag(ctx, p, len, 0xA5, &taglen); + if (tag != NULL && taglen) + sc_file_set_prop_attr(file, tag, taglen); + + tag = sc_asn1_find_tag(ctx, p, len, 0x86, &taglen); + if (tag != NULL && taglen) + sc_file_set_sec_attr(file, tag, taglen); + + tag = sc_asn1_find_tag(ctx, p, len, 0x8A, &taglen); + if (tag != NULL && taglen == 1) { + if (tag[0] == 0x01) + file->status = SC_FILE_STATUS_CREATION; + else if (tag[0] == 0x07 || tag[0] == 0x05) + file->status = SC_FILE_STATUS_ACTIVATED; + else if (tag[0] == 0x06 || tag[0] == 0x04) + file->status = SC_FILE_STATUS_INVALIDATED; + } + file->magic = SC_FILE_MAGIC; + + return 0; +} + + +static int +epass2003_construct_fci(struct sc_card *card, const sc_file_t * file, + u8 * out, size_t * outlen) +{ + u8 *p = out; + u8 buf[64]; + unsigned char ops[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + int rv; + unsigned ii; + + if (*outlen < 2) + return SC_ERROR_BUFFER_TOO_SMALL; + + *p++ = 0x62; + p++; + if (file->type == SC_FILE_TYPE_WORKING_EF) { + if (file->ef_structure == SC_FILE_EF_TRANSPARENT) { + buf[0] = (file->size >> 8) & 0xFF; + buf[1] = file->size & 0xFF; + sc_asn1_put_tag(0x80, buf, 2, p, *outlen - (p - out), &p); + } + } + if (file->type == SC_FILE_TYPE_DF) { + buf[0] = 0x38; + buf[1] = 0x00; + sc_asn1_put_tag(0x82, buf, 2, p, *outlen - (p - out), &p); + } + else if (file->type == SC_FILE_TYPE_WORKING_EF) { + buf[0] = file->ef_structure & 7; + if (file->ef_structure == SC_FILE_EF_TRANSPARENT) { + buf[1] = 0x00; + sc_asn1_put_tag(0x82, buf, 2, p, *outlen - (p - out), &p); + } + else if (file->ef_structure == SC_FILE_EF_LINEAR_FIXED + || file->ef_structure == SC_FILE_EF_LINEAR_VARIABLE) { + buf[1] = 0x00; + buf[2] = 0x00; + buf[3] = 0x40; /* record length */ + buf[4] = 0x00; /* record count */ + sc_asn1_put_tag(0x82, buf, 5, p, *outlen - (p - out), &p); + } + else { + return SC_ERROR_NOT_SUPPORTED; + } + + } + else if (file->type == SC_FILE_TYPE_INTERNAL_EF) { + if (file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_RSA_CRT) { + buf[0] = 0x11; + buf[1] = 0x00; + } + else if (file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC) { + buf[0] = 0x12; + buf[1] = 0x00; + } + else { + return SC_ERROR_NOT_SUPPORTED; + } + sc_asn1_put_tag(0x82, buf, 2, p, *outlen - (p - out), &p); + } + else if (file->type == SC_FILE_TYPE_BSO) { + buf[0] = 0x10; + buf[1] = 0x00; + sc_asn1_put_tag(0x82, buf, 2, p, *outlen - (p - out), &p); + } + + buf[0] = (file->id >> 8) & 0xFF; + buf[1] = file->id & 0xFF; + sc_asn1_put_tag(0x83, buf, 2, p, *outlen - (p - out), &p); + if (file->type == SC_FILE_TYPE_DF) { + if (file->namelen != 0) { + sc_asn1_put_tag(0x84, file->name, file->namelen, p, *outlen - (p - out), &p); + } + else { + return SC_ERROR_INVALID_ARGUMENTS; + } + } + if (file->type == SC_FILE_TYPE_DF) { + unsigned char data[2] = {0x00, 0x7F}; + /* 127 files at most */ + sc_asn1_put_tag(0x85, data, sizeof(data), p, *outlen - (p - out), &p); + } + else if (file->type == SC_FILE_TYPE_BSO) { + buf[0] = file->size & 0xff; + sc_asn1_put_tag(0x85, buf, 1, p, *outlen - (p - out), &p); + } + else if (file->type == SC_FILE_TYPE_INTERNAL_EF) { + if (file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_RSA_CRT || + file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC) { + buf[0] = (file->size >> 8) & 0xFF; + buf[1] = file->size & 0xFF; + sc_asn1_put_tag(0x85, buf, 2, p, *outlen - (p - out), &p); + } + } + if (file->sec_attr_len) { + memcpy(buf, file->sec_attr, file->sec_attr_len); + sc_asn1_put_tag(0x86, buf, file->sec_attr_len, p, *outlen - (p - out), &p); + } + else { + sc_log(card->ctx, "SC_FILE_ACL"); + if (file->type == SC_FILE_TYPE_DF) { + ops[0] = SC_AC_OP_LIST_FILES; + ops[1] = SC_AC_OP_CREATE; + ops[3] = SC_AC_OP_DELETE; + } + else if (file->type == SC_FILE_TYPE_WORKING_EF) { + if (file->ef_structure == SC_FILE_EF_TRANSPARENT) { + ops[0] = SC_AC_OP_READ; + ops[1] = SC_AC_OP_UPDATE; + ops[3] = SC_AC_OP_DELETE; + } + else if (file->ef_structure == SC_FILE_EF_LINEAR_FIXED + || file->ef_structure == SC_FILE_EF_LINEAR_VARIABLE) { + ops[0] = SC_AC_OP_READ; + ops[1] = SC_AC_OP_UPDATE; + ops[2] = SC_AC_OP_WRITE; + ops[3] = SC_AC_OP_DELETE; + } + else { + return SC_ERROR_NOT_SUPPORTED; + } + } + else if (file->type == SC_FILE_TYPE_BSO) { + ops[0] = SC_AC_OP_UPDATE; + ops[3] = SC_AC_OP_DELETE; + } + else if (file->type == SC_FILE_TYPE_INTERNAL_EF) { + if (file->ef_structure == + SC_CARDCTL_OBERTHUR_KEY_RSA_CRT) { + ops[1] = SC_AC_OP_UPDATE; + ops[2] = SC_AC_OP_CRYPTO; + ops[3] = SC_AC_OP_DELETE; + } + else if (file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC) { + ops[0] = SC_AC_OP_READ; + ops[1] = SC_AC_OP_UPDATE; + ops[2] = SC_AC_OP_CRYPTO; + ops[3] = SC_AC_OP_DELETE; + } + } + else { + return SC_ERROR_NOT_SUPPORTED; + } + + for (ii = 0; ii < sizeof(ops); ii++) { + const struct sc_acl_entry *entry; + + buf[ii] = 0xFF; + if (ops[ii] == 0xFF) + continue; + entry = sc_file_get_acl_entry(file, ops[ii]); + + rv = acl_to_ac_byte(card, entry); + LOG_TEST_RET(card->ctx, rv, "Invalid ACL"); + + buf[ii] = rv; + } + sc_asn1_put_tag(0x86, buf, sizeof(ops), p, *outlen - (p - out), &p); + + } + + /* VT ??? */ + if (file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC) { + unsigned char data[2] = {0x00, 0x66}; + sc_asn1_put_tag(0x87, data, sizeof(data), p, *outlen - (p - out), &p); + } + + out[1] = p - out - 2; + + *outlen = p - out; + return 0; +} + + +static int +epass2003_create_file(struct sc_card *card, sc_file_t * file) +{ + int r; + size_t len; + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE] = { 0 }; + struct sc_apdu apdu; + + len = SC_MAX_APDU_BUFFER_SIZE; + + epass2003_hook_file(file, 1); + + if (card->ops->construct_fci == NULL) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + + r = epass2003_construct_fci(card, file, sbuf, &len); + LOG_TEST_RET(card->ctx, r, "construct_fci() failed"); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x00, 0x00); + apdu.lc = len; + apdu.datalen = len; + apdu.data = sbuf; + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(card->ctx, r, "APDU sw1/2 wrong"); + + epass2003_hook_file(file, 0); + return r; +} + + +static int +epass2003_delete_file(struct sc_card *card, const sc_path_t * path) +{ + int r; + u8 sbuf[2]; + struct sc_apdu apdu; + + LOG_FUNC_CALLED(card->ctx); + + r = sc_select_file(card, path, NULL); + epass2003_hook_path((struct sc_path *)path, 1); + if (r == SC_SUCCESS) { + sbuf[0] = path->value[path->len - 2]; + sbuf[1] = path->value[path->len - 1]; + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE4, 0x00, 0x00); + apdu.lc = 2; + apdu.datalen = 2; + apdu.data = sbuf; + } + else { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + } + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(card->ctx, r, "Delete file failed"); + + LOG_FUNC_RETURN(card->ctx, r); +} + +#if 0 +static int +epass2003_list_files(struct sc_card *card, unsigned char *buf, size_t buflen) +{ + struct sc_apdu apdu; + unsigned char rbuf[SC_MAX_APDU_BUFFER_SIZE] = { 0 }; + int r; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x34, 0x00, 0x00); + apdu.cla = 0x80; + apdu.le = 0x40; + apdu.resplen = sizeof(rbuf); + apdu.resp = rbuf; + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(card->ctx, r, "Card returned error"); + + if (apdu.resplen == 0x100 && rbuf[0] == 0 && rbuf[1] == 0) + LOG_FUNC_RETURN(card->ctx, 0); + + buflen = buflen < apdu.resplen ? buflen : apdu.resplen; + memcpy(buf, rbuf, buflen); + + LOG_FUNC_RETURN(card->ctx, buflen); +} +#endif + + +static int +internal_write_rsa_key_factor(struct sc_card *card, unsigned short fid, u8 factor, + sc_pkcs15_bignum_t data) +{ + int r; + struct sc_apdu apdu; + u8 sbuff[SC_MAX_EXT_APDU_BUFFER_SIZE] = { 0 }; + + LOG_FUNC_CALLED(card->ctx); + + sbuff[0] = ((fid & 0xff00) >> 8); + sbuff[1] = (fid & 0x00ff); + memcpy(&sbuff[2], data.data, data.len); +// sc_mem_reverse(&sbuff[2], data.len); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xe7, factor, 0x00); + apdu.cla = 0x80; + apdu.lc = apdu.datalen = 2 + data.len; + apdu.data = sbuff; + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(card->ctx, r, "Write rsa key factor failed"); + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + + +static int +internal_write_rsa_key(struct sc_card *card, unsigned short fid, struct sc_pkcs15_prkey_rsa *rsa) +{ + int r; + + LOG_FUNC_CALLED(card->ctx); + + r = internal_write_rsa_key_factor(card, fid, 0x02, rsa->modulus); + LOG_TEST_RET(card->ctx, r, "write n failed"); + r = internal_write_rsa_key_factor(card, fid, 0x03, rsa->d); + LOG_TEST_RET(card->ctx, r, "write d failed"); + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + + +static int +hash_data(unsigned char *data, size_t datalen, unsigned char *hash) +{ + unsigned char data_hash[24] = { 0 }; + size_t len = 0; + + if ((NULL == data) || (NULL == hash)) + return SC_ERROR_INVALID_ARGUMENTS; + + sha1_digest(data, datalen, data_hash); + + len = REVERSE_ORDER4(datalen); + memcpy(&data_hash[20], &len, 4); + memcpy(hash, data_hash, 24); + + return SC_SUCCESS; +} + + +static int +install_secret_key(struct sc_card *card, unsigned char ktype, unsigned char kid, + unsigned char useac, unsigned char modifyac, unsigned char EC, + unsigned char *data, unsigned long dataLen) +{ + int r; + struct sc_apdu apdu; + unsigned char isapp = 0x00; /* appendable */ + unsigned char tmp_data[256] = { 0 }; + + tmp_data[0] = ktype; + tmp_data[1] = kid; + tmp_data[2] = useac; + tmp_data[3] = modifyac; + tmp_data[8] = 0xFF; + + if (0x04 == ktype || 0x06 == ktype) { + tmp_data[4] = EPASS2003_AC_MAC_NOLESS | EPASS2003_AC_SO; + tmp_data[5] = EPASS2003_AC_MAC_NOLESS | EPASS2003_AC_SO; + tmp_data[7] = (kid == PIN_ID[0] ? EPASS2003_AC_USER : EPASS2003_AC_SO); + tmp_data[9] = (EC << 4) | EC; + } + + memcpy(&tmp_data[10], data, dataLen); + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xe3, isapp, 0x00); + apdu.cla = 0x80; + apdu.lc = apdu.datalen = 10 + dataLen; + apdu.data = tmp_data; + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU install_secret_key failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(card->ctx, r, "install_secret_key failed"); + + return r; +} + + +static int +internal_install_pre(struct sc_card *card) +{ + int r; + /* init key for enc */ + r = install_secret_key(card, 0x01, 0x00, + EPASS2003_AC_MAC_NOLESS | EPASS2003_AC_EVERYONE, + EPASS2003_AC_MAC_NOLESS | EPASS2003_AC_EVERYONE, + 0, g_init_key_enc, 16); + LOG_TEST_RET(card->ctx, r, "Install init key failed"); + + /* init key for mac */ + r = install_secret_key(card, 0x02, 0x00, + EPASS2003_AC_MAC_NOLESS | EPASS2003_AC_EVERYONE, + EPASS2003_AC_MAC_NOLESS | EPASS2003_AC_EVERYONE, + 0, g_init_key_mac, 16); + LOG_TEST_RET(card->ctx, r, "Install init key failed"); + + return r; +} + + +/* use external auth secret as pin */ +static int +internal_install_pin(struct sc_card *card, sc_epass2003_wkey_data * pin) +{ + int r; + unsigned char hash[HASH_LEN] = { 0 }; + + r = hash_data(pin->key_data.es_secret.key_val, pin->key_data.es_secret.key_len, hash); + LOG_TEST_RET(card->ctx, r, "hash data failed"); + + r = install_secret_key(card, 0x04, pin->key_data.es_secret.kid, + pin->key_data.es_secret.ac[0], + pin->key_data.es_secret.ac[1], + pin->key_data.es_secret.EC, hash, HASH_LEN); + LOG_TEST_RET(card->ctx, r, "Install failed"); + + return r; +} + + +static int +epass2003_write_key(struct sc_card *card, sc_epass2003_wkey_data * data) +{ + LOG_FUNC_CALLED(card->ctx); + + if (data->type & SC_EPASS2003_KEY) { + if (data->type == SC_EPASS2003_KEY_RSA) + return internal_write_rsa_key(card, data->key_data.es_key.fid, + data->key_data.es_key.rsa); + else + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + } else if (data->type & SC_EPASS2003_SECRET) { + if (data->type == SC_EPASS2003_SECRET_PRE) + return internal_install_pre(card); + else if (data->type == SC_EPASS2003_SECRET_PIN) + return internal_install_pin(card, data); + else + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + } + else { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + } + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + + +static int +epass2003_gen_key(struct sc_card *card, sc_epass2003_gen_key_data * data) +{ + int r; + size_t len = data->key_length; + struct sc_apdu apdu; + u8 rbuf[SC_MAX_EXT_APDU_BUFFER_SIZE] = { 0 }; + u8 sbuf[SC_MAX_EXT_APDU_BUFFER_SIZE] = { 0 }; + + LOG_FUNC_CALLED(card->ctx); + + sbuf[0] = 0x01; + sbuf[1] = (u8) ((len >> 8) & 0xff); + sbuf[2] = (u8) (len & 0xff); + sbuf[3] = (u8) ((data->prkey_id >> 8) & 0xFF); + sbuf[4] = (u8) ((data->prkey_id) & 0xFF); + sbuf[5] = (u8) ((data->pukey_id >> 8) & 0xFF); + sbuf[6] = (u8) ((data->pukey_id) & 0xFF); + + /* generate key */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x46, 0x00, 0x00); + apdu.lc = apdu.datalen = 7; + apdu.data = sbuf; + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(card->ctx, r, "generate keypair failed"); + + /* read public key */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xb4, 0x02, 0x00); + apdu.cla = 0x80; + apdu.lc = apdu.datalen = 2; + apdu.data = &sbuf[5]; + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + apdu.le = 0x00; + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(card->ctx, r, "get pukey failed"); + + if (len < apdu.resplen) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + + data->modulus = (u8 *) malloc(len); + if (!data->modulus) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + + memcpy(data->modulus, rbuf, len); + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + + +static int +epass2003_erase_card(struct sc_card *card) +{ + int r; + + LOG_FUNC_CALLED(card->ctx); + card->cache.valid = 0; + + r = sc_delete_file(card, sc_get_mf_path()); + LOG_TEST_RET(card->ctx, r, "delete MF failed"); + + LOG_FUNC_RETURN(card->ctx, r); +} + + +static int +epass2003_get_serialnr(struct sc_card *card, sc_serial_number_t * serial) +{ + u8 rbuf[8]; + size_t rbuf_len = sizeof(rbuf); + + LOG_FUNC_CALLED(card->ctx); + + if (SC_SUCCESS != get_data(card, 0x80, rbuf, rbuf_len)) + return SC_ERROR_CARD_CMD_FAILED; + + card->serialnr.len = serial->len = 8; + memcpy(card->serialnr.value, rbuf, 8); + memcpy(serial->value, rbuf, 8); + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + + +static int +epass2003_card_ctl(struct sc_card *card, unsigned long cmd, void *ptr) +{ + LOG_FUNC_CALLED(card->ctx); + + switch (cmd) { + case SC_CARDCTL_ENTERSAFE_WRITE_KEY: + return epass2003_write_key(card, (sc_epass2003_wkey_data *) ptr); + case SC_CARDCTL_ENTERSAFE_GENERATE_KEY: + return epass2003_gen_key(card, (sc_epass2003_gen_key_data *) ptr); + case SC_CARDCTL_ERASE_CARD: + return epass2003_erase_card(card); + case SC_CARDCTL_GET_SERIALNR: + return epass2003_get_serialnr(card, (sc_serial_number_t *) ptr); + default: + return SC_ERROR_NOT_SUPPORTED; + } +} + + +static void +internal_sanitize_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 +get_external_key_maxtries(struct sc_card *card, unsigned char *maxtries) +{ + unsigned char maxcounter[2] = { 0 }; + static const sc_path_t file_path = { + {0x3f, 0x00, 0x50, 0x15, 0x9f, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 6, + 0, + 0, + SC_PATH_TYPE_PATH, + {{0}, 0} + }; + struct sc_file *ef_file; + int ret; + + ret = sc_select_file(card, &file_path, &ef_file); + LOG_TEST_RET(card->ctx, ret, "select max counter file failed"); + + ret = sc_read_binary(card, 0, maxcounter, 2, 0); + LOG_TEST_RET(card->ctx, ret, "read max counter file failed"); + + *maxtries = maxcounter[0]; + return SC_SUCCESS; +} + + +static int +get_external_key_retries(struct sc_card *card, unsigned char kid, unsigned char *retries) +{ + int r; + struct sc_apdu apdu; + unsigned char random[16] = { 0 }; + + r = sc_get_challenge(card, random, 8); + LOG_TEST_RET(card->ctx, r, "get challenge get_external_key_retries failed"); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x82, 0x01, 0x80 | kid); + apdu.resp = NULL; + apdu.resplen = 0; + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU get_external_key_retries failed"); + + if (retries && ((0x63 == (apdu.sw1 & 0xff)) && (0xC0 == (apdu.sw2 & 0xf0)))) { + *retries = (apdu.sw2 & 0x0f); + r = SC_SUCCESS; + } + else { + LOG_TEST_RET(card->ctx, r, "get_external_key_retries failed"); + r = SC_ERROR_CARD_CMD_FAILED; + } + + return r; +} + + +static int +external_key_auth(struct sc_card *card, unsigned char kid, + unsigned char *data, size_t datalen) +{ + int r; + struct sc_apdu apdu; + unsigned char random[16] = { 0 }; + unsigned char tmp_data[16] = { 0 }; + unsigned char hash[HASH_LEN] = { 0 }; + unsigned char iv[16] = { 0 }; + + r = sc_get_challenge(card, random, 8); + LOG_TEST_RET(card->ctx, r, "get challenge external_key_auth failed"); + + r = hash_data(data, datalen, hash); + LOG_TEST_RET(card->ctx, r, "hash data failed"); + + des3_encrypt_cbc(hash, HASH_LEN, iv, random, 8, tmp_data); + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x82, 0x01, 0x80 | kid); + apdu.lc = apdu.datalen = 8; + apdu.data = tmp_data; + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU external_key_auth failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(card->ctx, r, "external_key_auth failed"); + + return r; +} + + +static int +update_secret_key(struct sc_card *card, unsigned char ktype, unsigned char kid, + unsigned char *data, unsigned long datalen) +{ + int r; + struct sc_apdu apdu; + unsigned char hash[HASH_LEN] = { 0 }; + unsigned char tmp_data[256] = { 0 }; + unsigned char maxtries = 0; + + r = hash_data(data, datalen, hash); + LOG_TEST_RET(card->ctx, r, "hash data failed"); + + r = get_external_key_maxtries(card, &maxtries); + LOG_TEST_RET(card->ctx, r, "get max counter failed"); + + tmp_data[0] = (maxtries << 4) | maxtries; + memcpy(&tmp_data[1], hash, HASH_LEN); + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xe5, ktype, kid); + apdu.cla = 0x80; + apdu.lc = apdu.datalen = 1 + HASH_LEN; + apdu.data = tmp_data; + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU update_secret_key failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(card->ctx, r, "update_secret_key failed"); + + return r; +} + + +/* use external auth secret as pin */ +static int +epass2003_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) +{ + int r; + u8 kid; + unsigned char maxtries = 0; + + LOG_FUNC_CALLED(card->ctx); + + internal_sanitize_pin_info(&data->pin1, 0); + internal_sanitize_pin_info(&data->pin2, 1); + data->flags |= SC_PIN_CMD_NEED_PADDING; + kid = data->pin_reference; + /* get pin retries */ + if (data->cmd == SC_PIN_CMD_GET_INFO) { + u8 retries = 0; + + r = get_external_key_retries(card, 0x80 | kid, &retries); + if (r == SC_SUCCESS) { + data->pin1.tries_left = retries; + if (tries_left) + *tries_left = retries; + + r = get_external_key_maxtries(card, &maxtries); + LOG_TEST_RET(card->ctx, r, "get max counter failed"); + + data->pin1.max_tries = maxtries; + } + + return r; + } + /* verify */ + if (data->cmd == SC_PIN_CMD_UNBLOCK) { + r = external_key_auth(card, (kid + 1), (unsigned char *)data->pin1.data, + data->pin1.len); + LOG_TEST_RET(card->ctx, r, "verify pin failed"); + } + else { + r = external_key_auth(card, kid, (unsigned char *)data->pin1.data, + data->pin1.len); + LOG_TEST_RET(card->ctx, r, "verify pin failed"); + } + + if (data->cmd == SC_PIN_CMD_CHANGE || data->cmd == SC_PIN_CMD_UNBLOCK) { + /* change */ + r = update_secret_key(card, 0x04, kid, (unsigned char *)data->pin2.data, + (unsigned long)data->pin2.len); + LOG_TEST_RET(card->ctx, r, "verify pin failed"); + } + return r; +} + + +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; + + epass2003_ops = *iso_ops; + + epass2003_ops.match_card = epass2003_match_card; + epass2003_ops.init = epass2003_init; + epass2003_ops.write_binary = NULL; + epass2003_ops.write_record = NULL; + epass2003_ops.select_file = epass2003_select_file; + epass2003_ops.get_response = NULL; + epass2003_ops.restore_security_env = epass2003_restore_security_env; + epass2003_ops.set_security_env = epass2003_set_security_env; + epass2003_ops.decipher = epass2003_decipher; + epass2003_ops.compute_signature = epass2003_decipher; + epass2003_ops.create_file = epass2003_create_file; + epass2003_ops.delete_file = epass2003_delete_file; + /* epass2003_ops.list_files = epass2003_list_files; */ + epass2003_ops.card_ctl = epass2003_card_ctl; + epass2003_ops.process_fci = epass2003_process_fci; + epass2003_ops.construct_fci = epass2003_construct_fci; + epass2003_ops.pin_cmd = epass2003_pin_cmd; + return &epass2003_drv; +} + +struct sc_card_driver *sc_get_epass2003_driver(void) +{ + return sc_get_driver(); +} +#endif /* #ifdef ENABLE_OPENSSL */ +#endif /* #ifdef ENABLE_SM */ diff --git a/src/libopensc/cardctl.h b/src/libopensc/cardctl.h index 32edb586..2e39fa07 100644 --- a/src/libopensc/cardctl.h +++ b/src/libopensc/cardctl.h @@ -715,6 +715,48 @@ typedef struct sc_entersafe_gen_key_data_st { u8 *modulus; } sc_entersafe_gen_key_data; +#define SC_EPASS2003_KEY 0x00000010 +#define SC_EPASS2003_KEY_RSA 0x00000011 +#define SC_EPASS2003_SECRET 0x00000020 +#define SC_EPASS2003_SECRET_PRE 0x00000021 +#define SC_EPASS2003_SECRET_PIN 0x00000022 + +#define EPASS2003_AC_EVERYONE 0x00 +#define EPASS2003_AC_USER 0x06 +#define EPASS2003_AC_SO 0x08 +#define EPASS2003_AC_NOONE 0x0F +#define EPASS2003_AC_MAC_UNEQUAL 0x80 +#define EPASS2003_AC_MAC_NOLESS 0x90 +#define EPASS2003_AC_MAC_LESS 0xA0 +#define EPASS2003_AC_MAC_EQUAL 0xB0 + +#define FID_STEP 0x20 + +typedef struct sc_epass2003_wkey_data_st { + u8 type; + union { + struct { + unsigned short fid; + struct sc_pkcs15_prkey_rsa* rsa; + } es_key; + struct { + u8 kid; + u8 EC; + u8 ac[2]; + u8 key_val[256]; + size_t key_len; + } es_secret; + } key_data; +} sc_epass2003_wkey_data; + +typedef struct sc_epass2003_gen_key_data_st { + int prkey_id; + int pukey_id; + size_t key_length; + u8 *modulus; +} sc_epass2003_gen_key_data; + + #if defined(__APPLE__) || defined(sun) #pragma pack() #else diff --git a/src/libopensc/cards.h b/src/libopensc/cards.h index 8aef144f..84f384b3 100644 --- a/src/libopensc/cards.h +++ b/src/libopensc/cards.h @@ -156,6 +156,7 @@ enum { SC_CARD_TYPE_ENTERSAFE_BASE = 19000, SC_CARD_TYPE_ENTERSAFE_3K, SC_CARD_TYPE_ENTERSAFE_FTCOS_PK_01C, + SC_CARD_TYPE_ENTERSAFE_FTCOS_EPASS2003, /* MyEID cards */ SC_CARD_TYPE_MYEID_BASE = 20000, @@ -221,6 +222,7 @@ extern sc_card_driver_t *sc_get_javacard_driver(void); extern sc_card_driver_t *sc_get_itacns_driver(void); extern sc_card_driver_t *sc_get_authentic_driver(void); extern sc_card_driver_t *sc_get_iasecc_driver(void); +extern sc_card_driver_t *sc_get_epass2003_driver(void); #ifdef __cplusplus } diff --git a/src/libopensc/ctx.c b/src/libopensc/ctx.c index c8b84b36..59b193cb 100644 --- a/src/libopensc/ctx.c +++ b/src/libopensc/ctx.c @@ -88,6 +88,9 @@ static const struct _sc_driver_entry internal_card_drivers[] = { { "akis", (void *(*)(void)) sc_get_akis_driver }, #ifdef ENABLE_OPENSSL { "entersafe",(void *(*)(void)) sc_get_entersafe_driver }, +#ifdef ENABLE_SM + { "epass2003",(void *(*)(void)) sc_get_epass2003_driver }, +#endif #endif { "rutoken", (void *(*)(void)) sc_get_rutoken_driver }, { "rutoken_ecp",(void *(*)(void)) sc_get_rtecp_driver }, diff --git a/src/pkcs15init/Makefile.am b/src/pkcs15init/Makefile.am index f9158eea..ca2c00d1 100644 --- a/src/pkcs15init/Makefile.am +++ b/src/pkcs15init/Makefile.am @@ -21,6 +21,7 @@ dist_pkgdata_DATA = \ rutoken.profile \ asepcos.profile \ entersafe.profile \ + epass2003.profile \ rutoken_ecp.profile \ westcos.profile \ myeid.profile \ @@ -39,7 +40,8 @@ libpkcs15init_la_SOURCES = \ pkcs15-gpk.c pkcs15-miocos.c pkcs15-cflex.c \ pkcs15-cardos.c pkcs15-jcop.c pkcs15-starcos.c \ pkcs15-setcos.c pkcs15-incrypto34.c pkcs15-muscle.c \ - pkcs15-asepcos.c pkcs15-rutoken.c pkcs15-entersafe.c \ + pkcs15-asepcos.c pkcs15-rutoken.c \ + pkcs15-entersafe.c pkcs15-epass2003.c \ pkcs15-rtecp.c pkcs15-myeid.c \ pkcs15-oberthur.c pkcs15-oberthur-awp.c \ - pkcs15-authentic.c pkcs15-iasecc.c + pkcs15-authentic.c pkcs15-iasecc.c diff --git a/src/pkcs15init/Makefile.mak b/src/pkcs15init/Makefile.mak index a3eb895e..9b592573 100644 --- a/src/pkcs15init/Makefile.mak +++ b/src/pkcs15init/Makefile.mak @@ -8,9 +8,10 @@ OBJECTS = pkcs15-lib.obj profile.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 pkcs15-authentic.obj pkcs15-iasecc.obj + pkcs15-myeid.obj pkcs15-authentic.obj pkcs15-iasecc.obj \ + pkcs15-epass2003.obj -all: $(TARGET) +all: $(TARGET) $(TARGET): $(OBJECTS) lib $(LIBFLAGS) /out:$(TARGET) $(OBJECTS) diff --git a/src/pkcs15init/epass2003.profile b/src/pkcs15init/epass2003.profile new file mode 100644 index 00000000..1138f9b9 --- /dev/null +++ b/src/pkcs15init/epass2003.profile @@ -0,0 +1,207 @@ +# +# 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 = local, initialized, needs-padding; + min-pin-length = 4; + df_acl = *=NEVER; + ef_acl = *=NEVER, READ=NONE, UPDATE=NONE, WRITE=NONE, DELETE=NONE; + sf_acl = *=NEVER, UPDATE=NONE; + protected = *=$PIN,READ=NONE; + unprotected = *=NONE; + dir-size = 112; + 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; + maxPin-size = 2; + } +} + +option onepin { + macros { + pin-flags = local, initialized, needs-padding; +# df_acl = *=$PIN; + df_acl = *=NEVER, CRYPTO=NONE, FILES=NONE, CREATE=NONE, DELETE=NONE; + ef_acl = *=NEVER, READ=NONE, UPDATE=NONE, WRITE=NONE, DELETE=NONE; + sf_acl = *=NEVER, UPDATE=NONE; + protected = *=NEVER,READ=NONE, UPDATE=$PIN, DELETE=$PIN; + unprotected = *=NONE; + dir-size = 112; + tinfo-size = 128; + unusedspace-size = 128; + odf-size = 512; + aodf-size = 256; + cdf-size = 2048; + prkdf-size = 1024; + pukdf-size = 1024; + dodf-size = 256; + info-size = 128; + maxPin-size = 2; + } +} + +PIN so-pin { + reference = 1; + attempts = 6; + flags = $pin-flags; + min-length = $min-pin-length; +} +PIN so-puk { + attempts = 6; + flags = $pin-flags; + min-length = $min-pin-length; +} +PIN user-pin { + reference = 2; + attempts = 6; + flags = $pin-flags; + min-length = $min-pin-length; +} +PIN user-puk { + attempts = 6; + flags = $pin-flags; + min-length = $min-pin-length; +} + +# Additional filesystem info. +# This is added to the file system info specified in the +# main profile. +filesystem { + DF MF { + ACL = $df_acl; + size = 768; + file-id = 3F00; + aid = 65:6e:74:65:72:73:61:66:65:2d:66:69:70:73 + + BSO SKey-MF { + file-id = 5300; + ACL = $sf_acl + size = 4; + } + + EF DIR { + type = EF; + size = $dir-size; + ACL = $ef_acl; + file-id = 2F00; + structure = linear-variable; + } + + DF PKCS15-AppDF { + ACL = $df_acl; + size = 16000; + + BSO SKey-AppDF { + file-id = 5301; + ACL = $sf_acl + size = 32; + } + + EF MAXPIN { + file-id = 9F00; + size = $maxPin-size; + ACL = $unprotected; + } + + 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 { + EF private-key { + file-id = 2900; +#type = internal-ef; + structure = 0xA3; +#ACL = READ=CHV1,UPDATE=CHV1,CRYPTO=CHV1; + ACL = *=NONE; + } + + EF public-key { + file-id = 3000; + structure = transparent; + ACL = *=NONE; + } + + # Certificate template + EF certificate { + file-id = 3100; + structure = transparent; + ACL = READ=NONE,UPDATE=NONE; + } + + # Extractable private keys are stored in transparent EFs. + # Encryption of the content is performed by libopensc. + EF extractable-key { + file-id = 3200; + structure = transparent; + ACL = *=NEVER,READ=NONE,UPDATE=$PIN; + } + + # data objects are stored in transparent EFs. + EF data { + file-id = 3300; + structure = transparent; + ACL = *=NEVER,READ=NONE,UPDATE=NONE; + } + # data objects are stored in transparent EFs. + EF privdata { + file-id = 3400; + structure = transparent; + ACL = *=NEVER,READ=$PIN,UPDATE=$PIN; + } + + } + } + } +} diff --git a/src/pkcs15init/pkcs15-epass2003.c b/src/pkcs15init/pkcs15-epass2003.c new file mode 100644 index 00000000..be39babc --- /dev/null +++ b/src/pkcs15init/pkcs15-epass2003.c @@ -0,0 +1,700 @@ +/* + * Support for ePass2003 smart cards + * + * Copyright (C) 2008, Weitao Sun + * Copyright (C) 2011, Xiaoshuo Wu + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#include +#include +#include +#include + +#include "libopensc/log.h" +#include "libopensc/opensc.h" +#include "libopensc/cardctl.h" +#include "libopensc/cards.h" +#include "pkcs15-init.h" +#include "profile.h" +static int epass2003_pkcs15_erase_card(struct sc_profile *profile, + struct sc_pkcs15_card *p15card) +{ + SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE); + + if (sc_select_file(p15card->card, sc_get_mf_path(), NULL) < 0) + return SC_SUCCESS; + + return sc_card_ctl(p15card->card, SC_CARDCTL_ERASE_CARD, 0); +} + +static int epass2003_pkcs15_init_card(struct sc_profile *profile, + struct sc_pkcs15_card *p15card) +{ + struct sc_card *card = p15card->card; + int ret; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + { /* MF */ + struct sc_file *mf_file; + struct sc_file *skey_file; + + ret = sc_profile_get_file(profile, "MF", &mf_file); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, + "Get MF info failed"); + ret = sc_create_file(card, mf_file); + sc_file_free(mf_file); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, + "Create MF failed"); + + ret = sc_profile_get_file(profile, "SKey-MF", &skey_file); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, + "Get SKey info failed"); + ret = sc_create_file(card, skey_file); + sc_file_free(skey_file); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, + "Create SKey failed"); + + } + + { /* EF(DIR) */ + struct sc_file *dir_file; + + /* get dir profile */ + ret = sc_profile_get_file(profile, "DIR", &dir_file); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, + "Get EF(DIR) info failed"); + ret = sc_create_file(card, dir_file); + sc_file_free(dir_file); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, + "Create EF(DIR) failed"); + + sc_free_apps(card); + } + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); +} + +static int epass2003_pkcs15_create_dir(struct sc_profile *profile, + struct sc_pkcs15_card *p15card, + struct sc_file *df) +{ + struct sc_card *card = p15card->card; + int ret; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + { /* p15 DF */ + struct sc_file *df_file; + struct sc_file *skey_file; + struct sc_file *ef_file; + u8 max_counter[2] = { 0 }; + int id; + u8 user_maxtries = 0; + u8 so_maxtries = 0; + + ret = sc_profile_get_file(profile, "PKCS15-AppDF", &df_file); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, + "Get PKCS15-AppDF info failed"); + ret = sc_create_file(card, df_file); + sc_file_free(df_file); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, + "Create PKCS15-AppDF failed"); + + ret = sc_profile_get_file(profile, "SKey-AppDF", &skey_file); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, + "Get SKey info failed"); + ret = sc_create_file(card, skey_file); + sc_file_free(skey_file); + + ret = sc_profile_get_file(profile, "MAXPIN", &ef_file); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, + "Get MAXPIN info failed"); + ret = sc_create_file(card, ef_file); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, + "Create MAXPIN failed"); + ret = sc_select_file(card, &(ef_file->path), &ef_file); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, + "Select MAXPIN failed"); + + ret = sc_profile_get_pin_id(profile, 2, &id); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, + "Get User PIN id error!"); + user_maxtries = (u8) sc_profile_get_pin_retries(profile, id); + + ret = sc_profile_get_pin_id(profile, 1, &id); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, + "Get User PIN id error!"); + so_maxtries = (u8) sc_profile_get_pin_retries(profile, id); + + max_counter[0] = user_maxtries; + max_counter[1] = so_maxtries; + + ret = sc_update_binary(card, 0, max_counter, 2, 0); + + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, + "Update MAXPIN failed"); + sc_file_free(ef_file); + } + + { /* p15 efs */ + char *create_efs[] = { + "PKCS15-ODF", + "PKCS15-TokenInfo", + "PKCS15-UnusedSpace", + "PKCS15-AODF", + "PKCS15-PrKDF", + "PKCS15-PuKDF", + "PKCS15-CDF", + "PKCS15-DODF", + NULL, + }; + int i; + struct sc_file *file = 0; + + for (i = 0; create_efs[i]; ++i) { + if (sc_profile_get_file(profile, create_efs[i], &file)) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + "Inconsistent profile: cannot find %s", + create_efs[i]); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, + SC_ERROR_INCONSISTENT_PROFILE); + } + ret = sc_create_file(card, file); + sc_file_free(file); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, + "Create pkcs15 file failed"); + } + } + + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, ret); +} + +static int epass2003_pkcs15_pin_reference(struct sc_profile *profile, + struct sc_pkcs15_card *p15card, + struct sc_pkcs15_auth_info *auth_info) +{ + SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE); + + if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) + return SC_ERROR_OBJECT_NOT_VALID; + + if (auth_info->attrs.pin.reference < ENTERSAFE_USER_PIN_ID + || auth_info->attrs.pin.reference > ENTERSAFE_SO_PIN_ID) + return SC_ERROR_INVALID_PIN_REFERENCE; + + SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); +} + +static int epass2003_pkcs15_create_pin(struct sc_profile *profile, + struct sc_pkcs15_card *p15card, + struct sc_file *df, + struct sc_pkcs15_object *pin_obj, + const unsigned char *pin, size_t pin_len, + const unsigned char *puk, size_t puk_len) +{ + struct sc_card *card = p15card->card; + int r; + struct sc_pkcs15_auth_info *auth_info = + (struct sc_pkcs15_auth_info *)pin_obj->data; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) + return SC_ERROR_OBJECT_NOT_VALID; + + { /*pin */ + sc_epass2003_wkey_data data; + int id; + + if (!pin || !pin_len || pin_len > 16) + return SC_ERROR_INVALID_ARGUMENTS; + + data.type = SC_EPASS2003_SECRET_PIN; + data.key_data.es_secret.kid = auth_info->attrs.pin.reference; + data.key_data.es_secret.ac[0] = + EPASS2003_AC_MAC_NOLESS | EPASS2003_AC_EVERYONE; + data.key_data.es_secret.ac[1] = + EPASS2003_AC_MAC_NOLESS | EPASS2003_AC_USER; + + r = sc_profile_get_pin_id(profile, 2, &id); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, + "Get User PIN id error!"); + data.key_data.es_secret.EC = + sc_profile_get_pin_retries(profile, id); + + /* pad pin with 0 */ + memset(data.key_data.es_secret.key_val, 0, + sizeof(data.key_data.es_secret.key_val)); + memcpy(data.key_data.es_secret.key_val, pin, pin_len); + data.key_data.es_secret.key_len = pin_len; + + r = sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_WRITE_KEY, &data); + if (pin_obj) { + /* Cache new PIN value. */ + sc_pkcs15_pincache_add(p15card, pin_obj, pin, pin_len); + } + } + + { /*puk */ + sc_epass2003_wkey_data data; + int id; + + if (!puk || !puk_len || puk_len > 16) + return SC_ERROR_INVALID_ARGUMENTS; + + data.type = SC_EPASS2003_SECRET_PIN; + data.key_data.es_secret.kid = + auth_info->attrs.pin.reference + 1; + data.key_data.es_secret.ac[0] = + EPASS2003_AC_MAC_NOLESS | EPASS2003_AC_EVERYONE; + data.key_data.es_secret.ac[1] = + EPASS2003_AC_MAC_EQUAL | EPASS2003_AC_SO; + + r = sc_profile_get_pin_id(profile, 1, &id); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, + "Get User PIN id error!"); + data.key_data.es_secret.EC = + sc_profile_get_pin_retries(profile, id); + + /* pad pin with 0 */ + memset(data.key_data.es_secret.key_val, 0, + sizeof(data.key_data.es_secret.key_val)); + memcpy(data.key_data.es_secret.key_val, puk, puk_len); + data.key_data.es_secret.key_len = puk_len; + + r = sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_WRITE_KEY, &data); + } + + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); +} + +static int epass2003_pkcs15_key_reference(struct sc_profile *profile, + struct sc_pkcs15_card *p15card, + struct sc_pkcs15_prkey_info *prkey) +{ + SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE); + prkey->key_reference = prkey->path.value[prkey->path.len - 1]; + SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); +} + +/* from pkcs15-oberthur.c, modified */ +static int +cosm_new_file(struct sc_profile *profile, struct sc_card *card, + unsigned int type, unsigned int num, struct sc_file **out) +{ + struct sc_file *file; + const char *_template = NULL, *desc = NULL; + unsigned int structure = 0xFFFFFFFF; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "type %X; num %i\n", type, + num); + while (1) { + switch (type) { + case SC_PKCS15_TYPE_PRKEY_RSA: + desc = "RSA private key"; + _template = "private-key"; + structure = SC_CARDCTL_OBERTHUR_KEY_RSA_CRT; + break; + case SC_PKCS15_TYPE_PUBKEY_RSA: + desc = "RSA public key"; + _template = "public-key"; + structure = SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC; + break; + case SC_PKCS15_TYPE_PUBKEY_DSA: + desc = "DSA public key"; + _template = "public-key"; + break; + case SC_PKCS15_TYPE_PRKEY: + desc = "extractable private key"; + _template = "extractable-key"; + break; + case SC_PKCS15_TYPE_CERT: + desc = "certificate"; + _template = "certificate"; + break; + case SC_PKCS15_TYPE_DATA_OBJECT: + desc = "data object"; + _template = "data"; + break; + } + if (_template) + break; + /* If this is a specific type such as + * SC_PKCS15_TYPE_CERT_FOOBAR, fall back to + * the generic class (SC_PKCS15_TYPE_CERT) + */ + if (!(type & ~SC_PKCS15_TYPE_CLASS_MASK)) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + "File type %X not supported by card driver", + type); + return SC_ERROR_INVALID_ARGUMENTS; + } + type &= SC_PKCS15_TYPE_CLASS_MASK; + } + + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "template %s; num %i\n", + _template, num); + if (sc_profile_get_file(profile, _template, &file) < 0) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + "Profile doesn't define %s template '%s'\n", desc, + _template); + return SC_ERROR_NOT_SUPPORTED; + } + + file->id &= 0xFF00; + file->id |= (num & 0x00FF); + + file->path.value[file->path.len - 1] = (num & 0xFF); + file->type = SC_FILE_TYPE_INTERNAL_EF; + file->ef_structure = structure; + + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + "file size %i; ef type %i/%i; id %04X, path_len %i\n", + file->size, file->type, file->ef_structure, file->id, + file->path.len); + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "file path: %s", + sc_print_path(&(file->path))); + *out = file; + + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); +} + +static int epass2003_pkcs15_create_key(struct sc_profile *profile, + struct sc_pkcs15_card *p15card, + struct sc_pkcs15_object *obj) +{ + struct sc_card *card = p15card->card; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); +} + +static int epass2003_pkcs15_store_key(struct sc_profile *profile, + struct sc_pkcs15_card *p15card, + struct sc_pkcs15_object *obj, + struct sc_pkcs15_prkey *key) +{ + struct sc_card *card = p15card->card; + struct sc_pkcs15_prkey_info *key_info = + (struct sc_pkcs15_prkey_info *)obj->data; + size_t idx = key_info->key_reference; + size_t keybits = key_info->modulus_length; + struct sc_path path; + struct sc_file *tfile = NULL; + struct sc_file *file = NULL; + sc_epass2003_wkey_data data; + int r; + int fidl = 0; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "index %i; id %s\n", idx, + sc_pkcs15_print_id(&key_info->id)); + if (key->algorithm != SC_ALGORITHM_RSA + || key->algorithm != SC_ALGORITHM_RSA) + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, + SC_ERROR_NOT_SUPPORTED, + "store key: only support RSA"); + + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + "store key: with ID:%s and path:%s", + sc_pkcs15_print_id(&key_info->id), + sc_print_path(&key_info->path)); + + /* allocate key object */ + r = cosm_new_file(profile, card, SC_PKCS15_TYPE_PRKEY_RSA, + key_info->key_reference, &file); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, + "create key: failed to allocate new key object"); + file->size = keybits; + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "private key path: %s", + sc_print_path(&(file->path))); + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "private key_info path: %s", + sc_print_path(&(key_info->path))); + r = sc_delete_file(p15card->card, &file->path); + /* create */ + r = sc_pkcs15init_create_file(profile, p15card, file); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, + "create key: failed to create key file"); + + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "index %i; keybits %i\n", idx, + keybits); + if (keybits < 512 || keybits > 2048 || (keybits % 0x20)) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + "Unsupported key size %u\n", keybits); + return SC_ERROR_INVALID_ARGUMENTS; + } + + path = key_info->path; + path.len -= 2; + + r = sc_select_file(card, &path, &tfile); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, + "generate key: no private object DF"); + + r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, + "No authorisation to store private key"); + + sc_file_free(tfile); + + fidl = (file->id & 0xff) * FID_STEP; + file->id = (file->id & 0xff00) + fidl; + data.type = SC_EPASS2003_KEY_RSA; + data.key_data.es_key.fid = file->id; + data.key_data.es_key.rsa = (void *)&key->u.rsa; + + r = sc_card_ctl(p15card->card, SC_CARDCTL_ENTERSAFE_WRITE_KEY, &data); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, + "store key: cannot update private key"); + + if (file) + sc_file_free(file); + + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); +} + +static int epass2003_pkcs15_generate_key(struct sc_profile *profile, + struct sc_pkcs15_card *p15card, + struct sc_pkcs15_object *obj, + struct sc_pkcs15_pubkey *pubkey) +{ + struct sc_card *card = p15card->card; + int r; + sc_epass2003_gen_key_data gendat; + struct sc_pkcs15_prkey_info *key_info = + (struct sc_pkcs15_prkey_info *)obj->data; + size_t idx = key_info->key_reference; + size_t keybits = key_info->modulus_length; + struct sc_file *tfile = NULL, *prkf = NULL, *pukf = NULL; + struct sc_path path; + struct sc_file *file = NULL; + int fidl = 0; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) + return SC_ERROR_NOT_SUPPORTED; + + /* allocate key object */ + r = cosm_new_file(profile, card, SC_PKCS15_TYPE_PRKEY_RSA, idx, &file); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, + "create key: failed to allocate new key object"); + file->size = keybits; + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "private key path: %s", + sc_print_path(&file->path)); + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "private key_info path: %s", + sc_print_path(&(key_info->path))); + r = sc_delete_file(p15card->card, &file->path); + /* create */ + r = sc_pkcs15init_create_file(profile, p15card, file); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, + "create key: failed to create key file"); + + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "index %i; keybits %i\n", idx, + keybits); + if (keybits < 512 || keybits > 2048 || (keybits % 0x20)) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + "Unsupported key size %u\n", keybits); + return SC_ERROR_INVALID_ARGUMENTS; + } + + path = key_info->path; + path.len -= 2; + + r = sc_select_file(card, &path, &tfile); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, + "generate key: no private object DF"); + + r = sc_pkcs15init_authenticate(profile, p15card, tfile, + SC_AC_OP_CRYPTO); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, + "generate key: pkcs15init_authenticate(SC_AC_OP_CRYPTO) failed"); + + r = sc_pkcs15init_authenticate(profile, p15card, tfile, + SC_AC_OP_CREATE); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, + "generate key: pkcs15init_authenticate(SC_AC_OP_CREATE) failed"); + + sc_file_free(tfile); + + if ((r = cosm_new_file(profile, card, SC_PKCS15_TYPE_PUBKEY_RSA, idx, + &pukf)) < 0) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + "generate key: create temporary pukf failed\n"); + goto failed; + } + pukf->size = keybits; + pukf->id = pukf->path.value[pukf->path.len - 2] * 0x100 + + pukf->path.value[pukf->path.len - 1]; + + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + "public key size %i; ef type %i/%i; id %04X; path: %s", + pukf->size, pukf->type, pukf->ef_structure, pukf->id, + sc_print_path(&pukf->path)); + + r = sc_select_file(p15card->card, &pukf->path, NULL); + /* if exist, delete */ + if (r == SC_SUCCESS) { + r = sc_pkcs15init_delete_by_path(profile, p15card, &pukf->path); + if (r != SC_SUCCESS) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + "generate key: failed to delete existing key file\n"); + goto failed; + } + } + /* create */ + r = sc_pkcs15init_create_file(profile, p15card, pukf); + if (r != SC_SUCCESS) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + "generate key: pukf create file failed\n"); + goto failed; + } + + /* generate key pair */ + fidl = (file->id & 0xff) * FID_STEP; + file->id = (file->id & 0xff00) + fidl; + pukf->id = (pukf->id & 0xff00) + fidl; + gendat.prkey_id = file->id; + gendat.pukey_id = pukf->id; + gendat.key_length = keybits; + gendat.modulus = NULL; + r = sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_GENERATE_KEY, &gendat); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, + "generate RSA key pair failed"); + + /* get the modulus */ + if (pubkey) { + u8 *buf; + struct sc_pkcs15_pubkey_rsa *rsa = &pubkey->u.rsa; + /* set the modulus */ + rsa->modulus.data = gendat.modulus; + rsa->modulus.len = keybits >> 3; + /* set the exponent (always 0x10001) */ + buf = (u8 *) malloc(3); + if (!buf) { + r = SC_ERROR_OUT_OF_MEMORY; + goto failed; + } + 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); + +failed: + if (pukf) + sc_file_free(pukf); + if (prkf) + sc_file_free(prkf); + + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); +} + +static int epass2003_pkcs15_delete_object(struct sc_profile *profile, + struct sc_pkcs15_card *p15card, + struct sc_pkcs15_object *object, + const struct sc_path *path) +{ + SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE); + return sc_pkcs15init_delete_by_path(profile, p15card, path); +} + +static int epass2003_pkcs15_sanity_check(sc_profile_t * profile, + sc_pkcs15_card_t * p15card) +{ + struct sc_context *ctx = p15card->card->ctx; + struct sc_pkcs15_auth_info profile_auth; + struct sc_pkcs15_object *objs[32]; + int rv, nn, ii, update_df = 0; + + SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); + + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + "Check and if needed update PinFlags"); + rv = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH_PIN, objs, 32); + SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Failed to get PINs"); + nn = rv; + + sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PIN, &profile_auth); + SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Failed to get PIN info"); + + for (ii = 0; ii < nn; ii++) { + struct sc_pkcs15_auth_info *ainfo = + (struct sc_pkcs15_auth_info *)objs[ii]->data; + struct sc_pkcs15_pin_attributes *pin_attrs = &ainfo->attrs.pin; + + if (ainfo->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) + continue; + + if (pin_attrs->reference == profile_auth.attrs.pin.reference + && pin_attrs->flags != profile_auth.attrs.pin.flags) { + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + "Set flags of '%s'(flags:%X,ref:%i,id:%s) to %X", + objs[ii]->label, pin_attrs->flags, + pin_attrs->reference, + sc_pkcs15_print_id(&ainfo->auth_id), + profile_auth.attrs.pin.flags); + pin_attrs->flags = profile_auth.attrs.pin.flags; + update_df = 1; + } + } + if (update_df) { + struct sc_pkcs15_df *df = p15card->df_list; + + while (df != NULL && df->type != SC_PKCS15_AODF) + df = df->next; + if (!df) + SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, + SC_ERROR_OBJECT_NOT_FOUND, + "Cannot find AODF"); + rv = sc_pkcs15init_update_any_df(p15card, profile, df, 0); + SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Update AODF error"); + } + + SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, rv); +} + +static struct sc_pkcs15init_operations sc_pkcs15init_epass2003_operations = { + epass2003_pkcs15_erase_card, + epass2003_pkcs15_init_card, + epass2003_pkcs15_create_dir, + NULL, /* create_domain */ + epass2003_pkcs15_pin_reference, + epass2003_pkcs15_create_pin, + epass2003_pkcs15_key_reference, + epass2003_pkcs15_create_key, + epass2003_pkcs15_store_key, + epass2003_pkcs15_generate_key, + NULL, NULL, /* encode private/public key */ + NULL, /* finalize */ + epass2003_pkcs15_delete_object, + NULL, NULL, NULL, NULL, NULL, /* pkcs15init emulation */ + epass2003_pkcs15_sanity_check, +}; + +struct sc_pkcs15init_operations *sc_pkcs15init_get_epass2003_ops(void) +{ + return &sc_pkcs15init_epass2003_operations; +}