opensc/src/libopensc/card-flex.c

1358 lines
36 KiB
C
Raw Normal View History

/*
* card-flex.c: Support for Schlumberger cards
*
* Copyright (C) 2001, 2002 Juha Yrjölä <juha.yrjola@iki.fi>
*
* 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
*/
2015-04-22 21:55:33 +00:00
#if HAVE_CONFIG_H
#include "config.h"
2015-04-22 21:55:33 +00:00
#endif
#include <stdlib.h>
#include <string.h>
#include "internal.h"
#include "cardctl.h"
#define FLAG_KEYGEN 0x80000000
#define IS_CYBERFLEX(card) (card->type == SC_CARD_TYPE_FLEX_CYBER)
static const struct sc_atr_table flex_atrs[] = {
/* Cryptoflex */
/* 8k win2000 */
{ "3b:95:15:40:20:68:01:02:00:00", NULL, "Cryptoflex 8K", SC_CARD_TYPE_FLEX_CRYPTO, 0, NULL },
/* 8k */
{ "3B:95:15:40:FF:68:01:02:02:01", NULL, "Cryptoflex 8K", SC_CARD_TYPE_FLEX_CRYPTO, 0, NULL },
/* 8k */
{ "3B:95:15:40:FF:68:01:02:02:04", NULL, "Cryptoflex 8K", SC_CARD_TYPE_FLEX_CRYPTO, 0, NULL },
/* 8k */
{ "3B:85:40:20:68:01:01:05:01", NULL, "Cryptoflex 8K", SC_CARD_TYPE_FLEX_CRYPTO, 0, NULL },
/* 16k */
{ "3B:95:94:40:FF:63:01:01:02:01", NULL, "Cryptoflex 16K", SC_CARD_TYPE_FLEX_CRYPTO, FLAG_KEYGEN, NULL },
/* "16K+SS1" alias Cryptoflex 16 card with Standard Softmask V1 */
/* (taken from Cryptoflex Card Programmers Guide 4.5 Page xviii) */
/* last two bytes can be ignored - version of the softmask */
{ "3B:95:15:40:FF:63:01:01:02:01", "FF:FF:FF:FF:FF:FF:FF:FF:00:00",
"Cryptoflex 16K", SC_CARD_TYPE_FLEX_CRYPTO, FLAG_KEYGEN, NULL },
/* 32K v4 */
/* "32K+SS1" alias Cryptoflex 32 card with Standard Softmask V1 */
/* (taken from Cryptoflex Card Programmers Guide 4.5 Page xviii) */
/* last two bytes can be ignored - version of the softmask */
{ "3B:95:18:40:FF:64:02:01:01:02","FF:FF:FF:FF:FF:FF:FF:FF:00:00",
"Cryptoflex 32K v4", SC_CARD_TYPE_FLEX_CRYPTO, FLAG_KEYGEN, NULL },
/* "32K+e-gate" alias Cryptoflex e-gate 32K card */
/* (taken from Cryptoflex Card Programmers Guide 4.5 Page xviii) */
/* last two bytes can be ignored - version of the softmask */
{ "3B:95:18:40:FF:62:01:01:00:00", "FF:FF:FF:FF:FF:FF:FF:FF:00:00",
"Cryptoflex e-gate 32K", SC_CARD_TYPE_FLEX_CRYPTO, FLAG_KEYGEN, NULL },
/* 32K e-gate */
{ "3B:95:18:40:FF:62:01:02:01:04", NULL, "Cryptoflex 32K e-gate", SC_CARD_TYPE_FLEX_CRYPTO, FLAG_KEYGEN, NULL },
/* 32K e-gate v4 */
{ "3B:95:18:40:FF:62:04:01:01:05", NULL, "Cryptoflex 32K e-gate v4", SC_CARD_TYPE_FLEX_CRYPTO, FLAG_KEYGEN, NULL },
/* new cryptoflex 32k card - atr looks very similar to old 8k card */
{ "3b:95:15:40:ff:68:01:02:45:47", NULL, "Cryptoflex 32K", SC_CARD_TYPE_FLEX_CRYPTO, FLAG_KEYGEN, NULL },
{ "3B:E2:00:00:40:20:49:06", NULL, "Cryptoflex", SC_CARD_TYPE_FLEX_CRYPTO, 0, NULL },
/* + full DES option */
{ "3B:E2:00:00:40:20:49:05", NULL, "Cryptoflex", SC_CARD_TYPE_FLEX_CRYPTO, 0, NULL },
/* + Key Generation */
{ "3B:E2:00:00:40:20:49:07", NULL, "Cryptoflex", SC_CARD_TYPE_FLEX_CRYPTO, FLAG_KEYGEN, NULL },
/* + Key Generation */
{ "3B:85:40:20:68:01:01:03:05", NULL, "Cryptoflex", SC_CARD_TYPE_FLEX_CRYPTO, FLAG_KEYGEN, NULL },
/* Multiflex */
/* 3K */
{ "3B:02:14:50", NULL, "Multiflex 3K", SC_CARD_TYPE_FLEX_MULTI, 0, NULL },
/* 4K */
{ "3B:19:14:55:90:01:02:01:00:05:04:B0", NULL, "Multiflex 4K", SC_CARD_TYPE_FLEX_MULTI, 0, NULL },
/* 8K */
{ "3B:32:15:00:06:80", NULL, "Multiflex 8K", SC_CARD_TYPE_FLEX_MULTI, 0, NULL },
/* 8K + full DES option */
{ "3B:32:15:00:06:95", NULL, "Multiflex 8K", SC_CARD_TYPE_FLEX_MULTI, 0, NULL },
/* 8K */
{ "3B:19:14:59:01:01:0F:01:00:05:08:B0", NULL, "Multiflex 8K", SC_CARD_TYPE_FLEX_MULTI, 0, NULL },
/* 8K */
{ "3B:19:14:55:90:01:01:01:00:05:08:B0", NULL, "Multiflex 8K", SC_CARD_TYPE_FLEX_MULTI, 0, NULL },
/* Cyberflex Access */
/* Crypto */
{ "3B:16:94:81:10:06:01:81:3F", NULL, "Cyberflex Access", SC_CARD_TYPE_FLEX_CYBER, 0, NULL },
/* Aug. Crypto */
{ "3B:16:94:81:10:06:01:81:2F", NULL, "Cyberflex Access", SC_CARD_TYPE_FLEX_CYBER, 0, NULL },
{ NULL, NULL, NULL, 0, 0, NULL }
};
struct flex_private_data {
int rsa_key_ref;
/* Support card variations without having to
* do the if (card->type ...) thing
* all the time */
u8 aak_key_ref;
};
#define DRV_DATA(card) ((struct flex_private_data *) (card)->drv_data)
static struct sc_card_operations cryptoflex_ops;
static struct sc_card_operations cyberflex_ops;
static struct sc_card_operations *iso_ops;
static struct sc_card_driver cryptoflex_drv = {
"Schlumberger Multiflex/Cryptoflex",
"flex",
&cryptoflex_ops,
NULL, 0, NULL
};
static struct sc_card_driver cyberflex_drv = {
"Schlumberger Cyberflex",
"cyberflex",
&cyberflex_ops,
NULL, 0, NULL
};
static int flex_finish(sc_card_t *card)
{
free(card->drv_data);
return 0;
}
static int cryptoflex_match_card(sc_card_t *card)
{
int i;
i = _sc_match_atr(card, flex_atrs, NULL);
if (i < 0)
return 0;
switch (flex_atrs[i].type) {
case SC_CARD_TYPE_FLEX_CRYPTO:
case SC_CARD_TYPE_FLEX_MULTI:
card->name = flex_atrs[i].name;
card->type = flex_atrs[i].type;
card->flags = flex_atrs[i].flags;
return 1;
}
return 0;
}
static int cyberflex_match_card(sc_card_t *card)
{
int i;
i = _sc_match_atr(card, flex_atrs, NULL);
if (i < 0)
return 0;
switch (flex_atrs[i].type) {
case SC_CARD_TYPE_FLEX_CYBER:
card->name = flex_atrs[i].name;
card->type = flex_atrs[i].type;
card->flags = flex_atrs[i].flags;
return 1;
}
return 0;
}
static int flex_init(sc_card_t *card)
{
struct flex_private_data *data;
Do not cast the return value of malloc(3) and calloc(3) From http://en.wikipedia.org/wiki/Malloc#Casting_and_type_safety " Casting and type safety malloc returns a void pointer (void *), which indicates that it is a pointer to a region of unknown data type. One may "cast" (see type conversion) this pointer to a specific type, as in int *ptr = (int*)malloc(10 * sizeof (int)); When using C, this is considered bad practice; it is redundant under the C standard. Moreover, putting in a cast may mask failure to include the header stdlib.h, in which the prototype for malloc is found. In the absence of a prototype for malloc, the C compiler will assume that malloc returns an int, and will issue a warning in a context such as the above, provided the error is not masked by a cast. On certain architectures and data models (such as LP64 on 64 bit systems, where long and pointers are 64 bit and int is 32 bit), this error can actually result in undefined behavior, as the implicitly declared malloc returns a 32 bit value whereas the actually defined function returns a 64 bit value. Depending on calling conventions and memory layout, this may result in stack smashing. The returned pointer need not be explicitly cast to a more specific pointer type, since ANSI C defines an implicit conversion between the void pointer type and other pointers to objects. An explicit cast of malloc's return value is sometimes performed because malloc originally returned a char *, but this cast is unnecessary in standard C code.[4][5] Omitting the cast, however, creates an incompatibility with C++, which does require it. The lack of a specific pointer type returned from malloc is type-unsafe behaviour: malloc allocates based on byte count but not on type. This distinguishes it from the C++ new operator that returns a pointer whose type relies on the operand. (see C Type Safety). " See also http://www.opensc-project.org/pipermail/opensc-devel/2010-August/014586.html git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@4636 c6295689-39f2-0310-b995-f0e70906c6a9
2010-08-18 15:08:51 +00:00
if (!(data = malloc(sizeof(*data))))
return SC_ERROR_OUT_OF_MEMORY;
card->drv_data = data;
card->cla = 0xC0;
data->aak_key_ref = 1;
/* Override Cryptoflex defaults for specific card types */
switch (card->type) {
case SC_CARD_TYPE_FLEX_CYBER:
card->cla = 0x00;
data->aak_key_ref = 0;
break;
}
/* FIXME: Card type detection */
if (1) {
unsigned long flags;
flags = SC_ALGORITHM_RSA_RAW;
flags |= SC_ALGORITHM_RSA_HASH_NONE;
if (card->flags & FLAG_KEYGEN)
flags |= SC_ALGORITHM_ONBOARD_KEY_GEN;
_sc_card_add_rsa_alg(card, 512, flags, 0);
_sc_card_add_rsa_alg(card, 768, flags, 0);
_sc_card_add_rsa_alg(card, 1024, flags, 0);
_sc_card_add_rsa_alg(card, 2048, flags, 0);
}
/* SCardTransmit failed: 8010002f
* this can be solved with a small delay. */
msleep(100);
/* State that we have an RNG */
card->caps |= SC_CARD_CAP_RNG;
return 0;
}
static void
add_acl_entry(sc_card_t *card, sc_file_t *file, unsigned int op, u8 nibble)
{
struct flex_private_data *prv = DRV_DATA(card);
switch (nibble) {
case 0:
sc_file_add_acl_entry(file, op, SC_AC_NONE, SC_AC_KEY_REF_NONE);
break;
case 1:
sc_file_add_acl_entry(file, op, SC_AC_CHV, 1);
break;
case 2:
sc_file_add_acl_entry(file, op, SC_AC_CHV, 2);
break;
case 3:
sc_file_add_acl_entry(file, op, SC_AC_PRO, SC_AC_KEY_REF_NONE);
break;
case 4:
/* Assume the key is the AAK */
sc_file_add_acl_entry(file, op, SC_AC_AUT, prv->aak_key_ref);
break;
case 6:
sc_file_add_acl_entry(file, op, SC_AC_CHV, 1);
sc_file_add_acl_entry(file, op, SC_AC_PRO, SC_AC_KEY_REF_NONE);
break;
case 7:
sc_file_add_acl_entry(file, op, SC_AC_CHV, 2);
sc_file_add_acl_entry(file, op, SC_AC_PRO, SC_AC_KEY_REF_NONE);
break;
case 8:
sc_file_add_acl_entry(file, op, SC_AC_CHV, 1);
/* Assume the key is the AAK */
sc_file_add_acl_entry(file, op, SC_AC_AUT, prv->aak_key_ref);
break;
case 9:
sc_file_add_acl_entry(file, op, SC_AC_CHV, 2);
/* Assume the key is the AAK */
sc_file_add_acl_entry(file, op, SC_AC_AUT, prv->aak_key_ref);
break;
case 15:
sc_file_add_acl_entry(file, op, SC_AC_NEVER, SC_AC_KEY_REF_NONE);
break;
default:
sc_file_add_acl_entry(file, op, SC_AC_UNKNOWN, SC_AC_KEY_REF_NONE);
break;
}
}
static int
cryptoflex_get_ac_keys(sc_card_t *card, sc_file_t *file)
{
return 0;
}
static int
cryptoflex_process_file_attrs(sc_card_t *card, sc_file_t *file,
const u8 *buf, size_t buflen)
{
sc_context_t *ctx = card->ctx;
const u8 *p = buf + 2;
u8 b1, b2;
int is_mf = 0;
if (buflen < 14)
return -1;
b1 = *p++;
b2 = *p++;
file->size = (b1 << 8) + b2;
b1 = *p++;
b2 = *p++;
file->id = (b1 << 8) + b2;
if (file->id == 0x3F00)
is_mf = 1;
switch (*p) {
case 0x01:
file->type = SC_FILE_TYPE_WORKING_EF;
file->ef_structure = SC_FILE_EF_TRANSPARENT;
break;
case 0x02:
file->type = SC_FILE_TYPE_WORKING_EF;
file->ef_structure = SC_FILE_EF_LINEAR_FIXED;
break;
case 0x04:
file->type = SC_FILE_TYPE_WORKING_EF;
file->ef_structure = SC_FILE_EF_LINEAR_VARIABLE;
break;
case 0x06:
file->type = SC_FILE_TYPE_WORKING_EF;
file->ef_structure = SC_FILE_EF_CYCLIC;
break;
case 0x38:
file->type = SC_FILE_TYPE_DF;
break;
default:
2018-11-22 08:31:29 +00:00
sc_log(ctx, "invalid file type: 0x%02X\n", *p);
return SC_ERROR_UNKNOWN_DATA_RECEIVED;
}
p += 2;
if (file->type == SC_FILE_TYPE_DF) {
add_acl_entry(card, file, SC_AC_OP_LIST_FILES, (u8)(p[0] >> 4));
add_acl_entry(card, file, SC_AC_OP_DELETE, (u8)(p[1] >> 4));
add_acl_entry(card, file, SC_AC_OP_CREATE, (u8)(p[1] & 0x0F));
} else { /* EF */
add_acl_entry(card, file, SC_AC_OP_READ, (u8)(p[0] >> 4));
switch (file->ef_structure) {
case SC_FILE_EF_TRANSPARENT:
add_acl_entry(card, file, SC_AC_OP_UPDATE, (u8)(p[0] & 0x0F));
break;
case SC_FILE_EF_LINEAR_FIXED:
case SC_FILE_EF_LINEAR_VARIABLE:
add_acl_entry(card, file, SC_AC_OP_UPDATE, (u8)(p[0] & 0x0F));
break;
case SC_FILE_EF_CYCLIC:
break;
}
}
if (file->type != SC_FILE_TYPE_DF || is_mf) {
add_acl_entry(card, file, SC_AC_OP_REHABILITATE, (u8)(p[2] >> 4));
add_acl_entry(card, file, SC_AC_OP_INVALIDATE, (u8)(p[2] & 0x0F));
}
p += 3;
if (*p)
file->status = SC_FILE_STATUS_ACTIVATED;
else
file->status = SC_FILE_STATUS_INVALIDATED;
return cryptoflex_get_ac_keys(card, file);
}
static int
cyberflex_process_file_attrs(sc_card_t *card, sc_file_t *file,
const u8 *buf, size_t buflen)
{
sc_context_t *ctx = card->ctx;
const u8 *p = buf + 2;
const u8 *pos;
u8 b1, b2;
int is_mf = 0;
if (buflen < 14)
return -1;
b1 = *p++;
b2 = *p++;
file->size = (b1 << 8) + b2;
b1 = *p++;
b2 = *p++;
file->id = (b1 << 8) + b2;
switch (*p) {
case 0x01:
is_mf = 1;
break;
case 0x02:
file->type = SC_FILE_TYPE_DF;
break;
case 0x04:
file->type = SC_FILE_TYPE_WORKING_EF;
break;
default:
2018-11-22 08:31:29 +00:00
sc_log(ctx, "invalid file type: 0x%02X\n", *p);
return SC_ERROR_UNKNOWN_DATA_RECEIVED;
}
if (is_mf) {
sc_file_add_acl_entry(file, SC_AC_OP_LIST_FILES, SC_AC_AUT, 0);
sc_file_add_acl_entry(file, SC_AC_OP_DELETE, SC_AC_AUT, 0);
sc_file_add_acl_entry(file, SC_AC_OP_CREATE, SC_AC_AUT, 0);
} else {
p += 2;
if (file->type == SC_FILE_TYPE_DF) {
add_acl_entry(card, file, SC_AC_OP_LIST_FILES, (u8)(p[0] >> 4));
add_acl_entry(card, file, SC_AC_OP_DELETE, (u8)(p[1] >> 4));
add_acl_entry(card, file, SC_AC_OP_CREATE, (u8)(p[1] & 0x0F));
} else { /* EF */
add_acl_entry(card, file, SC_AC_OP_READ, (u8)(p[0] >> 4));
}
}
if (file->type != SC_FILE_TYPE_DF) {
add_acl_entry(card, file, SC_AC_OP_REHABILITATE, (u8)(p[2] >> 4));
add_acl_entry(card, file, SC_AC_OP_INVALIDATE, (u8)(p[2] & 0x0F));
}
pos = p;
p += 3;
if (*p++)
file->status = SC_FILE_STATUS_ACTIVATED;
else
file->status = SC_FILE_STATUS_INVALIDATED;
p++;
if (0 == is_mf) {
p++;
switch (*p) {
case 0x00:
file->ef_structure = SC_FILE_EF_TRANSPARENT;
break;
case 0x01:
file->ef_structure = SC_FILE_EF_LINEAR_FIXED;
break;
case 0x02:
file->ef_structure = SC_FILE_EF_LINEAR_VARIABLE;
break;
case 0x03:
file->ef_structure = SC_FILE_EF_CYCLIC;
break;
case 0x04:
break;
default:
2018-11-22 08:31:29 +00:00
sc_log(ctx, "invalid file type: 0x%02X\n", *p);
return SC_ERROR_UNKNOWN_DATA_RECEIVED;
}
switch (file->ef_structure) {
case SC_FILE_EF_TRANSPARENT:
add_acl_entry(card, file, SC_AC_OP_UPDATE, (u8)(pos[0] & 0x0F));
break;
case SC_FILE_EF_LINEAR_FIXED:
case SC_FILE_EF_LINEAR_VARIABLE:
add_acl_entry(card, file, SC_AC_OP_UPDATE, (u8)(pos[0] & 0x0F));
break;
case SC_FILE_EF_CYCLIC:
break;
}
}
return 0;
}
static int check_path(sc_card_t *card, const u8 **pathptr, size_t *pathlen,
int need_info)
{
const u8 *curptr = card->cache.current_path.value;
const u8 *ptr = *pathptr;
size_t curlen = card->cache.current_path.len;
size_t len = *pathlen;
if (curlen < 2)
return 0;
if (len < 2)
return 0;
if (memcmp(ptr, "\x3F\x00", 2) != 0) {
/* Skip the MF id */
curptr += 2;
curlen -= 2;
}
if (len == curlen && memcmp(ptr, curptr, len) == 0) {
if (need_info)
return 0;
*pathptr = ptr + len;
*pathlen = 0;
return 1;
}
if (curlen < len && memcmp(ptr, curptr, curlen) == 0) {
*pathptr = ptr + curlen;
*pathlen = len - curlen;
return 1;
}
/* FIXME: Build additional logic */
return 0;
}
static void cache_path(sc_card_t *card, const sc_path_t *path,
int result)
{
sc_path_t *curpath = &card->cache.current_path;
if (result < 0) {
curpath->len = 0;
return;
}
switch (path->type) {
case SC_PATH_TYPE_FILE_ID:
if (path->value[0] == 0x3F && path->value[1] == 0x00)
sc_format_path("3F00", curpath);
else {
if (curpath->len + 2 > SC_MAX_PATH_SIZE) {
curpath->len = 0;
return;
}
memcpy(curpath->value + curpath->len, path->value, 2);
curpath->len += 2;
}
break;
case SC_PATH_TYPE_PATH:
curpath->len = 0;
if (path->value[0] != 0x3F || path->value[1] != 0)
sc_format_path("3F00", curpath);
if (curpath->len + path->len > SC_MAX_PATH_SIZE) {
curpath->len = 0;
return;
}
memcpy(curpath->value + curpath->len, path->value, path->len);
curpath->len += path->len;
break;
case SC_PATH_TYPE_DF_NAME:
/* All bets are off */
curpath->len = 0;
break;
}
}
static int select_file_id(sc_card_t *card, const u8 *buf, size_t buflen,
u8 p1, sc_file_t **file_out)
{
int r;
sc_apdu_t apdu;
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
sc_file_t *file;
2018-11-22 08:31:29 +00:00
sc_log(card->ctx, "called, p1=%u\n", p1);
2018-11-23 15:51:14 +00:00
sc_log_hex(card->ctx, "path", buf, buflen);
sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, p1, 0);
apdu.resp = rbuf;
apdu.resplen = sizeof(rbuf);
apdu.datalen = buflen;
apdu.data = buf;
apdu.lc = buflen;
apdu.le = 252;
/* No need to get file information, if file is NULL. */
if (file_out == NULL) {
apdu.cse = SC_APDU_CASE_3_SHORT;
apdu.le = 0;
}
r = sc_transmit_apdu(card, &apdu);
2018-11-23 20:54:14 +00:00
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
2018-11-23 20:54:14 +00:00
LOG_TEST_RET(card->ctx, r, "Card returned error");
if (file_out == NULL)
return 0;
if (apdu.resplen < 14)
return SC_ERROR_UNKNOWN_DATA_RECEIVED;
if (apdu.resp[0] == 0x6F) {
2018-11-22 08:31:29 +00:00
sc_log(card->ctx, "unsupported: card returned FCI\n");
return SC_ERROR_UNKNOWN_DATA_RECEIVED; /* FIXME */
}
file = sc_file_new();
if (file == NULL)
LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY);
/* We abuse process_fci here even though it's not the real FCI. */
r = card->ops->process_fci(card, file, apdu.resp, apdu.resplen);
if (r) {
sc_file_free(file);
return r;
}
*file_out = file;
return 0;
}
static int flex_select_file(sc_card_t *card, const sc_path_t *path,
sc_file_t **file_out)
{
int r;
const u8 *pathptr = path->value;
size_t pathlen = path->len;
int locked = 0, magic_done;
u8 p1 = 0;
char pbuf[SC_MAX_PATH_STRING_SIZE];
r = sc_path_print(pbuf, sizeof(pbuf), &card->cache.current_path);
if (r != SC_SUCCESS)
pbuf[0] = '\0';
2018-11-22 08:31:29 +00:00
sc_log(card->ctx, "called, cached path=%s\n", pbuf);
switch (path->type) {
case SC_PATH_TYPE_PATH:
if ((pathlen & 1) != 0) /* not divisible by 2 */
return SC_ERROR_INVALID_ARGUMENTS;
magic_done = check_path(card, &pathptr, &pathlen, file_out != NULL);
if (pathlen == 0)
return 0;
if (pathlen != 2 || memcmp(pathptr, "\x3F\x00", 2) != 0) {
locked = 1;
r = sc_lock(card);
2018-11-23 20:54:14 +00:00
LOG_TEST_RET(card->ctx, r, "sc_lock() failed");
if (!magic_done && memcmp(pathptr, "\x3F\x00", 2) != 0) {
r = select_file_id(card, (const u8 *) "\x3F\x00", 2, 0, NULL);
if (r)
sc_unlock(card);
2018-11-23 20:54:14 +00:00
LOG_TEST_RET(card->ctx, r, "Unable to select Master File (MF)");
}
while (pathlen > 2) {
r = select_file_id(card, pathptr, 2, 0, NULL);
if (r)
sc_unlock(card);
2018-11-23 20:54:14 +00:00
LOG_TEST_RET(card->ctx, r, "Unable to select DF");
pathptr += 2;
pathlen -= 2;
}
}
break;
case SC_PATH_TYPE_DF_NAME:
p1 = 0x04;
break;
case SC_PATH_TYPE_FILE_ID:
if (pathlen != 2)
return SC_ERROR_INVALID_ARGUMENTS;
break;
}
r = select_file_id(card, pathptr, pathlen, p1, file_out);
if (locked)
sc_unlock(card);
cache_path(card, path, r);
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r);
}
static int cryptoflex_list_files(sc_card_t *card, u8 *buf, size_t buflen)
{
sc_apdu_t apdu;
u8 rbuf[4];
int r;
size_t count = 0;
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xA8, 0, 0);
apdu.cla = 0xF0;
apdu.le = 4;
apdu.resplen = 4;
apdu.resp = rbuf;
while (buflen > 2) {
r = sc_transmit_apdu(card, &apdu);
if (r)
return r;
if (apdu.sw1 == 0x6A && apdu.sw2 == 0x82)
break;
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
if (r)
return r;
if (apdu.resplen != 4) {
2018-11-22 08:31:29 +00:00
sc_log(card->ctx,
"expected 4 bytes, got %"SC_FORMAT_LEN_SIZE_T"u.\n",
apdu.resplen);
return SC_ERROR_UNKNOWN_DATA_RECEIVED;
}
memcpy(buf, rbuf + 2, 2);
buf += 2;
count += 2;
buflen -= 2;
}
return count;
}
/*
* The Cyberflex LIST FILES command is slightly different...
*/
static int cyberflex_list_files(sc_card_t *card, u8 *buf, size_t buflen)
{
sc_apdu_t apdu;
u8 rbuf[6];
int r;
size_t count = 0, p2 = 0;
while (buflen > 2) {
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xA8, 0, ++p2);
apdu.le = 6;
apdu.resplen = 6;
apdu.resp = rbuf;
r = sc_transmit_apdu(card, &apdu);
if (r)
return r;
if (apdu.sw1 == 0x6A && apdu.sw2 == 0x83)
break;
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
if (r)
return r;
if (apdu.resplen != 6) {
2018-11-22 08:31:29 +00:00
sc_log(card->ctx,
"expected 6 bytes, got %"SC_FORMAT_LEN_SIZE_T"u.\n",
apdu.resplen);
return SC_ERROR_UNKNOWN_DATA_RECEIVED;
}
memcpy(buf, rbuf + 4, 2);
buf += 2;
count += 2;
buflen -= 2;
}
return count;
}
static int flex_delete_file(sc_card_t *card, const sc_path_t *path)
{
sc_apdu_t apdu;
int r;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
if (path->type != SC_PATH_TYPE_FILE_ID && path->len != 2) {
2018-11-22 08:31:29 +00:00
sc_log(card->ctx, "File type has to be SC_PATH_TYPE_FILE_ID\n");
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
}
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE4, 0x00, 0x00);
if (!IS_CYBERFLEX(card))
apdu.cla = 0xF0; /* Override CLA byte */
apdu.data = path->value;
apdu.lc = 2;
apdu.datalen = 2;
r = sc_transmit_apdu(card, &apdu);
2018-11-23 20:54:14 +00:00
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
return sc_check_sw(card, apdu.sw1, apdu.sw2);
}
static int acl_to_ac_nibble(const sc_acl_entry_t *e)
{
if (e == NULL)
return -1;
if (e->next != NULL) /* FIXME */
return -1;
switch (e->method) {
case SC_AC_NONE:
return 0x00;
case SC_AC_CHV:
switch (e->key_ref) {
case 1:
return 0x01;
break;
case 2:
return 0x02;
break;
}
return -1;
case SC_AC_PRO:
return 0x03;
case SC_AC_AUT:
return 0x04;
case SC_AC_NEVER:
return 0x0f;
}
return -1;
}
static int acl_to_keynum_nibble(const sc_acl_entry_t *e)
{
while (e != NULL && e->method != SC_AC_AUT)
e = e->next;
if (e == NULL || e->key_ref == SC_AC_KEY_REF_NONE)
return 0;
return e->key_ref & 0x0F;
}
static int
cryptoflex_construct_file_attrs(sc_card_t *card, const sc_file_t *file,
u8 *buf, size_t *buflen)
{
u8 *p = buf;
int r, i;
int ops[6];
p[0] = 0xFF;
p[1] = 0xFF;
p[2] = file->size >> 8;
p[3] = file->size & 0xFF;
p[4] = file->id >> 8;
p[5] = file->id & 0xFF;
if (file->type == SC_FILE_TYPE_DF)
p[6] = 0x38;
else
switch (file->ef_structure) {
case SC_FILE_EF_TRANSPARENT:
p[6] = 0x01;
break;
case SC_FILE_EF_LINEAR_FIXED:
p[6] = 0x02;
break;
case SC_FILE_EF_LINEAR_VARIABLE:
p[6] = 0x04;
break;
case SC_FILE_EF_CYCLIC:
p[6] = 0x06;
break;
default:
2018-11-22 08:31:29 +00:00
sc_log(card->ctx, "Invalid EF structure\n");
return -1;
}
p[7] = 0xFF; /* allow Decrease and Increase */
for (i = 0; i < 6; i++)
ops[i] = -1;
if (file->type == SC_FILE_TYPE_DF) {
ops[0] = SC_AC_OP_LIST_FILES;
ops[2] = SC_AC_OP_DELETE;
ops[3] = SC_AC_OP_CREATE;
} else {
ops[0] = SC_AC_OP_READ;
ops[1] = SC_AC_OP_UPDATE;
ops[2] = SC_AC_OP_READ;
ops[3] = SC_AC_OP_UPDATE;
ops[4] = SC_AC_OP_REHABILITATE;
ops[5] = SC_AC_OP_INVALIDATE;
}
p[8] = p[9] = p[10] = 0;
p[13] = p[14] = p[15] = 0; /* Key numbers */
for (i = 0; i < 6; i++) {
const sc_acl_entry_t *entry;
if (ops[i] == -1)
continue;
entry = sc_file_get_acl_entry(file, ops[i]);
r = acl_to_ac_nibble(entry);
2018-11-23 20:54:14 +00:00
LOG_TEST_RET(card->ctx, r, "Invalid ACL value");
/* Do some magic to get the nibbles right */
p[8 + i/2] |= (r & 0x0F) << (((i+1) % 2) * 4);
r = acl_to_keynum_nibble(entry);
p[13 + i/2] |= (r & 0x0F) << (((i+1) % 2) * 4);
}
p[11] = (file->status == SC_FILE_STATUS_INVALIDATED) ? 0x00 : 0x01;
if (file->type != SC_FILE_TYPE_DF &&
(file->ef_structure == SC_FILE_EF_LINEAR_FIXED ||
file->ef_structure == SC_FILE_EF_CYCLIC))
p[12] = 0x04;
else
p[12] = 0x03;
if (p[12] == 0x04) {
p[16] = file->record_length;
*buflen = 17;
} else
*buflen = 16;
return 0;
}
static int
cyberflex_construct_file_attrs(sc_card_t *card, const sc_file_t *file,
u8 *buf, size_t *buflen)
{
u8 *p = buf;
size_t size = file->size;
/* cyberflex wants input parameters length added */
switch (file->type) {
case SC_FILE_TYPE_DF:
size += 24;
break;
case SC_FILE_TYPE_WORKING_EF:
default:
size += 16;
break;
}
2018-11-22 08:31:29 +00:00
sc_log(card->ctx,
"Creating %02x:%02x, size %"SC_FORMAT_LEN_SIZE_T"u %02"SC_FORMAT_LEN_SIZE_T"x:%02"SC_FORMAT_LEN_SIZE_T"x\n",
file->id >> 8,
file->id & 0xFF,
size,
size >> 8,
size & 0xFF);
p[0] = size >> 8;
p[1] = size & 0xFF;
p[2] = file->id >> 8;
p[3] = file->id & 0xFF;
if (file->type == SC_FILE_TYPE_DF)
p[4] = 0x20;
else
switch (file->ef_structure) {
case SC_FILE_EF_TRANSPARENT:
p[4] = 0x02;
break;
case SC_FILE_EF_LINEAR_FIXED:
p[4] = 0x0C;
break;
case SC_FILE_EF_LINEAR_VARIABLE:
p[4] = 0x19;
break;
case SC_FILE_EF_CYCLIC:
p[4] = 0x1D;
break;
default:
2018-11-22 08:31:29 +00:00
sc_log(card->ctx, "Invalid EF structure\n");
return -1;
}
p[5] = 0x01; /* status?? */
p[6] = p[7] = 0;
*buflen = 16;
p[8] = p[9] = p[11] = 0xFF;
p[10] = p[12] = p[13] = p[14] = p[15] = 0x00;
return 0;
}
static int flex_create_file(sc_card_t *card, sc_file_t *file)
{
u8 sbuf[18];
size_t sendlen;
int r, rec_nr;
sc_apdu_t apdu;
/* Build the file attrs. These are not the real FCI bytes
* in the standard sense, but its a convenient way of
* abstracting the Cryptoflex/Cyberflex differences */
r = card->ops->construct_fci(card, file, sbuf, &sendlen);
if (r) {
2018-11-22 08:31:29 +00:00
sc_log(card->ctx, "File structure encoding failed.\n");
return SC_ERROR_INVALID_ARGUMENTS;
}
if (file->type != SC_FILE_TYPE_DF && file->ef_structure != SC_FILE_EF_TRANSPARENT)
rec_nr = file->record_count;
else
rec_nr = 0;
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x00, rec_nr);
if (!IS_CYBERFLEX(card))
apdu.cla = 0xF0;
apdu.data = sbuf;
apdu.datalen = sendlen;
apdu.lc = sendlen;
r = sc_transmit_apdu(card, &apdu);
2018-11-23 20:54:14 +00:00
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
2018-11-23 20:54:14 +00:00
LOG_TEST_RET(card->ctx, r, "Card returned error");
if (card->cache.valid) {
u8 file_id[2];
file_id[0] = file->id >> 8;
file_id[1] = file->id & 0xFF;
if (card->cache.current_path.len != 0)
sc_append_path_id(&card->cache.current_path, file_id, 2);
}
return 0;
}
static int flex_set_security_env(sc_card_t *card,
const sc_security_env_t *env,
int se_num)
{
struct flex_private_data *prv = (struct flex_private_data *) card->drv_data;
if (env->operation != SC_SEC_OPERATION_SIGN &&
env->operation != SC_SEC_OPERATION_DECIPHER) {
2018-11-22 08:31:29 +00:00
sc_log(card->ctx, "Invalid crypto operation supplied.\n");
return SC_ERROR_NOT_SUPPORTED;
}
if (env->algorithm != SC_ALGORITHM_RSA) {
2018-11-22 08:31:29 +00:00
sc_log(card->ctx, "Invalid crypto algorithm supplied.\n");
return SC_ERROR_NOT_SUPPORTED;
}
if ((env->algorithm_flags & SC_ALGORITHM_RSA_PADS) ||
(env->algorithm_flags & SC_ALGORITHM_RSA_HASHES)) {
2018-11-22 08:31:29 +00:00
sc_log(card->ctx, "Card supports only raw RSA.\n");
return SC_ERROR_NOT_SUPPORTED;
}
if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) {
if (env->key_ref_len != 1 ||
(env->key_ref[0] != 0 && env->key_ref[0] != 1)) {
2018-11-22 08:31:29 +00:00
sc_log(card->ctx, "Invalid key reference supplied.\n");
return SC_ERROR_NOT_SUPPORTED;
}
prv->rsa_key_ref = env->key_ref[0];
}
if (env->flags & SC_SEC_ENV_ALG_REF_PRESENT) {
2018-11-22 08:31:29 +00:00
sc_log(card->ctx, "Algorithm reference not supported.\n");
return SC_ERROR_NOT_SUPPORTED;
}
if (env->flags & SC_SEC_ENV_FILE_REF_PRESENT)
if (memcmp(env->file_ref.value, "\x00\x12", 2) != 0) {
2018-11-22 08:31:29 +00:00
sc_log(card->ctx, "File reference is not 0012.\n");
return SC_ERROR_NOT_SUPPORTED;
}
return 0;
}
static int flex_restore_security_env(sc_card_t *card, int se_num)
{
return 0;
}
static int
cryptoflex_compute_signature(sc_card_t *card, const u8 *data,
size_t data_len, u8 * out, size_t outlen)
{
struct flex_private_data *prv = (struct flex_private_data *) card->drv_data;
sc_apdu_t apdu;
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
int r;
size_t i, i2;
if (data_len != 64 && data_len != 96 && data_len != 128 && data_len != 256) {
2018-11-22 08:31:29 +00:00
sc_log(card->ctx,
"Illegal input length: %"SC_FORMAT_LEN_SIZE_T"u\n",
data_len);
return SC_ERROR_INVALID_ARGUMENTS;
}
if (outlen < data_len) {
2018-11-22 08:31:29 +00:00
sc_log(card->ctx, "Output buffer too small.\n");
return SC_ERROR_BUFFER_TOO_SMALL;
}
sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x88, 0x00, prv->rsa_key_ref);
/* This works around a problem with some PC/SC IFD drivers that don't grok
* lc=00 (Chaskiel M Grundman <cg2v@andrew.cmu.edu>) */
if (data_len == 256) {
apdu.cla=0x10;
apdu.cse= SC_APDU_CASE_3_SHORT;
apdu.lc=10;
apdu.datalen=10;
apdu.data = sbuf;
for (i2 = 0; i2 < 10; i2++)
sbuf[i2]=data[data_len-1-i2];
r = sc_transmit_apdu(card, &apdu);
2018-11-23 20:54:14 +00:00
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
2018-11-23 20:54:14 +00:00
LOG_TEST_RET(card->ctx, r, "Card returned error");
data_len -= 10;
sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x88, 0x00, prv->rsa_key_ref);
apdu.cla=0x0;
}
apdu.lc = data_len;
apdu.datalen = data_len;
for (i = 0; i < data_len; i++)
sbuf[i] = data[data_len-1-i];
apdu.data = sbuf;
apdu.resplen = outlen > sizeof(sbuf) ? sizeof(sbuf) : outlen;
apdu.le = apdu.resplen > 256 ? 256 : apdu.resplen;
apdu.resp = sbuf;
r = sc_transmit_apdu(card, &apdu);
2018-11-23 20:54:14 +00:00
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
2018-11-23 20:54:14 +00:00
LOG_TEST_RET(card->ctx, r, "Card returned error");
for (i = 0; i < apdu.resplen; i++)
out[i] = sbuf[apdu.resplen-1-i];
return apdu.resplen;
}
static int
cyberflex_compute_signature(sc_card_t *card, const u8 *data,
size_t data_len, u8 * out, size_t outlen)
{
struct flex_private_data *prv = DRV_DATA(card);
sc_apdu_t apdu;
u8 alg_id, key_id;
int r;
switch (data_len) {
case 64: alg_id = 0xC4; break;
case 96: alg_id = 0xC6; break;
case 128: alg_id = 0xC8; break;
default:
2018-11-22 08:31:29 +00:00
sc_log(card->ctx,
"Illegal input length: %"SC_FORMAT_LEN_SIZE_T"u\n",
data_len);
return SC_ERROR_INVALID_ARGUMENTS;
}
key_id = prv->rsa_key_ref + 1; /* Why? */
if (outlen < data_len) {
2018-11-22 08:31:29 +00:00
sc_log(card->ctx, "Output buffer too small.\n");
return SC_ERROR_BUFFER_TOO_SMALL;
}
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x88, alg_id, key_id);
apdu.lc = data_len;
apdu.datalen = data_len;
apdu.data = data;
apdu.resplen = outlen;
apdu.resp = out;
r = sc_transmit_apdu(card, &apdu);
2018-11-23 20:54:14 +00:00
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
2018-11-23 20:54:14 +00:00
LOG_TEST_RET(card->ctx, r, "Card returned error");
return apdu.resplen;
}
static int flex_decipher(sc_card_t *card,
const u8 * crgram, size_t crgram_len,
u8 * out, size_t outlen)
{
/* There seems to be no Decipher command, but an RSA signature
* is the same operation as an RSA decryption.
* Of course, the (PKCS#1) padding is different, but at least
* a Cryptoflex 32K e-gate doesn't seem to check this. */
return card->ops->compute_signature(card, crgram, crgram_len, out, outlen);
}
/* Return the default AAK for this type of card */
static int flex_get_default_key(sc_card_t *card,
struct sc_cardctl_default_key *data)
{
struct flex_private_data *prv = DRV_DATA(card);
const char *key;
if (data->method != SC_AC_AUT || data->key_ref != prv->aak_key_ref)
return SC_ERROR_NO_DEFAULT_KEY;
/* These seem to be the default AAKs used by Schlumberger */
switch (card->type) {
case SC_CARD_TYPE_FLEX_CRYPTO:
key = "2c:15:e5:26:e9:3e:8a:19";
break;
case SC_CARD_TYPE_FLEX_CYBER:
key = "ad:9f:61:fe:fa:20:ce:63";
break;
default:
return SC_ERROR_NO_DEFAULT_KEY;
}
return sc_hex_to_bin(key, data->key_data, &data->len);
}
/* Generate key on-card */
static int flex_generate_key(sc_card_t *card, struct sc_cardctl_cryptoflex_genkey_info *data)
{
sc_apdu_t apdu;
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
int r, p1, p2;
switch (data->key_bits) {
case 512: p2 = 0x40; break;
case 768: p2 = 0x60; break;
case 1024: p2 = 0x80; break;
case 2048: p2 = 0x00; break;
default:
2018-11-22 08:31:29 +00:00
sc_log(card->ctx, "Illegal key length: %d\n", data->key_bits);
return SC_ERROR_INVALID_ARGUMENTS;
}
p1 = data->key_num;
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x46, p1, p2);
if (!IS_CYBERFLEX(card))
apdu.cla = 0xF0;
apdu.data = sbuf;
apdu.datalen = 4;
apdu.lc = 4;
/* Little endian representation of exponent */
sbuf[0] = data->exponent & 0xFF;
sbuf[1] = (data->exponent >> 8) & 0xFF;
sbuf[2] = (data->exponent >> 16) & 0xFF;
sbuf[3] = (data->exponent >> 24) & 0xFF;
r = sc_transmit_apdu(card, &apdu);
2018-11-23 20:54:14 +00:00
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
2018-11-23 20:54:14 +00:00
LOG_TEST_RET(card->ctx, r, "Card returned error");
data->pubkey_len = apdu.resplen;
return 0;
}
/* read the card serial number from the EF_gdo system file */
static int flex_get_serialnr(sc_card_t *card, sc_serial_number_t *serial)
{
int r;
u8 buf[16];
size_t len;
sc_path_t tpath;
sc_file_t *tfile = NULL;
if (!serial)
return SC_ERROR_INVALID_ARGUMENTS;
/* see if we have cached serial number */
if (card->serialnr.len) {
memcpy(serial, &card->serialnr, sizeof(*serial));
return SC_SUCCESS;
}
/* read EF_ICCSN */
sc_format_path("3F000002", &tpath);
r = sc_select_file(card, &tpath, &tfile);
if (r < 0)
return r;
len = tfile->size;
sc_file_free(tfile);
if (len != 8) {
2018-11-22 08:31:29 +00:00
sc_log(card->ctx, "unexpected file length of EF_ICCSN (%lu)\n",
(unsigned long) len);
return SC_ERROR_INTERNAL;
}
r = sc_read_binary(card, 0, buf, len, 0);
if (r < 0)
return r;
card->serialnr.len = len;
memcpy(card->serialnr.value, buf, len);
memcpy(serial, &card->serialnr, sizeof(*serial));
return SC_SUCCESS;
}
static int flex_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr)
{
switch (cmd) {
case SC_CARDCTL_GET_DEFAULT_KEY:
return flex_get_default_key(card,
(struct sc_cardctl_default_key *) ptr);
case SC_CARDCTL_CRYPTOFLEX_GENERATE_KEY:
return flex_generate_key(card,
(struct sc_cardctl_cryptoflex_genkey_info *) ptr);
case SC_CARDCTL_GET_SERIALNR:
return flex_get_serialnr(card, (sc_serial_number_t *) ptr);
}
return SC_ERROR_NOT_SUPPORTED;
}
static int flex_build_verify_apdu(sc_card_t *card, sc_apdu_t *apdu,
struct sc_pin_cmd_data *data)
{
static u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
int r, len;
int cla = card->cla, ins;
switch (data->pin_type) {
case SC_AC_CHV:
ins = 0x20;
break;
case SC_AC_AUT:
/* AUT keys cannot be entered through terminal */
if (data->flags & SC_PIN_CMD_USE_PINPAD)
return SC_ERROR_INVALID_ARGUMENTS;
/* Override CLA byte */
if (!IS_CYBERFLEX(card))
cla = 0xF0;
ins = 0x2A;
break;
default:
return SC_ERROR_INVALID_ARGUMENTS;
}
/* Copy the PIN, with padding */
if ((r = sc_build_pin(sbuf, sizeof(sbuf), &data->pin1, 1)) < 0)
return r;
len = r;
sc_format_apdu(card, apdu, SC_APDU_CASE_3_SHORT, ins, 0, data->pin_reference);
apdu->cla = cla;
apdu->data = sbuf;
apdu->datalen = len;
apdu->lc = len;
return 0;
}
static void flex_init_pin_info(struct sc_pin_cmd_pin *pin, unsigned int num)
{
pin->encoding = SC_PIN_ENCODING_ASCII;
pin->min_length = 4;
pin->max_length = 8;
pin->pad_length = 8;
pin->offset = 5 + num * 8;
}
static int flex_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data,
int *tries_left)
{
sc_apdu_t apdu;
int r;
int old_cla = -1;
/* Fix pin data */
data->flags |= SC_PIN_CMD_NEED_PADDING;
flex_init_pin_info(&data->pin1, 0);
flex_init_pin_info(&data->pin2, 1);
if (data->cmd == SC_PIN_CMD_VERIFY) {
r = flex_build_verify_apdu(card, &apdu, data);
if (r < 0)
return r;
data->apdu = &apdu;
} else if (data->cmd == SC_PIN_CMD_CHANGE || data->cmd == SC_PIN_CMD_UNBLOCK) {
if (data->pin_type != SC_AC_CHV)
return SC_ERROR_INVALID_ARGUMENTS;
old_cla = card->cla;
if (!IS_CYBERFLEX(card))
card->cla = 0xF0;
}
/* According to the Cryptoflex documentation, the card
* does not return the number of attempts left using
* the 63C0xx convention, hence we don't pass the
* tries_left pointer. */
r = iso_ops->pin_cmd(card, data, NULL);
if (old_cla != -1)
card->cla = old_cla;
data->apdu = NULL;
return r;
}
static int flex_logout(sc_card_t *card)
{
sc_apdu_t apdu;
int r;
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x22, 0x07, 0x00);
apdu.cla = 0xF0;
r = sc_transmit_apdu(card, &apdu);
2018-11-23 20:54:14 +00:00
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
2018-11-23 20:54:14 +00:00
LOG_TEST_RET(card->ctx, r, "Card returned error");
LOG_FUNC_RETURN(card->ctx, r);
}
struct sc_card_driver * sc_get_cryptoflex_driver(void)
{
if (iso_ops == NULL)
iso_ops = sc_get_iso7816_driver()->ops;
cryptoflex_ops = *iso_ops;
cryptoflex_ops.match_card = cryptoflex_match_card;
cryptoflex_ops.init = flex_init;
cryptoflex_ops.finish = flex_finish;
cryptoflex_ops.process_fci = cryptoflex_process_file_attrs;
cryptoflex_ops.construct_fci = cryptoflex_construct_file_attrs;
cryptoflex_ops.select_file = flex_select_file;
cryptoflex_ops.list_files = cryptoflex_list_files;
cryptoflex_ops.delete_file = flex_delete_file;
cryptoflex_ops.create_file = flex_create_file;
cryptoflex_ops.card_ctl = flex_card_ctl;
cryptoflex_ops.set_security_env = flex_set_security_env;
cryptoflex_ops.restore_security_env = flex_restore_security_env;
cryptoflex_ops.compute_signature = cryptoflex_compute_signature;
cryptoflex_ops.decipher = flex_decipher;
cryptoflex_ops.pin_cmd = flex_pin_cmd;
cryptoflex_ops.logout = flex_logout;
return &cryptoflex_drv;
}
struct sc_card_driver * sc_get_cyberflex_driver(void)
{
if (iso_ops == NULL)
iso_ops = sc_get_iso7816_driver()->ops;
cyberflex_ops = *iso_ops;
cyberflex_ops.match_card = cyberflex_match_card;
cyberflex_ops.init = flex_init;
cyberflex_ops.finish = flex_finish;
cyberflex_ops.process_fci = cyberflex_process_file_attrs;
cyberflex_ops.construct_fci = cyberflex_construct_file_attrs;
cyberflex_ops.select_file = flex_select_file;
cyberflex_ops.list_files = cyberflex_list_files;
cyberflex_ops.delete_file = flex_delete_file;
cyberflex_ops.create_file = flex_create_file;
cyberflex_ops.card_ctl = flex_card_ctl;
cyberflex_ops.set_security_env = flex_set_security_env;
cyberflex_ops.restore_security_env = flex_restore_security_env;
cyberflex_ops.compute_signature = cyberflex_compute_signature;
cyberflex_ops.decipher = flex_decipher;
cyberflex_ops.pin_cmd = flex_pin_cmd;
cyberflex_ops.logout = flex_logout;
return &cyberflex_drv;
}