From 1a58c429cbed13bed30e561896e747e3051a6d15 Mon Sep 17 00:00:00 2001 From: vtarasov Date: Thu, 30 Dec 2010 14:40:28 +0000 Subject: [PATCH] 'AuthentIC': basic support of Oberthur's 'COSMO.v7/AuthentIC.v3.2' ... it's the natively PKCS#15 card git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@5006 c6295689-39f2-0310-b995-f0e70906c6a9 --- src/libopensc/Makefile.am | 4 +- src/libopensc/Makefile.mak | 2 +- src/libopensc/authentic.h | 181 +++ src/libopensc/card-authentic.c | 2413 +++++++++++++++++++++++++++++ src/libopensc/cardctl.h | 9 + src/libopensc/cards.h | 3 + src/libopensc/ctx.c | 1 + src/libopensc/iso7816.h | 26 + src/pkcs15init/Makefile.am | 6 +- src/pkcs15init/Makefile.mak | 2 +- src/pkcs15init/pkcs15-authentic.c | 1021 ++++++++++++ src/pkcs15init/pkcs15-init.h | 2 +- src/pkcs15init/pkcs15-lib.c | 2 +- win32/opensc-msi/OpenSC.wxs | 5 + 14 files changed, 3669 insertions(+), 8 deletions(-) create mode 100644 src/libopensc/authentic.h create mode 100644 src/libopensc/card-authentic.c create mode 100644 src/libopensc/iso7816.h create mode 100644 src/pkcs15init/pkcs15-authentic.c diff --git a/src/libopensc/Makefile.am b/src/libopensc/Makefile.am index 1ac8280428..b5271cb2c6 100644 --- a/src/libopensc/Makefile.am +++ b/src/libopensc/Makefile.am @@ -9,7 +9,7 @@ noinst_HEADERS = cards.h ctbcs.h internal.h esteid.h muscle.h muscle-filesystem. internal-winscard.h p15card-helper.h \ opensc.h pkcs15.h \ cardctl.h asn1.h log.h \ - errors.h types.h compression.h itacns.h + errors.h types.h compression.h itacns.h iso7816.h authentic.h AM_CPPFLAGS = -DOPENSC_CONF_PATH=\"$(sysconfdir)/opensc.conf\" AM_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) $(OPTIONAL_OPENCT_CFLAGS) \ @@ -37,7 +37,7 @@ libopensc_la_SOURCES = \ 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 \ - card-javacard.c card-itacns.c \ + card-javacard.c card-itacns.c card-authentic.c \ \ pkcs15-openpgp.c pkcs15-infocamere.c pkcs15-starcert.c \ pkcs15-tcos.c pkcs15-esteid.c pkcs15-postecert.c pkcs15-gemsafeGPK.c \ diff --git a/src/libopensc/Makefile.mak b/src/libopensc/Makefile.mak index 1cb1a88500..14b6fdb7df 100644 --- a/src/libopensc/Makefile.mak +++ b/src/libopensc/Makefile.mak @@ -21,7 +21,7 @@ OBJECTS = \ 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 \ - card-javacard.obj card-itacns.obj \ + card-javacard.obj card-itacns.obj card-authentic.obj \ \ pkcs15-openpgp.obj pkcs15-infocamere.obj pkcs15-starcert.obj \ pkcs15-tcos.obj pkcs15-esteid.obj pkcs15-postecert.obj pkcs15-gemsafeGPK.obj \ diff --git a/src/libopensc/authentic.h b/src/libopensc/authentic.h new file mode 100644 index 0000000000..fbcd35a095 --- /dev/null +++ b/src/libopensc/authentic.h @@ -0,0 +1,181 @@ +/* + * authentic.h: Specific definitions for the Oberthur's card + * 'COSMO v7' with applet 'AuthentIC v3' + * + * Copyright (C) 2010 Viktor Tarasov + * OpenTrust + * + * 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 + */ + +#ifndef _OPENSC_AUTHENTIC_V3_H +#define _OPENSC_AUTHENTIC_V3_H + +#include "errors.h" +#include "types.h" +#include "iso7816.h" + +#define LOGN_FUNC_CALLED(ctx) SC_FUNC_CALLED((ctx), SC_LOG_DEBUG_NORMAL) +#define LOGN_FUNC_RETURN(ctx, r) SC_FUNC_RETURN((ctx), SC_LOG_DEBUG_NORMAL, (r)) +#define LOGN_TEST_RET(ctx, r, text) SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, text) + +#define AUTHENTIC_V3_TITLE "AuthentIC.v3" + +#define PAN_ISO7812_SN_TAG 0x5A +#define PAN_ISO7812_LENGTH 0x0C + +#ifndef CKM_RSA_PKCS + #define CKM_RSA_PKCS 0x00000001 + #define CKM_SHA1_RSA_PKCS 0x00000006 + #define CKM_SHA256_RSA_PKCS 0x00000040 + #define CKM_SHA_1 0x00000220 + #define CKM_SHA256 0x00000250 +#endif + +#define AUTHENTIC_V3_CREDENTIAL_ID_MASK 7 + +#define AUTHENTIC_V3_CRYPTO_OBJECT_REF_MIN 0x81 +#define AUTHENTIC_V3_CRYPTO_OBJECT_REF_MAX 0xFF + +#define _MAKE_AUTHENTIC_MAGIC(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | ((d))) + +#define AUTHENTIC_SDO_MAGIC _MAKE_AUTHENTIC_MAGIC('A', 'W', 'S', 'D') +#define AUTHENTIC_SDO_MAGIC_UPDATE _MAKE_AUTHENTIC_MAGIC('A', 'W', 'U', 'D') +#define AUTHENTIC_SDO_MAGIC_UPDATE_RSA _MAKE_AUTHENTIC_MAGIC('A', 'W', 'U', 'R') + +#define AUTHENTIC_OBJECT_REF_FLAG_LOCAL 0x80 + +#define AUTHENTIC_MECH_CREDENTIAL_PIN 0x00 +#define AUTHENTIC_MECH_CREDENTIAL_BIO 0x01 +#define AUTHENTIC_MECH_CREDENTIAL_DES 0x02 +#define AUTHENTIC_MECH_CREDENTIAL_2DES 0x03 +#define AUTHENTIC_MECH_CREDENTIAL_3DES 0x04 +#define AUTHENTIC_MECH_CREDENTIAL_AES128 0x05 +#define AUTHENTIC_MECH_CREDENTIAL_AES192 0x06 +#define AUTHENTIC_MECH_CREDENTIAL_AES256 0x07 + +#define AUTHENTIC_MECH_CRYPTO_DES 0x02 +#define AUTHENTIC_MECH_CRYPTO_2DES 0x03 +#define AUTHENTIC_MECH_CRYPTO_3DES 0x04 +#define AUTHENTIC_MECH_CRYPTO_AES128 0x05 +#define AUTHENTIC_MECH_CRYPTO_AES192 0x06 +#define AUTHENTIC_MECH_CRYPTO_AES256 0x07 +#define AUTHENTIC_MECH_CRYPTO_RSA1024 0x08 +#define AUTHENTIC_MECH_CRYPTO_RSA1280 0x09 +#define AUTHENTIC_MECH_CRYPTO_RSA1536 0x0A +#define AUTHENTIC_MECH_CRYPTO_RSA1792 0x0B +#define AUTHENTIC_MECH_CRYPTO_RSA2048 0x0C + +#define AUTHENTIC_TAG_DOCP 0xA1 +#define AUTHENTIC_TAG_DOCP_MECH 0x80 +#define AUTHENTIC_TAG_DOCP_ID 0x83 +#define AUTHENTIC_TAG_DOCP_ACLS 0x86 +#define AUTHENTIC_TAG_DOCP_SCP 0x87 +#define AUTHENTIC_TAG_DOCP_USAGE_COUNTER 0x90 + +#define AUTHENTIC_TAG_RSA 0xA5 + +#define AUTHENTIC_TAG_RSA_PRIVATE 0x7F48 +#define AUTHENTIC_TAG_RSA_PRIVATE_P 0x92 +#define AUTHENTIC_TAG_RSA_PRIVATE_Q 0x93 +#define AUTHENTIC_TAG_RSA_PRIVATE_PQ 0x94 +#define AUTHENTIC_TAG_RSA_PRIVATE_DP1 0x95 +#define AUTHENTIC_TAG_RSA_PRIVATE_DQ1 0x96 + +#define AUTHENTIC_TAG_RSA_PUBLIC 0x7F49 +#define AUTHENTIC_TAG_RSA_PUBLIC_MODULUS 0x81 +#define AUTHENTIC_TAG_RSA_PUBLIC_EXPONENT 0x82 + +#define AUTHENTIC_TAG_RSA_GENERATE_DATA 0xAC + +#define AUTHENTIC_TAG_CREDENTIAL 0x5F00 +#define AUTHENTIC_TAG_CREDENTIAL_TRYLIMIT 0x91 +#define AUTHENTIC_TAG_CREDENTIAL_PINPOLICY 0xA1 +#define AUTHENTIC_TAG_CREDENTIAL_PINPOLICY_MAXLENGTH 0x83 +#define AUTHENTIC_TAG_CREDENTIAL_PINPOLICY_MINLENGTH 0x84 +#define AUTHENTIC_TAG_CREDENTIAL_PINPOLICY_COMPLEXITY 0x85 + +#define AUTHENTIC_ALGORITHM_RSA_PKCS1 0x11 +#define AUTHENTIC_ALGORITHM_RSA_X509 0x12 +#define AUTHENTIC_ALGORITHM_RSA_OAEP 0x13 +#define AUTHENTIC_ALGORITHM_RSA_ISO9796 0x14 + +#define AUTHENTIC_TAG_CRT_AT 0xA4 +#define AUTHENTIC_TAG_CRT_HT 0xAA +#define AUTHENTIC_TAG_CRT_CCT 0xB4 +#define AUTHENTIC_TAG_CRT_DST 0xB6 +#define AUTHENTIC_TAG_CRT_CT 0xB8 + +#define AUTHENTIC_ACL_NUM_PIN_VERIFY 0 +#define AUTHENTIC_ACL_NUM_PIN_RESET 1 +#define AUTHENTIC_ACL_NUM_PIN_CHANGE 2 +#define AUTHENTIC_ACL_NUM_PIN_MODIFY 3 +#define AUTHENTIC_ACL_NUM_PIN_DELETE 4 + +/* SM related macros */ +#define AUTHENTIC_AC_SM_MASK 0x60 + +#define AUTHENTIC_GP_SM_LEVEL_MASK 0x6000 +#define AUTHENTIC_GP_SM_LEVEL_PLAIN 0x2000 +#define AUTHENTIC_GP_SM_LEVEL_MAC 0x4000 +#define AUTHENTIC_GP_SM_LEVEL_ENC_MAC 0x6000 + +struct sc_authentic_tlv { + unsigned tag; + size_t size; + unsigned char *value; +}; + +struct sc_authentic_sdo_docp { + unsigned char mech, id, security_parameter; + unsigned char velocity_limit, try_limit; + + unsigned char acl_data[16]; + size_t acl_data_len; + + unsigned char usage_counter[2]; +}; + +struct sc_authentic_sdo { + unsigned char sdo_class; + unsigned char sdo_ref; + + unsigned int usage; + + struct sc_authentic_sdo_docp docp; + + union { + struct sc_pkcs15_prkey *prvkey; + } data; + + struct sc_file *file; + + unsigned magic; +}; + +#define SC_MAX_AUTHENTIC_CPLC 45 +struct sc_authentic_cplc { + unsigned char ic_data[4]; + unsigned char ic_serial[4]; + unsigned char ic_batch[4]; + unsigned char aid_last_bytes[2]; + unsigned char keyset_version; + unsigned char starting_index; + + unsigned char value[SC_MAX_AUTHENTIC_CPLC]; + size_t len; +}; + +#endif diff --git a/src/libopensc/card-authentic.c b/src/libopensc/card-authentic.c new file mode 100644 index 0000000000..4dcc4553e1 --- /dev/null +++ b/src/libopensc/card-authentic.c @@ -0,0 +1,2413 @@ +/* + * card-authentic.c: Support for the Oberthur smart cards + * with PKI applet AuthentIC v3.1 + * + * Copyright (C) 2010 Viktor Tarasov + * OpenTrust + * + * 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 +#include + +#include "internal.h" +#include "asn1.h" +#include "cardctl.h" +#include "opensc.h" +#include "pkcs15.h" +#include "iso7816.h" +/* #include "hash-strings.h" */ +#include "authentic.h" + +#ifndef ENABLE_OPENSSL +#error "Need OpenSSL" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AUTHENTIC_CARD_DEFAULT_FLAGS (SC_CARD_FLAG_RNG \ + | SC_ALGORITHM_ONBOARD_KEY_GEN \ + | SC_ALGORITHM_RSA_PAD_ISO9796 \ + | SC_ALGORITHM_RSA_PAD_PKCS1 \ + | SC_ALGORITHM_RSA_HASH_NONE \ + | SC_ALGORITHM_RSA_HASH_SHA1 \ + | SC_ALGORITHM_RSA_HASH_SHA256) + +#define AUTHENTIC_READ_BINARY_LENGTH_MAX 0xE7 + +/* generic iso 7816 operations table */ +static const struct sc_card_operations *iso_ops = NULL; + +/* our operations table with overrides */ +static struct sc_card_operations authentic_ops; + +static struct sc_card_driver authentic_drv = { + "Oberthur AuthentIC v3.1", "authentic", &authentic_ops, + NULL, 0, NULL +}; + +/* + * FIXME: use dynamic allocation for the PIN data to reduce memory usage + * actually size of 'authentic_private_data' 140kb + */ +struct authentic_private_data { + struct sc_pin_cmd_data pins[8]; + unsigned char pins_sha1[8][SHA_DIGEST_LENGTH]; + + struct sc_authentic_cplc cplc; +}; + +static struct sc_atr_table authentic_known_atrs[] = { + { "3B:DD:18:00:81:31:FE:45:80:F9:A0:00:00:00:77:01:00:70:0A:90:00:8B", NULL, + "Oberthur AuthentIC 3.2.2", SC_CARD_TYPE_OBERTHUR_AUTHENTIC_3_2, 0, NULL }, + { NULL, NULL, NULL, 0, 0, NULL } +}; + +unsigned char aid_AuthentIC_3_2[] = { + 0xA0,0x00,0x00,0x00,0x77,0x01,0x00,0x70,0x0A,0x10,0x00,0xF1,0x00,0x00,0x01,0x00 +}; + +static int authentic_select_file(struct sc_card *card, const struct sc_path *path, struct sc_file **file_out); +static int authentic_process_fci(struct sc_card *card, struct sc_file *file, const unsigned char *buf, size_t buflen); +static int authentic_get_serialnr(struct sc_card *card, struct sc_serial_number *serial); +static int authentic_pin_get_policy (struct sc_card *card, struct sc_pin_cmd_data *data); +static int authentic_pin_is_verified(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd, int *tries_left); +static int authentic_select_mf(struct sc_card *card, struct sc_file **file_out); +static int authentic_card_ctl(struct sc_card *card, unsigned long cmd, void *ptr); +static void authentic_debug_select_file(struct sc_card *card, const struct sc_path *path); + +static int +authentic_update_blob(struct sc_context *ctx, unsigned tag, unsigned char *data, size_t data_len, + unsigned char **blob, size_t *blob_size) +{ + unsigned char *pp = NULL; + int offs = 0, sz; + + if (data_len == 0) + return SC_SUCCESS; + + sz = data_len + 2; + + if (tag > 0xFF) + sz++; + + if (data_len > 0x7F && data_len < 0x100) + sz++; + else if (data_len >= 0x100) + sz += 2; + + pp = realloc(*blob, *blob_size + sz); + if (!pp) + LOGN_FUNC_RETURN(ctx, SC_ERROR_MEMORY_FAILURE); + + if (tag > 0xFF) + *(pp + *blob_size + offs++) = (tag >> 8) & 0xFF; + *(pp + *blob_size + offs++) = tag & 0xFF; + + if (data_len >= 0x100) { + *(pp + *blob_size + offs++) = 0x82; + *(pp + *blob_size + offs++) = (data_len >> 8) & 0xFF; + } + else if (data_len > 0x7F) { + *(pp + *blob_size + offs++) = 0x81; + } + *(pp + *blob_size + offs++) = data_len & 0xFF; + + memcpy(pp + *blob_size + offs, data, data_len); + + *blob_size += sz; + *blob = pp; + + return SC_SUCCESS; +} + + +static int +authentic_parse_size(unsigned char *in, size_t *out) +{ + if (!in || !out) + return SC_ERROR_INVALID_ARGUMENTS; + + if (*in < 0x80) { + *out = *in; + return 1; + } + else if (*in == 0x81) { + *out = *(in + 1); + return 2; + } + else if (*in == 0x82) { + *out = *(in + 1) * 0x100 + *(in + 2); + return 3; + } + + return SC_ERROR_INVALID_DATA; +} + + +static int +authentic_get_tagged_data(struct sc_context *ctx, unsigned char *in, size_t in_len, + unsigned in_tag, unsigned char **out, size_t *out_len) +{ + size_t size_len, tag_len, offs, size; + unsigned tag; + + if (!out || !out_len) + LOGN_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + + for (offs = 0; offs < in_len; ) { + if ((*(in + offs) == 0x7F) || (*(in + offs) == 0x5F)) { + tag = *(in + offs) * 0x100 + *(in + offs + 1); + tag_len = 2; + } + else { + tag = *(in + offs); + tag_len = 1; + } + + size_len = authentic_parse_size(in + offs + tag_len, &size); + LOGN_TEST_RET(ctx, size_len, "parse error: invalid size data"); + + if (tag == in_tag) { + *out = in + offs + tag_len + size_len; + *out_len = size; + + return SC_SUCCESS; + } + + offs += tag_len + size_len + size; + } + + return SC_ERROR_ASN1_OBJECT_NOT_FOUND; +} + + +static int +authentic_decode_pubkey_rsa(struct sc_context *ctx, unsigned char *blob, size_t blob_len, + struct sc_pkcs15_prkey **out_key) +{ + struct sc_pkcs15_prkey_rsa *key; + unsigned char *data; + size_t data_len; + int rv; + + LOGN_FUNC_CALLED(ctx); + + if (!out_key) + LOGN_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + + if (!(*out_key)) { + *out_key = calloc(1, sizeof(struct sc_pkcs15_prkey)); + + if (!(*out_key)) + LOGN_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot callocate pkcs15 private key"); + + (*out_key)->algorithm = SC_ALGORITHM_RSA; + } + else if (*out_key && (*out_key)->algorithm != SC_ALGORITHM_RSA) { + LOGN_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); + } + + key = &(*out_key)->u.rsa; + + rv = authentic_get_tagged_data(ctx, blob, blob_len, AUTHENTIC_TAG_RSA_PUBLIC, &data, &data_len); + LOGN_TEST_RET(ctx, rv, "cannot get public key SDO data"); + + blob = data; + blob_len = data_len; + + /* Get RSA public modulus */ + rv = authentic_get_tagged_data(ctx, blob, blob_len, AUTHENTIC_TAG_RSA_PUBLIC_MODULUS, &data, &data_len); + LOGN_TEST_RET(ctx, rv, "cannot get public key SDO data"); + + if (key->modulus.data) + free(key->modulus.data); + key->modulus.data = calloc(1, data_len); + if (!key->modulus.data) + LOGN_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot callocate modulus BN"); + memcpy(key->modulus.data, data, data_len); + key->modulus.len = data_len; + + /* Get RSA public exponent */ + rv = authentic_get_tagged_data(ctx, blob, blob_len, AUTHENTIC_TAG_RSA_PUBLIC_EXPONENT, &data, &data_len); + LOGN_TEST_RET(ctx, rv, "cannot get public key SDO data"); + + if (key->exponent.data) + free(key->exponent.data); + key->exponent.data = calloc(1, data_len); + if (!key->exponent.data) + LOGN_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot callocate modulus BN"); + memcpy(key->exponent.data, data, data_len); + key->exponent.len = data_len; + + LOGN_FUNC_RETURN(ctx, rv); +} + + +static int +authentic_parse_credential_data(struct sc_context *ctx, struct sc_pin_cmd_data *pin_cmd, + unsigned char *blob, size_t blob_len) +{ + unsigned char *data; + size_t data_len; + int rv, ii; + unsigned tag = AUTHENTIC_TAG_CREDENTIAL | pin_cmd->pin_reference; + + rv = authentic_get_tagged_data(ctx, blob, blob_len, tag, &blob, &blob_len); + LOGN_TEST_RET(ctx, rv, "cannot get credential data"); + + rv = authentic_get_tagged_data(ctx, blob, blob_len, AUTHENTIC_TAG_CREDENTIAL_TRYLIMIT, &data, &data_len); + LOGN_TEST_RET(ctx, rv, "cannot get try limit"); + pin_cmd->pin1.max_tries = *data; + + rv = authentic_get_tagged_data(ctx, blob, blob_len, AUTHENTIC_TAG_DOCP_MECH, &data, &data_len); + LOGN_TEST_RET(ctx, rv, "cannot get PIN type"); + if (*data == 0) + pin_cmd->pin_type = SC_AC_CHV; + else if (*data >= 2 && *data <= 7) + pin_cmd->pin_type = SC_AC_AUT; + else + LOGN_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "unsupported Credential type"); + + rv = authentic_get_tagged_data(ctx, blob, blob_len, AUTHENTIC_TAG_DOCP_ACLS, &data, &data_len); + LOGN_TEST_RET(ctx, rv, "failed to get ACLs"); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "data_len:%i", data_len); + if (data_len == 10) { + for (ii=0; ii<5; ii++) { + unsigned char acl = *(data + ii*2); + unsigned char cred_id = *(data + ii*2 + 1); + unsigned sc = acl * 0x100 + cred_id; + + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "%i: SC:%X", ii, sc); + if (!sc) + continue; + + /* pin_cmd->pin1.acls[ii].raw_value = sc; */ + + if (acl & AUTHENTIC_AC_SM_MASK) { + pin_cmd->pin1.acls[ii].method = SC_AC_SCB; + pin_cmd->pin1.acls[ii].key_ref = sc; + } + else if (acl!=0xFF && cred_id) { + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "%i: ACL(method:SC_AC_CHV,id:%i)", ii, cred_id); + pin_cmd->pin1.acls[ii].method = SC_AC_CHV; + pin_cmd->pin1.acls[ii].key_ref = cred_id; + } + else { + pin_cmd->pin1.acls[ii].method = SC_AC_NEVER; + pin_cmd->pin1.acls[ii].key_ref = 0; + } + } + } + + rv = authentic_get_tagged_data(ctx, blob, blob_len, AUTHENTIC_TAG_CREDENTIAL_PINPOLICY, &data, &data_len); + if (!rv) { + blob = data; + blob_len = data_len; + + rv = authentic_get_tagged_data(ctx, blob, blob_len, AUTHENTIC_TAG_CREDENTIAL_PINPOLICY_MAXLENGTH, &data, &data_len); + LOGN_TEST_RET(ctx, rv, "failed to get PIN max.length value"); + pin_cmd->pin1.max_length = *data; + + rv = authentic_get_tagged_data(ctx, blob, blob_len, AUTHENTIC_TAG_CREDENTIAL_PINPOLICY_MINLENGTH, &data, &data_len); + LOGN_TEST_RET(ctx, rv, "failed to get PIN min.length value"); + pin_cmd->pin1.min_length = *data; + } + + return SC_SUCCESS; +} + + +static int +authentic_get_cplc(struct sc_card *card) +{ + struct authentic_private_data *prv_data = (struct authentic_private_data *) card->drv_data; + struct sc_apdu apdu; + int rv, ii; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xCA, 0x9F, 0x7F); + for (ii=0;ii<2;ii++) { + apdu.le = 0x2D; + apdu.resplen = sizeof(prv_data->cplc.value); + apdu.resp = prv_data->cplc.value; + + rv = sc_transmit_apdu(card, &apdu); + LOGN_TEST_RET(card->ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + if (rv != SC_ERROR_CLASS_NOT_SUPPORTED) + break; + + apdu.cla = 0x80; + } + LOGN_TEST_RET(card->ctx, rv, "'GET CPLC' error"); + + prv_data->cplc.len = 0x2D; + return SC_SUCCESS; +} + + +static int +authentic_select_aid(struct sc_card *card, unsigned char *aid, size_t aid_len, + unsigned char *out, size_t *out_len) +{ + struct sc_apdu apdu; + unsigned char apdu_resp[SC_MAX_APDU_BUFFER_SIZE]; + int rv; + + /* Select Card Manager (to deselect previously selected application) */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0x04, 0x00); + apdu.lc = aid_len; + apdu.data = aid; + apdu.datalen = aid_len; + apdu.resplen = sizeof(apdu_resp); + apdu.resp = apdu_resp; + + rv = sc_transmit_apdu(card, &apdu); + LOGN_TEST_RET(card->ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOGN_TEST_RET(card->ctx, rv, "Cannot select AID"); + + if (out && out_len) { + if (*out_len < apdu.resplen) + LOGN_TEST_RET(card->ctx, SC_ERROR_BUFFER_TOO_SMALL, "Cannot select AID"); + memcpy(out, apdu.resp, apdu.resplen); + } + + return SC_SUCCESS; +} + + +static int +authentic_match_card(struct sc_card *card) +{ + struct sc_context *ctx = card->ctx; + int i; + + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "try to match card with ATR %s", sc_dump_hex(card->atr, card->atr_len)); + i = _sc_match_atr(card, authentic_known_atrs, &card->type); + if (i < 0) { + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "card not matched"); + return 0; + } + + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "'%s' card matched", authentic_known_atrs[i].name); + return 1; +} + + +static int +authentic_init_oberthur_authentic_3_2(struct sc_card *card) +{ + struct sc_context *ctx = card->ctx; + unsigned char resp[0x100]; + size_t resp_len; + unsigned int flags; + int rv = 0; + + LOGN_FUNC_CALLED(ctx); + + flags = AUTHENTIC_CARD_DEFAULT_FLAGS; + + _sc_card_add_rsa_alg(card, 1024, flags, 0x10001); + _sc_card_add_rsa_alg(card, 2048, flags, 0x10001); + + card->caps = SC_CARD_CAP_RNG; + card->caps |= SC_CARD_CAP_RSA_2048; + card->caps |= SC_CARD_CAP_APDU_EXT; + card->caps |= SC_CARD_CAP_USE_FCI_AC; + + resp_len = sizeof(resp); + rv = authentic_select_aid(card, aid_AuthentIC_3_2, sizeof(aid_AuthentIC_3_2), NULL, NULL); + LOGN_TEST_RET(ctx, rv, "AuthentIC application select error"); + + rv = authentic_select_mf(card, NULL); + LOGN_TEST_RET(ctx, rv, "MF selection error"); + + LOGN_FUNC_RETURN(ctx, rv); +} + + +static int +authentic_init(struct sc_card *card) +{ + struct sc_context *ctx = card->ctx; + int ii, rv = SC_ERROR_NO_CARD_SUPPORT; + + LOGN_FUNC_CALLED(ctx); + for(ii=0;authentic_known_atrs[ii].atr;ii++) { + if (card->type == authentic_known_atrs[ii].type) { + card->name = authentic_known_atrs[ii].name; + card->flags = authentic_known_atrs[ii].flags; + break; + } + } + + if (!authentic_known_atrs[ii].atr) + LOGN_FUNC_RETURN(ctx, SC_ERROR_NO_CARD_SUPPORT); + + card->cla = 0x00; + card->drv_data = (struct authentic_private_data *) calloc(sizeof(struct authentic_private_data), 1); + if (!card->drv_data) + LOGN_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); +#if 0 + card->flags |= SC_CARD_FLAG_ZERO_LENGTH_PIN_ACCEPTED; +#endif + + if (card->type == SC_CARD_TYPE_OBERTHUR_AUTHENTIC_3_2) + rv = authentic_init_oberthur_authentic_3_2(card); + + if (!rv) + rv = authentic_get_serialnr(card, NULL); + + LOGN_FUNC_RETURN(ctx, rv); +} + + +#if 0 +static int +authentic_read_binary_long(struct sc_card *card, unsigned int offs, + unsigned char *buf, size_t count, unsigned long flags) +{ + struct sc_context *ctx = card->ctx; + const struct sc_acl_entry *entry = NULL; + int rv; + + LOGN_FUNC_CALLED(ctx); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "authentic_sc_read_binary(card:%p) offs %i; count %i\n", card, offs, count); + if (offs > 0x7fff) + LOGN_TEST_RET(ctx, SC_ERROR_OFFSET_TOO_LARGE, "Invalid arguments"); + + if (count == 0) + LOGN_FUNC_RETURN(ctx, SC_SUCCESS); + + authentic_debug_select_file(card, NULL); + + if (card->cache.valid && card->cache.current_ef) { + entry = sc_file_get_acl_entry(card->cache.current_ef, SC_AC_OP_READ); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "READ method/reference %X/%X\n", entry->method, entry->key_ref); + if (entry->method == SC_AC_SCB) { + rv = sm_read_binary(card, entry->key_ref, offs, count ? count : card->cache.current_ef->size, buf, count); + LOGN_FUNC_RETURN(ctx, rv); + } + } + + LOGN_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static int +authentic_update_binary_long(struct sc_card *card, unsigned int offs, + const unsigned char *buff, size_t count, unsigned long flags) +{ + struct sc_context *ctx = card->ctx; + const struct sc_acl_entry *entry = NULL; + int rv; + + LOGN_FUNC_CALLED(ctx); + if (count == 0) + return SC_SUCCESS; + + authentic_debug_select_file(card, NULL); + + if (card->cache.valid && card->cache.current_ef) { + entry = sc_file_get_acl_entry(card->cache.current_ef, SC_AC_OP_UPDATE); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "UPDATE method/reference %X/%X\n", entry->method, entry->key_ref); + if (entry->method == SC_AC_SCB) { + rv = sm_update_binary(card, entry->key_ref, offs, buff, count); + LOGN_FUNC_RETURN(ctx, rv); + } + } + + LOGN_FUNC_RETURN(ctx, SC_SUCCESS); +} +#endif + +static int +authentic_erase_binary(struct sc_card *card, unsigned int offs, size_t count, unsigned long flags) +{ + struct sc_context *ctx = card->ctx; + struct sc_apdu *apdus = NULL, *cur_apdu = NULL; + size_t sz, rest; + int rv; + unsigned char *buf_zero = NULL; + + LOGN_FUNC_CALLED(ctx); + if (!count) + LOGN_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "'ERASE BINARY' with ZERO count not supported"); + + if (card->cache.valid && card->cache.current_ef) + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "current_ef(type=%i) %s", card->cache.current_ef->path.type, + sc_print_path(&card->cache.current_ef->path)); + + buf_zero = calloc(1, count); + if (!buf_zero) + LOGN_TEST_RET(ctx, SC_ERROR_MEMORY_FAILURE, "cannot allocate buff 'zero'"); + + rv = sc_update_binary(card, offs, buf_zero, count, flags); + free(buf_zero); + LOGN_TEST_RET(ctx, rv, "'ERASE BINARY' failed"); + + LOGN_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static int +authentic_resize_file(struct sc_card *card, unsigned file_id, unsigned new_size) +{ + struct sc_context *ctx = card->ctx; + struct sc_apdu apdu; + unsigned char data[6] = { + 0x62, 0x04, 0x80, 0x02, 0xFF, 0xFF + }; + int rv; + + LOGN_FUNC_CALLED(ctx); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "try to set file size to %i bytes", new_size); + + data[4] = (new_size >> 8) & 0xFF; + data[5] = new_size & 0xFF; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xDB, (file_id >> 8) & 0xFF, file_id & 0xFF); + apdu.data = data; + apdu.datalen = sizeof(data); + apdu.lc = sizeof(data); + + rv = sc_transmit_apdu(card, &apdu); + LOGN_TEST_RET(ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOGN_TEST_RET(ctx, rv, "resize file failed"); + + if (card->cache.valid && card->cache.current_ef && card->cache.current_ef->id == file_id) + card->cache.current_ef->size = new_size; + + LOGN_FUNC_RETURN(ctx, rv); +} + + +static int +authentic_set_current_files(struct sc_card *card, struct sc_path *path, + unsigned char *resp, size_t resplen, struct sc_file **file_out) +{ + struct sc_context *ctx = card->ctx; + struct sc_file *file = NULL; + int rv; + + LOGN_FUNC_CALLED(ctx); + if (resplen) { + switch (resp[0]) { + case 0x62: + case 0x6F: + file = sc_file_new(); + if (file == NULL) + LOGN_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + if (path) + file->path = *path; + + rv = authentic_process_fci(card, file, resp, resplen); + LOGN_TEST_RET(ctx, rv, "cannot set 'current file': FCI process error"); + + break; + default: + LOGN_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); + } + + if (file->type == SC_FILE_TYPE_DF) { + struct sc_path cur_df_path; + + memset(&cur_df_path, 0, sizeof(cur_df_path)); + if (card->cache.valid && card->cache.current_df) { + cur_df_path = card->cache.current_df->path; + sc_file_free(card->cache.current_df); + } + card->cache.current_df = NULL; + sc_file_dup(&card->cache.current_df, file); + + if (cur_df_path.len) { + memcpy(card->cache.current_df->path.value + cur_df_path.len, + card->cache.current_df->path.value, + card->cache.current_df->path.len); + memcpy(card->cache.current_df->path.value, cur_df_path.value, cur_df_path.len); + card->cache.current_df->path.len += cur_df_path.len; + } + + if (card->cache.current_ef) { + sc_file_free(card->cache.current_ef); + card->cache.current_ef = NULL; + } + + card->cache.valid = 1; + } + else { + if (card->cache.current_ef) + sc_file_free(card->cache.current_ef); + card->cache.current_ef = NULL; + sc_file_dup(&card->cache.current_ef, file); + } + + if (file_out) + *file_out = file; + else + sc_file_free(file); + } + + LOGN_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static int +authentic_select_mf(struct sc_card *card, struct sc_file **file_out) +{ + struct sc_context *ctx = card->ctx; + struct sc_path mfpath; + int rv; + + struct sc_apdu apdu; + unsigned char rbuf[SC_MAX_APDU_BUFFER_SIZE]; + + LOGN_FUNC_CALLED(ctx); + + memset(&mfpath, 0, sizeof(struct sc_path)); + sc_format_path("3F00", &mfpath); + mfpath.type = SC_PATH_TYPE_PATH; + + if (card->cache.valid == 1 + && card->cache.current_df + && card->cache.current_df->path.len == 2 + && !memcmp(card->cache.current_df->path.value, "\x3F\x00", 2)) { + if (file_out) + sc_file_dup(file_out, card->cache.current_df); + + LOGN_FUNC_RETURN(ctx, SC_SUCCESS); + } + + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xA4, 0x00, 0x00); + + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + + rv = sc_transmit_apdu(card, &apdu); + LOGN_TEST_RET(ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOGN_TEST_RET(ctx, rv, "authentic_select_file() check SW failed"); + + if (card->cache.valid == 1) { + if (card->cache.current_df) + sc_file_free(card->cache.current_df); + card->cache.current_df = NULL; + + if (card->cache.current_ef) + sc_file_free(card->cache.current_ef); + card->cache.current_ef = NULL; + } + + rv = authentic_set_current_files(card, &mfpath, apdu.resp, apdu.resplen, file_out); + LOGN_TEST_RET(ctx, rv, "authentic_select_file() cannot set 'current_file'"); + + LOGN_FUNC_RETURN(ctx, rv); +} + + +static int +authentic_reduce_path(struct sc_card *card, struct sc_path *path) +{ + struct sc_context *ctx = card->ctx; + struct sc_path in_path, cur_path; + int offs; + + LOGN_FUNC_CALLED(ctx); + + if (path->len <= 2 || path->type == SC_PATH_TYPE_DF_NAME || !path) + LOGN_FUNC_RETURN(ctx, SC_SUCCESS); + + if (!card->cache.valid || !card->cache.current_df) + LOGN_FUNC_RETURN(ctx, 0); + + in_path = *path; + cur_path = card->cache.current_df->path; + + if (!memcmp(cur_path.value, "\x3F\x00", 2) && memcmp(in_path.value, "\x3F\x00", 2)) { + memcpy(in_path.value + 2, in_path.value, in_path.len); + memcpy(in_path.value, "\x3F\x00", 2); + in_path.len += 2; + } + + for (offs=0; offs < in_path.len && offs < cur_path.len; offs += 2) { + if (cur_path.value[offs] != in_path.value[offs]) + break; + if (cur_path.value[offs + 1] != in_path.value[offs + 1]) + break; + } + + memcpy(in_path.value, in_path.value + offs, sizeof(in_path.value) - offs); + in_path.len -= offs; + *path = in_path; + + LOGN_FUNC_RETURN(ctx, offs); +} + + +static void +authentic_debug_select_file(struct sc_card *card, const struct sc_path *path) +{ + struct sc_context *ctx = card->ctx; + struct sc_card_cache *cache = &card->cache; + + if (path) + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "try to select path(type:%i) %s", path->type, sc_print_path(path)); + + if (!cache->valid) + return; + + if (cache->current_df) + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "current_df(type=%i) %s", cache->current_df->path.type, sc_print_path(&cache->current_df->path)); + else + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "current_df empty"); + + if (cache->current_ef) + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "current_ef(type=%i) %s", cache->current_ef->path.type, sc_print_path(&cache->current_ef->path)); + else + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "current_ef empty"); +} + + +static int +authentic_is_selected(struct sc_card *card, const struct sc_path *path, struct sc_file **file_out) +{ + if (!path->len) { + if (file_out && card->cache.valid && card->cache.current_df) + sc_file_dup(file_out, card->cache.current_df); + return SC_SUCCESS; + } + else if (path->len == 2 && card->cache.valid && card->cache.current_ef) { + if (!memcmp(card->cache.current_ef->path.value, path->value, 2)) { + if (file_out) + sc_file_dup(file_out, card->cache.current_ef); + return SC_SUCCESS; + } + } + + return SC_ERROR_FILE_NOT_FOUND; +} + + +static int +authentic_select_file(struct sc_card *card, const struct sc_path *path, + struct sc_file **file_out) +{ + struct sc_context *ctx = card->ctx; + struct sc_apdu apdu; + struct sc_path lpath; + unsigned char rbuf[SC_MAX_APDU_BUFFER_SIZE]; + int pathlen, rv; + + LOGN_FUNC_CALLED(ctx); + authentic_debug_select_file(card, path); + + memcpy(&lpath, path, sizeof(struct sc_path)); + + rv = authentic_reduce_path(card, &lpath); + LOGN_TEST_RET(ctx, rv, "reduce path error"); + + if (lpath.len >= 2 && lpath.value[0] == 0x3F && lpath.value[1] == 0x00) { + rv = authentic_select_mf(card, file_out); + LOGN_TEST_RET(ctx, rv, "cannot select MF"); + + memcpy(&lpath.value[0], &lpath.value[2], lpath.len - 2); + lpath.len -= 2; + + if (!lpath.len) + LOGN_FUNC_RETURN(ctx, SC_SUCCESS); + } + + if (lpath.type == SC_PATH_TYPE_PATH && (lpath.len == 2)) + lpath.type = SC_PATH_TYPE_FILE_ID; + + rv = authentic_is_selected(card, &lpath, file_out); + if (!rv) + LOGN_FUNC_RETURN(ctx, SC_SUCCESS); + + pathlen = lpath.len; + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0x00, 0x00); + + if (card->type != SC_CARD_TYPE_OBERTHUR_AUTHENTIC_3_2) + LOGN_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Unsupported card"); + + if (lpath.type == SC_PATH_TYPE_FILE_ID) { + apdu.p1 = 0x00; + } + else if (lpath.type == SC_PATH_TYPE_PATH) { + apdu.p1 = 0x08; + } + else if (lpath.type == SC_PATH_TYPE_FROM_CURRENT) { + apdu.p1 = 0x09; + } + else if (lpath.type == SC_PATH_TYPE_DF_NAME) { + apdu.p1 = 4; + } + else if (lpath.type == SC_PATH_TYPE_PARENT) { + apdu.p1 = 0x03; + pathlen = 0; + apdu.cse = SC_APDU_CASE_2_SHORT; + } + else { + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Invalid PATH type: 0x%X", lpath.type); + LOGN_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "authentic_select_file() invalid PATH type"); + } + + apdu.lc = pathlen; + apdu.data = lpath.value; + apdu.datalen = pathlen; + + if (apdu.cse == SC_APDU_CASE_4_SHORT || apdu.cse == SC_APDU_CASE_2_SHORT) { + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + apdu.le = 256; + } + + rv = sc_transmit_apdu(card, &apdu); + LOGN_TEST_RET(ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOGN_TEST_RET(ctx, rv, "authentic_select_file() check SW failed"); + + rv = authentic_set_current_files(card, &lpath, apdu.resp, apdu.resplen, file_out); + LOGN_TEST_RET(ctx, rv, "authentic_select_file() cannot set 'current_file'"); + + LOGN_FUNC_RETURN(ctx, SC_SUCCESS); +} + +#if 0 +static int +authentic_apdus_allocate(struct sc_apdu **head, struct sc_apdu **new) +{ + struct sc_apdu *allocated_apdu = NULL, *tmp_apdu = NULL; + + if (!head) + return SC_ERROR_INVALID_ARGUMENTS; + + allocated_apdu = calloc(1, sizeof(struct sc_apdu)); + if (!allocated_apdu) + return SC_ERROR_MEMORY_FAILURE; + + if (*head == NULL) + *head = allocated_apdu; + + if (new) + *new = allocated_apdu; + + tmp_apdu = *head; + while(tmp_apdu->next) + tmp_apdu = tmp_apdu->next; + + tmp_apdu->next = allocated_apdu; + + return 0; +} + + +static void +authentic_apdus_free(struct sc_apdu *apdu) +{ + while(apdu) { + struct sc_apdu *tmp_apdu = apdu->next; + free(apdu); + apdu = tmp_apdu; + } +} + + +static int +authentic_read_binary(struct sc_card *card, unsigned int idx, + unsigned char *buf, size_t count, unsigned long flags) +{ + struct sc_context *ctx = card->ctx; + struct sc_apdu *apdus = NULL, *cur_apdu = NULL; + size_t sz, rest; + int rv; + + LOGN_FUNC_CALLED(ctx); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "offs:%i,count:%i,max_recv_size:%i", idx, count, card->max_recv_size); + + rest = count; + while(rest) { + if (authentic_apdus_allocate(&apdus, &cur_apdu)) + LOGN_TEST_RET(ctx, SC_ERROR_MEMORY_FAILURE, "cannot allocate APDU"); + + sz = rest > 256 ? 256 : rest; + sc_format_apdu(card, cur_apdu, SC_APDU_CASE_2_SHORT, 0xB0, (idx >> 8) & 0x7F, idx & 0xFF); + cur_apdu->le = sz; + cur_apdu->resplen = count; + cur_apdu->resp = buf; + + idx += sz; + rest -= sz; + } + + rv = sc_transmit_apdu(card, apdus); + if (!rv) + rv = sc_check_sw(card, apdus->sw1, apdus->sw2); + if (!rv) + count = apdus->resplen; + + authentic_apdus_free(apdus); + + LOGN_TEST_RET(ctx, rv, "authentic_read_binary() failed"); + LOGN_FUNC_RETURN(ctx, count); +} + + +static int +authentic_write_binary(struct sc_card *card, unsigned int idx, + const unsigned char *buf, size_t count, unsigned long flags) +{ + struct sc_context *ctx = card->ctx; + struct sc_apdu *apdus = NULL, *cur_apdu = NULL; + size_t sz, rest; + int rv; + + LOGN_FUNC_CALLED(ctx); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "offs:%i,count:%i,max_send_size:%i", idx, count, card->max_send_size); + + rest = count; + while(rest) { + if (authentic_apdus_allocate(&apdus, &cur_apdu)) + LOGN_TEST_RET(ctx, SC_ERROR_MEMORY_FAILURE, "cannot allocate APDU"); + + sz = rest > 255 ? 255 : rest; + sc_format_apdu(card, cur_apdu, SC_APDU_CASE_3_SHORT, 0xD0, (idx >> 8) & 0x7F, idx & 0xFF); + cur_apdu->lc = sz; + cur_apdu->datalen = sz; + cur_apdu->data = buf + count - rest; + + idx += sz; + rest -= sz; + } + + rv = sc_transmit_apdu(card, apdus); + if (!rv) + rv = sc_check_sw(card, apdus->sw1, apdus->sw2); + + authentic_apdus_free(apdus); + + LOGN_TEST_RET(ctx, rv, "authentic_write_binary() failed"); + LOGN_FUNC_RETURN(ctx, count); +} + + +static int +authentic_update_binary(struct sc_card *card, unsigned int idx, + const unsigned char *buf, size_t count, unsigned long flags) +{ + struct sc_context *ctx = card->ctx; + struct sc_apdu *apdus = NULL, *cur_apdu = NULL; + size_t sz, rest; + int rv; + + LOGN_FUNC_CALLED(ctx); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "offs:%i,count:%i,max_send_size:%i", idx, count, card->max_send_size); + + rest = count; + while(rest) { + if (authentic_apdus_allocate(&apdus, &cur_apdu)) + LOGN_TEST_RET(ctx, SC_ERROR_MEMORY_FAILURE, "cannot allocate APDU"); + + sz = rest > 255 ? 255 : rest; + sc_format_apdu(card, cur_apdu, SC_APDU_CASE_3_SHORT, 0xD6, (idx >> 8) & 0x7F, idx & 0xFF); + cur_apdu->lc = sz; + cur_apdu->datalen = sz; + cur_apdu->data = buf + count - rest; + + idx += sz; + rest -= sz; + } + + rv = sc_transmit_apdu(card, apdus); + if (!rv) + rv = sc_check_sw(card, apdus->sw1, apdus->sw2); + + authentic_apdus_free(apdus); + + LOGN_TEST_RET(ctx, rv, "authentic_write_binary() failed"); + LOGN_FUNC_RETURN(ctx, count); +} +#endif + +static int +authentic_process_fci(struct sc_card *card, struct sc_file *file, + const unsigned char *buf, size_t buflen) +{ + struct sc_context *ctx = card->ctx; + size_t taglen; + int rv, ii; + const unsigned char *acls = NULL, *tag = NULL; + unsigned char ops_DF[8] = { + SC_AC_OP_CREATE, SC_AC_OP_DELETE, SC_AC_OP_CRYPTO, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + unsigned char ops_EF[8] = { + SC_AC_OP_READ, SC_AC_OP_DELETE, SC_AC_OP_UPDATE, SC_AC_OP_RESIZE, 0xFF, 0xFF, 0xFF, 0xFF + }; + unsigned char acls_NEVER[8] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + + LOGN_FUNC_CALLED(ctx); + + tag = sc_asn1_find_tag(card->ctx, buf, buflen, 0x6F, &taglen); + if (tag != NULL) { + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, " FCP length %i\n", taglen); + buf = tag; + buflen = taglen; + } + + tag = sc_asn1_find_tag(card->ctx, buf, buflen, 0x62, &taglen); + if (tag != NULL) { + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, " FCP length %i\n", taglen); + buf = tag; + buflen = taglen; + } + + rv = iso_ops->process_fci(card, file, buf, buflen); + LOGN_TEST_RET(ctx, rv, "ISO parse FCI failed"); + + if (!file->sec_attr_len) { + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "ACLs not found in data(%i) %s", buflen, sc_dump_hex(buf, buflen)); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Path:%s; Type:%X; PathType:%X", sc_print_path(&file->path), file->type, file->path.type); + if (file->path.type == SC_PATH_TYPE_DF_NAME || file->type == SC_FILE_TYPE_DF) { + acls = acls_NEVER; + file->type = SC_FILE_TYPE_DF; + } + else { + LOGN_TEST_RET(ctx, SC_ERROR_OBJECT_NOT_FOUND, "ACLs tag missing"); + } + } + + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "ACL data(%i):%s", file->sec_attr_len, sc_dump_hex(file->sec_attr, file->sec_attr_len)); + for (ii = 0; ii < file->sec_attr_len / 2; ii++) { + unsigned char op = file->type == SC_FILE_TYPE_DF ? ops_DF[ii] : ops_EF[ii]; + unsigned char acl = *(file->sec_attr + ii*2); + unsigned char cred_id = *(file->sec_attr + ii*2 + 1); + unsigned sc = acl * 0x100 + cred_id; + + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "ACL(%i) op 0x%X, acl %X:%X", ii, op, acl, cred_id); + if (op == 0xFF) + ; + else if (!acl && !cred_id) + sc_file_add_acl_entry(file, op, SC_AC_NONE, 0); + else if (acl == 0xFF) + sc_file_add_acl_entry(file, op, SC_AC_NEVER, 0); + else if (acl & AUTHENTIC_AC_SM_MASK) + sc_file_add_acl_entry(file, op, SC_AC_SCB, sc); + else if (cred_id) + sc_file_add_acl_entry(file, op, SC_AC_CHV, cred_id); + else + sc_file_add_acl_entry(file, op, SC_AC_NEVER, 0); + } + + LOGN_FUNC_RETURN(ctx, 0); +} + + +static int +authentic_fcp_encode(struct sc_card *card, struct sc_file *file, unsigned char *out, size_t out_len) +{ + struct sc_context *ctx = card->ctx; + unsigned char buf[0x80]; + size_t ii, offs; + unsigned char ops_ef[4] = { SC_AC_OP_READ, SC_AC_OP_DELETE, SC_AC_OP_UPDATE, SC_AC_OP_RESIZE }; + unsigned char ops_df[3] = { SC_AC_OP_CREATE, SC_AC_OP_DELETE, SC_AC_OP_CRYPTO }; + unsigned char *ops = file->type == SC_FILE_TYPE_DF ? ops_df : ops_ef; + size_t ops_len = file->type == SC_FILE_TYPE_DF ? 3 : 4; + + LOGN_FUNC_CALLED(ctx); + + offs = 0; + buf[offs++] = ISO7816_TAG_FCP_SIZE; + buf[offs++] = 2; + buf[offs++] = (file->size >> 8) & 0xFF; + buf[offs++] = file->size & 0xFF; + + buf[offs++] = ISO7816_TAG_FCP_TYPE; + buf[offs++] = 1; + buf[offs++] = file->type == SC_FILE_TYPE_DF ? ISO7816_FILE_TYPE_DF : ISO7816_FILE_TYPE_TRANSPARENT_EF; + + buf[offs++] = ISO7816_TAG_FCP_ID; + buf[offs++] = 2; + buf[offs++] = (file->id >> 8) & 0xFF; + buf[offs++] = file->id & 0xFF; + + buf[offs++] = ISO7816_TAG_FCP_ACLS; + buf[offs++] = ops_len * 2; + for (ii=0; ii < ops_len; ii++) { + const struct sc_acl_entry *entry; + + entry = sc_file_get_acl_entry(file, ops[ii]); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "acl entry(method:%X,ref:%X)", entry->method, entry->key_ref); + + if (entry->method == SC_AC_NEVER) { + /* TODO: After development change for 0xFF */ + buf[offs++] = 0x00; + buf[offs++] = 0x00; + } + else if (entry->method == SC_AC_NONE) { + buf[offs++] = 0x00; + buf[offs++] = 0x00; + } + else if (entry->method == SC_AC_CHV) { + if (!(entry->key_ref & AUTHENTIC_V3_CREDENTIAL_ID_MASK) + || (entry->key_ref & ~AUTHENTIC_V3_CREDENTIAL_ID_MASK)) + LOGN_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Non supported Credential Reference"); + buf[offs++] = 0x00; + buf[offs++] = 0x01 << (entry->key_ref - 1); + } + else + LOGN_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Non supported AC method"); + } + + if (out) { + if (out_len < offs) + LOGN_TEST_RET(ctx, SC_ERROR_BUFFER_TOO_SMALL, "Buffer too small to encode FCP"); + memcpy(out, buf, offs); + } + + LOGN_FUNC_RETURN(ctx, offs); +} + + +static int +authentic_create_file(struct sc_card *card, struct sc_file *file) +{ + struct sc_context *ctx = card->ctx; + struct sc_apdu apdu; + unsigned char sbuf[0x100]; + size_t sbuf_len; + struct sc_path path; + int rv; + + LOGN_FUNC_CALLED(ctx); + + if (file->type != SC_FILE_TYPE_WORKING_EF) + LOGN_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Creation of the file with of this type is not supported"); + + authentic_debug_select_file(card, &file->path); + + sbuf_len = authentic_fcp_encode(card, file, sbuf + 2, sizeof(sbuf)-2); + LOGN_TEST_RET(ctx, sbuf_len, "FCP encode error"); + + sbuf[0] = ISO7816_TAG_FCP; + sbuf[1] = sbuf_len; + + if (card->cache.valid && card->cache.current_df) { + const struct sc_acl_entry *entry = sc_file_get_acl_entry(card->cache.current_df, SC_AC_OP_CREATE); + + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "CREATE method/reference %X/%X\n", entry->method, entry->key_ref); + if (entry->method == SC_AC_SCB) { +#if 0 + rv = sm_create_file(card, entry->key_ref, sbuf, sbuf_len + 2); + LOGN_FUNC_RETURN(ctx, rv); +#else + LOGN_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Not yet"); +#endif + } + } + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0, 0); + apdu.data = sbuf; + apdu.datalen = sbuf_len + 2; + apdu.lc = sbuf_len + 2; + + rv = sc_transmit_apdu(card, &apdu); + LOGN_TEST_RET(ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOGN_TEST_RET(ctx, rv, "authentic_create_file() create file error"); + + path = file->path; + memcpy(path.value, path.value + path.len - 2, 2); + path.len = 2; + rv = authentic_set_current_files(card, &path, sbuf, sbuf_len + 2, NULL); + LOGN_TEST_RET(ctx, rv, "authentic_select_file() cannot set 'current_file'"); + + LOGN_FUNC_RETURN(ctx, rv); +} + + +static int +authentic_delete_file(struct sc_card *card, const struct sc_path *path) +{ + struct sc_context *ctx = card->ctx; + struct sc_apdu apdu; + unsigned char p1; + int rv, ii; + + LOGN_FUNC_CALLED(ctx); + + if (!path) + LOGN_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + + for (ii=0, p1 = 0x02; ii<2; ii++, p1 = 0x01) { + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE4, p1, 0x00); + apdu.data = path->value + path->len - 2; + apdu.datalen = 2; + apdu.lc = 2; + + rv = sc_transmit_apdu(card, &apdu); + LOGN_TEST_RET(ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + if (rv != SC_ERROR_FILE_NOT_FOUND || p1 != 0x02) + break; + } + LOGN_TEST_RET(ctx, rv, "Delete file failed"); + + if (card->cache.valid && card->cache.current_ef) { + sc_file_free(card->cache.current_ef); + card->cache.current_ef = NULL; + } + + LOGN_FUNC_RETURN(ctx, rv); +} + + +static int +authentic_chv_verify_pinpad(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd, int *tries_left) +{ + struct sc_context *ctx = card->ctx; + unsigned char ffs[0x100]; + struct sc_pin_cmd_pin *pin1 = &pin_cmd->pin1; + int rv; + + LOGN_FUNC_CALLED(ctx); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Verify PIN(ref:%i) with pin-pad", pin_cmd->pin_reference); + + rv = authentic_pin_is_verified(card, pin_cmd, tries_left); + if (!rv) + LOGN_FUNC_RETURN(ctx, rv); + + if (!card->reader || !card->reader->ops || !card->reader->ops->perform_verify) { + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Reader not ready for PIN PAD"); + LOGN_FUNC_RETURN(ctx, SC_ERROR_READER); + } + + pin1->len = pin1->min_length; + pin1->max_length = 8; + + memset(ffs, pin1->pad_char, sizeof(ffs)); + pin1->data = ffs; + + pin_cmd->cmd = SC_PIN_CMD_VERIFY; + pin_cmd->flags |= SC_PIN_CMD_USE_PINPAD; + + rv = iso_ops->pin_cmd(card, pin_cmd, tries_left); + + LOGN_FUNC_RETURN(ctx, rv); +} + + +static int +authentic_chv_verify(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd, + int *tries_left) +{ + struct sc_context *ctx = card->ctx; + struct sc_apdu apdu; + struct sc_pin_cmd_pin *pin1 = &pin_cmd->pin1; + int rv; + + LOGN_FUNC_CALLED(ctx); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "CHV PIN reference %i, pin1(%p,len:%i)", pin_cmd->pin_reference, pin1->data, pin1->len); + + if (pin1->data && !pin1->len) { + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0, pin_cmd->pin_reference); + } + else if (pin1->data && pin1->len) { + unsigned char pin_buff[SC_MAX_APDU_BUFFER_SIZE]; + size_t pin_len; + + memcpy(pin_buff, pin1->data, pin1->len); + pin_len = pin1->len; + + if (pin1->pad_length && pin_cmd->flags & SC_PIN_CMD_NEED_PADDING) { + memset(pin_buff + pin1->len, pin1->pad_char, pin1->pad_length - pin1->len); + pin_len = pin1->pad_length; + } + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x20, 0, pin_cmd->pin_reference); + apdu.data = pin_buff; + apdu.datalen = pin_len; + apdu.lc = pin_len; + } + else if ((card->reader->capabilities & SC_READER_CAP_PIN_PAD) && !pin1->data && !pin1->len) { + rv = authentic_chv_verify_pinpad(card, pin_cmd, tries_left); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "authentic_chv_verify() authentic_chv_verify_pinpad returned %i", rv); + LOGN_FUNC_RETURN(ctx, rv); + } + else { + LOGN_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); + } + + rv = sc_transmit_apdu(card, &apdu); + LOGN_TEST_RET(ctx, rv, "APDU transmit failed"); + + if (apdu.sw1 == 0x63 && (apdu.sw2 & 0xF0) == 0xC0) { + pin1->tries_left = apdu.sw2 & 0x0F; + if (tries_left) + *tries_left = apdu.sw2 & 0x0F; + } + + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + + LOGN_FUNC_RETURN(ctx, rv); +} + + +static int +authentic_pin_is_verified(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd_data, + int *tries_left) +{ + struct sc_context *ctx = card->ctx; + struct sc_pin_cmd_data pin_cmd; + int rv; + + LOGN_FUNC_CALLED(ctx); + + if (pin_cmd_data->pin_type != SC_AC_CHV) + LOGN_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "PIN type is not supported for the verification"); + + pin_cmd = *pin_cmd_data; + pin_cmd.pin1.data = (unsigned char *)""; + pin_cmd.pin1.len = 0; + + rv = authentic_chv_verify(card, &pin_cmd, tries_left); + + LOGN_FUNC_RETURN(ctx, rv); +} + + +static int +authentic_pin_verify(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd) +{ + struct sc_context *ctx = card->ctx; + struct authentic_private_data *prv_data = (struct authentic_private_data *) card->drv_data; + unsigned char pin_sha1[SHA_DIGEST_LENGTH]; + int rv; + + LOGN_FUNC_CALLED(ctx); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "PIN(type:%X,reference:%X,data:%p,length:%i)", + pin_cmd->pin_type, pin_cmd->pin_reference, pin_cmd->pin1.data, pin_cmd->pin1.len); + + if (pin_cmd->pin1.data && !pin_cmd->pin1.len) { + pin_cmd->pin1.tries_left = -1; + rv = authentic_pin_is_verified(card, pin_cmd, &pin_cmd->pin1.tries_left); + LOGN_FUNC_RETURN(ctx, rv); + } + + if (pin_cmd->pin1.data) + SHA1(pin_cmd->pin1.data, pin_cmd->pin1.len, pin_sha1); + else + SHA1("", 0, pin_sha1); + + if (!memcmp(pin_sha1, prv_data->pins_sha1[pin_cmd->pin_reference], SHA_DIGEST_LENGTH)) { + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Already verified"); + LOGN_FUNC_RETURN(ctx, SC_SUCCESS); + } + + memset(prv_data->pins_sha1[pin_cmd->pin_reference], 0, sizeof(prv_data->pins_sha1[0])); + + rv = authentic_pin_get_policy(card, pin_cmd); + LOGN_TEST_RET(ctx, rv, "Get 'PIN policy' error"); + + if (pin_cmd->pin1.len > pin_cmd->pin1.max_length) + LOGN_TEST_RET(ctx, SC_ERROR_INVALID_PIN_LENGTH, "PIN policy check failed"); + + pin_cmd->pin1.tries_left = -1; + rv = authentic_chv_verify(card, pin_cmd, &pin_cmd->pin1.tries_left); + LOGN_TEST_RET(ctx, rv, "PIN CHV verification error"); + + memcpy(prv_data->pins_sha1[pin_cmd->pin_reference], pin_sha1, SHA_DIGEST_LENGTH); + LOGN_FUNC_RETURN(ctx, rv); +} + + +static int +authentic_pin_change_pinpad(struct sc_card *card, unsigned reference, int *tries_left) +{ + struct sc_context *ctx = card->ctx; + struct sc_pin_cmd_data pin_cmd; + unsigned char pin1_data[SC_MAX_APDU_BUFFER_SIZE], pin2_data[SC_MAX_APDU_BUFFER_SIZE]; + int rv; + + LOGN_FUNC_CALLED(ctx); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "CHV PINPAD PIN reference %i", reference); + + if (!card->reader || !card->reader->ops || !card->reader->ops->perform_verify) { + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Reader not ready for PIN PAD"); + LOGN_FUNC_RETURN(ctx, SC_ERROR_READER); + } + + memset(&pin_cmd, 0, sizeof(pin_cmd)); + pin_cmd.pin_type = SC_AC_CHV; + pin_cmd.pin_reference = reference; + pin_cmd.cmd = SC_PIN_CMD_CHANGE; + pin_cmd.flags |= SC_PIN_CMD_USE_PINPAD | SC_PIN_CMD_NEED_PADDING; + + rv = authentic_pin_get_policy(card, &pin_cmd); + LOGN_TEST_RET(ctx, rv, "Get 'PIN policy' error"); + + memset(pin1_data, pin_cmd.pin1.pad_char, sizeof(pin1_data)); + pin_cmd.pin1.data = pin1_data; + + pin_cmd.pin1.len = pin_cmd.pin1.min_length; + pin_cmd.pin1.max_length = 8; + + memcpy(&pin_cmd.pin2, &pin_cmd.pin1, sizeof(pin_cmd.pin1)); + memset(pin2_data, pin_cmd.pin2.pad_char, sizeof(pin2_data)); + pin_cmd.pin2.data = pin2_data; + + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "PIN1 lengths max/min/pad: %i/%i/%i", pin_cmd.pin1.max_length, pin_cmd.pin1.min_length, + pin_cmd.pin1.pad_length); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "PIN2 lengths max/min/pad: %i/%i/%i", pin_cmd.pin2.max_length, pin_cmd.pin2.min_length, + pin_cmd.pin2.pad_length); + + rv = iso_ops->pin_cmd(card, &pin_cmd, tries_left); + + LOGN_FUNC_RETURN(ctx, rv); +} + + +static int +authentic_pin_change(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) +{ + struct sc_context *ctx = card->ctx; + struct authentic_private_data *prv_data = (struct authentic_private_data *) card->drv_data; + struct sc_apdu apdu; + unsigned char pin_data[SC_MAX_APDU_BUFFER_SIZE]; + size_t offs; + int rv; + + rv = authentic_pin_get_policy(card, data); + LOGN_TEST_RET(ctx, rv, "Get 'PIN policy' error"); + + memset(prv_data->pins_sha1[data->pin_reference], 0, sizeof(prv_data->pins_sha1[0])); + + if (!data->pin1.data && !data->pin1.len && &data->pin2.data && !data->pin2.len) { + if (!(card->reader->capabilities & SC_READER_CAP_PIN_PAD)) + LOGN_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "PIN pad not supported"); + rv = authentic_pin_change_pinpad(card, data->pin_reference, tries_left); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "authentic_pin_cmd(SC_PIN_CMD_CHANGE) chv_change_pinpad returned %i", rv); + LOGN_FUNC_RETURN(ctx, rv); + } + + if (card->max_send_size && data->pin1.len + data->pin2.len > card->max_send_size) + LOGN_TEST_RET(ctx, SC_ERROR_INVALID_PIN_LENGTH, "APDU transmit failed"); + + memset(pin_data, data->pin1.pad_char, sizeof(pin_data)); + offs = 0; + if (data->pin1.data && data->pin1.len) { + memcpy(pin_data, data->pin1.data, data->pin1.len); + offs += data->pin1.pad_length; + } + if (data->pin2.data && data->pin2.len) + memcpy(pin_data + offs, data->pin2.data, data->pin2.len); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x24, offs ? 0x00 : 0x01, data->pin_reference); + apdu.data = pin_data; + apdu.datalen = offs + data->pin1.pad_length; + apdu.lc = offs + data->pin1.pad_length; + + rv = sc_transmit_apdu(card, &apdu); + LOGN_TEST_RET(ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + + LOGN_FUNC_RETURN(ctx, rv); +} + + +static int +authentic_chv_set_pinpad(struct sc_card *card, unsigned char reference) +{ + struct sc_context *ctx = card->ctx; + struct sc_pin_cmd_data pin_cmd; + unsigned char pin_data[0x100]; + int rv; + + LOGN_FUNC_CALLED(ctx); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Set CHV PINPAD PIN reference %i", reference); + + if (!card->reader || !card->reader->ops || !card->reader->ops->perform_verify) { + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Reader not ready for PIN PAD"); + LOGN_FUNC_RETURN(ctx, SC_ERROR_READER); + } + + memset(&pin_cmd, 0, sizeof(pin_cmd)); + pin_cmd.pin_type = SC_AC_CHV; + pin_cmd.pin_reference = reference; + pin_cmd.cmd = SC_PIN_CMD_UNBLOCK; + pin_cmd.flags |= SC_PIN_CMD_USE_PINPAD | SC_PIN_CMD_NEED_PADDING; + + rv = authentic_pin_get_policy(card, &pin_cmd); + LOGN_TEST_RET(ctx, rv, "Get 'PIN policy' error"); + + memset(pin_data, pin_cmd.pin1.pad_char, sizeof(pin_data)); + pin_cmd.pin1.data = pin_data; + + pin_cmd.pin1.len = pin_cmd.pin1.min_length; + pin_cmd.pin1.max_length = 8; + + memcpy(&pin_cmd.pin2, &pin_cmd.pin1, sizeof(pin_cmd.pin1)); + memset(&pin_cmd.pin1, 0, sizeof(pin_cmd.pin1)); + + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "PIN2 max/min/pad %i/%i/%i", + pin_cmd.pin2.max_length, pin_cmd.pin2.min_length, pin_cmd.pin2.pad_length); + rv = iso_ops->pin_cmd(card, &pin_cmd, NULL); + + LOGN_FUNC_RETURN(ctx, rv); +} + + +static int +authentic_pin_get_policy (struct sc_card *card, struct sc_pin_cmd_data *data) +{ + struct sc_context *ctx = card->ctx; + struct sc_apdu apdu; + unsigned char rbuf[0x100]; + int ii, rv; + + LOGN_FUNC_CALLED(ctx); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "get PIN(type:%X,ref:%X)", data->pin_type, data->pin_reference); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xCA, 0x5F, data->pin_reference); + for (ii=0;ii<2;ii++) { + apdu.le = sizeof(rbuf); + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + + rv = sc_transmit_apdu(card, &apdu); + LOGN_TEST_RET(ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + + if (rv != SC_ERROR_CLASS_NOT_SUPPORTED) + break; + + apdu.cla = 0x80; + } + LOGN_TEST_RET(ctx, rv, "'GET DATA' error"); + + rv = authentic_parse_credential_data(ctx, data, apdu.resp, apdu.resplen); + LOGN_TEST_RET(ctx, rv, "Cannot parse credential data"); + + data->pin1.encoding = SC_PIN_ENCODING_ASCII; + data->pin1.offset = 5; + data->pin1.pad_char = 0xFF; + data->pin1.pad_length = data->pin1.max_length; + + data->flags |= SC_PIN_CMD_NEED_PADDING; + + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "PIN policy: size max/min/pad %i/%i/%i, tries max/left %i/%i", + data->pin1.max_length, data->pin1.min_length, data->pin1.pad_length, + data->pin1.max_tries, data->pin1.tries_left); + + LOGN_FUNC_RETURN(ctx, rv); +} + + +static int +authentic_pin_reset(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) +{ + struct sc_context *ctx = card->ctx; + struct authentic_private_data *prv_data = (struct authentic_private_data *) card->drv_data; + struct sc_file *save_current = NULL; + struct sc_pin_cmd_data pin_cmd, puk_cmd; + struct sc_apdu apdu; + unsigned reference; + int rv, ii; + + LOGN_FUNC_CALLED(ctx); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "reset PIN (ref:%i,lengths %i/%i)", data->pin_reference, data->pin1.len, data->pin2.len); + + memset(prv_data->pins_sha1[data->pin_reference], 0, sizeof(prv_data->pins_sha1[0])); + + memset(&pin_cmd, 0, sizeof(pin_cmd)); + pin_cmd.pin_reference = data->pin_reference; + pin_cmd.pin_type = data->pin_type; + + rv = authentic_pin_get_policy(card, &pin_cmd); + LOGN_TEST_RET(ctx, rv, "Get 'PIN policy' error"); + + if (pin_cmd.pin1.acls[AUTHENTIC_ACL_NUM_PIN_RESET].method == SC_AC_CHV) { + for (ii=0;ii<8;ii++) { + unsigned char mask = 0x01 << ii; + if (pin_cmd.pin1.acls[AUTHENTIC_ACL_NUM_PIN_RESET].key_ref & mask) { + memset(&puk_cmd, 0, sizeof(puk_cmd)); + puk_cmd.pin_reference = ii + 1; + + rv = authentic_pin_get_policy(card, &puk_cmd); + LOGN_TEST_RET(ctx, rv, "Get 'PIN policy' error"); + + if (puk_cmd.pin_type == SC_AC_CHV) + break; + } + } + if (ii < 8) { + puk_cmd.pin1.data = data->pin1.data; + puk_cmd.pin1.len = data->pin1.len; + + rv = authentic_pin_verify(card, &puk_cmd); + + if (tries_left && rv == SC_ERROR_PIN_CODE_INCORRECT) + *tries_left = puk_cmd.pin1.tries_left; + + LOGN_TEST_RET(ctx, rv, "Cannot verify PUK"); + } + } + + reference = data->pin_reference; + if (data->pin2.len) { + unsigned char pin_data[SC_MAX_APDU_BUFFER_SIZE]; + + memset(pin_data, pin_cmd.pin1.pad_char, sizeof(pin_data)); + memcpy(pin_data, data->pin2.data, data->pin2.len); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x2C, 0x02, reference); + apdu.data = pin_data; + apdu.datalen = pin_cmd.pin1.pad_length; + apdu.lc = pin_cmd.pin1.pad_length; + + rv = sc_transmit_apdu(card, &apdu); + LOGN_TEST_RET(ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOGN_TEST_RET(ctx, rv, "PIN cmd failed"); + } + else if (data->pin2.data) { + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x2C, 3, reference); + + rv = sc_transmit_apdu(card, &apdu); + LOGN_TEST_RET(ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOGN_TEST_RET(ctx, rv, "PIN cmd failed"); + } + else { + rv = authentic_chv_set_pinpad(card, reference); + LOGN_TEST_RET(ctx, rv, "Failed to set PIN with pin-pad"); + } + + if (save_current) { + struct sc_file *dummy_file = NULL; + + rv = authentic_select_file(card, &save_current->path, &dummy_file); + LOGN_TEST_RET(ctx, rv, "Cannot return to saved PATH"); + } + LOGN_FUNC_RETURN(ctx, rv); +} + + +static int +authentic_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) +{ + struct sc_context *ctx = card->ctx; + int rv; + + LOGN_FUNC_CALLED(ctx); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "PIN-CMD:%X,PIN(type:%X,ret:%i),PIN1(%p,len:%i),PIN2(%p,len:%i)", data->cmd, data->pin_type, + data->pin_reference, data->pin1.data, data->pin1.len, data->pin2.data, data->pin2.len); + + switch (data->cmd) { + case SC_PIN_CMD_VERIFY: + rv = authentic_pin_verify(card, data); + break; + case SC_PIN_CMD_CHANGE: + rv = authentic_pin_change(card, data, tries_left); + break; + case SC_PIN_CMD_UNBLOCK: + rv = authentic_pin_reset(card, data, tries_left); + break; + case SC_PIN_CMD_GET_INFO: + rv = authentic_pin_get_policy(card, data); + break; + default: + LOGN_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Unupported PIN command"); + } + + if (rv == SC_ERROR_PIN_CODE_INCORRECT && tries_left) + *tries_left = data->pin1.tries_left; + + LOGN_FUNC_RETURN(ctx, rv); +} + + +static int +authentic_get_serialnr(struct sc_card *card, struct sc_serial_number *serial) +{ + struct sc_context *ctx = card->ctx; + struct authentic_private_data *prv_data = (struct authentic_private_data *) card->drv_data; + int rv; + + LOGN_FUNC_CALLED(ctx); + if (!card->serialnr.len) { + rv = authentic_get_cplc(card); + LOGN_TEST_RET(ctx, rv, "get CPLC data error"); + + card->serialnr.len = 4; + memcpy(card->serialnr.value, prv_data->cplc.value + 15, 4); + + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "serial %02X%02X%02X%02X", + card->serialnr.value[0], card->serialnr.value[1], + card->serialnr.value[2], card->serialnr.value[3]); + } + + if (serial) + memcpy(serial, &card->serialnr, sizeof(*serial)); + + LOGN_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +/* 'GET CHALLENGE' returns always 24 bytes */ +static int +authentic_get_challenge(struct sc_card *card, unsigned char *rnd, size_t len) +{ + struct sc_context *ctx = card->ctx; + struct sc_apdu apdu; + unsigned char rbuf[0x18]; + int rv, nn; + + LOGN_FUNC_CALLED(ctx); + if (!rnd) + return SC_ERROR_INVALID_ARGUMENTS; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x84, 0x00, 0x00); + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + apdu.le = sizeof(rbuf); + + while (len > 0) { + rv = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOGN_TEST_RET(ctx, rv, "PIN cmd failed"); + + if (apdu.resplen != sizeof(rbuf)) + return sc_check_sw(card, apdu.sw1, apdu.sw2); + + nn = len > apdu.resplen ? apdu.resplen : len; + memcpy(rnd, apdu.resp, nn); + len -= nn; + rnd += nn; + } + + LOGN_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +int +authentic_manage_sdo_encode_prvkey(struct sc_card *card, struct sc_pkcs15_prkey *prvkey, + unsigned char **out, size_t *out_len) +{ + struct sc_context *ctx = card->ctx; + struct sc_pkcs15_prkey_rsa rsa; + unsigned char *blob = NULL, *blob01 = NULL; + size_t blob_len = 0, blob01_len = 0; + int rv; + + if (!prvkey || !out || !out_len) + LOGN_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid arguments"); + if (prvkey->algorithm != SC_ALGORITHM_RSA) + LOGN_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Invalid SDO operation"); + + rsa = prvkey->u.rsa; + /* Encode private RSA key part */ + rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PRIVATE_P, rsa.p.data, rsa.p.len, &blob, &blob_len); + LOGN_TEST_RET(ctx, rv, "SDO RSA P encode error"); + + rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PRIVATE_Q, rsa.q.data, rsa.q.len, &blob, &blob_len); + LOGN_TEST_RET(ctx, rv, "SDO RSA Q encode error"); + + rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PRIVATE_PQ, rsa.iqmp.data, rsa.iqmp.len, &blob, &blob_len); + LOGN_TEST_RET(ctx, rv, "SDO RSA PQ encode error"); + + rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PRIVATE_DP1, rsa.dmp1.data, rsa.dmp1.len, &blob, &blob_len); + LOGN_TEST_RET(ctx, rv, "SDO RSA DP1 encode error"); + + rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PRIVATE_DQ1, rsa.dmq1.data, rsa.dmq1.len, &blob, &blob_len); + LOGN_TEST_RET(ctx, rv, "SDO RSA DQ1 encode error"); + + rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PRIVATE, blob, blob_len, &blob01, &blob01_len); + LOGN_TEST_RET(ctx, rv, "SDO RSA Private encode error"); + + free (blob); + blob = NULL; + blob_len = 0; + + /* Encode public RSA key part */ + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "modulus.len:%i blob_len:%i", rsa.modulus.len, blob_len); + rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PUBLIC_MODULUS, rsa.modulus.data, rsa.modulus.len, &blob, &blob_len); + LOGN_TEST_RET(ctx, rv, "SDO RSA Modulus encode error"); + + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "exponent.len:%i blob_len:%i", rsa.exponent.len, blob_len); + rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PUBLIC_EXPONENT, rsa.exponent.data, rsa.exponent.len, &blob, &blob_len); + LOGN_TEST_RET(ctx, rv, "SDO RSA Exponent encode error"); + + rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PUBLIC, blob, blob_len, &blob01, &blob01_len); + LOGN_TEST_RET(ctx, rv, "SDO RSA Private encode error"); + + free (blob); + + rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA, blob01, blob01_len, out, out_len); + LOGN_TEST_RET(ctx, rv, "SDO RSA encode error"); + + free(blob01); + + LOGN_FUNC_RETURN(ctx, rv); +} + + +int +authentic_manage_sdo_encode(struct sc_card *card, struct sc_authentic_sdo *sdo, unsigned long cmd, + unsigned char **out, size_t *out_len) +{ + struct sc_context *ctx = card->ctx; + unsigned char *data = NULL; + size_t data_len = 0; + unsigned char data_tag = AUTHENTIC_TAG_DOCP; + int rv; + + LOGN_FUNC_CALLED(ctx); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "encode SDO operation (cmd:%lX,mech:%X,id:%X)", cmd, sdo->docp.mech, sdo->docp.id); + + if (!out || !out_len) + LOGN_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid arguments"); + + rv = authentic_update_blob(ctx, AUTHENTIC_TAG_DOCP_MECH, &sdo->docp.mech, sizeof(sdo->docp.mech), + &data, &data_len); + LOGN_TEST_RET(ctx, rv, "DOCP MECH encode error"); + + rv = authentic_update_blob(ctx, AUTHENTIC_TAG_DOCP_ID, &sdo->docp.id, sizeof(sdo->docp.id), + &data, &data_len); + LOGN_TEST_RET(ctx, rv, "DOCP ID encode error"); + + if (cmd == SC_CARDCTL_AUTHENTIC_SDO_CREATE) { + rv = authentic_update_blob(ctx, AUTHENTIC_TAG_DOCP_ACLS, sdo->docp.acl_data, sdo->docp.acl_data_len, + &data, &data_len); + LOGN_TEST_RET(ctx, rv, "DOCP ACLs encode error"); + + if (sdo->docp.security_parameter) { + rv = authentic_update_blob(ctx, AUTHENTIC_TAG_DOCP_SCP, + &sdo->docp.security_parameter, sizeof(sdo->docp.security_parameter), + &data, &data_len); + LOGN_TEST_RET(ctx, rv, "DOCP ACLs encode error"); + } + if (sdo->docp.usage_counter[0] || sdo->docp.usage_counter[1]) { + rv = authentic_update_blob(ctx, AUTHENTIC_TAG_DOCP_USAGE_COUNTER, + sdo->docp.usage_counter, sizeof(sdo->docp.usage_counter), + &data, &data_len); + LOGN_TEST_RET(ctx, rv, "DOCP ACLs encode error"); + } + } + else if (cmd == SC_CARDCTL_AUTHENTIC_SDO_STORE) { + if (sdo->docp.mech == AUTHENTIC_MECH_CRYPTO_RSA1024 + || sdo->docp.mech == AUTHENTIC_MECH_CRYPTO_RSA1280 + || sdo->docp.mech == AUTHENTIC_MECH_CRYPTO_RSA1536 + || sdo->docp.mech == AUTHENTIC_MECH_CRYPTO_RSA1792 + || sdo->docp.mech == AUTHENTIC_MECH_CRYPTO_RSA2048) { + rv = authentic_manage_sdo_encode_prvkey(card, sdo->data.prvkey, &data, &data_len); + LOGN_TEST_RET(ctx, rv, "SDO RSA encode error"); + } + else { + LOGN_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Cryptographic object unsupported for encoding"); + } + } + else if (cmd == SC_CARDCTL_AUTHENTIC_SDO_GENERATE) { + if (sdo->data.prvkey) { + rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PUBLIC_EXPONENT, + sdo->data.prvkey->u.rsa.exponent.data, sdo->data.prvkey->u.rsa.exponent.len, + &data, &data_len); + LOGN_TEST_RET(ctx, rv, "SDO RSA Exponent encode error"); + } + + data_tag = AUTHENTIC_TAG_RSA_GENERATE_DATA; + } + else if (cmd != SC_CARDCTL_AUTHENTIC_SDO_DELETE) { + LOGN_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Invalid SDO operation"); + } + + rv = authentic_update_blob(ctx, data_tag, data, data_len, out, out_len); + LOGN_TEST_RET(ctx, rv, "SDO DOCP encode error"); + + free(data); + + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "encoded SDO operation data %s", sc_dump_hex(*out, *out_len)); + LOGN_FUNC_RETURN(ctx, rv); +} + + +static int +authentic_manage_sdo_generate(struct sc_card *card, struct sc_authentic_sdo *sdo) +{ + struct sc_context *ctx = card->ctx; + struct sc_apdu apdu; + unsigned char rbuf[0x400]; + unsigned char *data = NULL; + size_t data_len = 0; + int rv; + + LOGN_FUNC_CALLED(ctx); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Generate SDO(mech:%X,id:%X)", sdo->docp.mech, sdo->docp.id); + + rv = authentic_manage_sdo_encode(card, sdo, SC_CARDCTL_AUTHENTIC_SDO_GENERATE, &data, &data_len); + LOGN_TEST_RET(ctx, rv, "Cannot encode SDO data"); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "encoded SDO length %i", data_len); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x47, 0x00, 0x00); + apdu.data = data; + apdu.datalen = data_len; + apdu.lc = data_len; + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + apdu.le = 0x100; + + rv = sc_transmit_apdu(card, &apdu); + LOGN_TEST_RET(ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOGN_TEST_RET(ctx, rv, "authentic_sdo_create() SDO put data error"); + + rv = authentic_decode_pubkey_rsa(ctx, apdu.resp, apdu.resplen, &sdo->data.prvkey); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "cannot decode public key"); + + free(data); + LOGN_FUNC_RETURN(ctx, rv); +} + + +static int +authentic_manage_sdo(struct sc_card *card, struct sc_authentic_sdo *sdo, unsigned long cmd) +{ + struct sc_context *ctx = card->ctx; + struct sc_apdu apdu; + unsigned char rbuf[0x400]; + unsigned char *data = NULL; + size_t data_len = 0, save_max_send = card->max_send_size; + int rv; + + LOGN_FUNC_CALLED(ctx); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "SDO(cmd:%lX,mech:%X,id:%X)", cmd, sdo->docp.mech, sdo->docp.id); + + rv = authentic_manage_sdo_encode(card, sdo, cmd, &data, &data_len); + LOGN_TEST_RET(ctx, rv, "Cannot encode SDO data"); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "encoded SDO length %i", data_len); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xDB, 0x3F, 0xFF); + apdu.data = data; + apdu.datalen = data_len; + apdu.lc = data_len; + apdu.flags |= SC_APDU_FLAGS_CHAINING; + + if (card->max_send_size > 255) + card->max_send_size = 255; + rv = sc_transmit_apdu(card, &apdu); + card->max_send_size = save_max_send; + LOGN_TEST_RET(ctx, rv, "APDU transmit failed"); + + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOGN_TEST_RET(ctx, rv, "authentic_sdo_create() SDO put data error"); + + free(data); + LOGN_FUNC_RETURN(ctx, rv); +} + + +static int +authentic_card_ctl(struct sc_card *card, unsigned long cmd, void *ptr) +{ + struct sc_context *ctx = card->ctx; + struct sc_authentic_sdo *sdo = (struct sc_authentic_sdo *) ptr; + + switch (cmd) { + case SC_CARDCTL_GET_SERIALNR: + return authentic_get_serialnr(card, (struct sc_serial_number *)ptr); + case SC_CARDCTL_AUTHENTIC_SDO_CREATE: + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "CARDCTL SDO_CREATE: sdo(mech:%X,id:%X)", sdo->docp.mech, sdo->docp.id); + return authentic_manage_sdo(card, (struct sc_authentic_sdo *) ptr, cmd); + case SC_CARDCTL_AUTHENTIC_SDO_DELETE: + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "CARDCTL SDO_DELETE: sdo(mech:%X,id:%X)", sdo->docp.mech, sdo->docp.id); + return authentic_manage_sdo(card, (struct sc_authentic_sdo *) ptr, cmd); + case SC_CARDCTL_AUTHENTIC_SDO_STORE: + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "CARDCTL SDO_STORE: sdo(mech:%X,id:%X)", sdo->docp.mech, sdo->docp.id); + return authentic_manage_sdo(card, (struct sc_authentic_sdo *) ptr, cmd); + case SC_CARDCTL_AUTHENTIC_SDO_GENERATE: + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "CARDCTL SDO_GENERATE: sdo(mech:%X,id:%X)", sdo->docp.mech, sdo->docp.id); + return authentic_manage_sdo_generate(card, (struct sc_authentic_sdo *) ptr); + } + return SC_ERROR_NOT_SUPPORTED; +} + + +static int +authentic_set_security_env(struct sc_card *card, + const struct sc_security_env *env, int se_num) +{ + struct sc_context *ctx = card->ctx; + struct sc_apdu apdu; + unsigned char cse_crt_dst[] = { + 0x80, 0x01, AUTHENTIC_ALGORITHM_RSA_PKCS1, + 0x83, 0x01, env->key_ref[0] & ~AUTHENTIC_OBJECT_REF_FLAG_LOCAL, + }; + unsigned char cse_crt_ct[] = { + 0x80, 0x01, AUTHENTIC_ALGORITHM_RSA_PKCS1, + 0x83, 0x01, env->key_ref[0] & ~AUTHENTIC_OBJECT_REF_FLAG_LOCAL, + }; + int rv; + + LOGN_FUNC_CALLED(ctx); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "set SE#%i(op:0x%X,algo:0x%X,algo_ref:0x%X,flags:0x%X), key_ref:0x%X", + se_num, env->operation, env->algorithm, env->algorithm_ref, env->algorithm_flags, env->key_ref[0]); + switch (env->operation) { + case SC_SEC_OPERATION_SIGN: + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, AUTHENTIC_TAG_CRT_DST); + apdu.data = cse_crt_dst; + apdu.datalen = sizeof(cse_crt_dst); + apdu.lc = sizeof(cse_crt_dst); + break; + case SC_SEC_OPERATION_DECIPHER: + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, AUTHENTIC_TAG_CRT_CT); + apdu.data = cse_crt_ct; + apdu.datalen = sizeof(cse_crt_ct); + apdu.lc = sizeof(cse_crt_ct); + break; + default: + LOGN_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); + } +#if 0 + apdu.flags |= SC_APDU_FLAGS_CAN_WAIT; +#endif + + rv = sc_transmit_apdu(card, &apdu); + LOGN_TEST_RET(ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOGN_TEST_RET(ctx, rv, "MSE restore error"); + + LOGN_FUNC_RETURN(ctx, rv); +} + + +static int +authentic_decipher(struct sc_card *card, const unsigned char *in, size_t in_len, + unsigned char *out, size_t out_len) +{ + struct sc_context *ctx = card->ctx; + struct sc_apdu apdu; + unsigned char resp[SC_MAX_APDU_BUFFER_SIZE]; + int rv; + + LOGN_FUNC_CALLED(ctx); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "crgram_len %i; outlen %i\n", in_len, out_len); + if (!out || !out_len || in_len > SC_MAX_APDU_BUFFER_SIZE) + LOGN_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x80, 0x86); + apdu.flags |= SC_APDU_FLAGS_CHAINING; + apdu.data = in; + apdu.datalen = in_len; + apdu.lc = in_len; + apdu.resp = resp; + apdu.resplen = sizeof(resp); + apdu.le = in_len - (in_len % 8); + + rv = sc_transmit_apdu(card, &apdu); + LOGN_TEST_RET(ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOGN_TEST_RET(ctx, rv, "Card returned error"); + + if (out_len > apdu.resplen) + out_len = apdu.resplen; + + memcpy(out, apdu.resp, out_len); + rv = out_len; + + LOGN_FUNC_RETURN(ctx, rv); +} + + +/* + * Remote Access + */ +#if 0 +static int +authentic_encode_answer(struct sc_context *ctx, struct sc_remote_apdu *in_apdus, + unsigned char *out, size_t *out_len) +{ + struct sc_remote_apdu *p_apdu = in_apdus; + char ticket[SC_MAX_APDU_BUFFER_SIZE * 2 + 32]; + char *answer = NULL; + unsigned sw = 0; + int ii; + + printf("TODO: use ASN1 encoder\n"); + LOGN_FUNC_CALLED(ctx); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "encode_answer: out %p/%p", out, out_len); + + answer = calloc(1, 16); + if (!answer) + LOGN_TEST_RET(ctx, SC_ERROR_MEMORY_FAILURE, "encode_answer: answer allocate failed"); + strcpy(answer, "DATA="); + + memset(ticket, 0, sizeof(ticket)); + for (p_apdu = in_apdus, ii=0; p_apdu; p_apdu = p_apdu->next, ii++) { + unsigned char tmp_buff[0x200]; + size_t len, offs = sizeof(tmp_buff); + + sw = p_apdu->apdu.sw1 * 0x100 + p_apdu->apdu.sw2; + + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "%i: encode_answer: SW %X; resplen %i", ii, sw, p_apdu->apdu.resplen); + if (p_apdu->apdu.resplen) { + offs -= p_apdu->apdu.resplen; + memcpy(tmp_buff + offs--, p_apdu->apdu.resp, p_apdu->apdu.resplen); + + *(tmp_buff + offs--) = p_apdu->apdu.resplen & 0xFF; + if (p_apdu->apdu.resplen > 0xFF) + *(tmp_buff + offs--) = (p_apdu->apdu.resplen >> 8) & 0xFF; + + if (p_apdu->apdu.resplen > 0xFF) + *(tmp_buff + offs--) = 0x82; + else if (p_apdu->apdu.resplen > 0x7F) + *(tmp_buff + offs--) = 0x81; + + *(tmp_buff + offs--) = SM_RESPONSE_CONTEXT_DATA_TAG; + } + else { + offs--; + } + + *(tmp_buff + offs--) = p_apdu->apdu.sw2; + *(tmp_buff + offs--) = p_apdu->apdu.sw1; + if (p_apdu->apdu.sw1 & 0x80) { + *(tmp_buff + offs--) = 0x00; + *(tmp_buff + offs--) = 3; + } + else { + *(tmp_buff + offs--) = 2; + } + *(tmp_buff + offs--) = SC_ASN1_TAG_INTEGER; + + *(tmp_buff + offs--) = ii + 1; + *(tmp_buff + offs--) = 1; + *(tmp_buff + offs--) = SC_ASN1_TAG_INTEGER; + + len = sizeof(tmp_buff) - offs - 1; + + *(tmp_buff + offs--) = len & 0xFF; + + if (len >= 0x80 && len < 0x100) { + *(tmp_buff + offs--) = 0x81; + } + else if (len >= 0x100) { + *(tmp_buff + offs--) = (len > 8) & 0xFF; + *(tmp_buff + offs--) = 0x82; + } + + *(tmp_buff + offs) = SM_RESPONSE_CONTEXT_TAG; + + len = sizeof(tmp_buff) - offs; + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "%i: EncodedAnswer(%i) %s\n", ii, len, sc_dump_hex(tmp_buff + offs, len)); + + answer = realloc(answer, strlen(answer) + len*2 + 16); + if (!answer) + LOGN_TEST_RET(ctx, SC_ERROR_MEMORY_FAILURE, "encode_answer: answer (re)allocate failed"); + + sprintf(answer + strlen(answer), "%s", sc_dump_hex(tmp_buff + offs, len)); + } + + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "encode_answer: SW:%X,ticket:%s", sw, ticket); + if (strlen(ticket)) { + answer = realloc(answer, strlen(answer) + strlen(ticket) + 16); + if (!answer) + LOGN_TEST_RET(ctx, SC_ERROR_MEMORY_FAILURE, "encode_answer: answer (re)allocate for ticket failed"); + sprintf(answer + strlen(answer), ";%s", ticket); + } + + answer = realloc(answer, strlen(answer) + 16); + if (!answer) + LOGN_TEST_RET(ctx, SC_ERROR_MEMORY_FAILURE, "encode_answer: answer (re)allocate failed"); + sprintf(answer + strlen(answer), ";SW=%04X", sw); + + if (out && out_len) { + if (strlen(answer) + 1 > *out_len) { + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "encode_answer: buffer too small: need %i, have %i", strlen(answer) + 1, *out_len); + LOGN_FUNC_RETURN(ctx, SC_ERROR_BUFFER_TOO_SMALL); + } + + strcpy((char *)out, answer); + *out_len = strlen(answer); + } + else if (out_len) { + *out_len = 0; + } + + free(answer); + + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "encode_answer: returns '%s'", out); + LOGN_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static int +authentic_external_apdus(struct sc_card *card, char *encoded_apdus, + unsigned char *out, size_t *out_len) +{ + struct sc_context *ctx = card->ctx; + int rv = 0, rvv = 0; + size_t _out_len = 0; + + printf("TODO: implement non card specific procedures -- common procedures for IAS/ECC and AuthentIC\n"); + LOGN_FUNC_CALLED(ctx); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "external_apdus: out(%p) %p", out_len, out); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "external_apdus: encoded_apdus:'%s'", encoded_apdus); + + if (out && out_len) { + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "external_apdus: out_len:%i", *out_len); + memset(out, 0, *out_len); + _out_len = *out_len; + } + + do { + struct sc_remote_apdu *remote_apdus = NULL, *current = NULL; + char header[0x200]; + + if (encoded_apdus && strlen(encoded_apdus)) { + rv = sc_hash_decode_remote_apdus(card->ctx, encoded_apdus, &remote_apdus); + if (rv) + break; + + if (!remote_apdus) + break; + + current = remote_apdus; + while(current) { + rv = sc_transmit_apdu(card, ¤t->apdu); + if (rv) { + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "external_apdus: transmit APDU error %i", rv); + break; + } + + rv = sc_check_sw(card, current->apdu.sw1, current->apdu.sw2); + if (rv && !current->fatal) { + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "external_apdus: non fatal APDU error %i", rv); + rv = 0; + } + + if (rv) { + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "external_apdus: APDU error %i", rv); + break; + } + + current = current->next; + } + + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "external_apdus: out before encode %p/%p", out, out_len); + rvv = authentic_encode_answer(card->ctx, remote_apdus, out, out_len); + if (rvv) { + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "external_apdus: encode answer error %i", rvv); + if (rv!=0) + rv = rvv; + } + + sc_remote_apdu_free(remote_apdus); + remote_apdus = NULL; + } + + memset(header, 0, sizeof(header)); + sprintf(header + strlen(header), "SERIAL=%s;", sc_dump_hex(card->serialnr.value,card->serialnr.len)); + + if (out && out_len) { + int str_len = strlen((char *)out); + + if (strlen(header) > _out_len - str_len) { + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "external_apdus: unsufficient buffer; need/have %i/%i", + strlen(header) + str_len, _out_len); + LOGN_TEST_RET(card->ctx, SC_ERROR_BUFFER_TOO_SMALL, "authentic_external_apdus() buffer too small"); + } + + sprintf((char *)(out + str_len), "%s%s", str_len ? ";" : "", header); + } + + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "external_apdus returns '%s'", out); + } while (0); + + LOGN_FUNC_RETURN(ctx, rv); +} +#endif + +static int +authentic_finish(struct sc_card *card) +{ + struct sc_context *ctx = card->ctx; + + LOGN_FUNC_CALLED(ctx); + if (card->drv_data) + free(card->drv_data); + card->drv_data = NULL; + LOGN_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static struct sc_card_driver * +sc_get_driver(void) +{ + struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); + + if (!iso_ops) + iso_ops = iso_drv->ops; + + authentic_ops = *iso_ops; + + authentic_ops.match_card = authentic_match_card; + authentic_ops.init = authentic_init; + authentic_ops.finish = authentic_finish; + /* + authentic_ops.read_binary = authentic_read_binary; + authentic_ops.write_binary = authentic_write_binary; + authentic_ops.update_binary = authentic_update_binary; + */ + authentic_ops.erase_binary = authentic_erase_binary; + /* authentic_ops.resize_file = authentic_resize_file; */ + authentic_ops.select_file = authentic_select_file; + /* get_response: Untested */ + authentic_ops.get_challenge = authentic_get_challenge; + authentic_ops.set_security_env = authentic_set_security_env; + /* decipher: Untested */ + authentic_ops.decipher = authentic_decipher; + /* authentic_ops.compute_signature = authentic_compute_signature; */ + authentic_ops.create_file = authentic_create_file; + authentic_ops.delete_file = authentic_delete_file; + authentic_ops.card_ctl = authentic_card_ctl; + authentic_ops.process_fci = authentic_process_fci; + authentic_ops.pin_cmd = authentic_pin_cmd; + + /* + authentic_ops.external_apdus = authentic_external_apdus; + authentic_ops.update_binary_long = authentic_update_binary_long; + authentic_ops.read_binary_long = authentic_read_binary_long; + */ + return &authentic_drv; +} + +struct sc_card_driver * +sc_get_authentic_driver(void) +{ + return sc_get_driver(); +} diff --git a/src/libopensc/cardctl.h b/src/libopensc/cardctl.h index 9797a95afa..c49d203595 100644 --- a/src/libopensc/cardctl.h +++ b/src/libopensc/cardctl.h @@ -204,6 +204,15 @@ enum { SC_CARDCTL_PIV_GENERATE_KEY, SC_CARDCTL_PIV_PIN_PREFERENCE, SC_CARDCTL_PIV_OBJECT_PRESENT, + + /* + * AuthentIC v3 + */ + SC_CARDCTL_AUTHENTIC_BASE = _CTL_PREFIX('A','V','3'), + SC_CARDCTL_AUTHENTIC_SDO_CREATE, + SC_CARDCTL_AUTHENTIC_SDO_DELETE, + SC_CARDCTL_AUTHENTIC_SDO_STORE, + SC_CARDCTL_AUTHENTIC_SDO_GENERATE, }; enum { diff --git a/src/libopensc/cards.h b/src/libopensc/cards.h index 1fcafaf895..c8fc24e769 100644 --- a/src/libopensc/cards.h +++ b/src/libopensc/cards.h @@ -113,6 +113,8 @@ enum { SC_CARD_TYPE_OBERTHUR_32K, SC_CARD_TYPE_OBERTHUR_32K_BIO, SC_CARD_TYPE_OBERTHUR_64K, + /* Oberthur 'COSMO v7' with applet 'AuthentIC v3.2' */ + SC_CARD_TYPE_OBERTHUR_AUTHENTIC_3_2 = 11100, /* belpic driver */ SC_CARD_TYPE_BELPIC_BASE = 12000, @@ -209,6 +211,7 @@ extern sc_card_driver_t *sc_get_myeid_driver(void); extern sc_card_driver_t *sc_get_ias_driver(void); 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(); #ifdef __cplusplus } diff --git a/src/libopensc/ctx.c b/src/libopensc/ctx.c index e012d02f39..c60750051e 100644 --- a/src/libopensc/ctx.c +++ b/src/libopensc/ctx.c @@ -66,6 +66,7 @@ static const struct _sc_driver_entry internal_card_drivers[] = { { "jcop", (void *(*)(void)) sc_get_jcop_driver }, #ifdef ENABLE_OPENSSL { "oberthur", (void *(*)(void)) sc_get_oberthur_driver }, + { "authentic", (void *(*)(void)) sc_get_authentic_driver }, #endif { "belpic", (void *(*)(void)) sc_get_belpic_driver }, { "ias", (void *(*)(void)) sc_get_ias_driver }, diff --git a/src/libopensc/iso7816.h b/src/libopensc/iso7816.h new file mode 100644 index 0000000000..43d1a2f565 --- /dev/null +++ b/src/libopensc/iso7816.h @@ -0,0 +1,26 @@ +/* + * iso7816.h: ISO-7816 defines + */ + +#ifndef _ISO7816_TYPES_H +#define _ISO7816_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define ISO7816_TAG_FCP 0x62 +#define ISO7816_TAG_FCP_SIZE 0x80 +#define ISO7816_TAG_FCP_TYPE 0x82 +#define ISO7816_TAG_FCP_ID 0x83 +#define ISO7816_TAG_FCP_ACLS 0x86 + + +#define ISO7816_FILE_TYPE_TRANSPARENT_EF 0x01 +#define ISO7816_FILE_TYPE_DF 0x38 + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/pkcs15init/Makefile.am b/src/pkcs15init/Makefile.am index 12574b2f53..07819a42ef 100644 --- a/src/pkcs15init/Makefile.am +++ b/src/pkcs15init/Makefile.am @@ -23,7 +23,8 @@ dist_pkgdata_DATA = \ entersafe.profile \ rutoken_ecp.profile \ westcos.profile \ - myeid.profile + myeid.profile \ + authentic.profile AM_CPPFLAGS = -DSC_PKCS15_PROFILE_DIRECTORY=\"$(pkgdatadir)\" AM_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) $(LTLIB_CFLAGS) @@ -37,4 +38,5 @@ libpkcs15init_la_SOURCES = \ pkcs15-setcos.c pkcs15-incrypto34.c pkcs15-muscle.c \ pkcs15-asepcos.c pkcs15-rutoken.c pkcs15-entersafe.c \ pkcs15-rtecp.c pkcs15-myeid.c \ - pkcs15-oberthur.c pkcs15-oberthur-awp.c + pkcs15-oberthur.c pkcs15-oberthur-awp.c \ + pkcs15-authentic.c diff --git a/src/pkcs15init/Makefile.mak b/src/pkcs15init/Makefile.mak index 690fe90db2..fe68c0624b 100644 --- a/src/pkcs15init/Makefile.mak +++ b/src/pkcs15init/Makefile.mak @@ -8,7 +8,7 @@ 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-myeid.obj pkcs15-authentic.obj all: $(TARGET) diff --git a/src/pkcs15init/pkcs15-authentic.c b/src/pkcs15init/pkcs15-authentic.c new file mode 100644 index 0000000000..ecd97f6933 --- /dev/null +++ b/src/pkcs15init/pkcs15-authentic.c @@ -0,0 +1,1021 @@ +/* + * Specific operations for PKCS #15 initialization of the Oberthur's card + * COSMO v7 with applet AuthentIC v3 . + * + * Copyright (C) 2002 Juha Yrjölä + * Copyright (C) 2010 Viktor Tarasov + * OpenTrust + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#ifndef ENABLE_OPENSSL +#error "Need OpenSSL" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../libopensc/opensc.h" +#include "../libopensc/cardctl.h" +#include "../libopensc/log.h" +#include "../libopensc/pkcs15.h" +#include "../libopensc/cards.h" +#include "../libopensc/authentic.h" + +#include "pkcs15-init.h" +#include "profile.h" + +#define AUTHENTIC_CACHE_TIMESTAMP_PATH "3F0050159999" + +unsigned char authentic_v3_rsa_mechs[5] = { + AUTHENTIC_MECH_CRYPTO_RSA1024, + AUTHENTIC_MECH_CRYPTO_RSA1280, + AUTHENTIC_MECH_CRYPTO_RSA1536, + AUTHENTIC_MECH_CRYPTO_RSA1792, + AUTHENTIC_MECH_CRYPTO_RSA2048 +}; + +unsigned char authentic_v3_rsa_ac_ops[6] = { + SC_AC_OP_UPDATE, + SC_AC_OP_DELETE, + SC_AC_OP_PSO_DECRYPT, + SC_AC_OP_PSO_COMPUTE_SIGNATURE, + SC_AC_OP_INTERNAL_AUTHENTICATE, + SC_AC_OP_GENERATE +}; + +struct authentic_ac_access_usage { + unsigned ac_op; + unsigned access_rule; + unsigned usage; +}; +struct authentic_ac_access_usage authentic_v3_rsa_map_attributes[7] = { + {SC_AC_OP_UPDATE, SC_PKCS15_ACCESS_RULE_MODE_UPDATE, 0}, + {SC_AC_OP_DELETE, SC_PKCS15_ACCESS_RULE_MODE_DELETE, 0}, + {SC_AC_OP_PSO_DECRYPT, SC_PKCS15_ACCESS_RULE_MODE_PSO_DECRYPT, + SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP}, + {SC_AC_OP_PSO_COMPUTE_SIGNATURE, SC_PKCS15_ACCESS_RULE_MODE_PSO_CDS, + SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION}, + {SC_AC_OP_INTERNAL_AUTHENTICATE, SC_PKCS15_ACCESS_RULE_MODE_INT_AUTH, + SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER}, + {SC_AC_OP_GENERATE, SC_PKCS15_ACCESS_RULE_MODE_EXECUTE, 0}, + {0, 0, 0} +}; + +int authentic_pkcs15_delete_file(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_file *df); + +void +authentic_reference_to_pkcs15_id (unsigned int ref, struct sc_pkcs15_id *id) +{ + int ii, sz; + + for (ii=0, sz = 0; ii> 8*ii) + sz++; + + for (ii=0; ii < sz; ii++) + id->value[sz - ii - 1] = (ref >> 8*ii) & 0xFF; + + id->len = sz; +} + + +int +authentic_pkcs15_delete_file(struct sc_pkcs15_card *p15card, struct sc_profile *profile, + struct sc_file *df) +{ + struct sc_context *ctx = p15card->card->ctx; + struct sc_card *card = p15card->card; + struct sc_path path; + unsigned long caps = card->caps; + int rv = 0; + + LOGN_FUNC_CALLED(ctx); + + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "authentic_pkcs15_delete_file() id %04X\n", df->id); + + card->caps |= SC_CARD_CAP_USE_FCI_AC; + rv = sc_pkcs15init_authenticate(profile, p15card, df, SC_AC_OP_DELETE); + card->caps = caps; + + LOGN_TEST_RET(ctx, rv, "Cannnot authenticate SC_AC_OP_DELETE"); + + memset(&path, 0, sizeof(path)); + path.type = SC_PATH_TYPE_FILE_ID; + path.value[0] = df->id >> 8; + path.value[1] = df->id & 0xFF; + path.len = 2; + + rv = sc_delete_file(card, &path); + LOGN_FUNC_RETURN(ctx, rv); +} + + +/* + * Erase the card + * + */ +static int +authentic_pkcs15_erase_card(struct sc_profile *profile, struct sc_pkcs15_card *p15card) +{ + struct sc_context *ctx = p15card->card->ctx; + struct sc_file *file = NULL; + struct sc_pkcs15_df *df; + int rv; + + LOGN_FUNC_CALLED(ctx); + + if (p15card->file_odf) { + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Select ODF path: %s", sc_print_path(&p15card->file_odf->path)); + rv = sc_select_file(p15card->card, &p15card->file_odf->path, NULL); + LOGN_TEST_RET(ctx, rv, "Erase application error: cannot select ODF path"); + } + + for (df = p15card->df_list; df; df = df->next) { + struct sc_pkcs15_object *objs[32]; + unsigned obj_type = 0; + int ii; + + if (df->type == SC_PKCS15_PRKDF) + obj_type = SC_PKCS15_TYPE_PRKEY; + else if (df->type == SC_PKCS15_PUKDF) + obj_type = SC_PKCS15_TYPE_PUBKEY; + else if (df->type == SC_PKCS15_CDF) + obj_type = SC_PKCS15_TYPE_CERT; + else if (df->type == SC_PKCS15_DODF) + obj_type = SC_PKCS15_TYPE_DATA_OBJECT; + else + continue; + + if (df->enumerated) { + rv = sc_pkcs15_get_objects(p15card, obj_type, objs, 32); + LOGN_TEST_RET(ctx, rv, "Failed to get PKCS#15 objects to remove"); + + for (ii=0; iicard, &df->path, &file); + if (rv == SC_ERROR_FILE_NOT_FOUND) + continue; + LOGN_TEST_RET(ctx, rv, "Cannot select object file"); + + rv = sc_erase_binary(p15card->card, 0, file->size, 0); + if (rv == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED) { + rv = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); + LOGN_TEST_RET(ctx, rv, "SC_AC_OP_UPDATE authentication failed"); + + rv = sc_erase_binary(p15card->card, 0, file->size, 0); + } + LOGN_TEST_RET(ctx, rv, "Binary erase error"); + + sc_file_free(file); + + profile->dirty = 1; + } + + LOGN_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +/* + * Allocate a file + */ +static int +authentic_pkcs15_new_file(struct sc_profile *profile, struct sc_card *card, + unsigned int type, unsigned int num, struct sc_file **out) +{ + struct sc_context *ctx = card->ctx; + struct sc_file *file = NULL; + const char *t_name = NULL; + int rv; + + LOGN_FUNC_CALLED(ctx); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "type %X; num %i", type, num); + switch (type) { + case SC_PKCS15_TYPE_PRKEY_RSA: + t_name = "template-private-key"; + break; + case SC_PKCS15_TYPE_PUBKEY_RSA: + t_name = "template-public-key"; + break; + case SC_PKCS15_TYPE_CERT: + t_name = "template-certificate"; + break; + case SC_PKCS15_TYPE_DATA_OBJECT: + t_name = "template-public-data"; + break; + default: + LOGN_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Profile template not supported"); + } + + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "df_info path '%s'", sc_print_path(&profile->df_info->file->path)); + rv = sc_profile_get_file(profile, t_name, &file); + LOGN_TEST_RET(ctx, rv, "Error when getting file from template"); + + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "file(type:%X), path(type:%X;path:%s)", file->type, file->path.type, sc_print_path(&file->path)); + + file->id = (file->id & 0xFF00) | (num & 0xFF); + if (file->type != SC_FILE_TYPE_BSO) { + if (file->path.len == 0) { + file->path.type = SC_PATH_TYPE_FILE_ID; + file->path.len = 2; + } + file->path.value[file->path.len - 2] = (file->id >> 8) & 0xFF; + file->path.value[file->path.len - 1] = file->id & 0xFF; + file->path.count = -1; + } + + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "file size %i; ef type %i/%i; id %04X", file->size, file->type, file->ef_structure, file->id); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "path type %X; path '%s'", file->path.type, sc_print_path(&file->path)); + + if (out) + *out = file; + + LOGN_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +/* + * Select a key reference + */ +static int +authentic_pkcs15_select_key_reference(struct sc_profile *profile, struct sc_pkcs15_card *p15card, + struct sc_pkcs15_prkey_info *key_info) +{ + struct sc_context *ctx = p15card->card->ctx; + + LOGN_FUNC_CALLED(ctx); + + /* In authentic PKCS#15 all crypto objects are locals */ + key_info->key_reference |= AUTHENTIC_OBJECT_REF_FLAG_LOCAL; + + if (key_info->key_reference > AUTHENTIC_V3_CRYPTO_OBJECT_REF_MAX) + LOGN_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + + if (key_info->key_reference < AUTHENTIC_V3_CRYPTO_OBJECT_REF_MIN) + key_info->key_reference = AUTHENTIC_V3_CRYPTO_OBJECT_REF_MIN; + + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "returns key reference %i", key_info->key_reference); + LOGN_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static int +authentic_docp_set_acls(struct sc_card *card, struct sc_file *file, + unsigned char *ops, size_t ops_len, + struct sc_authentic_sdo_docp *docp) +{ + struct sc_context *ctx = card->ctx; + int ii, offs; + + LOGN_FUNC_CALLED(ctx); + if (ops_len > sizeof(docp->acl_data) / 2) + LOGN_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + + for (ii=0, offs=0; iimethod == SC_AC_NEVER) { + docp->acl_data[offs++] = 0x00; + docp->acl_data[offs++] = 0x00; + } + else if (entry->method == SC_AC_NONE) { + docp->acl_data[offs++] = 0x00; + docp->acl_data[offs++] = 0x00; + } + else if (entry->method == SC_AC_CHV) { + if (!(entry->key_ref & AUTHENTIC_V3_CREDENTIAL_ID_MASK) + || (entry->key_ref & ~AUTHENTIC_V3_CREDENTIAL_ID_MASK)) + LOGN_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Non supported Credential Reference"); + + docp->acl_data[offs++] = 0x00; + docp->acl_data[offs++] = 0x01 << (entry->key_ref - 1); + } + } + + docp->acl_data_len = offs; + LOGN_FUNC_RETURN(ctx, offs); +} + + +static int +authentic_sdo_allocate_prvkey(struct sc_profile *profile, struct sc_card *card, + struct sc_pkcs15_prkey_info *key_info, struct sc_authentic_sdo **out) +{ + struct sc_context *ctx = card->ctx; + struct sc_authentic_sdo *sdo = NULL; + struct sc_file *file = NULL; + int rv; + + LOGN_FUNC_CALLED(ctx); + + if (!out) + LOGN_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + + if ((key_info->modulus_length % 256) || key_info->modulus_length < 1024 || key_info->modulus_length > 2048) + LOGN_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + + rv = authentic_pkcs15_new_file(profile, card, SC_PKCS15_TYPE_PRKEY_RSA, key_info->key_reference, &file); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "authentic_pkcs15_create_key() new_file(TYPE_PRKEY_RSA) rv %i", rv); + LOGN_TEST_RET(ctx, rv, "IasEcc pkcs15 new PRKEY_RSA file error"); + + sdo = calloc(1, sizeof(struct sc_authentic_sdo)); + if (!sdo) + LOGN_TEST_RET(ctx, SC_ERROR_MEMORY_FAILURE, "Cannot allocate 'sc_authentic_sdo'"); + + sdo->magic = AUTHENTIC_SDO_MAGIC; + sdo->docp.id = key_info->key_reference & ~AUTHENTIC_OBJECT_REF_FLAG_LOCAL; + sdo->docp.mech = authentic_v3_rsa_mechs[(key_info->modulus_length - 1024) / 256]; + + rv = authentic_docp_set_acls(card, file, authentic_v3_rsa_ac_ops, + sizeof(authentic_v3_rsa_ac_ops)/sizeof(authentic_v3_rsa_ac_ops[0]), &sdo->docp); + LOGN_TEST_RET(ctx, rv, "Cannot set key ACLs from file"); + + sc_file_free(file); + + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "sdo(mech:%X,id:%X,acls:%s)", sdo->docp.mech, sdo->docp.id, + sc_dump_hex(sdo->docp.acl_data, sdo->docp.acl_data_len)); + if (out) + *out = sdo; + else + free(sdo); + + LOGN_FUNC_RETURN(ctx, SC_SUCCESS); +} + +#if 0 +static int +authentic_sdo_convert_to_file(struct sc_card *card, struct sc_authentic_sdo *sdo, struct sc_file **out) +{ + struct sc_context *ctx = card->ctx; + struct sc_file *file = sc_file_new(); + int rv, ii; + + LOGN_FUNC_CALLED(ctx); + if (file == NULL) + LOGN_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + else if (!card || !sdo) + LOGN_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + + sc_debug_normal(card->ctx, "sdo->sdo_class %X", sdo->sdo_class); + + if (sdo->sdo_class == IASECC_SDO_CLASS_RSA_PRIVATE) { + unsigned char ops[] = { + SC_AC_OP_PSO_COMPUTE_SIGNATURE, SC_AC_OP_INTERNAL_AUTHENTICATE, SC_AC_OP_PSO_DECRYPT, + SC_AC_OP_GENERATE, SC_AC_OP_UPDATE, SC_AC_OP_READ + }; + + for (ii=0; iictx, "ii:%i, method:%X, ref:%X", ii, op_method, op_ref); + + sc_file_add_acl_entry(file, ops[ii], op_method, op_ref); + } + } + + if (out) + *out = file; + LOGN_FUNC_RETURN(ctx, SC_SUCCESS); +} +#endif + + +static int +authentic_pkcs15_add_access_rule(struct sc_pkcs15_object *object, unsigned access_mode, struct sc_pkcs15_id *auth_id) +{ + int ii; + + for (ii=0;iiaccess_rules[ii].access_mode) { + object->access_rules[ii].access_mode = access_mode; + if (auth_id) + object->access_rules[ii].auth_id = *auth_id; + else + object->access_rules[ii].auth_id.len = 0; + break; + } + else if (!auth_id && !object->access_rules[ii].auth_id.len) { + object->access_rules[ii].access_mode |= access_mode; + break; + } + else if (auth_id && sc_pkcs15_compare_id(&object->access_rules[ii].auth_id, auth_id)) { + object->access_rules[ii].access_mode |= access_mode; + break; + } + } + + if (ii==SC_PKCS15_MAX_ACCESS_RULES) + return SC_ERROR_TOO_MANY_OBJECTS; + + return SC_SUCCESS; +} + +#if 0 +static int +authentic_pkcs15_get_auth_id_from_se(struct sc_pkcs15_card *p15card, unsigned char scb, + struct sc_pkcs15_id *auth_id) +{ + struct sc_context *ctx = p15card->card->ctx; + struct sc_pkcs15_object *pin_objs[32]; + int rv, ii, nn_pins, se_ref, pin_ref; + + LOGN_FUNC_CALLED(ctx); + if (auth_id) + memset(auth_id, 0, sizeof(struct sc_pkcs15_id)); + + if (!(scb & IASECC_SCB_METHOD_USER_AUTH)) + LOGN_FUNC_RETURN(ctx, SC_SUCCESS); + + rv = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH_PIN, pin_objs, 32); + LOGN_TEST_RET(ctx, rv, "Error while getting AUTH objects"); + nn_pins = rv; + + se_ref = scb & 0x0F; + rv = sc_card_ctl(p15card->card, SC_CARDCTL_GET_CHV_REFERENCE_IN_SE, (void *)(&se_ref)); + LOGN_TEST_RET(ctx, rv, "Card CTL error: cannot get CHV reference from SE"); + pin_ref = rv; + for (ii=0; iidata; + + if (pin_ref == pin_info->reference) { + *auth_id = pin_info->auth_id; + break; + } + } + if (ii == nn_pins) + LOGN_TEST_RET(ctx, SC_ERROR_OBJECT_NOT_FOUND, "No AUTH object found"); + + LOGN_FUNC_RETURN(ctx, SC_SUCCESS); +} +#endif + +static int +authentic_pkcs15_fix_file_access_rule(struct sc_pkcs15_card *p15card, struct sc_file *file, + unsigned ac_op, unsigned rule_mode, struct sc_pkcs15_object *object) +{ + struct sc_context *ctx = p15card->card->ctx; + const struct sc_acl_entry *acl = NULL; + struct sc_pkcs15_id id; + unsigned ref; + int rv; + + LOGN_FUNC_CALLED(ctx); + acl = sc_file_get_acl_entry(file, ac_op); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Fix access rule(op:%i;mode:%i) with ACL(method:%X,ref:%X)", + ac_op, rule_mode, acl->method, acl->key_ref); + if (acl->method == SC_AC_NEVER) { + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "ignore access rule(op:%i,mode:%i)", ac_op, rule_mode); + } + else if (acl->method == SC_AC_NONE) { + rv = authentic_pkcs15_add_access_rule(object, rule_mode, NULL); + LOGN_TEST_RET(ctx, rv, "Fix file access rule error"); + } + else { + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "ACL(method:%X,ref:%X)", acl->method, acl->key_ref); + if (acl->method == SC_AC_CHV) { + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "ACL(method:%X,ref:%X)", acl->method, acl->key_ref); + ref = acl->key_ref; + authentic_reference_to_pkcs15_id (ref, &id); + } + else { + LOGN_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Fix file access error"); + } + + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "ACL(method:%X,ref:%X)", acl->method, acl->key_ref); + rv = authentic_pkcs15_add_access_rule(object, rule_mode, &id); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "rv %i", rv); + LOGN_TEST_RET(ctx, rv, "Fix file access rule error"); + } + + LOGN_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static int +authentic_pkcs15_fix_access(struct sc_pkcs15_card *p15card, struct sc_file *file, + struct sc_pkcs15_object *object) +{ + struct sc_context *ctx = p15card->card->ctx; + int rv, ii; + + LOGN_FUNC_CALLED(ctx); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "authID %s", sc_pkcs15_print_id(&object->auth_id)); + + memset(object->access_rules, 0, sizeof(object->access_rules)); + + for (ii=0; authentic_v3_rsa_map_attributes[ii].access_rule; ii++) { + rv = authentic_pkcs15_fix_file_access_rule(p15card, file, + authentic_v3_rsa_map_attributes[ii].ac_op, + authentic_v3_rsa_map_attributes[ii].access_rule, + object); + LOGN_TEST_RET(ctx, rv, "Fix file READ access error"); + } + + LOGN_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static int +authentic_pkcs15_fix_usage(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *object) +{ + struct sc_context *ctx = p15card->card->ctx; + int ii, jj; + + LOGN_FUNC_CALLED(ctx); + if (object->type == SC_PKCS15_TYPE_PRKEY_RSA) { + struct sc_pkcs15_prkey_info *prkey_info = (struct sc_pkcs15_prkey_info *) object->data; + + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "fix private key usage 0x%X", prkey_info->usage); + for (ii=0;iiaccess_rules[ii].access_mode) + break; + + for (jj=0; authentic_v3_rsa_map_attributes[jj].access_rule; jj++) + if (authentic_v3_rsa_map_attributes[jj].access_rule & object->access_rules[ii].access_mode) + prkey_info->usage |= authentic_v3_rsa_map_attributes[jj].usage; + } + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "fixed private key usage 0x%X", prkey_info->usage); + } + LOGN_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static int +authentic_pkcs15_fix_supported_algos(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *object) +{ + struct sc_context *ctx = p15card->card->ctx; + struct sc_pkcs15_prkey_info *prkey_info = (struct sc_pkcs15_prkey_info *) object->data; + struct sc_supported_algo_info *algo; + int rv = SC_SUCCESS, ii; + + LOGN_FUNC_CALLED(ctx); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "encode supported algos for object(%s,type:%X)", object->label, object->type); +#if 0 + switch (object->type) { + case SC_PKCS15_TYPE_PRKEY_RSA: + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "PrKey Usage:%X,Access:%X", prkey_info->usage, prkey_info->access_flags); + if (prkey_info->usage & (SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP)) { + algo = sc_pkcs15_get_supported_algo(p15card, SC_PKCS15_ALGO_OP_DECIPHER, CKM_RSA_PKCS); + rv = sc_pkcs15_add_supported_algo_ref(object, algo); + LOGN_TEST_RET(ctx, rv, "cannot add supported algorithm DECIPHER:CKM_RSA_PKCS"); + } + + if (prkey_info->usage & SC_PKCS15_PRKEY_USAGE_SIGN) { + if (prkey_info->usage & SC_PKCS15_PRKEY_USAGE_NONREPUDIATION) { + algo = sc_pkcs15_get_supported_algo(p15card, SC_PKCS15_ALGO_OP_COMPUTE_SIGNATURE, CKM_SHA1_RSA_PKCS); + rv = sc_pkcs15_add_supported_algo_ref(object, algo); + LOGN_TEST_RET(ctx, rv, "cannot add supported algorithm SIGN:CKM_SHA1_RSA_PKCS"); + + algo = sc_pkcs15_get_supported_algo(p15card, SC_PKCS15_ALGO_OP_COMPUTE_SIGNATURE, CKM_SHA256_RSA_PKCS); + rv = sc_pkcs15_add_supported_algo_ref(object, algo); + LOGN_TEST_RET(ctx, rv, "cannot add supported algorithm SIGN:CKM_SHA256_RSA_PKCS"); + } + else { + algo = sc_pkcs15_get_supported_algo(p15card, SC_PKCS15_ALGO_OP_COMPUTE_SIGNATURE, CKM_RSA_PKCS); + rv = sc_pkcs15_add_supported_algo_ref(object, algo); + LOGN_TEST_RET(ctx, rv, "cannot add supported algorithm SIGN:CKM_RSA_PKCS"); + } + } + + for (ii=0; iialgo_refs[ii]; ii++) + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "algoReference %i", prkey_info->algo_refs[ii]); + + break; + default: + rv = SC_ERROR_NOT_SUPPORTED; + break; + } +#else + printf("%s +%i: FIXME\n", __FILE__, __LINE__); +#endif + LOGN_FUNC_RETURN(ctx, rv); +} + + +static void +authentic_free_sdo_data(struct sc_authentic_sdo *sdo) +{ + int rsa_mechs_num = sizeof(authentic_v3_rsa_mechs)/sizeof(authentic_v3_rsa_mechs[0]); + int ii; + + if (!sdo) + return; + + if (sdo->file) + sc_file_free(sdo->file); + + for (ii=0; iisdo_class == authentic_v3_rsa_mechs[ii]) + break; + if (iidata.prvkey); +} + + +static int +authentic_pkcs15_create_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, + struct sc_pkcs15_object *object) +{ + struct sc_card *card = p15card->card; + struct sc_context *ctx = card->ctx; + struct sc_authentic_sdo *sdo = NULL; + struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *) object->data; + struct sc_file *file_p_prvkey = NULL, *parent = NULL; + size_t keybits = key_info->modulus_length; + int rv; + + LOGN_FUNC_CALLED(ctx); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "create private key(keybits:%i,usage:%X,access:%X,ref:%X)", keybits, + key_info->usage, key_info->access_flags, key_info->key_reference); + if (keybits < 1024 || keybits > 2048 || (keybits % 256)) + LOGN_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid RSA key size"); + + rv = authentic_pkcs15_new_file(profile, card, SC_PKCS15_TYPE_PRKEY_RSA, key_info->key_reference, &file_p_prvkey); + LOGN_TEST_RET(ctx, rv, "IasEcc pkcs15 new PRKEY_RSA file error"); + + key_info->key_reference |= AUTHENTIC_OBJECT_REF_FLAG_LOCAL; + + rv = sc_select_file(card, &file_p_prvkey->path, &parent); + LOGN_TEST_RET(ctx, rv, "DF for the private objects not defined"); + + rv = sc_pkcs15init_authenticate(profile, p15card, parent, SC_AC_OP_CRYPTO); + LOGN_TEST_RET(ctx, rv, "SC_AC_OP_CRYPTO authentication failed for parent DF"); + + sc_file_free(parent); + + key_info->access_flags = SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE + | SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE + | SC_PKCS15_PRKEY_ACCESS_SENSITIVE; + + rv = authentic_sdo_allocate_prvkey(profile, card, key_info, &sdo); + LOGN_TEST_RET(ctx, rv, "IasEcc: init SDO private key failed"); + + rv = sc_card_ctl(card, SC_CARDCTL_AUTHENTIC_SDO_CREATE, sdo); + if (rv == SC_ERROR_FILE_ALREADY_EXISTS) { + unsigned long caps = p15card->card->caps; + + p15card->card->caps &= ~SC_CARD_CAP_USE_FCI_AC; + rv = sc_pkcs15init_authenticate(profile, p15card, file_p_prvkey, SC_AC_OP_DELETE); + p15card->card->caps = caps; + LOGN_TEST_RET(ctx, rv, "SC_AC_OP_CRYPTO authentication failed for parent DF"); + + rv = sc_card_ctl(card, SC_CARDCTL_AUTHENTIC_SDO_DELETE, sdo); + LOGN_TEST_RET(ctx, rv, "SC_CARDCTL_AUTHENTIC_SDO_DELETE failed for private key"); + + rv = sc_card_ctl(card, SC_CARDCTL_AUTHENTIC_SDO_CREATE, sdo); + } + LOGN_TEST_RET(ctx, rv, "SC_CARDCTL_AUTHENTIC_SDO_CREATE failed"); + + rv = authentic_pkcs15_fix_access(p15card, file_p_prvkey, object); + LOGN_TEST_RET(ctx, rv, "cannot fix access rules for private key"); + + rv = authentic_pkcs15_fix_usage(p15card, object); + LOGN_TEST_RET(ctx, rv, "cannot fix access rules for private key"); + + rv = authentic_pkcs15_fix_supported_algos(p15card, object); + LOGN_TEST_RET(ctx, rv, "encode private key access rules failed"); + + sdo->file = file_p_prvkey; + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "sdo->file:%p", sdo->file); + + rv = sc_pkcs15_allocate_object_content(object, (unsigned char *)sdo, sizeof(struct sc_authentic_sdo)); + LOGN_TEST_RET(ctx, rv, "Failed to allocate PrvKey SDO as object content"); + + LOGN_FUNC_RETURN(ctx, rv); +} + + +/* + * RSA key generation + */ +static int +authentic_pkcs15_generate_key(struct sc_profile *profile, sc_pkcs15_card_t *p15card, + struct sc_pkcs15_object *object, struct sc_pkcs15_pubkey *pubkey) +{ + struct sc_card *card = p15card->card; + struct sc_context *ctx = card->ctx; + struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *) object->data; + size_t keybits = key_info->modulus_length; + struct sc_authentic_sdo *sdo = NULL; + unsigned long caps; + int rv; + + LOGN_FUNC_CALLED(ctx); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "generate key(bits:%i,path:%s,AuthID:%s\n", keybits, + sc_print_path(&key_info->path), sc_pkcs15_print_id(&object->auth_id)); + + if (!object->content.value || object->content.len != sizeof(struct sc_authentic_sdo)) + LOGN_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Invalid PrKey SDO data"); + else if (keybits < 1024 || keybits > 2048 || (keybits % 256)) + LOGN_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid RSA key size"); + + sdo = (struct sc_authentic_sdo *)object->content.value; + if (sdo->magic != AUTHENTIC_SDO_MAGIC) + LOGN_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "'Magic' control failed for SDO PrvKey"); + + rv = sc_select_file(card, &key_info->path, NULL); + LOGN_TEST_RET(ctx, rv, "failed to select parent DF"); + + caps = card->caps; + card->caps &= ~SC_CARD_CAP_USE_FCI_AC; + rv = sc_pkcs15init_authenticate(profile, p15card, sdo->file, SC_AC_OP_GENERATE); + card->caps = caps; + LOGN_TEST_RET(ctx, rv, "SC_AC_OP_GENERATE authentication failed"); + + key_info->access_flags |= SC_PKCS15_PRKEY_ACCESS_LOCAL; + + rv = sc_card_ctl(card, SC_CARDCTL_AUTHENTIC_SDO_GENERATE, sdo); + LOGN_TEST_RET(ctx, rv, "generate key failed"); + + pubkey->algorithm = SC_ALGORITHM_RSA; + //FIXME: allocate/copy/free to reduce memory likage + pubkey->u.rsa.modulus = sdo->data.prvkey->u.rsa.modulus; + pubkey->u.rsa.exponent = sdo->data.prvkey->u.rsa.exponent; + sdo->data.prvkey = NULL; + + rv = sc_pkcs15_encode_pubkey(ctx, pubkey, &pubkey->data.value, &pubkey->data.len); + LOGN_TEST_RET(ctx, rv, "encode public key failed"); + + rv = authentic_pkcs15_fix_supported_algos(p15card, object); + LOGN_TEST_RET(ctx, rv, "encode private key access rules failed"); + + authentic_free_sdo_data(sdo); + + rv = sc_pkcs15_allocate_object_content(object, pubkey->data.value, pubkey->data.len); + LOGN_TEST_RET(ctx, rv, "Failed to allocate public key as object content"); + + LOGN_FUNC_RETURN(ctx, rv); +} + + +/* + * Store a private key + */ +static int +authentic_pkcs15_store_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, + struct sc_pkcs15_object *object, struct sc_pkcs15_prkey *prvkey) +{ + struct sc_card *card = p15card->card; + struct sc_context *ctx = card->ctx; + struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *) object->data; + size_t keybits = key_info->modulus_length; + unsigned long caps; + struct sc_authentic_sdo *sdo; + int rv; + + LOGN_FUNC_CALLED(ctx); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Store IAS/ECC key(keybits:%i,AuthID:%s,path:%s)", + keybits, sc_pkcs15_print_id(&object->auth_id), sc_print_path(&key_info->path)); + + if (!object->content.value || object->content.len != sizeof(struct sc_authentic_sdo)) + LOGN_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Invalid PrKey SDO data"); + else if (keybits < 1024 || keybits > 2048 || (keybits % 256)) + LOGN_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid RSA key size"); + + key_info->access_flags &= ~SC_PKCS15_PRKEY_ACCESS_LOCAL; + + sdo = (struct sc_authentic_sdo *)object->content.value; + if (sdo->magic != AUTHENTIC_SDO_MAGIC) + LOGN_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "'Magic' control failed for SDO PrvKey"); + + rv = sc_select_file(card, &key_info->path, NULL); + LOGN_TEST_RET(ctx, rv, "failed to select parent DF"); + + sdo->data.prvkey = prvkey; + + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "sdo(mech:%X,id:%X,acls:%s)", sdo->docp.mech, sdo->docp.id, + sc_dump_hex(sdo->docp.acl_data, sdo->docp.acl_data_len)); + + caps = card->caps; + card->caps &= ~SC_CARD_CAP_USE_FCI_AC; + rv = sc_pkcs15init_authenticate(profile, p15card, sdo->file, SC_AC_OP_UPDATE); + LOGN_TEST_RET(ctx, rv, "SC_AC_OP_GENERATE authentication failed"); + + rv = sc_card_ctl(card, SC_CARDCTL_AUTHENTIC_SDO_STORE, sdo); + LOGN_TEST_RET(ctx, rv, "store IAS SDO PRIVATE KEY failed"); + + authentic_free_sdo_data(sdo); + sc_pkcs15_free_object_content(object); + + LOGN_FUNC_RETURN(ctx, rv); +} + + +static int +authentic_pkcs15_delete_rsa_sdo (struct sc_profile *profile, struct sc_pkcs15_card *p15card, + struct sc_pkcs15_prkey_info *key_info) +{ + struct sc_context *ctx = p15card->card->ctx; + unsigned long caps = p15card->card->caps; + struct sc_authentic_sdo sdo; + struct sc_file *file = NULL; + int rv; + + LOGN_FUNC_CALLED(ctx); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "delete SDO RSA key (ref:%i,size:%i)", key_info->key_reference, key_info->modulus_length); + + rv = authentic_pkcs15_new_file(profile, p15card->card, SC_PKCS15_TYPE_PRKEY_RSA, key_info->key_reference, &file); + LOGN_TEST_RET(ctx, rv, "PRKEY_RSA instantiation file error"); + + p15card->card->caps &= ~SC_CARD_CAP_USE_FCI_AC; + rv = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_DELETE); + p15card->card->caps = caps; + LOGN_TEST_RET(ctx, rv, "'DELETE' authentication failed for parent RSA key"); + + sdo.magic = AUTHENTIC_SDO_MAGIC; + sdo.docp.id = key_info->key_reference & ~AUTHENTIC_OBJECT_REF_FLAG_LOCAL; + sdo.docp.mech = authentic_v3_rsa_mechs[(key_info->modulus_length - 1024) / 256]; + + rv = sc_card_ctl(p15card->card, SC_CARDCTL_AUTHENTIC_SDO_DELETE, &sdo); + if (rv == SC_ERROR_DATA_OBJECT_NOT_FOUND) + rv = SC_SUCCESS; + LOGN_TEST_RET(ctx, rv, "SC_CARDCTL_AUTHENTIC_SDO_DELETE failed for private key"); + + LOGN_FUNC_RETURN(ctx, rv); +} + + +static int +authentic_pkcs15_delete_object (struct sc_profile *profile, struct sc_pkcs15_card *p15card, + unsigned int type, const void *data, const struct sc_path *path) +{ + struct sc_context *ctx = p15card->card->ctx; + int rv; + + LOGN_FUNC_CALLED(ctx); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "delete PKCS15 object: type %X; path %s\n", type, sc_print_path(path)); + + switch(type & SC_PKCS15_TYPE_CLASS_MASK) { + case SC_PKCS15_TYPE_PRKEY: + rv = authentic_pkcs15_delete_rsa_sdo (profile, p15card, (struct sc_pkcs15_prkey_info *)data); + LOGN_FUNC_RETURN(ctx, rv); + case SC_PKCS15_TYPE_PUBKEY: + LOGN_FUNC_RETURN(ctx, SC_SUCCESS); + default: + LOGN_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); + } + + LOGN_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static int +authentic_store_pubkey(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *object, + struct sc_pkcs15_der *data, struct sc_path *path) +{ + struct sc_context *ctx = p15card->card->ctx; + struct sc_pkcs15_pubkey_info *pubkey_info = (struct sc_pkcs15_pubkey_info *)object->data; + struct sc_pkcs15_prkey_info *prkey_info = NULL; + struct sc_pkcs15_object *prkey_object = NULL; + int rv; + + LOGN_FUNC_CALLED(ctx); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Public Key id '%s'", sc_pkcs15_print_id(&pubkey_info->id)); + + rv = sc_pkcs15_find_prkey_by_id(p15card, &pubkey_info->id, &prkey_object); + LOGN_TEST_RET(ctx, rv, "Find related PrKey error"); + + prkey_info = (struct sc_pkcs15_prkey_info *)prkey_object->data; + + pubkey_info->key_reference = prkey_info->key_reference; + + pubkey_info->access_flags = prkey_info->access_flags & SC_PKCS15_PRKEY_ACCESS_LOCAL; + pubkey_info->access_flags |= SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE; + + pubkey_info->native = 0; + + pubkey_info->usage |= prkey_info->usage & SC_PKCS15_PRKEY_USAGE_SIGN ? SC_PKCS15_PRKEY_USAGE_VERIFY : 0; + pubkey_info->usage |= prkey_info->usage & SC_PKCS15_PRKEY_USAGE_SIGNRECOVER ? SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER : 0; + pubkey_info->usage |= prkey_info->usage & SC_PKCS15_PRKEY_USAGE_NONREPUDIATION ? SC_PKCS15_PRKEY_USAGE_VERIFY : 0; + pubkey_info->usage |= prkey_info->usage & SC_PKCS15_PRKEY_USAGE_DECRYPT ? SC_PKCS15_PRKEY_USAGE_ENCRYPT : 0; + pubkey_info->usage |= prkey_info->usage & SC_PKCS15_PRKEY_USAGE_UNWRAP ? SC_PKCS15_PRKEY_USAGE_WRAP : 0; + + authentic_pkcs15_add_access_rule(object, SC_PKCS15_ACCESS_RULE_MODE_READ, NULL); + +#if 0 + memcpy(&pubkey_info->algo_refs[0], &prkey_info->algo_refs[0], sizeof(pubkey_info->algo_refs)); +#else + printf("%s +%i: FiXME\n", __FILE__, __LINE__); +#endif + + LOGN_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static int +authentic_emu_store_data(struct sc_pkcs15_card *p15card, struct sc_profile *profile, + struct sc_pkcs15_object *object, + struct sc_pkcs15_der *data, struct sc_path *path) + +{ + struct sc_context *ctx = p15card->card->ctx; + int rv = SC_ERROR_NOT_IMPLEMENTED; + + LOGN_FUNC_CALLED(ctx); + + switch (object->type & SC_PKCS15_TYPE_CLASS_MASK) { + case SC_PKCS15_TYPE_PUBKEY: + rv = authentic_store_pubkey(p15card, profile, object, data, path); + break; + } + + LOGN_FUNC_RETURN(ctx, rv); +} + + +static int +authentic_emu_update_tokeninfo(struct sc_profile *profile, struct sc_pkcs15_card *p15card, + struct sc_pkcs15_tokeninfo *tinfo) +{ + struct sc_context *ctx = p15card->card->ctx; + struct sc_file *file = NULL; + struct sc_path path; + unsigned char buffer[8]; + int rv,len; + + sc_format_path(AUTHENTIC_CACHE_TIMESTAMP_PATH, &path); + rv = sc_select_file(p15card->card, &path, &file); + if (!rv) { + rv = sc_get_challenge(p15card->card, buffer, sizeof(buffer)); + LOGN_TEST_RET(ctx, rv, "Get challenge error"); + + len = file->size > sizeof(buffer) ? sizeof(buffer) : file->size; + rv = sc_update_binary(p15card->card, 0, buffer, len, 0); + LOGN_TEST_RET(ctx, rv, "Get challenge error"); + + sc_file_free(file); + } + + LOGN_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static struct sc_pkcs15init_operations +sc_pkcs15init_authentic_operations = { + authentic_pkcs15_erase_card, + NULL, /* init_card */ + NULL, /* create_dir */ + NULL, /* create_domain */ + NULL, /* select_pin_reference */ + NULL, /* create_pin */ + authentic_pkcs15_select_key_reference, + authentic_pkcs15_create_key, + authentic_pkcs15_store_key, + authentic_pkcs15_generate_key, + NULL, /* encode private key */ + NULL, /* encode public key */ + NULL, /* finalize_card */ + authentic_pkcs15_delete_object, + + /* pkcs15init emulation */ + NULL, + NULL, + authentic_emu_update_tokeninfo, + NULL, + authentic_emu_store_data, + + NULL, /* sanity_check */ +}; + + +struct sc_pkcs15init_operations * +sc_pkcs15init_get_authentic_ops(void) +{ + return &sc_pkcs15init_authentic_operations; +} diff --git a/src/pkcs15init/pkcs15-init.h b/src/pkcs15init/pkcs15-init.h index 5466c261f9..5940450745 100644 --- a/src/pkcs15init/pkcs15-init.h +++ b/src/pkcs15init/pkcs15-init.h @@ -386,7 +386,7 @@ extern struct sc_pkcs15init_operations *sc_pkcs15init_get_entersafe_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_rtecp_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_westcos_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_myeid_ops(void); -/* extern struct sc_pkcs15init_operations *sc_pkcs15init_get_authentic_ops(void); */ +extern struct sc_pkcs15init_operations *sc_pkcs15init_get_authentic_ops(void); #ifdef __cplusplus } diff --git a/src/pkcs15init/pkcs15-lib.c b/src/pkcs15init/pkcs15-lib.c index 5d09fc82ff..ad962d75bf 100644 --- a/src/pkcs15init/pkcs15-lib.c +++ b/src/pkcs15init/pkcs15-lib.c @@ -151,7 +151,7 @@ static struct profile_operations { { "rutoken_ecp", (void *) sc_pkcs15init_get_rtecp_ops }, { "westcos", (void *) sc_pkcs15init_get_westcos_ops }, { "myeid", (void *) sc_pkcs15init_get_myeid_ops }, - /* { "authentic", (void *) sc_pkcs15init_get_authentic_ops }, */ + { "authentic", (void *) sc_pkcs15init_get_authentic_ops }, { NULL, NULL }, }; diff --git a/win32/opensc-msi/OpenSC.wxs b/win32/opensc-msi/OpenSC.wxs index 2894be2d78..8515deaeb5 100755 --- a/win32/opensc-msi/OpenSC.wxs +++ b/win32/opensc-msi/OpenSC.wxs @@ -113,6 +113,10 @@ + + + @@ -160,6 +164,7 @@ +