opensc/src/libopensc/card-gpk.c

1903 lines
47 KiB
C
Raw Normal View History

/*
* card-gpk: Driver for GPK 4000 cards
*
* Copyright (C) 2002 Olaf Kirch <okir@lst.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#ifdef ENABLE_OPENSSL /* empty file without 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
/*
* GPK4000 private data
*/
struct gpk_private_data {
/* The GPK usually do file offsets in multiples of
* 4 bytes. This can be customized however. We
* should really query for this during gpk_init */
unsigned int offset_shift;
unsigned int offset_mask;
unsigned int locked : 1,
sample_card : 1;
/* access control bits of file most recently selected */
unsigned short int ac[3];
/* is non-zero if we should use secure messaging */
unsigned key_set : 1;
unsigned int key_reference;
u8 key[16];
/* crypto related data from set_security_env */
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);
/*
* ATRs of GPK4000 cards courtesy of libscez
*/
static 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 }
};
/*
* Driver and card ops structures
*/
static struct sc_card_operations gpk_ops, *iso_ops;
static struct sc_card_driver gpk_drv = {
"Gemplus GPK",
"gpk",
&gpk_ops,
NULL, 0, NULL
};
/*
* return 1 if this driver can handle the card
*/
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;
/* Gemplus GPK docs say we can use just the
* FMN and PRN fields of the historical bytes
* to recognize a GPK card
* See Table 43, pp. 188
* We'll use the first 2 bytes as well
*/
if ((card->reader->atr_info.hist_bytes_len >= 7)
&& (hist_bytes[0] == 0x80)
&& (hist_bytes[1] == 0x65)
&& (hist_bytes[2] == 0xa2)) { /* FMN */
if (hist_bytes[3] == 0x08) { /* PRN? */
card->type = SC_CARD_TYPE_GPK_GPK8000;
return 1;
}
if (hist_bytes[3] == 0x09) { /* PRN? */
card->type = SC_CARD_TYPE_GPK_GPK16000;
return 1;
}
}
return 0;
}
return 1;
}
/*
* Initialize the card struct
*/
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 = (struct gpk_private_data *) calloc(1, sizeof(*priv));
if (card->drv_data == NULL)
return SC_ERROR_OUT_OF_MEMORY;
/* read/write/update binary expect offset to be the
* number of 32 bit words.
* offset_shift is the shift value.
* offset_mask is the corresponding mask. */
priv->offset_shift = 2;
priv->offset_mask = 3;
card->cla = 0x00;
/* Set up algorithm info. GPK 16000 will do any RSA
* exponent, earlier ones are restricted to 0x10001 */
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);
/* Inspect the LOCK byte */
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) {
/* DSA supported - add algo information.
* It's highly unlikely we'll ever see this.
*/
}
if (info[12] & 0x08) {
priv->locked = 1;
}
/* Sample cards use a transport key of "TEST KEYTEST KEY" */
if (!memcmp(info+5, "\x00\xff\x00", 3)) {
priv->sample_card = 1;
}
}
/* State that we have an RNG */
card->caps |= SC_CARD_CAP_RNG;
/* Make sure max send/receive size is 4 byte aligned. */
card->max_send_size &= ~3;
if (card->max_recv_size >= 256)
card->max_recv_size = 252;
else
card->max_recv_size &= ~3;
return 0;
}
/*
* Card is being closed; discard any private data etc
*/
static int
gpk_finish(sc_card_t *card)
{
if (card->drv_data)
free(card->drv_data);
card->drv_data = NULL;
return 0;
}
/*
* Error code handling for the GPK4000.
* sc_check_sw doesn't seem to handle all of the
* status words the GPK is capable of returning
*/
#if 0
static int
gpk_check_sw(sc_card_t *card, u8 sw1, u8 sw2)
{
unsigned short int sw = (sw1 << 8) | sw2;
if ((sw & 0xFFF0) == 0x63C0) {
sc_debug(card->ctx, "wrong PIN, %u tries left\n", sw&0xf);
return SC_ERROR_PIN_CODE_INCORRECT;
}
switch (sw) {
case 0x6400:
sc_debug(card->ctx, "wrong crypto context\n");
return SC_ERROR_OBJECT_NOT_VALID; /* XXX ??? */
/* The following are handled by iso7816_check_sw
* but all return SC_ERROR_UNKNOWN_DATA_RECEIVED
* XXX: fix in the iso driver? */
case 0x6983:
sc_debug(card->ctx, "PIN is blocked\n");
return SC_ERROR_PIN_CODE_INCORRECT;
case 0x6581:
sc_debug(card->ctx, "out of space on card or file\n");
return SC_ERROR_OUT_OF_MEMORY;
case 0x6981:
return SC_ERROR_FILE_NOT_FOUND;
case 0x6A80:
case 0x6b00:
return SC_ERROR_INVALID_ARGUMENTS;
}
return iso7816_check_sw(card, sw1, sw2);
}
#endif
/*
* Select a DF/EF
*/
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;
/* Make sure path starts with MF.
* Note the cached path should always begin with MF. */
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) {
/* Caller asked us to select the DF, but the
* current file is some EF within the DF we're
* interested in. Say ACK */
if (len == 2)
goto okay;
/* Anything else won't work */
return 0;
}
/* In the case of an exact match:
* If the caller needs info on the file to be selected,
* make sure we at least select the file itself.
* If the DF matches the current DF, just return the
* FID */
if (i == len && need_info) {
if (i > 1) {
*pathptr = ptr + len - 1;
*pathlen = len - 1;
return 1;
}
/* bummer */
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);
/* Check whether secure messaging key is specified */
if (ac & 0x3F00)
sc_file_add_acl_entry(file, op, SC_AC_PRO, (ac & 0x3F00) >> 8);
}
/*
* Convert ACLs requested by the application to access condition
* bits supported by the GPK. Since these do not map 1:1 there's
* some fuzz involved.
*/
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) {
/* Support up to 2 PINS only */
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) {
/* unknown purpose - usually the name, but
* the content looks weird, such as
* 84 0D A0 00 00 00 18 0F 00 00 01 63 00 01 04
*/
} else
if (tag == 0xC1 && len >= 2) {
/* Seems to be the file id, followed by something
* C1 04 02 00 00 00 */
file->id = (buf[0] << 8) | buf[1];
} else
if (tag == 0xC2) {
/* unknown purpose
* C2 01 01
*/
}
}
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) {
/* ignore if name is longer than what it should be */
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;
file->id = (sp[4] << 8) | sp[5];
file->size = (sp[8] << 8) | sp[9];
file->record_length = sp[7];
/* Map ACLs. Note the third AC byte is
* valid of EFs only */
for (n = 0; n < 3; n++)
ac[n] = (sp[10+2*n] << 8) | sp[11+2*n];
/* Examine file type */
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: /* 0x38 is DF */
file->type = SC_FILE_TYPE_DF;
/* Icky: the GPK uses different ACLs
* for creating data files and
* 'sensitive' i.e. key files */
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) {
/* oops - this is a directory with an IADF.
* This happens with the personalized GemSafe cards
* for instance. */
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 we're about to select a DF, invalidate secure messaging keys */
if (kind == GPK_SEL_MF || kind == GPK_SEL_DF) {
memset(priv->key, 0, sizeof(priv->key));
priv->key_set = 0;
}
/* do the apdu thing */
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);
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
SC_TEST_RET(card->ctx, r, "Card returned error");
/* Nothing we can say about it... invalidate
* path cache */
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;
if (card->ctx->debug)
sc_debug(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);
/* Fix up the path cache.
* NB we never cache the ID of an EF, just the DF path */
if (r == 0) {
unsigned short int *path;
switch (kind) {
case GPK_SEL_MF:
cp->len = 0;
/* fallthru */
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, 1);
/* Handle the AID case first */
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;
}
/* Now we know we're dealing with 16bit FIDs, either as
* an absolute path name (SC_PATH_TYPE_PATH) or a relative
* FID (SC_PATH_TYPE_FILE_ID)
*
* The API should really tell us whether this is a DF or EF
* we're selecting. All we can do is read tea leaves...
*/
leaf_type = GPK_SEL_EF;
try_again:
if ((path->len & 1) || path->len > sizeof(pathtmp))
return SC_ERROR_INVALID_ARGUMENTS;
pathptr = pathtmp;
for (n = 0; n < path->len; n += 2)
pathptr[n>>1] = (path->value[n] << 8)|path->value[n+1];
pathlen = path->len >> 1;
/* See whether we can skip an initial portion of the
* (absolute) path */
if (path->type == SC_PATH_TYPE_PATH) {
/* Do not retry selecting if this cannot be a DF */
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 {
/* SC_PATH_TYPE_FILEID */
if (pathlen > 1)
return SC_ERROR_INVALID_ARGUMENTS;
use_relative = 1;
}
if (pathlen == 1 && pathptr[0] == GPK_FID_MF) {
/* Select just the MF */
leaf_type = GPK_SEL_MF;
} else {
if (!locked++) {
r = sc_lock(card);
SC_TEST_RET(card->ctx, r, "sc_lock() failed");
}
/* Do we need to select the MF first? */
if (!use_relative) {
r = gpk_select_id(card, GPK_SEL_MF, GPK_FID_MF, NULL);
if (r)
sc_unlock(card);
SC_TEST_RET(card->ctx, r, "Unable to select MF");
/* Consume the MF FID if it's there */
if (pathptr[0] == GPK_FID_MF) {
pathptr++;
pathlen--;
}
if (pathlen == 0)
goto done;
}
/* Next comes a DF, if at all.
* This loop can deal with nesting levels > 1 even
* though the GPK4000 doesn't support it. */
while (pathlen > 1) {
r = gpk_select_id(card, GPK_SEL_DF, pathptr[0], NULL);
if (r)
sc_unlock(card);
SC_TEST_RET(card->ctx, r, "Unable to select DF");
pathptr++;
pathlen--;
}
}
/* Remaining component will be a DF or EF. How do we find out?
* All we can do is try */
r = gpk_select_id(card, leaf_type, pathptr[0], file);
if (r) {
/* Did we guess EF, and were wrong? If so, invalidate
* path cache and try again; this time aiming for a DF */
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;
}
/*
* GPK versions of {read,write,update}_binary functions.
* Required because by default the GPKs do word offsets
*/
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_debug(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_debug(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_debug(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);
}
/*
* Secure messaging
*/
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;
/* Fill block with 0x00 and then with the data. */
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;
/* Set IV */
memset(in, 0x00, 8);
EVP_CIPHER_CTX_init(&ctx);
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;
}
}
if (!EVP_CIPHER_CTX_cleanup(&ctx))
r = SC_ERROR_INTERNAL;
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;
}
/*
* Verify secure messaging response
*/
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)) {
if (card->ctx->debug)
sc_debug(card->ctx, "Invalid secure messaging reply\n");
return SC_ERROR_UNKNOWN_DATA_RECEIVED;
}
apdu->resplen -= 3;
return 0;
}
/*
* Create a file or directory.
* This is a bit tricky because we abuse the ef_structure
* field to transport file types that are non-standard
* (the GPK4000 has lots of bizarre file types).
*/
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;
if (card->ctx->debug)
sc_debug(card->ctx, "gpk_create_file(0x%04X)\n", file->id);
/* Prepare APDU */
memset(&apdu, 0, sizeof(apdu));
apdu.cla = 0x80; /* assume no secure messaging */
apdu.cse = SC_APDU_CASE_3_SHORT;
apdu.ins = 0xE0;
apdu.p2 = 0x00;
/* clear data */
memset(data, 0, sizeof(data));
datalen = 12;
/* FID */
data[0] = file->id >> 8;
data[1] = file->id & 0xFF;
/* encode ACLs */
if (file->type == SC_FILE_TYPE_DF) {
/* The GPK4000 has separate AC bits for
* creating sensitive files and creating
* data files. Since OpenSC has just the notion
* of "file" we use the same ACL for both AC words
*/
apdu.p1 = 0x01; /* create DF */
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; /* create EF */
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); /* XXX? */
}
r = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
SC_TEST_RET(card->ctx, r, "Card returned error");
/* verify secure messaging response */
if (priv->key_set)
r = gpk_verify_crycks(card, &apdu, crycks);
return r;
}
/*
* Set the secure messaging key following a Select FileKey
*/
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;
u8 out[16];
memcpy(out, key+8, 8);
memcpy(out+8, key, 8);
EVP_CIPHER_CTX_init(&ctx);
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));
/* Verify Cryptogram presented by the card terminal
* XXX: what is the appropriate error code to return
* here? INVALID_ARGS doesn't seem quite right
*/
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 (!EVP_CIPHER_CTX_cleanup(&ctx))
r = SC_ERROR_INTERNAL;
if (memcmp(r_rn, out+4, 4) != 0)
r = SC_ERROR_INVALID_ARGUMENTS;
}
sc_mem_clear(out, sizeof(out));
return r;
}
/*
* Verify a key presented by the user for secure messaging
*/
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, 1);
if (buflen != 16)
return SC_ERROR_INVALID_ARGUMENTS;
/* now do the SelFk */
RAND_pseudo_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);
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
SC_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;
}
/*
* Select a security environment (Set Crypto Context in GPK docs).
* When we get here, the PK file has already been selected.
*
* Issue: the GPK distinguishes between "signing" and
* "card internal authentication". I don't know whether this
* makes any difference in practice...
*
* Issue: it seems that sc_compute_signature() does not hash
* the data for the caller. So what is the purpose of HASH_SHA
* and other flags?
*/
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;
/* According to several sources from GemPlus, they don't
* have off the shelf cards that do DSA. So I won't bother
* with implementing this stuff here. */
algorithm = SC_ALGORITHM_RSA;
if (env->flags & SC_SEC_ENV_ALG_PRESENT)
algorithm = env->algorithm;
if (algorithm != SC_ALGORITHM_RSA) {
sc_debug(card->ctx, "Algorithm not supported.\n");
return SC_ERROR_NOT_SUPPORTED;
}
priv->sec_algorithm = algorithm;
/* If there's a key reference, it must be 0 */
if ((env->flags & SC_SEC_ENV_KEY_REF_PRESENT)
&& (env->key_ref_len != 1 || env->key_ref[0] != 0)) {
sc_debug(card->ctx, "Unknown key referenced.\n");
return SC_ERROR_NOT_SUPPORTED;
}
/* Right now, the OpenSC flags do not support any padding
* other than PKCS#1. */
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_debug(card->ctx, "Padding algorithm not supported.\n");
return SC_ERROR_NOT_SUPPORTED;
}
switch (env->operation) {
case SC_SEC_OPERATION_SIGN:
/* Again, the following may not make any difference
* because we don't do any hashing on-card. But
* what the hell, we have all those nice macros,
* so why not use them :)
*/
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_debug(card->ctx, "Unsupported signature algorithm");
return SC_ERROR_NOT_SUPPORTED;
}
break;
case SC_SEC_OPERATION_DECIPHER:
context = GPK_UNWRAP_RSA;
break;
default:
sc_debug(card->ctx, "Crypto operation not supported.\n");
return SC_ERROR_NOT_SUPPORTED;
}
/* Get the file ID */
if (env->flags & SC_SEC_ENV_FILE_REF_PRESENT) {
if (env->file_ref.len != 2) {
sc_debug(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_debug(card->ctx, "File reference missing.\n");
return SC_ERROR_INVALID_ARGUMENTS;
}
/* Select the PK file. The caller has already selected
* the DF. */
r = gpk_select_id(card, GPK_SEL_EF, file_id, NULL);
SC_TEST_RET(card->ctx, r, "Failed to select PK file");
/* Read the sys record of the PK file to find out the key length */
r = sc_read_record(card, 1, sysrec, sizeof(sysrec),
SC_RECORD_BY_REC_NR);
SC_TEST_RET(card->ctx, r, "Failed to read PK sysrec");
if (r != 7 || sysrec[0] != 0) {
sc_debug(card->ctx, "First record of file is not the sysrec");
return SC_ERROR_OBJECT_NOT_VALID;
}
if (sysrec[5] != 0x00) {
sc_debug(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_debug(card->ctx, "Unsupported modulus length");
return SC_ERROR_OBJECT_NOT_VALID;
}
/* Now do SelectCryptoContext */
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);
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
SC_TEST_RET(card->ctx, r, "Card returned error");
return r;
}
/*
* Restore security environment
* Not sure what this is supposed to do.
*/
static int
gpk_restore_security_env(sc_card_t *card, int se_num)
{
return 0;
}
/*
* Revert buffer (needed for all GPK crypto operations because
* they want LSB byte order internally
*/
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;
}
/*
* Use the card's on-board hashing functions to hash some data
*/
#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);
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
SC_TEST_RET(card->ctx, r, "Card returned error");
chain = 0;
}
return 0;
}
#endif
/*
* Send the hashed data to the card.
*/
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);
SC_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);
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
SC_TEST_RET(card->ctx, r, "Card returned error");
return r;
}
/*
* Compute a signature.
* Note we hash everything manually and send it to the card.
*/
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_debug(card->ctx,
"Data length (%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);
SC_TEST_RET(card->ctx, r, "Failed to send hash to card");
/* Now sign the hash.
* The GPK has Internal Authenticate and PK_Sign. I am not
* sure what the difference between the two is. */
memset(&apdu, 0, sizeof(apdu));
apdu.cse = SC_APDU_CASE_2_SHORT;
apdu.cla = 0x80;
apdu.ins = 0x86;
#if 0
/* Don't know why I did this. It conflicts with the spec
* (but it worked with the gpk4k, strange). --okir */
apdu.p1 = priv->sec_padding;
apdu.p2 = priv->sec_mod_len;
#else
apdu.p2 = priv->sec_padding;
#endif
apdu.resp= cardsig;
apdu.resplen = sizeof(cardsig);
apdu.le = priv->sec_mod_len;
r = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
SC_TEST_RET(card->ctx, r, "Card returned error");
/* The GPK returns the signature as little endian numbers.
* Need to revert these */
r = reverse(out, outlen, cardsig, apdu.resplen);
SC_TEST_RET(card->ctx, r, "Failed to reverse signature");
return r;
}
/*
* Decrypt some RSA encrypted piece of data.
* Due to legal restrictions, the GPK will not let you see the
* full cleartext block, just the last N bytes.
* The GPK documentation refers to N as the MaxSessionKey size,
* probably because this feature limits the maximum size of an
* SSL session key you will be able to decrypt using this card.
*/
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_debug(card->ctx,
"Data length (%u) does not match key modulus %u.\n",
inlen, priv->sec_mod_len);
return SC_ERROR_INVALID_ARGUMENTS;
}
/* First revert the cryptogram */
r = reverse(buffer, sizeof(buffer), in, inlen);
SC_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; /* give me all you got :) */
apdu.resp = buffer;
apdu.resplen = sizeof(buffer);
r = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
SC_TEST_RET(card->ctx, r, "Card returned error");
/* Reverse the data we got back */
r = reverse(out, outlen, buffer, apdu.resplen);
SC_TEST_RET(card->ctx, r, "Failed to reverse buffer");
return r;
}
/*
* Erase card
*/
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, 1);
switch (card->type) {
case SC_CARD_TYPE_GPK_GPK4000_su256:
case SC_CARD_TYPE_GPK_GPK4000_sdo:
offset = 0x6B; /* courtesy gemplus hotline */
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);
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
SC_TEST_RET(card->ctx, r, "Card returned error");
priv->key_set = 0;
SC_FUNC_RETURN(card->ctx, 2, r);
}
/*
* Lock a file Access Condition.
*
* File must be selected, and we assume that any authentication
* that needs to be presented in order to allow this operation
* have been presented (ACs from the DF; AC1 for sensitive files,
* AC2 for normal files).
*/
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;
if (card->ctx->debug)
sc_debug(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); /* XXX? */
}
r = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
SC_TEST_RET(card->ctx, r, "Card returned error");
if (priv->key_set)
r = gpk_verify_crycks(card, &apdu, crycks);
return r;
}
/*
* Initialize the private portion of a public key file
*/
static int
gpk_pkfile_init(sc_card_t *card, struct sc_cardctl_gpk_pkinit *args)
{
sc_apdu_t apdu;
int r;
if (card->ctx->debug)
sc_debug(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);
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
SC_TEST_RET(card->ctx, r, "Card returned error");
return r;
}
/*
* Initialize the private portion of a public key file
*/
static int
gpk_generate_key(sc_card_t *card, struct sc_cardctl_gpk_genkey *args)
{
sc_apdu_t apdu;
int r;
u8 buffer[256];
if (card->ctx->debug)
sc_debug(card->ctx, "gpk_generate_key(%u)\n", args->privlen);
if (args->privlen != 512 && args->privlen != 1024) {
sc_debug(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);
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
SC_TEST_RET(card->ctx, r, "Card returned error");
/* Return the public key, inverted.
* The first two bytes must be stripped off. */
if (args->pubkey_len && apdu.resplen > 2) {
r = reverse(args->pubkey, args->pubkey_len,
buffer + 2, apdu.resplen - 2);
SC_TEST_RET(card->ctx, r, "Failed to reverse buffer");
args->pubkey_len = r;
}
return r;
}
/*
* Store a privat key component
*/
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_debug(card->ctx, "gpk_pkfile_load(fid=%04x, len=%d, datalen=%d)\n",
args->file->id, args->len, args->datalen);
#if 0
if (card->ctx->debug > 5) {
char buf[2048];
sc_hex_dump(card->ctx, args->data, args->datalen,
buf, sizeof(buf));
sc_debug(card->ctx, "Sending %d bytes (cleartext):\n%s",
args->datalen, buf);
}
#endif
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;
/* encrypt the private key material */
assert(args->datalen <= sizeof(temp));
if (!priv->key_set) {
sc_debug(card->ctx, "No secure messaging key set!\n");
return SC_ERROR_SECURITY_STATUS_NOT_SATISFIED;
}
EVP_CIPHER_CTX_init(&ctx);
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 (!EVP_CIPHER_CTX_cleanup(&ctx) || r != SC_SUCCESS)
return SC_ERROR_INTERNAL;
apdu.data = temp;
apdu.datalen = args->datalen;
/* Forget the key. The card seems to forget it, too :) */
priv->key_set = 0;
r = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
SC_TEST_RET(card->ctx, r, "Card returned error");
SC_FUNC_RETURN(card->ctx, 1, r);
}
/*
* This function lets pkcs15init query for the transport key
*/
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;
}
/*
* Get the maximum size of a session key the card is
* willing to decrypt
*/
#if 0
static int
gpk_max_session_key(sc_card_t *card)
{
struct gpk_private_data *priv = DRVDATA(card);
sc_path_t path;
u8 value;
int r;
if (priv->max_session_key)
return priv->max_session_key;
/* GPK cards limit the amount of data they're willing
* to RSA decrypt. This data is stored in EFMaxSessionKey */
sc_format_path("01000001", &path);
if ((r = sc_select_file(card, &path, NULL)) < 0
|| (r = sc_read_binary(card, 0, &value, 1, 0)) < 0)
return r;
priv->max_session_key = value;
return value;
}
#endif
/*
* GetInfo call
*/
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;
/* We may have to retry the get info command. It
* returns 6B00 if a previous command returned a 61xx response,
* but the host failed to collect the results.
*
* Note the additional sc_lock/sc_unlock pair, which
* is required to prevent sc_transmit_apdu from
* calling logout(), which in turn does a SELECT MF
* without collecting the response :)
*/
r = sc_lock(card);
SC_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_debug(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);
SC_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;
/* see if we have cached serial number */
if (card->serialnr.len) {
memcpy(serial, &card->serialnr, sizeof(*serial));
return SC_SUCCESS;
}
/* get serial number via Get CSN */
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);
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00)
return SC_ERROR_INTERNAL;
/* cache serial number */
memcpy(card->serialnr.value, apdu.resp, apdu.resplen);
card->serialnr.len = apdu.resplen;
/* copy and return serial number */
memcpy(serial, &card->serialnr, sizeof(*serial));
return SC_SUCCESS;
}
/*
* Dispatch card_ctl calls
*/
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;
/* XXX deal with secure messaging here */
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:
/* Copy PIN to buffer and pad */
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:
/* Copy PINs to buffer, BCD-encoded, and pad */
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;
/* Special case - External Authenticate */
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);
}
/*
* Initialize the driver struct
*/
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.check_sw = gpk_check_sw; */
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();
}
Complete rewrite of OpenSC build system. 1. Build system now supports MinGW (Windows) compilation using msys and cross compilation. 2. Ability to explicitly disable and enable dependencies of the package. 3. openct, pcsc and nsplugins features are disabled by default. 4. Modified pcsc driver to use pcsc dynamically, no compile time dependency is required. 5. --enable-pcsc-lite configuration option renamed to --enable-pcsc. 6. Install opensc.conf file (as opensc.conf.new if opensc.conf exists). 7. Add--enable-doc configuration option, allow installing documentation into target. 8. Add --disable-man configuration option, allow msys mingw32 users to build from svn without extra dependencies. 9. Add export files to each library in order to export only required symbols. Windows native build may use these files instead of scanning objects' symbols. 10. Add opensc-tool --info to display some general information about the build. 11. Create compatibility library to be linked against library instread of recompiling the same source files in different places. 12. Add different win32 version resource to each class of outputs. 13. Make xsl-stylesheets location selectable. 14. Some win32 fixups. 15. Some warning fixups. 16. Many other autoconf/automake cleanups. Alon Bar-Lev svn diff -r 3315:3399 https://www.opensc-project.org/svn/opensc/branches/alonbl/mingw _M . D configure.in _M src _M src/openssh M src/openssh/Makefile.am _M src/tools M src/tools/rutoken-tool.c M src/tools/opensc-tool.c M src/tools/cardos-info.c M src/tools/pkcs15-crypt.c M src/tools/pkcs15-init.c M src/tools/piv-tool.c M src/tools/netkey-tool.c M src/tools/eidenv.c M src/tools/cryptoflex-tool.c M src/tools/util.c M src/tools/pkcs11-tool.c M src/tools/pkcs15-tool.c M src/tools/util.h M src/tools/opensc-explorer.c M src/tools/Makefile.am _M src/pkcs11 M src/pkcs11/pkcs11-global.c M src/pkcs11/framework-pkcs15.c M src/pkcs11/mechanism.c M src/pkcs11/pkcs11-display.c M src/pkcs11/pkcs11-object.c A src/pkcs11/opensc-pkcs11.exports M src/pkcs11/sc-pkcs11.h M src/pkcs11/pkcs11-spy.c M src/pkcs11/openssl.c M src/pkcs11/Makefile.am A src/pkcs11/pkcs11-spy.exports _M src/tests _M src/tests/regression M src/tests/regression/Makefile.am M src/tests/sc-test.c M src/tests/pintest.c M src/tests/Makefile.am _M src/include _M src/include/opensc M src/include/opensc/Makefile.am A src/include/opensc/svnignore M src/include/Makefile.am _M src/signer _M src/signer/npinclude M src/signer/npinclude/Makefile.am M src/signer/Makefile.am A src/signer/signer.exports _M src/common A src/common/compat_dummy.c D src/common/getopt.txt D src/common/strlcpy.c D src/common/LICENSE A src/common/compat_getopt.txt A src/common/compat_strlcpy.c A src/common/LICENSE.compat_getopt A src/common/compat_getopt.c D src/common/strlcpy.h D src/common/ChangeLog D src/common/getpass.c D src/common/my_getopt.c A src/common/compat_strlcpy.h A src/common/compat_getpass.c A src/common/compat_getopt.h A src/common/ChangeLog.compat_getopt D src/common/README.strlcpy D src/common/my_getopt.h A src/common/compat_getpass.h A src/common/README.compat_strlcpy D src/common/strlcpy.3 A src/common/README.compat_getopt D src/common/getopt.3 D src/common/README.my_getopt A src/common/compat_strlcpy.3 A src/common/compat_getopt.3 M src/common/Makefile.am M src/Makefile.am _M src/pkcs15init M src/pkcs15init/pkcs15-oberthur.c M src/pkcs15init/profile.c M src/pkcs15init/pkcs15-lib.c M src/pkcs15init/pkcs15-rutoken.c A src/pkcs15init/pkcs15init.exports M src/pkcs15init/pkcs15-gpk.c M src/pkcs15init/Makefile.am _M src/scconf M src/scconf/Makefile.am M src/scconf/parse.c A src/scconf/scconf.exports _M src/libopensc M src/libopensc/card-rutoken.c M src/libopensc/compression.c M src/libopensc/sc.c M src/libopensc/card-piv.c M src/libopensc/pkcs15-openpgp.c M src/libopensc/pkcs15-postecert.c M src/libopensc/pkcs15-tcos.c M src/libopensc/opensc-config.in M src/libopensc/reader-pcsc.c A src/libopensc/internal-winscard.h M src/libopensc/ctx.c A src/libopensc/libopensc.exports M src/libopensc/pkcs15-piv.c M src/libopensc/pkcs15-infocamere.c M src/libopensc/internal.h M src/libopensc/pkcs15-actalis.c M src/libopensc/pkcs15-starcert.c M src/libopensc/card-oberthur.c M src/libopensc/pkcs15-atrust-acos.c M src/libopensc/p15card-helper.c D src/libopensc/part10.h M src/libopensc/ui.c M src/libopensc/card-gpk.c M src/libopensc/pkcs15-wrap.c M src/libopensc/pkcs15-gemsafeGPK.c M src/libopensc/log.c M src/libopensc/pkcs15-esteid.c M src/libopensc/pkcs15-prkey-rutoken.c M src/libopensc/log.h M src/libopensc/Makefile.am M src/libopensc/reader-openct.c _M aclocal M aclocal/Makefile.am _M win32 M win32/Makefile.am A win32/versioninfo.rc.in A win32/ltrc.inc A configure.ac _M doc _M doc/tools M doc/tools/pkcs15-profile.xml D doc/changelog.sh D doc/export-wiki.xsl _M doc/api _M doc/api/file M doc/api/man.xsl _M doc/api/asn1 _M doc/api/apps _M doc/api/init _M doc/api/types _M doc/api/card M doc/api/html.xsl _M doc/api/misc _M doc/api/util M doc/Makefile.am D doc/export-wiki.sh AM doc/nonpersistent A doc/nonpersistent/export-wiki.xsl A doc/nonpersistent/Makefile.am A doc/nonpersistent/export-wiki.sh A doc/nonpersistent/svn2cl.xsl D doc/generate-man.sh D doc/svn2cl.xsl M Makefile.am A svnignore _M etc M etc/opensc.conf.in M etc/Makefile.am D man _M solaris M solaris/Makefile git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@3405 c6295689-39f2-0310-b995-f0e70906c6a9
2008-03-06 16:06:59 +00:00
#endif /* ENABLE_OPENSSL */