902 lines
33 KiB
C
902 lines
33 KiB
C
/*
|
|
* Copyright (C) 2018 Frank Morgner <frankmorgner@gmail.com>
|
|
*
|
|
* This file is part of OpenSC.
|
|
*
|
|
* 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 "config.h"
|
|
#endif
|
|
|
|
#include "fread_to_eof.h"
|
|
#include <string.h>
|
|
#include "goid-tool-cmdline.h"
|
|
#include "libopensc/asn1.h"
|
|
#include "libopensc/log.h"
|
|
#include "libopensc/opensc.h"
|
|
#include "sm/sm-eac.h"
|
|
#ifdef ENABLE_OPENPACE
|
|
#include <eac/eac.h>
|
|
#endif
|
|
#include <stdlib.h>
|
|
#include "util.h"
|
|
#include <ctype.h>
|
|
|
|
const unsigned char aid_soc_manager[] = {
|
|
0xD2, 0x76, 0x00, 0x01, 0x72, 0x53, 0x6F, 0x43, 0x4D, 0x01
|
|
};
|
|
static const unsigned char paccess_aid[] = {
|
|
0xD2, 0x76, 0x00, 0x01, 0x72, 0x50, 0x41, 0x63, 0x63, 0x01,
|
|
};
|
|
static const char *app_name = "goid-tool";
|
|
|
|
#define SOCM_AUTHOBJECT_PIN 0x80
|
|
#define SOCM_AUTHOBJECT_BIO 0x40
|
|
#define SOCM_AUTHOBJECT_GP 0x20
|
|
|
|
void
|
|
print_permissions(u8 permissions)
|
|
{
|
|
size_t perms_printed = 0;
|
|
if (permissions & SOCM_AUTHOBJECT_PIN) {
|
|
printf("verification of PIN");
|
|
perms_printed++;
|
|
}
|
|
if (permissions & SOCM_AUTHOBJECT_BIO) {
|
|
printf("%s BIO", perms_printed ? " or" : "verification of");
|
|
perms_printed++;
|
|
}
|
|
if (permissions & SOCM_AUTHOBJECT_GP) {
|
|
printf("%s GP key", perms_printed ? " or" : "verification of");
|
|
perms_printed++;
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
void
|
|
soc_info(sc_context_t *ctx, sc_card_t *card)
|
|
{
|
|
sc_apdu_t apdu;
|
|
unsigned char rbuf[SC_MAX_APDU_RESP_SIZE];
|
|
u8 information_applets[SC_MAX_APDU_RESP_SIZE];
|
|
size_t information_applets_len = sizeof information_applets;
|
|
int pin_initialized = 0, bio_initialized = 0;
|
|
int pin_max_retries = 0, pin_cur_retries = 0, bio_max_retries = 0, bio_cur_retries = 0;
|
|
int pin_length = 0;
|
|
u8 pin_unblock = 0, pin_change = 0, bio_unblock = 0, bio_change = 0;
|
|
size_t pin_change_len = sizeof pin_change, pin_unblock_len = sizeof pin_unblock,
|
|
bio_change_len = sizeof bio_change, bio_unblock_len = sizeof bio_unblock;
|
|
int bio_count = 0;
|
|
u8 bio_initialized_templates[2];
|
|
size_t bio_initialized_templates_len = sizeof bio_initialized_templates;
|
|
|
|
struct sc_asn1_entry rapdu_get_information[] = {
|
|
{ "Sequence of (applet register)", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_SEQUENCE|SC_ASN1_CONS, 0, NULL, NULL },
|
|
{ "Initialized PIN", SC_ASN1_STRUCT, SC_ASN1_APP|SC_ASN1_CONS|0x02, SC_ASN1_OPTIONAL, NULL, NULL },
|
|
{ "Initialized BIO", SC_ASN1_STRUCT, SC_ASN1_APP|SC_ASN1_CONS|0x03, SC_ASN1_OPTIONAL, NULL, NULL },
|
|
{ NULL , 0 , 0 , 0 , NULL , NULL }
|
|
};
|
|
|
|
struct sc_asn1_entry rapdu_get_information_pin[] = {
|
|
{ "Initialization state of the PIN", SC_ASN1_BOOLEAN, SC_ASN1_TAG_BOOLEAN, 0, NULL, NULL },
|
|
{ "maximum remaining tries", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL },
|
|
{ "current remaining tries", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL },
|
|
{ "Unblock requirements Mask", SC_ASN1_OCTET_STRING, SC_ASN1_APP|0x1, 0, NULL, NULL },
|
|
{ "Change requirements Mask", SC_ASN1_OCTET_STRING, SC_ASN1_APP|0x2, 0, NULL, NULL },
|
|
{ "PIN size", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL },
|
|
{ NULL , 0 , 0 , 0 , NULL , NULL }
|
|
};
|
|
|
|
struct sc_asn1_entry rapdu_get_information_bio[] = {
|
|
{ "Initialization state of the BIO", SC_ASN1_BOOLEAN, SC_ASN1_TAG_BOOLEAN, 0, NULL, NULL },
|
|
{ "maximum remaining tries", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL },
|
|
{ "current remaining tries", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL },
|
|
{ "Unblock requirements Mask", SC_ASN1_OCTET_STRING, SC_ASN1_APP|0x1, 0, NULL, NULL },
|
|
{ "Change requirements Mask", SC_ASN1_OCTET_STRING, SC_ASN1_APP|0x2, 0, NULL, NULL },
|
|
{ "Min minutiae", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL },
|
|
{ "Max minutiae", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL },
|
|
{ "number of templates", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL },
|
|
{ "Bitmap of initialized templates", SC_ASN1_BIT_STRING, SC_ASN1_TAG_BIT_STRING, 0, NULL, NULL },
|
|
{ "Algorithm parameters, allocation strategy", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_OCTET_STRING, 0, NULL, NULL },
|
|
{ NULL , 0 , 0 , 0 , NULL , NULL }
|
|
};
|
|
|
|
sc_format_apdu_ex(&apdu, 0x00, 0x61, 0x00, 0x00, NULL, 0, rbuf, sizeof rbuf);
|
|
apdu.cla = 0x80;
|
|
|
|
if (sc_transmit_apdu(card, &apdu) != SC_SUCCESS) {
|
|
return;
|
|
}
|
|
|
|
sc_format_asn1_entry(rapdu_get_information + 0, information_applets, &information_applets_len, 0);
|
|
sc_format_asn1_entry(rapdu_get_information + 1, rapdu_get_information_pin, NULL, 0);
|
|
sc_format_asn1_entry(rapdu_get_information + 2, rapdu_get_information_bio, NULL, 0);
|
|
|
|
sc_format_asn1_entry(rapdu_get_information_pin + 0, &pin_initialized, NULL, 0);
|
|
sc_format_asn1_entry(rapdu_get_information_pin + 1, &pin_max_retries, NULL, 0);
|
|
sc_format_asn1_entry(rapdu_get_information_pin + 2, &pin_cur_retries, NULL, 0);
|
|
sc_format_asn1_entry(rapdu_get_information_pin + 3, &pin_unblock, &pin_unblock_len, 0);
|
|
sc_format_asn1_entry(rapdu_get_information_pin + 4, &pin_change, &pin_change_len, 0);
|
|
sc_format_asn1_entry(rapdu_get_information_pin + 5, &pin_length, NULL, 0);
|
|
|
|
sc_format_asn1_entry(rapdu_get_information_bio + 0, &bio_initialized, NULL, 0);
|
|
sc_format_asn1_entry(rapdu_get_information_bio + 1, &bio_max_retries, NULL, 0);
|
|
sc_format_asn1_entry(rapdu_get_information_bio + 2, &bio_cur_retries, NULL, 0);
|
|
sc_format_asn1_entry(rapdu_get_information_bio + 3, &bio_unblock, &bio_unblock_len, 0);
|
|
sc_format_asn1_entry(rapdu_get_information_bio + 4, &bio_change, &bio_change_len, 0);
|
|
sc_format_asn1_entry(rapdu_get_information_bio + 7, &bio_count, NULL, 0);
|
|
sc_format_asn1_entry(rapdu_get_information_bio + 8, bio_initialized_templates, &bio_initialized_templates_len, 0);
|
|
|
|
if (sc_asn1_decode(ctx, rapdu_get_information,
|
|
apdu.resp, apdu.resplen, NULL, NULL) != SC_SUCCESS) {
|
|
return;
|
|
}
|
|
|
|
if (rapdu_get_information[0].flags & SC_ASN1_PRESENT && information_applets_len > 0) {
|
|
const unsigned char *p = information_applets, *end = information_applets + information_applets_len;
|
|
unsigned int cla = 0, tag = 0;
|
|
size_t length = information_applets_len;
|
|
|
|
if (SC_SUCCESS == sc_asn1_read_tag(&p, length, &cla, &tag, &length)
|
|
&& cla == SC_ASN1_TAG_UNIVERSAL && tag == SC_ASN1_TAG_INTEGER) {
|
|
int applet_count = 0;
|
|
/* number of applets */
|
|
if (SC_SUCCESS == sc_asn1_decode_integer(p, length, &applet_count)) {
|
|
printf("SoCManager knows %d applet%s%s\n", applet_count,
|
|
applet_count == 1 ? "" : "s", applet_count == 0 ? "" : ":");
|
|
/* AID of client applet #x */
|
|
for (p += length, length = end - p;
|
|
p < end;
|
|
p += length, length = end - p) {
|
|
size_t i;
|
|
if (SC_SUCCESS != sc_asn1_read_tag(&p, length, &cla, &tag, &length)
|
|
|| p == NULL || cla != SC_ASN1_TAG_CONTEXT) {
|
|
break;
|
|
}
|
|
putchar('\t');
|
|
util_hex_dump(stdout, p, length, "");
|
|
/* align with the maximum lenght of an AID */
|
|
for (i = length; i < 0x10 + 1; i++)
|
|
printf(" ");
|
|
|
|
/* i now counts the number of flags that were printed */
|
|
i = 0;
|
|
if (tag & 0x02) {
|
|
printf("default selected");
|
|
i++;
|
|
}
|
|
if (tag & 0x01) {
|
|
printf("%sinteracts with SoCManager", i ? ", " : "");
|
|
i++;
|
|
}
|
|
if (tag & 0x04) {
|
|
printf("%sBIO enabled", i ? ", " : "");
|
|
i++;
|
|
}
|
|
if (tag & 0x08) {
|
|
printf("%sPIN enabled", i ? ", " : "");
|
|
i++;
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (rapdu_get_information[1].flags & SC_ASN1_PRESENT) {
|
|
if (pin_initialized) {
|
|
printf("PIN is initialized with %d digits (%d of %d tries left).\n",
|
|
pin_length, pin_cur_retries, pin_max_retries);
|
|
} else {
|
|
printf("PIN is not initialized.\n");
|
|
}
|
|
printf("\tChanging PIN requires ");
|
|
print_permissions(pin_change);
|
|
printf("\tUnblocking PIN requires ");
|
|
print_permissions(pin_unblock);
|
|
}
|
|
|
|
if (rapdu_get_information[2].flags & SC_ASN1_PRESENT) {
|
|
if (bio_initialized) {
|
|
int bio_used = 0;
|
|
size_t i, j;
|
|
for (i = 0; i < sizeof bio_initialized_templates; i++) {
|
|
for (j = 0; j < 8; j++) {
|
|
if (bio_initialized_templates[i] >> j & 0x1)
|
|
bio_used++;
|
|
}
|
|
}
|
|
printf("BIO is initialized with %d of %d templates (%d of %d tries left).\n",
|
|
bio_used, bio_count, bio_cur_retries, bio_max_retries);
|
|
} else {
|
|
printf("BIO is not initialized.\n");
|
|
}
|
|
printf("\tChanging BIO requires ");
|
|
print_permissions(bio_change);
|
|
printf("\tUnblocking BIO requires ");
|
|
print_permissions(bio_unblock);
|
|
}
|
|
}
|
|
|
|
int
|
|
soc_verify(sc_card_t *card, unsigned char p2)
|
|
{
|
|
int ok = 0;
|
|
sc_apdu_t apdu;
|
|
sc_format_apdu_ex(&apdu, 0x00, 0x20, 0x00, p2, NULL, 0, NULL, 0);
|
|
SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
sc_transmit_apdu(card, &apdu),
|
|
"Verification failed");
|
|
SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
sc_check_sw(card, apdu.sw1, apdu.sw2),
|
|
"Verification failed");
|
|
ok = 1;
|
|
err:
|
|
return ok;
|
|
}
|
|
|
|
int
|
|
soc_reset(sc_card_t *card, unsigned char p2)
|
|
{
|
|
int ok = 0;
|
|
sc_apdu_t apdu;
|
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0xFF, p2);
|
|
SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
sc_transmit_apdu(card, &apdu),
|
|
"Reset failed");
|
|
if (apdu.sw1 != 0x63) {
|
|
SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
sc_check_sw(card, apdu.sw1, apdu.sw2),
|
|
"Reset failed");
|
|
}
|
|
ok = 1;
|
|
err:
|
|
return ok;
|
|
}
|
|
|
|
int
|
|
soc_change(sc_card_t *card, unsigned char p1, unsigned char p2)
|
|
{
|
|
int ok = 0;
|
|
sc_apdu_t apdu;
|
|
sc_format_apdu_ex(&apdu, 0x00, 0x24, p1, p2, NULL, 0, NULL, 0);
|
|
SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
sc_transmit_apdu(card, &apdu),
|
|
"Changing secret failed");
|
|
while (apdu.sw1 == 0x91 && apdu.sw2 == 0x00) {
|
|
switch (p2) {
|
|
case SOCM_AUTHOBJECT_PIN:
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
"Verify your PIN on the card using the same position.");
|
|
break;
|
|
case SOCM_AUTHOBJECT_BIO:
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
"Verify your finger print on the card using the same position.");
|
|
break;
|
|
}
|
|
SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
sc_transmit_apdu(card, &apdu),
|
|
"Changing secret failed");
|
|
}
|
|
SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
sc_check_sw(card, apdu.sw1, apdu.sw2),
|
|
"Verification failed");
|
|
ok = 1;
|
|
err:
|
|
return ok;
|
|
}
|
|
|
|
int soc_main(struct sc_context *ctx, sc_card_t *card, struct gengetopt_args_info *cmdline)
|
|
{
|
|
int ok = 0;
|
|
sc_file_t *file = NULL;
|
|
struct sc_path path;
|
|
unsigned char soc_manager_minor = 0;
|
|
unsigned char soc_manager_major = 0;
|
|
unsigned char soc_reset_authobject = 0;
|
|
|
|
sc_path_set(&path, SC_PATH_TYPE_DF_NAME, aid_soc_manager, sizeof aid_soc_manager, 0, 0);
|
|
SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
sc_select_file(card, &path, &file), "SoCManager not found.");
|
|
if (file && file->prop_attr && file->prop_attr_len) {
|
|
size_t prop_len = 0;
|
|
const u8 *prop = sc_asn1_find_tag(ctx, file->prop_attr,
|
|
file->prop_attr_len, 0x81, &prop_len);
|
|
if (prop && prop_len == 2) {
|
|
soc_manager_major = prop[0];
|
|
soc_manager_minor = prop[1];
|
|
sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
"SoCManager version %u.%u",
|
|
soc_manager_major, soc_manager_minor);
|
|
}
|
|
}
|
|
|
|
if (cmdline->info_given) {
|
|
if ((soc_manager_major == 2 && soc_manager_minor < 7)
|
|
|| soc_manager_major < 2)
|
|
SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
SC_ERROR_NOT_SUPPORTED, "Get Information only supported with version 2.07 and later.");
|
|
soc_info(ctx, card);
|
|
}
|
|
if (cmdline->verify_pin_given) {
|
|
sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
"Verify PIN on the card.");
|
|
if (!soc_verify(card, SOCM_AUTHOBJECT_PIN))
|
|
goto err;
|
|
soc_reset_authobject |= SOCM_AUTHOBJECT_PIN;
|
|
}
|
|
if (cmdline->verify_bio_given) {
|
|
sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
"Verify finger print on the card.");
|
|
if (!soc_verify(card, SOCM_AUTHOBJECT_BIO))
|
|
goto err;
|
|
soc_reset_authobject |= SOCM_AUTHOBJECT_BIO;
|
|
}
|
|
if (cmdline->verify_pin_or_bio_given) {
|
|
sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
"Verify finger print or PIN on the card.");
|
|
if (!soc_verify(card, SOCM_AUTHOBJECT_PIN|SOCM_AUTHOBJECT_BIO))
|
|
goto err;
|
|
soc_reset_authobject |= SOCM_AUTHOBJECT_PIN|SOCM_AUTHOBJECT_BIO;
|
|
}
|
|
|
|
if (cmdline->new_pin_given) {
|
|
sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
"Initialize the PIN on the card.");
|
|
if (!soc_change(card, 0x00, SOCM_AUTHOBJECT_BIO))
|
|
goto err;
|
|
}
|
|
if (cmdline->new_bio_given) {
|
|
size_t i = 0;
|
|
while (i < cmdline->new_bio_given) {
|
|
sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
"Initialize finger print template %u on the card.",
|
|
(unsigned char) i);
|
|
if (!soc_change(card, (unsigned char) i, SOCM_AUTHOBJECT_BIO))
|
|
goto err;
|
|
i++;
|
|
}
|
|
}
|
|
|
|
ok = 1;
|
|
|
|
err:
|
|
if (soc_reset_authobject)
|
|
soc_reset(card, soc_reset_authobject);
|
|
|
|
return ok;
|
|
}
|
|
|
|
static int
|
|
paccess_construct_fci(struct sc_card *card, const sc_file_t *file,
|
|
u8 *out, size_t *outlen)
|
|
{
|
|
u8 *p = out;
|
|
u8 buf[64];
|
|
|
|
if (*outlen < 2)
|
|
return SC_ERROR_BUFFER_TOO_SMALL;
|
|
*p++ = 0x62;
|
|
p++;
|
|
|
|
buf[0] = (file->size >> 8) & 0xFF;
|
|
buf[1] = file->size & 0xFF;
|
|
sc_asn1_put_tag(0x80, buf, 2, p, *outlen - (p - out), &p);
|
|
|
|
buf[0] = (file->id >> 8) & 0xFF;
|
|
buf[1] = file->id & 0xFF;
|
|
sc_asn1_put_tag(0x83, buf, 2, p, *outlen - (p - out), &p);
|
|
|
|
memcpy(buf, file->sec_attr, file->sec_attr_len);
|
|
sc_asn1_put_tag(0x86, buf, file->sec_attr_len,
|
|
p, *outlen - (p - out), &p);
|
|
|
|
buf[0] = file->sid & 0xFF;
|
|
sc_asn1_put_tag(0x88, buf, 1, p, *outlen - (p - out), &p);
|
|
|
|
out[1] = p - out - 2;
|
|
|
|
*outlen = p - out;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
paccess_create_file(struct sc_card *card, size_t size, int fid, u8 *sec_attr, size_t sec_attr_len, int sfid)
|
|
{
|
|
int ok = 0;
|
|
sc_file_t *file = sc_file_new();
|
|
if (!file)
|
|
goto err;
|
|
|
|
file->size = size;
|
|
file->id = fid;
|
|
file->sid = sfid;
|
|
file->sec_attr = sec_attr;
|
|
file->sec_attr_len = sec_attr_len;
|
|
|
|
card->ops->construct_fci = paccess_construct_fci;
|
|
SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
sc_create_file(card, file), "Create file failed.");
|
|
|
|
ok = 1;
|
|
err:
|
|
return ok;
|
|
}
|
|
|
|
int
|
|
paccess_delete_file(struct sc_card *card, int fid)
|
|
{
|
|
int ok = 0;
|
|
u8 buf[2];
|
|
struct sc_path path;
|
|
buf[0] = (fid >> 8) & 0xFF;
|
|
buf[1] = fid & 0xFF;
|
|
sc_path_set(&path, SC_PATH_TYPE_FILE_ID, buf, sizeof buf, 0, 0);
|
|
SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
sc_delete_file(card, &path), "Delete file failed.");
|
|
ok = 1;
|
|
err:
|
|
return ok;
|
|
}
|
|
|
|
int
|
|
paccess_get_security_attributes(struct sc_context *ctx, const char *ac, int* chatbits, size_t chatbits_len, u8 sec_attr[2])
|
|
{
|
|
int ok = 0;
|
|
memset(sec_attr, 0, 2);
|
|
if (!ac || 0 == strcmp(ac, "never")) {
|
|
sec_attr[0] |= 0xFF;
|
|
} else if (0 == strcmp(ac, "always")) {
|
|
/* nothing else to do */
|
|
} else {
|
|
size_t i;
|
|
if (0 == strcmp(ac, "ta")) {
|
|
sec_attr[0] |= 0xA0;
|
|
} else if (0 == strcmp(ac, "sm")) {
|
|
sec_attr[0] |= 0xC0;
|
|
} else {
|
|
SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
SC_ERROR_INVALID_ARGUMENTS, "unknown access condition.");
|
|
}
|
|
for (i = 0; i < chatbits_len; i++) {
|
|
u8 byte = chatbits[i] / 8;
|
|
u8 bit = chatbits[i] % 8 + 1;
|
|
if (byte > 5)
|
|
SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
SC_ERROR_INVALID_ARGUMENTS, "Only CHAT bits with index 0..39 are available.");
|
|
sec_attr[0] |= 0x8 | byte;
|
|
sec_attr[1] |= bit;
|
|
}
|
|
}
|
|
ok = 1;
|
|
err:
|
|
return ok;
|
|
}
|
|
|
|
#define PXS_AUTHOBJECT_PIN 0x80
|
|
#define PXS_AUTHOBJECT_BIO 0x40
|
|
|
|
int
|
|
paccess_verify(sc_card_t *card, unsigned char p2)
|
|
{
|
|
int ok = 0;
|
|
sc_apdu_t apdu;
|
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x2E, 0x24, p2);
|
|
SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
sc_transmit_apdu(card, &apdu),
|
|
"Verification failed");
|
|
SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
sc_check_sw(card, apdu.sw1, apdu.sw2),
|
|
"Verification failed");
|
|
ok = 1;
|
|
err:
|
|
return ok;
|
|
}
|
|
|
|
int paccess_main(struct sc_context *ctx, sc_card_t *card, struct gengetopt_args_info *cmdline)
|
|
{
|
|
int ok = 0, r;
|
|
sc_file_t *file = NULL;
|
|
struct sc_path path;
|
|
size_t i, ef_cardsecurity_len = 0, privkey_len = 0, *certs_lens = NULL;
|
|
unsigned char *ef_cardsecurity = NULL, *privkey = NULL,
|
|
**certs = NULL;
|
|
unsigned char auxiliary_data[] = {0x67, 0x00};
|
|
unsigned char paccess_minor = 0;
|
|
unsigned char paccess_major = 0;
|
|
int pxs_reset_authobjects = 0;
|
|
|
|
sc_path_set(&path, SC_PATH_TYPE_DF_NAME, paccess_aid, sizeof paccess_aid, 0, 0);
|
|
SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
sc_select_file(card, &path, &file), "PAccess not found.");
|
|
if (file && file->prop_attr && file->prop_attr_len) {
|
|
const unsigned char *p, *end;
|
|
unsigned int cla = 0, tag = 0;
|
|
size_t length;
|
|
|
|
for (p = file->prop_attr, length = file->prop_attr_len, end = file->prop_attr + file->prop_attr_len;
|
|
p < end;
|
|
p += length, length = end - p) {
|
|
if (SC_SUCCESS != sc_asn1_read_tag(&p, length, &cla, &tag, &length)
|
|
|| p == NULL) {
|
|
break;
|
|
}
|
|
switch (cla | tag) {
|
|
case 0x81:
|
|
if (p && length == 2) {
|
|
paccess_major = p[0];
|
|
paccess_minor = p[1];
|
|
sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
"PAccess version %u.%u",
|
|
paccess_major, paccess_minor);
|
|
}
|
|
break;
|
|
case 0x82:
|
|
if (p && length == 1) {
|
|
sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
"Number of Session Contexts %u",
|
|
p[0]);
|
|
}
|
|
break;
|
|
case 0x87:
|
|
sc_debug_hex(ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
"Certificate Authority Reference of the primary CVCA trust anchor",
|
|
p, length);
|
|
break;
|
|
case 0x88:
|
|
sc_debug_hex(ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
"Certificate Authority Reference of the secondary CVCA trust anchor",
|
|
p, length);
|
|
break;
|
|
case 0x1fe5:
|
|
case 0x9F65:
|
|
if (p && length == 2) {
|
|
size_t max_command_size = (p[0]<<8)|p[1];
|
|
card->max_recv_size = max_command_size;
|
|
card->max_send_size = max_command_size;
|
|
sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
"Maximum data length in command message %"SC_FORMAT_LEN_SIZE_T"u bytes",
|
|
max_command_size);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (cmdline->certificate_given || cmdline->key_given) {
|
|
if (!fread_to_eof(cmdline->key_arg,
|
|
&privkey, &privkey_len)) {
|
|
SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
SC_ERROR_INVALID_ARGUMENTS, "Could not parse private key.\n");
|
|
}
|
|
|
|
certs = calloc(sizeof *certs, cmdline->certificate_given + 1);
|
|
certs_lens = calloc(sizeof *certs_lens,
|
|
cmdline->certificate_given + 1);
|
|
if (!certs || !certs_lens) {
|
|
SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, SC_ERROR_NOT_ENOUGH_MEMORY,
|
|
"Internal error.");
|
|
}
|
|
for (i = 0; i < cmdline->certificate_given; i++) {
|
|
if (!fread_to_eof(cmdline->certificate_arg[i],
|
|
(unsigned char **) &certs[i], &certs_lens[i])) {
|
|
SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
SC_ERROR_INVALID_ARGUMENTS, "Could not read certificate.\n");
|
|
}
|
|
}
|
|
|
|
#ifdef ENABLE_OPENPACE
|
|
EAC_init();
|
|
#endif
|
|
SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
perform_terminal_authentication(card,
|
|
(const unsigned char **) certs, certs_lens,
|
|
privkey, privkey_len,
|
|
auxiliary_data, sizeof auxiliary_data),
|
|
"Terminal authentication failed.");
|
|
|
|
SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
perform_chip_authentication(card,
|
|
&ef_cardsecurity, &ef_cardsecurity_len),
|
|
"Chip authentication failed.");
|
|
}
|
|
|
|
if (cmdline->verify_pin_given) {
|
|
sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
"Verify PIN on the card.");
|
|
if (!paccess_verify(card, PXS_AUTHOBJECT_PIN))
|
|
goto err;
|
|
pxs_reset_authobjects++;
|
|
}
|
|
if (cmdline->verify_bio_given) {
|
|
sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
"Verify finger print on the card.");
|
|
if (!paccess_verify(card, PXS_AUTHOBJECT_BIO))
|
|
goto err;
|
|
pxs_reset_authobjects++;
|
|
}
|
|
if (cmdline->verify_pin_or_bio_given) {
|
|
sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
"Verify finger print or PIN on the card.");
|
|
if (!paccess_verify(card, PXS_AUTHOBJECT_PIN|PXS_AUTHOBJECT_BIO))
|
|
goto err;
|
|
pxs_reset_authobjects++;
|
|
}
|
|
|
|
for (i = 0; i < cmdline->delete_dg_given; i++) {
|
|
int fid = 0x0100 | cmdline->delete_dg_arg[i];
|
|
|
|
if ((paccess_major == 2 && paccess_minor < 6)
|
|
|| paccess_major < 2)
|
|
SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
SC_ERROR_NOT_SUPPORTED, "Create File only supported with version 2.06 and later.");
|
|
if (!paccess_delete_file(card, fid))
|
|
goto err;
|
|
}
|
|
|
|
for (i = 0; i < cmdline->create_dg_given; i++) {
|
|
u8 sec_attr[4];
|
|
int fid = 0x0100 | cmdline->create_dg_arg[i];
|
|
|
|
if ((paccess_major == 2 && paccess_minor < 6)
|
|
|| paccess_major < 2)
|
|
SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
SC_ERROR_NOT_SUPPORTED, "Create File only supported with version 2.06 and later.");
|
|
|
|
if (cmdline->new_size_arg < 0)
|
|
SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
SC_ERROR_INVALID_ARGUMENTS, "`--new-size' needs a positive size.\n");
|
|
if (!paccess_get_security_attributes(ctx, cmdline->new_read_ac_arg,
|
|
cmdline->new_read_ac_chatbit_arg,
|
|
cmdline->new_read_ac_chatbit_given, sec_attr + 0)
|
|
|| !paccess_get_security_attributes(ctx, cmdline->new_write_ac_arg,
|
|
cmdline->new_write_ac_chatbit_arg,
|
|
cmdline->new_write_ac_chatbit_given, sec_attr + 2)
|
|
|| !paccess_create_file(card, cmdline->new_size_arg, fid,
|
|
sec_attr, sizeof sec_attr, cmdline->create_dg_arg[i]))
|
|
goto err;
|
|
}
|
|
|
|
if (cmdline->out_file_given > 0 && cmdline->out_file_given != cmdline->read_dg_given) {
|
|
SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
SC_ERROR_INVALID_ARGUMENTS, "If `--out-file' is specified, it must be used as many times as `--read-dg'.\n");
|
|
}
|
|
|
|
for (i = 0; i < cmdline->read_dg_given; i++) {
|
|
u8 *ef = NULL;
|
|
size_t ef_len = 0;
|
|
r = iso7816_read_binary_sfid(card, cmdline->read_dg_arg[i],
|
|
&ef, &ef_len);
|
|
if (r >= 0) {
|
|
if (cmdline->out_file_given == cmdline->read_dg_given) {
|
|
FILE *f = fopen(cmdline->out_file_arg[i], "wb");
|
|
if (f) {
|
|
fwrite(ef, ef_len, 1, f);
|
|
fclose(f);
|
|
} else {
|
|
sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
"Error opening %s: %s\n",
|
|
cmdline->out_file_arg[i], strerror(errno));
|
|
r = SC_ERROR_FILE_NOT_FOUND;
|
|
}
|
|
} else {
|
|
char label[32];
|
|
snprintf(label, sizeof label, "Data Group %u", (unsigned char) cmdline->read_dg_arg[i]);
|
|
sc_debug_hex(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, label, ef, ef_len);
|
|
}
|
|
free(ef);
|
|
}
|
|
SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, r,
|
|
"Error reading data group.");
|
|
}
|
|
|
|
if (cmdline->print_cardid_given) {
|
|
u8 *ef = NULL;
|
|
size_t ef_len = 0;
|
|
r = iso7816_read_binary_sfid(card, 0x1E, &ef, &ef_len);
|
|
if (r >= 0) {
|
|
const u8 *p = ef;
|
|
unsigned int cla = 0, tag = 0;
|
|
if (SC_SUCCESS == sc_asn1_read_tag(&p, ef_len,
|
|
&cla, &tag, &ef_len)
|
|
&& (tag | cla) == 0x7E
|
|
&& SC_SUCCESS == sc_asn1_read_tag(&p, ef_len,
|
|
&cla, &tag, &ef_len)
|
|
&& (tag | cla) == 0x13) {
|
|
const char *cardid = (const char *) p;
|
|
while (cardid && ef_len) {
|
|
if (isprint(*cardid)) {
|
|
printf("%c", *cardid);
|
|
} else {
|
|
printf(".");
|
|
}
|
|
cardid++;
|
|
ef_len--;
|
|
}
|
|
if (cardid)
|
|
printf("\n");
|
|
} else {
|
|
r = SC_ERROR_INVALID_DATA;
|
|
}
|
|
}
|
|
free(ef);
|
|
SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, r,
|
|
"Error reading card ID.");
|
|
}
|
|
|
|
if (cmdline->print_paccessid_given) {
|
|
u8 *ef = NULL;
|
|
size_t ef_len = 0;
|
|
r = iso7816_read_binary_sfid(card, 0x06, &ef, &ef_len);
|
|
if (r >= 0) {
|
|
const u8 *p = ef;
|
|
unsigned int cla = 0, tag = 0;
|
|
if (SC_SUCCESS == sc_asn1_read_tag(&p, ef_len,
|
|
&cla, &tag, &ef_len)
|
|
&& (tag | cla) == 0x66
|
|
&& SC_SUCCESS == sc_asn1_read_tag((const u8 **) &p, ef_len,
|
|
&cla, &tag, &ef_len)
|
|
&& (tag | cla) == 0x13) {
|
|
const char *paccessid = (const char *) p;
|
|
while (paccessid && ef_len) {
|
|
if (isprint(*paccessid)) {
|
|
printf("%c", *paccessid);
|
|
} else {
|
|
printf(".");
|
|
}
|
|
paccessid++;
|
|
ef_len--;
|
|
}
|
|
if (paccessid)
|
|
printf("\n");
|
|
} else {
|
|
r = SC_ERROR_INVALID_DATA;
|
|
}
|
|
}
|
|
free(ef);
|
|
SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, r,
|
|
"Error reading card ID.");
|
|
}
|
|
|
|
if (cmdline->in_file_given != cmdline->write_dg_given) {
|
|
SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
SC_ERROR_INVALID_ARGUMENTS, "If `--in-file' is specified, it must be used as many times as `--write-dg'.\n");
|
|
}
|
|
|
|
for (i = 0; i < cmdline->write_dg_given; i++) {
|
|
u8 *ef = NULL;
|
|
size_t ef_len = 0;
|
|
if (!fread_to_eof(cmdline->in_file_arg[i],
|
|
&ef, &ef_len)) {
|
|
SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
|
|
SC_ERROR_INVALID_ARGUMENTS, "Could not read input file.\n");
|
|
}
|
|
r = iso7816_update_binary_sfid(card, cmdline->write_dg_arg[i], ef, ef_len);
|
|
free(ef);
|
|
SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, r,
|
|
"Error writing data group.");
|
|
}
|
|
|
|
if (cmdline->write_cardid_arg) {
|
|
size_t cardid_len = strlen(cmdline->write_cardid_arg);
|
|
u8 ef[256];
|
|
if (cardid_len > (sizeof ef) - 4) {
|
|
SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, SC_ERROR_INVALID_ARGUMENTS,
|
|
"Card ID too long.");
|
|
}
|
|
ef[0] = 0x7E;
|
|
ef[1] = 2 + cardid_len;
|
|
ef[2] = 0x13;
|
|
ef[3] = cardid_len;
|
|
memcpy(ef + 4, cmdline->write_cardid_arg, cardid_len);
|
|
r = iso7816_update_binary_sfid(card, 0x1E, ef, 4 + cardid_len);
|
|
SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, r,
|
|
"Error writing card ID.");
|
|
}
|
|
|
|
if (cmdline->write_paccessid_arg) {
|
|
size_t paccessid_len = strlen(cmdline->write_paccessid_arg);
|
|
u8 ef[256];
|
|
if (paccessid_len > (sizeof ef) - 4) {
|
|
SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, SC_ERROR_INVALID_ARGUMENTS,
|
|
"Card ID too long.");
|
|
}
|
|
ef[0] = 0x66;
|
|
ef[1] = 2 + paccessid_len;
|
|
ef[2] = 0x13;
|
|
ef[3] = paccessid_len;
|
|
memcpy(ef + 4, cmdline->write_paccessid_arg, paccessid_len);
|
|
r = iso7816_update_binary_sfid(card, 0x06, ef, 4 + paccessid_len);
|
|
SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, r,
|
|
"Error writing PAccess ID.");
|
|
}
|
|
|
|
ok = 1;
|
|
|
|
err:
|
|
if (pxs_reset_authobjects)
|
|
sc_reset(card, 0);
|
|
if (certs) {
|
|
for (i = 0; certs[i]; i++) {
|
|
free((unsigned char *) certs[i]);
|
|
}
|
|
free(certs);
|
|
}
|
|
free(ef_cardsecurity);
|
|
free(certs_lens);
|
|
free(privkey);
|
|
sc_file_free(file);
|
|
|
|
return ok;
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
struct gengetopt_args_info cmdline;
|
|
struct sc_context *ctx = NULL;
|
|
struct sc_card *card = NULL;
|
|
int r, fail = 1;
|
|
sc_context_param_t ctx_param;
|
|
|
|
if (cmdline_parser(argc, argv, &cmdline) != 0)
|
|
exit(1);
|
|
|
|
memset(&ctx_param, 0, sizeof(ctx_param));
|
|
ctx_param.ver = 0;
|
|
ctx_param.app_name = app_name;
|
|
|
|
r = sc_context_create(&ctx, &ctx_param);
|
|
if (r) {
|
|
fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r));
|
|
exit(1);
|
|
}
|
|
|
|
if (cmdline.verbose_given > 1) {
|
|
ctx->debug = cmdline.verbose_given;
|
|
sc_ctx_log_to_file(ctx, "stderr");
|
|
}
|
|
|
|
r = sc_set_card_driver(ctx, "default");
|
|
SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, r,
|
|
"Error selecting card driver.");
|
|
|
|
r = util_connect_card_ex(ctx, &card, cmdline.reader_arg, 0, 0, cmdline.verbose_given);
|
|
SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, r,
|
|
"Error connecting to card.");
|
|
|
|
if (cmdline.soc_mode_counter && !soc_main(ctx, card, &cmdline))
|
|
goto err;
|
|
if (cmdline.pxs_mode_counter && !paccess_main(ctx, card, &cmdline))
|
|
goto err;
|
|
if (cmdline.soc_mode_counter == 0 && cmdline.pxs_mode_counter == 0
|
|
&& (cmdline.verify_pin_given
|
|
|| cmdline.verify_bio_given
|
|
|| cmdline.verify_pin_or_bio_given)
|
|
&& !soc_main(ctx, card, &cmdline))
|
|
goto err;
|
|
|
|
fail = 0;
|
|
|
|
err:
|
|
sc_disconnect_card(card);
|
|
sc_release_context(ctx);
|
|
cmdline_parser_free (&cmdline);
|
|
|
|
return fail;
|
|
}
|
|
/*printf("%s:%d\n", __FILE__, __LINE__);*/
|