#if HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef ENABLE_OPENSSL
#include <stdlib.h>
#include <string.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include "internal.h"
#include "cardctl.h"
#include "pkcs15.h"
#define GPK_SEL_MF 0x00
#define GPK_SEL_DF 0x01
#define GPK_SEL_EF 0x02
#define GPK_SEL_AID 0x04
#define GPK_FID_MF 0x3F00
#define GPK_FTYPE_SC 0x21
#define GPK_SIGN_RSA_MD5 0x11
#define GPK_SIGN_RSA_SHA 0x12
#define GPK_SIGN_RSA_SSL 0x18
#define GPK_VERIFY_RSA_MD5 0x21
#define GPK_VERIFY_RSA_SHA 0x22
#define GPK_AUTH_RSA_MD5 0x31
#define GPK_AUTH_RSA_SHA 0x32
#define GPK_AUTH_RSA_SSL 0x38
#define GPK_UNWRAP_RSA 0x77
#define GPK_MAX_PINS 8
#define GPK_HASH_CHUNK 62
struct gpk_private_data {
unsigned int offset_shift;
unsigned int offset_mask;
unsigned int locked : 1,
sample_card : 1;
unsigned short int ac[3];
unsigned key_set : 1;
unsigned int key_reference;
u8 key[16];
unsigned int sec_algorithm;
unsigned int sec_hash_len;
unsigned int sec_mod_len;
unsigned int sec_padding;
};
#define DRVDATA(card) ((struct gpk_private_data *) ((card)->drv_data))
static int gpk_get_info(sc_card_t *, int, int, u8 *, size_t);
static const struct sc_atr_table gpk_atrs[] = {
{ "3B:27:00:80:65:A2:04:01:01:37", NULL, "GPK 4K", SC_CARD_TYPE_GPK_GPK4000_s, 0, NULL },
{ "3B:27:00:80:65:A2:05:01:01:37", NULL, "GPK 4K", SC_CARD_TYPE_GPK_GPK4000_sp, 0, NULL },
{ "3B:27:00:80:65:A2:0C:01:01:37", NULL, "GPK 4K", SC_CARD_TYPE_GPK_GPK4000_su256, 0, NULL },
{ "3B:A7:00:40:14:80:65:A2:14:01:01:37", NULL, "GPK 4K", SC_CARD_TYPE_GPK_GPK4000_sdo, 0, NULL },
{ "3B:A7:00:40:18:80:65:A2:08:01:01:52", NULL, "GPK 8K", SC_CARD_TYPE_GPK_GPK8000_8K, 0, NULL },
{ "3B:A7:00:40:18:80:65:A2:09:01:01:52", NULL, "GPK 8K", SC_CARD_TYPE_GPK_GPK8000_16K, 0, NULL },
{ "3B:A7:00:40:18:80:65:A2:09:01:02:52", NULL, "GPK 16K", SC_CARD_TYPE_GPK_GPK16000, 0, NULL },
{ "3B:A7:00:40:18:80:65:A2:09:01:03:52", NULL, "GPK 16K", SC_CARD_TYPE_GPK_GPK16000, 0, NULL },
{ NULL, NULL, NULL, 0, 0, NULL }
};
static struct sc_card_operations gpk_ops, *iso_ops;
static struct sc_card_driver gpk_drv = {
"Gemplus GPK",
"gpk",
&gpk_ops,
NULL, 0, NULL
};
static int
gpk_match_card(sc_card_t *card)
{
int i;
i = _sc_match_atr(card, gpk_atrs, &card->type);
if (i < 0) {
const u8 *hist_bytes = card->reader->atr_info.hist_bytes;
if ((card->reader->atr_info.hist_bytes_len >= 7)
&& (hist_bytes[0] == 0x80)
&& (hist_bytes[1] == 0x65)
&& (hist_bytes[2] == 0xa2)) {
if (hist_bytes[3] == 0x08) {
card->type = SC_CARD_TYPE_GPK_GPK8000;
return 1;
}
if (hist_bytes[3] == 0x09) {
card->type = SC_CARD_TYPE_GPK_GPK16000;
return 1;
}
}
return 0;
}
return 1;
}
static int
gpk_init(sc_card_t *card)
{
struct gpk_private_data *priv;
unsigned long exponent, flags, kg;
unsigned char info[13];
card->drv_data = priv = calloc(1, sizeof(*priv));
if (card->drv_data == NULL)
return SC_ERROR_OUT_OF_MEMORY;
priv->offset_shift = 2;
priv->offset_mask = 3;
card->cla = 0x00;
flags = SC_ALGORITHM_RSA_HASH_MD5 | SC_ALGORITHM_RSA_HASH_SHA1
| SC_ALGORITHM_RSA_HASH_MD5_SHA1;
flags |= SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_PAD_ANSI
| SC_ALGORITHM_RSA_PAD_ISO9796;
exponent = (card->type < SC_CARD_TYPE_GPK_GPK16000) ? 0x10001 : 0;
kg = (card->type >= SC_CARD_TYPE_GPK_GPK8000) ? SC_ALGORITHM_ONBOARD_KEY_GEN : 0;
_sc_card_add_rsa_alg(card, 512, flags|kg, exponent);
_sc_card_add_rsa_alg(card, 768, flags, exponent);
_sc_card_add_rsa_alg(card, 1024, flags|kg, exponent);
if (gpk_get_info(card, 0x02, 0xA4, info, sizeof(info)) >= 0) {
if (info[12] & 0x40) {
priv->offset_shift = 0;
priv->offset_mask = 0;
}
if (info[12] & 0x10) {
}
if (info[12] & 0x08) {
priv->locked = 1;
}
if (!memcmp(info+5, "\x00\xff\x00", 3)) {
priv->sample_card = 1;
}
}
card->caps |= SC_CARD_CAP_RNG;
card->max_recv_size = 252;
return SC_SUCCESS;
}
static int
gpk_finish(sc_card_t *card)
{
if (card->drv_data)
free(card->drv_data);
card->drv_data = NULL;
return 0;
}
static int
match_path(sc_card_t *card, unsigned short int **pathptr, size_t *pathlen,
int need_info)
{
unsigned short int *curptr, *ptr;
size_t curlen, len;
size_t i;
curptr = (unsigned short int *) card->cache.current_path.value;
curlen = card->cache.current_path.len;
ptr = *pathptr;
len = *pathlen;
if (curlen < 1 || len < 1)
return 0;
if (ptr[0] != GPK_FID_MF || curptr[0] != GPK_FID_MF)
return 0;
for (i = 1; i < len && i < curlen; i++) {
if (ptr[i] != curptr[i])
break;
}
if (len < curlen) {
if (len == 2)
goto okay;
return 0;
}
if (i == len && need_info) {
if (i > 1) {
*pathptr = ptr + len - 1;
*pathlen = len - 1;
return 1;
}
return 0;
}
okay:
*pathptr = ptr + i;
*pathlen = len - i;
return 1;
}
static void
ac_to_acl(unsigned int ac, sc_file_t *file, unsigned int op)
{
unsigned int npins, pin;
npins = (ac >> 14) & 3;
if (npins == 3) {
sc_file_add_acl_entry(file, op, SC_AC_NEVER,
SC_AC_KEY_REF_NONE);
return;
}
sc_file_add_acl_entry(file, op, SC_AC_NONE, SC_AC_KEY_REF_NONE);
pin = ac & 0xFF;
if (npins >= 1)
sc_file_add_acl_entry(file, op, SC_AC_CHV, (pin >> 4) & 0xF);
if (npins == 2)
sc_file_add_acl_entry(file, op, SC_AC_CHV, pin & 0xF);
if (ac & 0x3F00)
sc_file_add_acl_entry(file, op, SC_AC_PRO, (ac & 0x3F00) >> 8);
}
static void
acl_to_ac(sc_file_t *file, unsigned int op, u8 *ac)
{
const sc_acl_entry_t *acl;
unsigned int npins = 0;
ac[0] = ac[1] = 0;
if ((acl = sc_file_get_acl_entry(file, op)) == NULL)
return;
assert(acl->method != SC_AC_UNKNOWN);
switch (acl->method) {
case SC_AC_NEVER:
ac[0] = 0xC0;
return;
case SC_AC_NONE:
return;
}
while (acl) {
if (acl->method == SC_AC_CHV) {
if (++npins >= 2)
continue;
ac[1] >>= 4;
ac[1] |= acl->key_ref << 4;
ac[0] += 0x40;
}
if (acl->method == SC_AC_PRO) {
ac[0] |= acl->key_ref & 0x1f;
}
acl = acl->next;
}
}
static int
gpk_parse_fci(sc_card_t *card,
const u8 *buf, size_t buflen,
sc_file_t *file)
{
const u8 *end, *next;
unsigned int tag, len;
end = buf + buflen;
for (; buf + 2 < end; buf = next) {
next = buf + 2 + buf[1];
if (next > end)
break;
tag = *buf++;
len = *buf++;
if (tag == 0x84) {
} else
if (tag == 0xC1 && len >= 2) {
file->id = (buf[0] << 8) | buf[1];
} else
if (tag == 0xC2) {
}
}
return 0;
}
static int
gpk_parse_fileinfo(sc_card_t *card,
const u8 *buf, size_t buflen,
sc_file_t *file)
{
const u8 *sp, *end, *next;
int i, rc;
memset(file, 0, sizeof(*file));
for (i = 0; i < SC_MAX_AC_OPS; i++)
sc_file_add_acl_entry(file, i, SC_AC_UNKNOWN, SC_AC_KEY_REF_NONE);
end = buf + buflen;
for (sp = buf; sp + 2 < end; sp = next) {
next = sp + 2 + sp[1];
if (next > end)
break;
if (sp[0] == 0x84) {
if (sp[1] > sizeof(file->name))
continue;
memset(file->name, 0, sizeof(file->name));
memcpy(file->name, sp+2, sp[1]);
} else
if (sp[0] == 0x85) {
unsigned int ac[3], n;
if (sp + 11 + 2*3 >= end)
break;
file->id = (sp[4] << 8) | sp[5];
file->size = (sp[8] << 8) | sp[9];
file->record_length = sp[7];
for (n = 0; n < 3; n++)
ac[n] = (sp[10+2*n] << 8) | sp[11+2*n];
switch (sp[6] & 7) {
case 0x01: case 0x02: case 0x03: case 0x04:
case 0x05: case 0x06: case 0x07:
file->type = SC_FILE_TYPE_WORKING_EF;
file->ef_structure = sp[6] & 7;
ac_to_acl(ac[0], file, SC_AC_OP_UPDATE);
ac_to_acl(ac[1], file, SC_AC_OP_WRITE);
ac_to_acl(ac[2], file, SC_AC_OP_READ);
break;
case 0x00:
file->type = SC_FILE_TYPE_DF;
ac_to_acl(ac[0], file, SC_AC_OP_LOCK);
ac_to_acl(ac[1], file, SC_AC_OP_CREATE);
sc_file_add_acl_entry(file, SC_AC_OP_SELECT,
SC_AC_NONE, SC_AC_KEY_REF_NONE);
sc_file_add_acl_entry(file, SC_AC_OP_DELETE,
SC_AC_NEVER, SC_AC_KEY_REF_NONE);
sc_file_add_acl_entry(file, SC_AC_OP_REHABILITATE,
SC_AC_NEVER, SC_AC_KEY_REF_NONE);
sc_file_add_acl_entry(file, SC_AC_OP_INVALIDATE,
SC_AC_NEVER, SC_AC_KEY_REF_NONE);
sc_file_add_acl_entry(file, SC_AC_OP_LIST_FILES,
SC_AC_NEVER, SC_AC_KEY_REF_NONE);
break;
}
} else
if (sp[0] == 0x6f) {
file->type = SC_FILE_TYPE_DF;
rc = gpk_parse_fci(card, sp + 2, sp[1], file);
if (rc < 0)
return rc;
}
}
if (file->record_length)
file->record_count = file->size / file->record_length;
file->magic = SC_FILE_MAGIC;
return 0;
}
static int
gpk_select(sc_card_t *card, int kind,
const u8 *buf, size_t buflen,
sc_file_t **file)
{
struct gpk_private_data *priv = DRVDATA(card);
sc_apdu_t apdu;
u8 resbuf[256];
int r;
if (kind == GPK_SEL_MF || kind == GPK_SEL_DF) {
memset(priv->key, 0, sizeof(priv->key));
priv->key_set = 0;
}
memset(&apdu, 0, sizeof(apdu));
apdu.cla = 0x00;
apdu.cse = SC_APDU_CASE_3_SHORT;
apdu.ins = 0xA4;
apdu.p1 = kind;
apdu.p2 = 0;
apdu.data = buf;
apdu.datalen = buflen;
apdu.lc = apdu.datalen;
if (file) {
apdu.cse = SC_APDU_CASE_4_SHORT;
apdu.resp = resbuf;
apdu.resplen = sizeof(resbuf);
apdu.le = sizeof(resbuf);
}
r = sc_transmit_apdu(card, &apdu);
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
LOG_TEST_RET(card->ctx, r, "Card returned error");
if (kind == GPK_SEL_AID) {
card->cache.current_path.len = 0;
}
if (file == NULL)
return 0;
*file = sc_file_new();
r = gpk_parse_fileinfo(card, apdu.resp, apdu.resplen, *file);
if (r < 0) {
sc_file_free(*file);
*file = NULL;
}
return r;
}
static int
gpk_select_id(sc_card_t *card, int kind, unsigned int fid,
sc_file_t **file)
{
sc_path_t *cp = &card->cache.current_path;
u8 fbuf[2];
int r;
sc_log(card->ctx,
"gpk_select_id(0x%04X, kind=%u)\n", fid, kind);
fbuf[0] = fid >> 8;
fbuf[1] = fid & 0xff;
r = gpk_select(card, kind, fbuf, 2, file);
if (r == 0) {
unsigned short int *path;
switch (kind) {
case GPK_SEL_MF:
cp->len = 0;
case GPK_SEL_DF:
assert(cp->len + 1 <= SC_MAX_PATH_SIZE / 2);
path = (unsigned short int *) cp->value;
path[cp->len++] = fid;
}
} else {
cp->len = 0;
}
return r;
}
static int
gpk_select_file(sc_card_t *card, const sc_path_t *path,
sc_file_t **file)
{
unsigned short int pathtmp[SC_MAX_PATH_SIZE/2];
unsigned short int *pathptr;
size_t pathlen, n;
int locked = 0, r = 0, use_relative = 0, retry = 1;
u8 leaf_type;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
if (path->type == SC_PATH_TYPE_DF_NAME) {
if (path->len > 16)
return SC_ERROR_INVALID_ARGUMENTS;
r = gpk_select(card, GPK_SEL_AID,
path->value, path->len, file);
goto done;
}
leaf_type = GPK_SEL_EF;
try_again:
if ((path->len & 1) || path->len > sizeof(pathtmp))
return SC_ERROR_INVALID_ARGUMENTS;
pathptr = pathtmp;
memset(pathtmp, 0, sizeof pathtmp);
for (n = 0; n < path->len; n += 2)
pathptr[n>>1] = (path->value[n] << 8)|path->value[n+1];
pathlen = path->len >> 1;
if (path->type == SC_PATH_TYPE_PATH) {
if ((pathptr[0] == GPK_FID_MF && pathlen > 2)
|| (pathptr[0] != GPK_FID_MF && pathlen > 1))
retry = 0;
use_relative = match_path(card, &pathptr, &pathlen, file != 0);
if (pathlen == 0)
goto done;
} else {
if (pathlen > 1)
return SC_ERROR_INVALID_ARGUMENTS;
use_relative = 1;
}
if (pathlen == 1 && pathptr[0] == GPK_FID_MF) {
leaf_type = GPK_SEL_MF;
} else {
if (!locked++) {
r = sc_lock(card);
LOG_TEST_RET(card->ctx, r, "sc_lock() failed");
}
if (!use_relative) {
r = gpk_select_id(card, GPK_SEL_MF, GPK_FID_MF, NULL);
if (r)
sc_unlock(card);
LOG_TEST_RET(card->ctx, r, "Unable to select MF");
if (pathptr[0] == GPK_FID_MF) {
pathptr++;
pathlen--;
}
if (pathlen == 0)
goto done;
}
while (pathlen > 1) {
r = gpk_select_id(card, GPK_SEL_DF, pathptr[0], NULL);
if (r)
sc_unlock(card);
LOG_TEST_RET(card->ctx, r, "Unable to select DF");
pathptr++;
pathlen--;
}
}
r = gpk_select_id(card, leaf_type, pathptr[0], file);
if (r) {
if (leaf_type == GPK_SEL_EF && retry) {
card->cache.current_path.len = 0;
leaf_type = GPK_SEL_DF;
goto try_again;
}
}
done:
if (locked)
sc_unlock(card);
return r;
}
static int
gpk_read_binary(sc_card_t *card, unsigned int offset,
u8 *buf, size_t count, unsigned long flags)
{
struct gpk_private_data *priv = DRVDATA(card);
if (offset & priv->offset_mask) {
sc_log(card->ctx, "Invalid file offset (not a multiple of %d)",
priv->offset_mask + 1);
return SC_ERROR_INVALID_ARGUMENTS;
}
return iso_ops->read_binary(card, offset >> priv->offset_shift,
buf, count, flags);
}
static int
gpk_write_binary(sc_card_t *card, unsigned int offset,
const u8 *buf, size_t count, unsigned long flags)
{
struct gpk_private_data *priv = DRVDATA(card);
if (offset & priv->offset_mask) {
sc_log(card->ctx, "Invalid file offset (not a multiple of %d)",
priv->offset_mask + 1);
return SC_ERROR_INVALID_ARGUMENTS;
}
return iso_ops->write_binary(card, offset >> priv->offset_shift,
buf, count, flags);
}
static int
gpk_update_binary(sc_card_t *card, unsigned int offset,
const u8 *buf, size_t count, unsigned long flags)
{
struct gpk_private_data *priv = DRVDATA(card);
if (offset & priv->offset_mask) {
sc_log(card->ctx, "Invalid file offset (not a multiple of %d)",
priv->offset_mask + 1);
return SC_ERROR_INVALID_ARGUMENTS;
}
return iso_ops->update_binary(card, offset >> priv->offset_shift,
buf, count, flags);
}
static int
gpk_compute_crycks(sc_card_t *card, sc_apdu_t *apdu,
u8 *crycks1)
{
struct gpk_private_data *priv = DRVDATA(card);
u8 in[8], out[8], block[64];
unsigned int len = 0, i;
int r = SC_SUCCESS, outl;
EVP_CIPHER_CTX *ctx = NULL;
ctx = EVP_CIPHER_CTX_new();
if (ctx == NULL)
return SC_ERROR_INTERNAL;
memset(block, 0x00, sizeof(block));
block[len++] = apdu->cla;
block[len++] = apdu->ins;
block[len++] = apdu->p1;
block[len++] = apdu->p2;
block[len++] = apdu->lc + 3;
if ((i = apdu->datalen) + len > sizeof(block))
i = sizeof(block) - len;
memcpy(block+len, apdu->data, i);
len += i;
memset(in, 0x00, 8);
EVP_EncryptInit_ex(ctx, EVP_des_ede_cbc(), NULL, priv->key, in);
for (i = 0; i < len; i += 8) {
if (!EVP_EncryptUpdate(ctx, out, &outl, &block[i], 8)) {
r = SC_ERROR_INTERNAL;
break;
}
}
EVP_CIPHER_CTX_free(ctx);
memcpy((u8 *) (apdu->data + apdu->datalen), out + 5, 3);
apdu->datalen += 3;
apdu->lc += 3;
apdu->le += 3;
if (crycks1)
memcpy(crycks1, out, 3);
sc_mem_clear(in, sizeof(in));
sc_mem_clear(out, sizeof(out));
sc_mem_clear(block, sizeof(block));
return r;
}
static int
gpk_verify_crycks(sc_card_t *card, sc_apdu_t *apdu, u8 *crycks)
{
if (apdu->resplen < 3
|| memcmp(apdu->resp + apdu->resplen - 3, crycks, 3)) {
sc_log(card->ctx,
"Invalid secure messaging reply\n");
return SC_ERROR_UNKNOWN_DATA_RECEIVED;
}
apdu->resplen -= 3;
return 0;
}
static int
gpk_create_file(sc_card_t *card, sc_file_t *file)
{
struct gpk_private_data *priv = DRVDATA(card);
sc_apdu_t apdu;
u8 data[28+3], crycks[3], resp[3];
size_t datalen, namelen;
int r;
sc_log(card->ctx,
"gpk_create_file(0x%04X)\n", file->id);
memset(&apdu, 0, sizeof(apdu));
apdu.cla = 0x80;
apdu.cse = SC_APDU_CASE_3_SHORT;
apdu.ins = 0xE0;
apdu.p2 = 0x00;
memset(data, 0, sizeof(data));
datalen = 12;
data[0] = file->id >> 8;
data[1] = file->id & 0xFF;
if (file->type == SC_FILE_TYPE_DF) {
apdu.p1 = 0x01;
data[2] = 0x38;
acl_to_ac(file, SC_AC_OP_CREATE, data + 6);
acl_to_ac(file, SC_AC_OP_CREATE, data + 8);
if ((namelen = file->namelen) != 0) {
if (namelen > 16)
return SC_ERROR_INVALID_ARGUMENTS;
memcpy(data+datalen, file->name, namelen);
data[5] = namelen;
datalen += namelen;
}
} else {
apdu.p1 = 0x02;
data[2] = file->ef_structure;
data[3] = file->record_length;
data[4] = file->size >> 8;
data[5] = file->size & 0xff;
acl_to_ac(file, SC_AC_OP_UPDATE, data + 6);
acl_to_ac(file, SC_AC_OP_WRITE, data + 8);
acl_to_ac(file, SC_AC_OP_READ, data + 10);
}
apdu.data = data;
apdu.datalen = datalen;
apdu.lc = datalen;
if (priv->key_set) {
apdu.cla = 0x84;
apdu.cse = SC_APDU_CASE_4_SHORT;
r = gpk_compute_crycks(card, &apdu, crycks);
if (r)
return r;
apdu.resp = resp;
apdu.resplen = sizeof(resp);
}
r = sc_transmit_apdu(card, &apdu);
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
LOG_TEST_RET(card->ctx, r, "Card returned error");
if (priv->key_set)
r = gpk_verify_crycks(card, &apdu, crycks);
return r;
}
static int
gpk_set_filekey(const u8 *key, const u8 *challenge,
const u8 *r_rn, u8 *kats)
{
int r = SC_SUCCESS, outl;
EVP_CIPHER_CTX * ctx = NULL;
u8 out[16];
memcpy(out, key+8, 8);
memcpy(out+8, key, 8);
ctx = EVP_CIPHER_CTX_new();
if (ctx == NULL)
return SC_ERROR_INTERNAL;
EVP_EncryptInit_ex(ctx, EVP_des_ede(), NULL, key, NULL);
if (!EVP_EncryptUpdate(ctx, kats, &outl, r_rn+4, 8))
r = SC_ERROR_INTERNAL;
if (!EVP_CIPHER_CTX_cleanup(ctx))
r = SC_ERROR_INTERNAL;
if (r == SC_SUCCESS) {
EVP_CIPHER_CTX_init(ctx);
EVP_EncryptInit_ex(ctx, EVP_des_ede(), NULL, out, NULL);
if (!EVP_EncryptUpdate(ctx, kats+8, &outl, r_rn+4, 8))
r = SC_ERROR_INTERNAL;
if (!EVP_CIPHER_CTX_cleanup(ctx))
r = SC_ERROR_INTERNAL;
}
memset(out, 0, sizeof(out));
if (r == SC_SUCCESS) {
EVP_CIPHER_CTX_init(ctx);
EVP_EncryptInit_ex(ctx, EVP_des_ede(), NULL, kats, NULL);
if (!EVP_EncryptUpdate(ctx, out, &outl, challenge, 8))
r = SC_ERROR_INTERNAL;
if (memcmp(r_rn, out+4, 4) != 0)
r = SC_ERROR_INVALID_ARGUMENTS;
}
if (ctx)
EVP_CIPHER_CTX_free(ctx);
sc_mem_clear(out, sizeof(out));
return r;
}
static int
gpk_select_key(sc_card_t *card, int key_sfi, const u8 *buf, size_t buflen)
{
struct gpk_private_data *priv = DRVDATA(card);
sc_apdu_t apdu;
u8 rnd[8], resp[258];
int r;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
if (buflen != 16)
return SC_ERROR_INVALID_ARGUMENTS;
RAND_bytes(rnd, sizeof(rnd));
memset(&apdu, 0, sizeof(apdu));
apdu.cla = 0x80;
apdu.cse = SC_APDU_CASE_4_SHORT;
apdu.ins = 0x28;
apdu.p1 = 0;
apdu.p2 = key_sfi;
apdu.data = rnd;
apdu.datalen = sizeof(rnd);
apdu.lc = apdu.datalen;
apdu.resp = resp;
apdu.resplen = sizeof(resp);
apdu.le = 12;
r = sc_transmit_apdu(card, &apdu);
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
LOG_TEST_RET(card->ctx, r, "Card returned error");
if (apdu.resplen != 12) {
r = SC_ERROR_UNKNOWN_DATA_RECEIVED;
} else
if ((r = gpk_set_filekey(buf, rnd, resp, priv->key)) == 0) {
priv->key_set = 1;
priv->key_reference = key_sfi;
}
sc_mem_clear(resp, sizeof(resp));
return r;
}
static int
gpk_set_security_env(sc_card_t *card,
const sc_security_env_t *env,
int se_num)
{
struct gpk_private_data *priv = DRVDATA(card);
sc_apdu_t apdu;
unsigned int context, algorithm;
unsigned int file_id;
u8 sysrec[7];
int r;
algorithm = SC_ALGORITHM_RSA;
if (env->flags & SC_SEC_ENV_ALG_PRESENT)
algorithm = env->algorithm;
if (algorithm != SC_ALGORITHM_RSA) {
sc_log(card->ctx, "Algorithm not supported.\n");
return SC_ERROR_NOT_SUPPORTED;
}
priv->sec_algorithm = algorithm;
if ((env->flags & SC_SEC_ENV_KEY_REF_PRESENT)
&& (env->key_ref_len != 1 || env->key_ref[0] != 0)) {
sc_log(card->ctx, "Unknown key referenced.\n");
return SC_ERROR_NOT_SUPPORTED;
}
if (env->flags & SC_ALGORITHM_RSA_PAD_PKCS1)
priv->sec_padding = 0;
else if (env->flags & SC_ALGORITHM_RSA_PAD_ANSI)
priv->sec_padding = 1;
else if (env->flags & SC_ALGORITHM_RSA_PAD_ISO9796)
priv->sec_padding = 2;
else {
sc_log(card->ctx, "Padding algorithm not supported.\n");
return SC_ERROR_NOT_SUPPORTED;
}
switch (env->operation) {
case SC_SEC_OPERATION_SIGN:
if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) {
context = GPK_SIGN_RSA_SHA;
priv->sec_hash_len = 20;
} else
if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_MD5_SHA1) {
context = GPK_SIGN_RSA_SSL;
priv->sec_hash_len = 36;
} else
if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_MD5) {
context = GPK_SIGN_RSA_MD5;
priv->sec_hash_len = 16;
} else {
sc_log(card->ctx, "Unsupported signature algorithm");
return SC_ERROR_NOT_SUPPORTED;
}
break;
case SC_SEC_OPERATION_DECIPHER:
context = GPK_UNWRAP_RSA;
break;
default:
sc_log(card->ctx, "Crypto operation not supported.\n");
return SC_ERROR_NOT_SUPPORTED;
}
if (env->flags & SC_SEC_ENV_FILE_REF_PRESENT) {
if (env->file_ref.len != 2) {
sc_log(card->ctx, "File reference: invalid length.\n");
return SC_ERROR_INVALID_ARGUMENTS;
}
file_id = (env->file_ref.value[0] << 8)
| env->file_ref.value[1];
} else {
sc_log(card->ctx, "File reference missing.\n");
return SC_ERROR_INVALID_ARGUMENTS;
}
r = gpk_select_id(card, GPK_SEL_EF, file_id, NULL);
LOG_TEST_RET(card->ctx, r, "Failed to select PK file");
r = sc_read_record(card, 1, sysrec, sizeof(sysrec),
SC_RECORD_BY_REC_NR);
LOG_TEST_RET(card->ctx, r, "Failed to read PK sysrec");
if (r != 7 || sysrec[0] != 0) {
sc_log(card->ctx, "First record of file is not the sysrec");
return SC_ERROR_OBJECT_NOT_VALID;
}
if (sysrec[5] != 0x00) {
sc_log(card->ctx, "Public key is not an RSA key");
return SC_ERROR_OBJECT_NOT_VALID;
}
switch (sysrec[1]) {
case 0x00: priv->sec_mod_len = 512 / 8; break;
case 0x10: priv->sec_mod_len = 768 / 8; break;
case 0x11: priv->sec_mod_len = 1024 / 8; break;
default:
sc_log(card->ctx, "Unsupported modulus length");
return SC_ERROR_OBJECT_NOT_VALID;
}
memset(&apdu, 0, sizeof(apdu));
apdu.cse = SC_APDU_CASE_1;
apdu.cla = 0x80;
apdu.ins = 0xA6;
apdu.p1 = file_id & 0x1f;
apdu.p2 = context;
r = sc_transmit_apdu(card, &apdu);
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
LOG_TEST_RET(card->ctx, r, "Card returned error");
return r;
}
static int
gpk_restore_security_env(sc_card_t *card, int se_num)
{
return 0;
}
static int
reverse(u8 *out, size_t outlen, const u8 *in, size_t inlen)
{
if (inlen > outlen)
return SC_ERROR_BUFFER_TOO_SMALL;
outlen = inlen;
while (inlen--)
*out++ = in[inlen];
return outlen;
}
#ifdef dontuse
static int
gpk_hash(sc_card_t *card, const u8 *data, size_t datalen)
{
sc_apdu_t apdu;
unsigned int count, chain, len;
int r;
chain = 0x01;
for (count = 0; count < datalen; count += len) {
unsigned char buffer[GPK_HASH_CHUNK+2];
if ((len = datalen - count) > GPK_HASH_CHUNK)
len = GPK_HASH_CHUNK;
else
chain |= 0x10;
buffer[0] = 0x55;
buffer[1] = len;
memcpy(buffer+2, data + count, len);
memset(&apdu, 0, sizeof(apdu));
apdu.cse = SC_APDU_CASE_3_SHORT;
apdu.cla = 0x80;
apdu.ins = 0xDA;
apdu.p1 = chain;
apdu.p2 = len;
apdu.lc = len + 2;
apdu.data= buffer;
apdu.datalen = len + 2;
r = sc_transmit_apdu(card, &apdu);
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
LOG_TEST_RET(card->ctx, r, "Card returned error");
chain = 0;
}
return 0;
}
#endif
static int
gpk_init_hashed(sc_card_t *card, const u8 *digest, unsigned int len)
{
sc_apdu_t apdu;
u8 tsegid[64];
int r;
r = reverse(tsegid, sizeof(tsegid), digest, len);
LOG_TEST_RET(card->ctx, r, "Failed to reverse buffer");
memset(&apdu, 0, sizeof(apdu));
apdu.cse = SC_APDU_CASE_3_SHORT;
apdu.cla = 0x80;
apdu.ins = 0xEA;
apdu.lc = len;
apdu.data= tsegid;
apdu.datalen = len;
r = sc_transmit_apdu(card, &apdu);
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
LOG_TEST_RET(card->ctx, r, "Card returned error");
return r;
}
static int
gpk_compute_signature(sc_card_t *card, const u8 *data,
size_t data_len, u8 * out, size_t outlen)
{
struct gpk_private_data *priv = DRVDATA(card);
sc_apdu_t apdu;
u8 cardsig[1024/8];
int r;
if (data_len > priv->sec_mod_len) {
sc_log(card->ctx,
"Data length (%"SC_FORMAT_LEN_SIZE_T"u) does not match key modulus %u.\n",
data_len, priv->sec_mod_len);
return SC_ERROR_INTERNAL;
}
if (sizeof(cardsig) < priv->sec_mod_len)
return SC_ERROR_BUFFER_TOO_SMALL;
r = gpk_init_hashed(card, data, data_len);
LOG_TEST_RET(card->ctx, r, "Failed to send hash to card");
memset(&apdu, 0, sizeof(apdu));
apdu.cse = SC_APDU_CASE_2_SHORT;
apdu.cla = 0x80;
apdu.ins = 0x86;
apdu.p2 = priv->sec_padding;
apdu.resp= cardsig;
apdu.resplen = sizeof(cardsig);
apdu.le = priv->sec_mod_len;
r = sc_transmit_apdu(card, &apdu);
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
LOG_TEST_RET(card->ctx, r, "Card returned error");
r = reverse(out, outlen, cardsig, apdu.resplen);
LOG_TEST_RET(card->ctx, r, "Failed to reverse signature");
return r;
}
static int
gpk_decipher(sc_card_t *card, const u8 *in, size_t inlen,
u8 *out, size_t outlen)
{
struct gpk_private_data *priv = DRVDATA(card);
sc_apdu_t apdu;
u8 buffer[256];
int r;
if (inlen != priv->sec_mod_len) {
sc_log(card->ctx,
"Data length (%"SC_FORMAT_LEN_SIZE_T"u) does not match key modulus %u.\n",
inlen, priv->sec_mod_len);
return SC_ERROR_INVALID_ARGUMENTS;
}
r = reverse(buffer, sizeof(buffer), in, inlen);
LOG_TEST_RET(card->ctx, r, "Cryptogram too large");
in = buffer;
sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x1C, 0x00, 0x00);
apdu.cla |= 0x80;
apdu.lc = inlen;
apdu.data = in;
apdu.datalen = inlen;
apdu.le = 256;
apdu.resp = buffer;
apdu.resplen = sizeof(buffer);
r = sc_transmit_apdu(card, &apdu);
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
LOG_TEST_RET(card->ctx, r, "Card returned error");
r = reverse(out, outlen, buffer, apdu.resplen);
LOG_TEST_RET(card->ctx, r, "Failed to reverse buffer");
return r;
}
static int
gpk_erase_card(sc_card_t *card)
{
struct gpk_private_data *priv = DRVDATA(card);
sc_apdu_t apdu;
u8 offset;
int r;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
switch (card->type) {
case SC_CARD_TYPE_GPK_GPK4000_su256:
case SC_CARD_TYPE_GPK_GPK4000_sdo:
offset = 0x6B;
break;
case SC_CARD_TYPE_GPK_GPK4000_s:
offset = 7;
break;
case SC_CARD_TYPE_GPK_GPK8000:
case SC_CARD_TYPE_GPK_GPK8000_8K:
case SC_CARD_TYPE_GPK_GPK8000_16K:
case SC_CARD_TYPE_GPK_GPK16000:
offset = 0;
break;
default:
return SC_ERROR_NOT_SUPPORTED;
}
memset(&apdu, 0, sizeof(apdu));
apdu.cse = SC_APDU_CASE_1;
apdu.cla = 0xDB;
apdu.ins = 0xDE;
apdu.p2 = offset;
r = sc_transmit_apdu(card, &apdu);
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
LOG_TEST_RET(card->ctx, r, "Card returned error");
priv->key_set = 0;
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r);
}
static int
gpk_lock(sc_card_t *card, struct sc_cardctl_gpk_lock *args)
{
struct gpk_private_data *priv = DRVDATA(card);
sc_file_t *file = args->file;
sc_apdu_t apdu;
u8 data[8], crycks[3], resp[3];
int r;
sc_log(card->ctx,
"gpk_lock(0x%04X, %u)\n", file->id, args->operation);
memset(data, 0, sizeof(data));
data[0] = file->id >> 8;
data[1] = file->id;
switch (args->operation) {
case SC_AC_OP_UPDATE:
data[2] = 0x40; break;
case SC_AC_OP_WRITE:
data[3] = 0x40; break;
case SC_AC_OP_READ:
data[4] = 0x40; break;
default:
return SC_ERROR_INVALID_ARGUMENTS;
}
memset(&apdu, 0, sizeof(apdu));
apdu.cse = SC_APDU_CASE_3_SHORT;
apdu.cla = 0x80;
apdu.ins = 0x16;
apdu.p1 = (file->type == SC_FILE_TYPE_DF)? 1 : 2;
apdu.p2 = 0;
apdu.lc = 5;
apdu.datalen = 5;
apdu.data = data;
if (priv->key_set) {
apdu.cla = 0x84;
apdu.cse = SC_APDU_CASE_4_SHORT;
r = gpk_compute_crycks(card, &apdu, crycks);
if (r)
return r;
apdu.resp = resp;
apdu.resplen = sizeof(resp);
}
r = sc_transmit_apdu(card, &apdu);
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
LOG_TEST_RET(card->ctx, r, "Card returned error");
if (priv->key_set)
r = gpk_verify_crycks(card, &apdu, crycks);
return r;
}
static int
gpk_pkfile_init(sc_card_t *card, struct sc_cardctl_gpk_pkinit *args)
{
sc_apdu_t apdu;
int r;
sc_log(card->ctx,
"gpk_pkfile_init(%u)\n", args->privlen);
memset(&apdu, 0, sizeof(apdu));
apdu.cse = SC_APDU_CASE_1;
apdu.cla = 0x80;
apdu.ins = 0x12;
apdu.p1 = args->file->id & 0x1F;
apdu.p2 = args->privlen / 4;
r = sc_transmit_apdu(card, &apdu);
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
LOG_TEST_RET(card->ctx, r, "Card returned error");
return r;
}
static int
gpk_generate_key(sc_card_t *card, struct sc_cardctl_gpk_genkey *args)
{
sc_apdu_t apdu;
int r;
u8 buffer[256];
sc_log(card->ctx,
"gpk_generate_key(%u)\n", args->privlen);
if (args->privlen != 512 && args->privlen != 1024) {
sc_log(card->ctx,
"Key generation not supported for key length %d",
args->privlen);
return SC_ERROR_NOT_SUPPORTED;
}
memset(&apdu, 0, sizeof(apdu));
apdu.cse = SC_APDU_CASE_2_SHORT;
apdu.cla = 0x80;
apdu.ins = 0xD2;
apdu.p1 = 0x80 | (args->fid & 0x1F);
apdu.p2 = (args->privlen == 1024) ? 0x11 : 0;
apdu.le = args->privlen / 8 + 2;
apdu.resp = buffer;
apdu.resplen = 256;
r = sc_transmit_apdu(card, &apdu);
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
LOG_TEST_RET(card->ctx, r, "Card returned error");
if (args->pubkey_len && apdu.resplen > 2) {
r = reverse(args->pubkey, args->pubkey_len,
buffer + 2, apdu.resplen - 2);
LOG_TEST_RET(card->ctx, r, "Failed to reverse buffer");
args->pubkey_len = r;
}
return r;
}
static int
gpk_pkfile_load(sc_card_t *card, struct sc_cardctl_gpk_pkload *args)
{
struct gpk_private_data *priv = DRVDATA(card);
sc_apdu_t apdu;
unsigned int n;
u8 temp[256];
int r = SC_SUCCESS, outl;
EVP_CIPHER_CTX * ctx;
sc_log(card->ctx, "gpk_pkfile_load(fid=%04x, len=%d, datalen=%d)\n",
args->file->id, args->len, args->datalen);
ctx = EVP_CIPHER_CTX_new();
if (ctx == NULL)
return SC_ERROR_INTERNAL;
if (0) {
sc_log_hex(card->ctx, "Sending (cleartext)",
args->data, args->datalen);
}
memset(&apdu, 0, sizeof(apdu));
apdu.cse = SC_APDU_CASE_3_SHORT;
apdu.cla = 0x80;
apdu.ins = 0x18;
apdu.p1 = args->file->id & 0x1F;
apdu.p2 = args->len;
apdu.lc = args->datalen;
assert(args->datalen <= sizeof(temp));
if (!priv->key_set) {
sc_log(card->ctx, "No secure messaging key set!\n");
return SC_ERROR_SECURITY_STATUS_NOT_SATISFIED;
}
EVP_EncryptInit_ex(ctx, EVP_des_ede(), NULL, priv->key, NULL);
for (n = 0; n < args->datalen; n += 8) {
if (!EVP_EncryptUpdate(ctx, temp+n, &outl, args->data + n, 8)) {
r = SC_ERROR_INTERNAL;
break;
}
}
if (ctx)
EVP_CIPHER_CTX_free(ctx);
if (r != SC_SUCCESS)
return SC_ERROR_INTERNAL;
apdu.data = temp;
apdu.datalen = args->datalen;
priv->key_set = 0;
r = sc_transmit_apdu(card, &apdu);
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
LOG_TEST_RET(card->ctx, r, "Card returned error");
LOG_FUNC_RETURN(card->ctx, r);
}
static int
gpk_get_default_key(sc_card_t *card, struct sc_cardctl_default_key *data)
{
if (data->method == SC_AC_PRO && data->key_ref == 1) {
if (data->len < 16)
return SC_ERROR_BUFFER_TOO_SMALL;
memcpy(data->key_data, "TEST KEYTEST KEY", 16);
data->len = 16;
return 0;
}
return SC_ERROR_NO_DEFAULT_KEY;
}
static int gpk_get_info(sc_card_t *card, int p1, int p2, u8 *buf,
size_t buflen)
{
sc_apdu_t apdu;
int r, retry = 0;
r = sc_lock(card);
LOG_TEST_RET(card->ctx, r, "sc_lock() failed");
do {
memset(&apdu, 0, sizeof(apdu));
apdu.cse = SC_APDU_CASE_2_SHORT;
apdu.cla = 0x80;
apdu.ins = 0xC0;
apdu.p1 = p1;
apdu.p2 = p2;
apdu.le = buflen;
apdu.resp = buf;
apdu.resplen = buflen;
if ((r = sc_transmit_apdu(card, &apdu)) < 0) {
sc_log(card->ctx, "APDU transmit failed: %s",
sc_strerror(r));
sc_unlock(card);
return r;
}
} while (apdu.sw1 == 0x6B && apdu.sw2 == 0x00 && retry++ < 1);
sc_unlock(card);
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
LOG_TEST_RET(card->ctx, r, "Card returned error");
return r;
}
static int gpk_get_serialnr(sc_card_t *card, sc_serial_number_t *serial)
{
int r;
u8 rbuf[10];
sc_apdu_t apdu;
if (card->type != SC_CARD_TYPE_GPK_GPK16000)
return SC_ERROR_NOT_SUPPORTED;
if (!serial)
return SC_ERROR_INVALID_ARGUMENTS;
if (card->serialnr.len) {
memcpy(serial, &card->serialnr, sizeof(*serial));
return SC_SUCCESS;
}
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xb8, 0x00, 0x00);
apdu.cla |= 0x80;
apdu.resp = rbuf;
apdu.resplen = sizeof(rbuf);
apdu.le = 8;
apdu.lc = 0;
apdu.datalen = 0;
r = sc_transmit_apdu(card, &apdu);
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00)
return SC_ERROR_INTERNAL;
memcpy(card->serialnr.value, apdu.resp, apdu.resplen);
card->serialnr.len = apdu.resplen;
memcpy(serial, &card->serialnr, sizeof(*serial));
return SC_SUCCESS;
}
static int
gpk_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr)
{
switch (cmd) {
case SC_CARDCTL_ERASE_CARD:
return gpk_erase_card(card);
case SC_CARDCTL_GET_DEFAULT_KEY:
return gpk_get_default_key(card,
(struct sc_cardctl_default_key *) ptr);
case SC_CARDCTL_GPK_VARIANT:
*(int *) ptr = card->type;
return 0;
case SC_CARDCTL_GPK_LOCK:
return gpk_lock(card, (struct sc_cardctl_gpk_lock *) ptr);
case SC_CARDCTL_GPK_PKINIT:
return gpk_pkfile_init(card,
(struct sc_cardctl_gpk_pkinit *) ptr);
case SC_CARDCTL_GPK_PKLOAD:
return gpk_pkfile_load(card,
(struct sc_cardctl_gpk_pkload *) ptr);
case SC_CARDCTL_GPK_IS_LOCKED:
*(int *) ptr = DRVDATA(card)->locked;
return 0;
case SC_CARDCTL_GPK_GENERATE_KEY:
return gpk_generate_key(card,
(struct sc_cardctl_gpk_genkey *) ptr);
case SC_CARDCTL_GET_SERIALNR:
return gpk_get_serialnr(card, (sc_serial_number_t *) ptr);
}
return SC_ERROR_NOT_SUPPORTED;
}
static int
gpk_build_pin_apdu(sc_card_t *card, sc_apdu_t *apdu, struct sc_pin_cmd_data *data)
{
static u8 sbuf[8];
int r;
if (data->pin_type != SC_AC_CHV)
return SC_ERROR_INVALID_ARGUMENTS;
memset(apdu, 0, sizeof(*apdu));
apdu->cse = SC_APDU_CASE_3_SHORT;
data->flags |= SC_PIN_CMD_NEED_PADDING;
switch (data->cmd) {
case SC_PIN_CMD_VERIFY:
data->pin1.encoding = SC_PIN_ENCODING_ASCII;
data->pin1.pad_length = 8;
data->pin1.pad_char = 0x00;
data->pin1.offset = 5;
r = sc_build_pin(sbuf, 8, &data->pin1, 1);
if (r < 0)
return r;
apdu->cla = 0x00;
apdu->ins = 0x20;
apdu->p1 = 0x00;
break;
case SC_PIN_CMD_CHANGE:
case SC_PIN_CMD_UNBLOCK:
data->pin1.encoding = SC_PIN_ENCODING_BCD;
data->pin1.pad_length = 8;
data->pin1.pad_char = 0x00;
data->pin1.offset = 5;
data->pin2.encoding = SC_PIN_ENCODING_BCD;
data->pin2.pad_length = 8;
data->pin2.pad_char = 0x00;
data->pin2.offset = 5 + 4;
if ((r = sc_build_pin(sbuf, 4, &data->pin1, 1)) < 0
|| (r = sc_build_pin(sbuf + 4, 4, &data->pin2, 1)) < 0)
return r;
apdu->cla = 0x80;
apdu->ins = 0x24;
apdu->p1 = (data->cmd == SC_PIN_CMD_CHANGE)? 0x00 : 0x01;
break;
default:
return SC_ERROR_NOT_SUPPORTED;
}
apdu->p2 = data->pin_reference & 7;
apdu->lc = 8;
apdu->datalen = 8;
apdu->data = sbuf;
return 0;
}
static int
gpk_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left)
{
sc_apdu_t apdu;
int r;
if (data->cmd == SC_PIN_CMD_VERIFY
&& data->pin_type == SC_AC_PRO)
return gpk_select_key(card,
data->pin_reference,
data->pin1.data,
data->pin1.len);
r = gpk_build_pin_apdu(card, &apdu, data);
if (r < 0)
return r;
data->apdu = &apdu;
return iso_ops->pin_cmd(card, data, tries_left);
}
static struct sc_card_driver *
sc_get_driver(void)
{
struct sc_card_driver *iso_drv;
iso_drv = sc_get_iso7816_driver();
iso_ops = iso_drv->ops;
gpk_ops = *iso_ops;
gpk_ops.match_card = gpk_match_card;
gpk_ops.init = gpk_init;
gpk_ops.finish = gpk_finish;
gpk_ops.select_file = gpk_select_file;
gpk_ops.read_binary = gpk_read_binary;
gpk_ops.write_binary = gpk_write_binary;
gpk_ops.update_binary = gpk_update_binary;
gpk_ops.create_file = gpk_create_file;
gpk_ops.card_ctl = gpk_card_ctl;
gpk_ops.set_security_env= gpk_set_security_env;
gpk_ops.restore_security_env= gpk_restore_security_env;
gpk_ops.compute_signature= gpk_compute_signature;
gpk_ops.decipher = gpk_decipher;
gpk_ops.pin_cmd = gpk_pin_cmd;
return &gpk_drv;
}
struct sc_card_driver *
sc_get_gpk_driver(void)
{
return sc_get_driver();
}
#endif