2002-02-10 18:04:03 +00:00
|
|
|
/*
|
2002-02-10 18:09:20 +00:00
|
|
|
* card-gpk: Driver for GPK 4000 cards
|
2002-02-10 18:04:03 +00:00
|
|
|
*
|
2015-10-05 12:06:23 +00:00
|
|
|
* Copyright (C) 2002 Olaf Kirch <okir@suse.de>
|
2002-02-10 18:09:20 +00:00
|
|
|
*
|
|
|
|
* 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
|
2002-02-10 18:04:03 +00:00
|
|
|
*/
|
|
|
|
|
2015-04-22 21:55:33 +00:00
|
|
|
#if HAVE_CONFIG_H
|
2010-03-04 08:14:36 +00:00
|
|
|
#include "config.h"
|
2015-04-22 21:55:33 +00:00
|
|
|
#endif
|
2010-03-04 08:14:36 +00:00
|
|
|
#ifdef ENABLE_OPENSSL /* empty file without openssl */
|
|
|
|
|
2002-02-10 18:04:03 +00:00
|
|
|
#include <stdlib.h>
|
2002-02-21 18:53:23 +00:00
|
|
|
#include <string.h>
|
2007-01-06 13:23:52 +00:00
|
|
|
#include <openssl/evp.h>
|
2002-02-10 18:04:03 +00:00
|
|
|
#include <openssl/rand.h>
|
|
|
|
|
2010-03-04 08:14:36 +00:00
|
|
|
#include "internal.h"
|
|
|
|
#include "cardctl.h"
|
|
|
|
#include "pkcs15.h"
|
|
|
|
|
2002-02-25 18:48:34 +00:00
|
|
|
#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
|
2002-02-10 18:04:03 +00:00
|
|
|
|
2002-02-25 18:48:34 +00:00
|
|
|
#define GPK_FTYPE_SC 0x21
|
2002-02-21 18:53:23 +00:00
|
|
|
|
2002-02-25 18:48:34 +00:00
|
|
|
#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
|
2002-02-21 18:53:23 +00:00
|
|
|
|
2002-02-10 18:04:03 +00:00
|
|
|
/*
|
|
|
|
* GPK4000 private data
|
|
|
|
*/
|
|
|
|
struct gpk_private_data {
|
2002-03-06 12:33:33 +00:00
|
|
|
/* 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;
|
2002-03-13 20:25:37 +00:00
|
|
|
unsigned int offset_mask;
|
2002-12-04 09:26:40 +00:00
|
|
|
unsigned int locked : 1,
|
|
|
|
sample_card : 1;
|
2002-03-06 12:33:33 +00:00
|
|
|
|
2002-02-10 18:04:03 +00:00
|
|
|
/* access control bits of file most recently selected */
|
2002-11-08 13:50:33 +00:00
|
|
|
unsigned short int ac[3];
|
2002-02-10 18:04:03 +00:00
|
|
|
|
|
|
|
/* is non-zero if we should use secure messaging */
|
2002-02-11 11:01:27 +00:00
|
|
|
unsigned key_set : 1;
|
2002-02-21 18:53:23 +00:00
|
|
|
unsigned int key_reference;
|
|
|
|
u8 key[16];
|
2002-02-25 18:48:34 +00:00
|
|
|
|
|
|
|
/* 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;
|
2002-02-10 18:04:03 +00:00
|
|
|
};
|
2002-02-24 19:32:14 +00:00
|
|
|
#define DRVDATA(card) ((struct gpk_private_data *) ((card)->drv_data))
|
2002-02-10 18:04:03 +00:00
|
|
|
|
2005-08-23 21:16:55 +00:00
|
|
|
static int gpk_get_info(sc_card_t *, int, int, u8 *, size_t);
|
2002-02-27 22:15:48 +00:00
|
|
|
|
2002-02-10 18:04:03 +00:00
|
|
|
/*
|
|
|
|
* ATRs of GPK4000 cards courtesy of libscez
|
|
|
|
*/
|
2005-02-06 19:40:40 +00:00
|
|
|
static struct sc_atr_table gpk_atrs[] = {
|
2005-09-07 08:33:55 +00:00
|
|
|
{ "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 }
|
2002-02-10 18:04:03 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Driver and card ops structures
|
|
|
|
*/
|
2002-03-06 12:33:33 +00:00
|
|
|
static struct sc_card_operations gpk_ops, *iso_ops;
|
2003-02-20 12:51:07 +00:00
|
|
|
static struct sc_card_driver gpk_drv = {
|
2005-02-09 14:47:46 +00:00
|
|
|
"Gemplus GPK",
|
2002-02-10 18:04:03 +00:00
|
|
|
"gpk",
|
2005-09-07 08:33:55 +00:00
|
|
|
&gpk_ops,
|
|
|
|
NULL, 0, NULL
|
2002-02-10 18:04:03 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
2005-02-06 19:40:40 +00:00
|
|
|
* return 1 if this driver can handle the card
|
2002-02-10 18:04:03 +00:00
|
|
|
*/
|
2002-08-20 08:59:58 +00:00
|
|
|
static int
|
2005-03-08 20:59:35 +00:00
|
|
|
gpk_match_card(sc_card_t *card)
|
2002-02-10 18:04:03 +00:00
|
|
|
{
|
2005-02-06 19:40:40 +00:00
|
|
|
int i;
|
2002-02-10 18:04:03 +00:00
|
|
|
|
2005-02-06 19:40:40 +00:00
|
|
|
i = _sc_match_atr(card, gpk_atrs, &card->type);
|
|
|
|
if (i < 0) {
|
2010-01-24 15:25:08 +00:00
|
|
|
const u8 *hist_bytes = card->reader->atr_info.hist_bytes;
|
2002-08-20 08:59:58 +00:00
|
|
|
|
2005-02-06 19:40:40 +00:00
|
|
|
/* 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
|
|
|
|
*/
|
2002-02-10 18:04:03 +00:00
|
|
|
|
2010-01-24 15:25:08 +00:00
|
|
|
if ((card->reader->atr_info.hist_bytes_len >= 7)
|
2005-02-06 19:40:40 +00:00
|
|
|
&& (hist_bytes[0] == 0x80)
|
|
|
|
&& (hist_bytes[1] == 0x65)
|
|
|
|
&& (hist_bytes[2] == 0xa2)) { /* FMN */
|
|
|
|
if (hist_bytes[3] == 0x08) { /* PRN? */
|
2005-02-10 10:07:13 +00:00
|
|
|
card->type = SC_CARD_TYPE_GPK_GPK8000;
|
2005-02-06 19:40:40 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (hist_bytes[3] == 0x09) { /* PRN? */
|
2005-02-10 10:07:13 +00:00
|
|
|
card->type = SC_CARD_TYPE_GPK_GPK16000;
|
2005-02-06 19:40:40 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
2002-02-10 18:04:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize the card struct
|
|
|
|
*/
|
|
|
|
static int
|
2005-03-08 20:59:35 +00:00
|
|
|
gpk_init(sc_card_t *card)
|
2002-02-10 18:04:03 +00:00
|
|
|
{
|
|
|
|
struct gpk_private_data *priv;
|
2002-03-15 09:43:10 +00:00
|
|
|
unsigned long exponent, flags, kg;
|
2002-11-08 13:50:33 +00:00
|
|
|
unsigned char info[13];
|
2002-02-10 18:04:03 +00:00
|
|
|
|
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
|
|
|
card->drv_data = priv = calloc(1, sizeof(*priv));
|
2002-02-24 19:32:14 +00:00
|
|
|
if (card->drv_data == NULL)
|
2002-02-10 18:04:03 +00:00
|
|
|
return SC_ERROR_OUT_OF_MEMORY;
|
2002-08-20 08:59:58 +00:00
|
|
|
|
2002-03-13 20:25:37 +00:00
|
|
|
/* 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. */
|
2002-03-06 12:33:33 +00:00
|
|
|
priv->offset_shift = 2;
|
2002-03-13 20:25:37 +00:00
|
|
|
priv->offset_mask = 3;
|
2005-02-07 11:40:58 +00:00
|
|
|
card->cla = 0x00;
|
2002-02-10 18:04:03 +00:00
|
|
|
|
2002-03-08 19:46:33 +00:00
|
|
|
/* Set up algorithm info. GPK 16000 will do any RSA
|
|
|
|
* exponent, earlier ones are restricted to 0x10001 */
|
2002-03-10 11:48:57 +00:00
|
|
|
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;
|
2005-02-10 10:07:13 +00:00
|
|
|
exponent = (card->type < SC_CARD_TYPE_GPK_GPK16000) ? 0x10001 : 0;
|
|
|
|
kg = (card->type >= SC_CARD_TYPE_GPK_GPK8000) ? SC_ALGORITHM_ONBOARD_KEY_GEN : 0;
|
2002-03-15 09:43:10 +00:00
|
|
|
_sc_card_add_rsa_alg(card, 512, flags|kg, exponent);
|
2002-03-10 11:48:57 +00:00
|
|
|
_sc_card_add_rsa_alg(card, 768, flags, exponent);
|
2002-03-15 09:43:10 +00:00
|
|
|
_sc_card_add_rsa_alg(card, 1024, flags|kg, exponent);
|
2002-03-08 19:46:33 +00:00
|
|
|
|
2002-11-08 13:50:33 +00:00
|
|
|
/* 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;
|
|
|
|
}
|
2002-12-04 09:26:40 +00:00
|
|
|
/* Sample cards use a transport key of "TEST KEYTEST KEY" */
|
|
|
|
if (!memcmp(info+5, "\x00\xff\x00", 3)) {
|
|
|
|
priv->sample_card = 1;
|
|
|
|
}
|
2002-11-08 13:50:33 +00:00
|
|
|
}
|
|
|
|
|
2003-01-15 13:20:02 +00:00
|
|
|
/* State that we have an RNG */
|
|
|
|
card->caps |= SC_CARD_CAP_RNG;
|
|
|
|
|
2010-09-09 18:58:44 +00:00
|
|
|
/* Make sure max send/receive size is 4 byte aligned and <256. */
|
|
|
|
card->max_recv_size = 252;
|
2003-12-18 16:35:28 +00:00
|
|
|
|
2002-02-10 18:04:03 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Card is being closed; discard any private data etc
|
|
|
|
*/
|
|
|
|
static int
|
2005-03-08 20:59:35 +00:00
|
|
|
gpk_finish(sc_card_t *card)
|
2002-02-10 18:04:03 +00:00
|
|
|
{
|
2002-02-24 19:32:14 +00:00
|
|
|
if (card->drv_data)
|
|
|
|
free(card->drv_data);
|
|
|
|
card->drv_data = NULL;
|
2002-02-10 18:04:03 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Select a DF/EF
|
|
|
|
*/
|
|
|
|
static int
|
2005-03-08 20:59:35 +00:00
|
|
|
match_path(sc_card_t *card, unsigned short int **pathptr, size_t *pathlen,
|
2002-02-10 18:04:03 +00:00
|
|
|
int need_info)
|
|
|
|
{
|
2002-02-11 10:49:36 +00:00
|
|
|
unsigned short int *curptr, *ptr;
|
2002-02-10 18:04:03 +00:00
|
|
|
size_t curlen, len;
|
|
|
|
size_t i;
|
|
|
|
|
2002-02-11 10:49:36 +00:00
|
|
|
curptr = (unsigned short int *) card->cache.current_path.value;
|
2002-02-10 18:04:03 +00:00
|
|
|
curlen = card->cache.current_path.len;
|
|
|
|
ptr = *pathptr;
|
|
|
|
len = *pathlen;
|
|
|
|
|
|
|
|
if (curlen < 1 || len < 1)
|
|
|
|
return 0;
|
|
|
|
|
2002-02-25 18:48:34 +00:00
|
|
|
/* 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;
|
2002-02-10 18:04:03 +00:00
|
|
|
|
2002-04-08 15:45:13 +00:00
|
|
|
for (i = 1; i < len && i < curlen; i++) {
|
2002-02-10 18:04:03 +00:00
|
|
|
if (ptr[i] != curptr[i])
|
2002-02-25 18:48:34 +00:00
|
|
|
break;
|
2002-02-10 18:04:03 +00:00
|
|
|
}
|
|
|
|
|
2002-04-08 15:45:13 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2002-02-25 18:48:34 +00:00
|
|
|
/* In the case of an exact match:
|
2002-03-11 12:41:18 +00:00
|
|
|
* If the caller needs info on the file to be selected,
|
2002-02-25 18:48:34 +00:00
|
|
|
* 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 */
|
2002-02-10 18:04:03 +00:00
|
|
|
return 0;
|
2002-02-25 18:48:34 +00:00
|
|
|
}
|
2002-02-10 18:04:03 +00:00
|
|
|
|
2002-04-08 15:45:13 +00:00
|
|
|
okay:
|
2002-02-10 18:04:03 +00:00
|
|
|
*pathptr = ptr + i;
|
|
|
|
*pathlen = len - i;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2002-02-21 18:53:23 +00:00
|
|
|
static void
|
2005-08-23 21:16:55 +00:00
|
|
|
ac_to_acl(unsigned int ac, sc_file_t *file, unsigned int op)
|
2002-02-10 18:04:03 +00:00
|
|
|
{
|
|
|
|
unsigned int npins, pin;
|
|
|
|
|
|
|
|
npins = (ac >> 14) & 3;
|
2002-02-21 18:53:23 +00:00
|
|
|
if (npins == 3) {
|
|
|
|
sc_file_add_acl_entry(file, op, SC_AC_NEVER,
|
|
|
|
SC_AC_KEY_REF_NONE);
|
|
|
|
return;
|
2002-02-10 18:04:03 +00:00
|
|
|
}
|
|
|
|
|
2002-02-21 18:53:23 +00:00
|
|
|
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);
|
2002-02-10 18:04:03 +00:00
|
|
|
|
2002-02-21 18:53:23 +00:00
|
|
|
/* Check whether secure messaging key is specified */
|
|
|
|
if (ac & 0x3F00)
|
|
|
|
sc_file_add_acl_entry(file, op, SC_AC_PRO, (ac & 0x3F00) >> 8);
|
2002-02-10 18:04:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2002-02-21 18:53:23 +00:00
|
|
|
static void
|
2005-03-08 20:59:35 +00:00
|
|
|
acl_to_ac(sc_file_t *file, unsigned int op, u8 *ac)
|
2002-02-10 18:04:03 +00:00
|
|
|
{
|
2005-03-08 20:59:35 +00:00
|
|
|
const sc_acl_entry_t *acl;
|
2002-02-21 18:53:23 +00:00
|
|
|
unsigned int npins = 0;
|
|
|
|
|
2002-02-10 18:04:03 +00:00
|
|
|
ac[0] = ac[1] = 0;
|
|
|
|
|
2003-01-09 07:31:58 +00:00
|
|
|
if ((acl = sc_file_get_acl_entry(file, op)) == NULL)
|
|
|
|
return;
|
2002-02-21 18:53:23 +00:00
|
|
|
|
|
|
|
assert(acl->method != SC_AC_UNKNOWN);
|
|
|
|
switch (acl->method) {
|
|
|
|
case SC_AC_NEVER:
|
2002-02-10 18:04:03 +00:00
|
|
|
ac[0] = 0xC0;
|
|
|
|
return;
|
2002-02-21 18:53:23 +00:00
|
|
|
case SC_AC_NONE:
|
|
|
|
return;
|
2002-02-10 18:04:03 +00:00
|
|
|
}
|
|
|
|
|
2002-02-21 18:53:23 +00:00
|
|
|
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;
|
2002-02-10 18:04:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-11-08 12:10:55 +00:00
|
|
|
static int
|
2005-03-08 20:59:35 +00:00
|
|
|
gpk_parse_fci(sc_card_t *card,
|
2002-11-08 12:10:55 +00:00
|
|
|
const u8 *buf, size_t buflen,
|
2005-03-08 20:59:35 +00:00
|
|
|
sc_file_t *file)
|
2002-11-08 12:10:55 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2002-02-10 18:04:03 +00:00
|
|
|
static int
|
2005-03-08 20:59:35 +00:00
|
|
|
gpk_parse_fileinfo(sc_card_t *card,
|
2002-02-11 10:49:36 +00:00
|
|
|
const u8 *buf, size_t buflen,
|
2005-03-08 20:59:35 +00:00
|
|
|
sc_file_t *file)
|
2002-02-10 18:04:03 +00:00
|
|
|
{
|
2002-02-11 10:49:36 +00:00
|
|
|
const u8 *sp, *end, *next;
|
2002-11-08 12:10:55 +00:00
|
|
|
int i, rc;
|
2002-02-10 18:04:03 +00:00
|
|
|
|
|
|
|
memset(file, 0, sizeof(*file));
|
|
|
|
for (i = 0; i < SC_MAX_AC_OPS; i++)
|
2002-02-21 18:53:23 +00:00
|
|
|
sc_file_add_acl_entry(file, i, SC_AC_UNKNOWN, SC_AC_KEY_REF_NONE);
|
2002-02-10 18:04:03 +00:00
|
|
|
|
|
|
|
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) {
|
2002-02-21 18:53:23 +00:00
|
|
|
unsigned int ac[3], n;
|
2002-02-10 18:04:03 +00:00
|
|
|
|
|
|
|
file->id = (sp[4] << 8) | sp[5];
|
|
|
|
file->size = (sp[8] << 8) | sp[9];
|
|
|
|
file->record_length = sp[7];
|
|
|
|
|
2002-02-21 18:53:23 +00:00
|
|
|
/* 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];
|
2002-02-10 18:04:03 +00:00
|
|
|
|
|
|
|
/* 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;
|
2002-02-21 18:53:23 +00:00
|
|
|
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);
|
2002-02-10 18:04:03 +00:00
|
|
|
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 */
|
2002-02-21 18:53:23 +00:00
|
|
|
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);
|
2002-02-10 18:04:03 +00:00
|
|
|
break;
|
|
|
|
}
|
2002-11-08 12:10:55 +00:00
|
|
|
} 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;
|
2002-02-10 18:04:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (file->record_length)
|
|
|
|
file->record_count = file->size / file->record_length;
|
|
|
|
file->magic = SC_FILE_MAGIC;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-08-23 21:16:55 +00:00
|
|
|
gpk_select(sc_card_t *card, int kind,
|
2002-02-11 10:49:36 +00:00
|
|
|
const u8 *buf, size_t buflen,
|
2005-03-08 20:59:35 +00:00
|
|
|
sc_file_t **file)
|
2002-02-10 18:04:03 +00:00
|
|
|
{
|
2002-02-24 19:32:14 +00:00
|
|
|
struct gpk_private_data *priv = DRVDATA(card);
|
2005-03-08 20:59:35 +00:00
|
|
|
sc_apdu_t apdu;
|
2003-10-14 09:02:51 +00:00
|
|
|
u8 resbuf[256];
|
2002-02-10 18:04:03 +00:00
|
|
|
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 */
|
2002-02-21 18:53:23 +00:00
|
|
|
memset(&apdu, 0, sizeof(apdu));
|
|
|
|
apdu.cla = 0x00;
|
2003-10-14 09:02:51 +00:00
|
|
|
apdu.cse = SC_APDU_CASE_3_SHORT;
|
2002-02-21 18:53:23 +00:00
|
|
|
apdu.ins = 0xA4;
|
|
|
|
apdu.p1 = kind;
|
|
|
|
apdu.p2 = 0;
|
2002-02-10 18:04:03 +00:00
|
|
|
apdu.data = buf;
|
|
|
|
apdu.datalen = buflen;
|
|
|
|
apdu.lc = apdu.datalen;
|
2003-10-14 09:02:51 +00:00
|
|
|
|
|
|
|
if (file) {
|
|
|
|
apdu.cse = SC_APDU_CASE_4_SHORT;
|
|
|
|
apdu.resp = resbuf;
|
|
|
|
apdu.resplen = sizeof(resbuf);
|
|
|
|
apdu.le = sizeof(resbuf);
|
|
|
|
}
|
2002-02-10 18:04:03 +00:00
|
|
|
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
|
2002-02-21 18:53:23 +00:00
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error");
|
2002-02-10 18:04:03 +00:00
|
|
|
|
|
|
|
/* 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;
|
2002-02-21 18:53:23 +00:00
|
|
|
*file = sc_file_new();
|
2002-02-10 18:04:03 +00:00
|
|
|
|
2002-02-21 18:53:23 +00:00
|
|
|
r = gpk_parse_fileinfo(card, apdu.resp, apdu.resplen, *file);
|
|
|
|
if (r < 0) {
|
|
|
|
sc_file_free(*file);
|
|
|
|
*file = NULL;
|
|
|
|
}
|
|
|
|
return r;
|
2002-02-10 18:04:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-08-23 21:16:55 +00:00
|
|
|
gpk_select_id(sc_card_t *card, int kind, unsigned int fid,
|
2005-03-08 20:59:35 +00:00
|
|
|
sc_file_t **file)
|
2002-02-10 18:04:03 +00:00
|
|
|
{
|
2005-03-08 20:59:35 +00:00
|
|
|
sc_path_t *cp = &card->cache.current_path;
|
2002-02-21 18:53:23 +00:00
|
|
|
u8 fbuf[2];
|
2003-10-14 09:56:50 +00:00
|
|
|
int r;
|
2002-02-21 18:53:23 +00:00
|
|
|
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
|
|
|
|
"gpk_select_id(0x%04X, kind=%u)\n", fid, kind);
|
2002-02-10 18:04:03 +00:00
|
|
|
|
|
|
|
fbuf[0] = fid >> 8;
|
|
|
|
fbuf[1] = fid & 0xff;
|
2002-02-21 18:53:23 +00:00
|
|
|
|
2002-02-10 18:04:03 +00:00
|
|
|
r = gpk_select(card, kind, fbuf, 2, file);
|
|
|
|
|
2002-03-11 12:41:18 +00:00
|
|
|
/* Fix up the path cache.
|
|
|
|
* NB we never cache the ID of an EF, just the DF path */
|
2002-02-10 18:04:03 +00:00
|
|
|
if (r == 0) {
|
2002-03-11 12:41:18 +00:00
|
|
|
unsigned short int *path;
|
2002-02-10 18:04:03 +00:00
|
|
|
|
2002-03-11 12:41:18 +00:00
|
|
|
switch (kind) {
|
|
|
|
case GPK_SEL_MF:
|
2002-02-10 18:04:03 +00:00
|
|
|
cp->len = 0;
|
2002-03-11 12:41:18 +00:00
|
|
|
/* fallthru */
|
|
|
|
case GPK_SEL_DF:
|
|
|
|
assert(cp->len + 1 <= SC_MAX_PATH_SIZE / 2);
|
|
|
|
path = (unsigned short int *) cp->value;
|
|
|
|
path[cp->len++] = fid;
|
2002-02-10 18:04:03 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
cp->len = 0;
|
|
|
|
}
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-03-08 20:59:35 +00:00
|
|
|
gpk_select_file(sc_card_t *card, const sc_path_t *path,
|
|
|
|
sc_file_t **file)
|
2002-02-10 18:04:03 +00:00
|
|
|
{
|
2002-02-11 10:49:36 +00:00
|
|
|
unsigned short int pathtmp[SC_MAX_PATH_SIZE/2];
|
|
|
|
unsigned short int *pathptr;
|
2002-02-21 18:53:23 +00:00
|
|
|
size_t pathlen, n;
|
|
|
|
int locked = 0, r = 0, use_relative = 0, retry = 1;
|
|
|
|
u8 leaf_type;
|
2002-02-10 18:04:03 +00:00
|
|
|
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
|
2002-02-10 18:04:03 +00:00
|
|
|
|
|
|
|
/* 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) {
|
2002-02-21 18:53:23 +00:00
|
|
|
/* 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;
|
2002-02-10 18:04:03 +00:00
|
|
|
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);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "sc_lock() failed");
|
2002-02-10 18:04:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Unable to select MF");
|
2002-02-10 18:04:03 +00:00
|
|
|
|
|
|
|
/* 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);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Unable to select DF");
|
2002-02-10 18:04:03 +00:00
|
|
|
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 */
|
2002-02-21 18:53:23 +00:00
|
|
|
if (leaf_type == GPK_SEL_EF && retry) {
|
2002-02-10 18:04:03 +00:00
|
|
|
card->cache.current_path.len = 0;
|
|
|
|
leaf_type = GPK_SEL_DF;
|
|
|
|
goto try_again;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
|
|
|
if (locked)
|
|
|
|
sc_unlock(card);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2002-03-06 12:33:33 +00:00
|
|
|
/*
|
|
|
|
* GPK versions of {read,write,update}_binary functions.
|
|
|
|
* Required because by default the GPKs do word offsets
|
|
|
|
*/
|
|
|
|
static int
|
2005-03-08 20:59:35 +00:00
|
|
|
gpk_read_binary(sc_card_t *card, unsigned int offset,
|
2002-03-06 12:33:33 +00:00
|
|
|
u8 *buf, size_t count, unsigned long flags)
|
|
|
|
{
|
|
|
|
struct gpk_private_data *priv = DRVDATA(card);
|
|
|
|
|
2002-03-13 20:25:37 +00:00
|
|
|
if (offset & priv->offset_mask) {
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid file offset (not a multiple of %d)",
|
2002-03-13 20:25:37 +00:00
|
|
|
priv->offset_mask + 1);
|
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
|
|
}
|
2002-03-06 12:33:33 +00:00
|
|
|
return iso_ops->read_binary(card, offset >> priv->offset_shift,
|
|
|
|
buf, count, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-03-08 20:59:35 +00:00
|
|
|
gpk_write_binary(sc_card_t *card, unsigned int offset,
|
2002-03-06 12:33:33 +00:00
|
|
|
const u8 *buf, size_t count, unsigned long flags)
|
|
|
|
{
|
|
|
|
struct gpk_private_data *priv = DRVDATA(card);
|
|
|
|
|
2002-03-13 20:25:37 +00:00
|
|
|
if (offset & priv->offset_mask) {
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid file offset (not a multiple of %d)",
|
2002-03-13 20:25:37 +00:00
|
|
|
priv->offset_mask + 1);
|
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
|
|
}
|
2002-03-06 12:33:33 +00:00
|
|
|
return iso_ops->write_binary(card, offset >> priv->offset_shift,
|
|
|
|
buf, count, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-03-08 20:59:35 +00:00
|
|
|
gpk_update_binary(sc_card_t *card, unsigned int offset,
|
2002-03-06 12:33:33 +00:00
|
|
|
const u8 *buf, size_t count, unsigned long flags)
|
|
|
|
{
|
|
|
|
struct gpk_private_data *priv = DRVDATA(card);
|
|
|
|
|
2002-03-13 20:25:37 +00:00
|
|
|
if (offset & priv->offset_mask) {
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid file offset (not a multiple of %d)",
|
2002-03-13 20:25:37 +00:00
|
|
|
priv->offset_mask + 1);
|
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
|
|
}
|
2002-03-06 12:33:33 +00:00
|
|
|
return iso_ops->update_binary(card, offset >> priv->offset_shift,
|
|
|
|
buf, count, flags);
|
|
|
|
}
|
|
|
|
|
2002-02-10 18:04:03 +00:00
|
|
|
/*
|
|
|
|
* Secure messaging
|
|
|
|
*/
|
|
|
|
static int
|
2005-03-08 20:59:35 +00:00
|
|
|
gpk_compute_crycks(sc_card_t *card, sc_apdu_t *apdu,
|
2002-02-11 10:49:36 +00:00
|
|
|
u8 *crycks1)
|
2002-02-10 18:04:03 +00:00
|
|
|
{
|
2002-02-24 19:32:14 +00:00
|
|
|
struct gpk_private_data *priv = DRVDATA(card);
|
2002-02-21 18:53:23 +00:00
|
|
|
u8 in[8], out[8], block[64];
|
2007-01-06 13:23:52 +00:00
|
|
|
unsigned int len = 0, i;
|
|
|
|
int r = SC_SUCCESS, outl;
|
2016-01-06 14:40:59 +00:00
|
|
|
EVP_CIPHER_CTX *ctx = NULL;
|
|
|
|
|
|
|
|
ctx = EVP_CIPHER_CTX_new();
|
|
|
|
if (ctx == NULL)
|
|
|
|
return SC_ERROR_INTERNAL;
|
|
|
|
|
2002-02-10 18:04:03 +00:00
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
2016-01-06 14:40:59 +00:00
|
|
|
EVP_EncryptInit_ex(ctx, EVP_des_ede_cbc(), NULL, priv->key, in);
|
2007-01-06 13:23:52 +00:00
|
|
|
for (i = 0; i < len; i += 8) {
|
2016-01-06 14:40:59 +00:00
|
|
|
if (!EVP_EncryptUpdate(ctx, out, &outl, &block[i], 8)) {
|
2007-01-06 13:23:52 +00:00
|
|
|
r = SC_ERROR_INTERNAL;
|
|
|
|
break;
|
|
|
|
}
|
2002-02-10 18:04:03 +00:00
|
|
|
}
|
2016-01-06 14:40:59 +00:00
|
|
|
EVP_CIPHER_CTX_free(ctx);
|
2002-02-10 18:04:03 +00:00
|
|
|
|
2002-02-11 10:49:36 +00:00
|
|
|
memcpy((u8 *) (apdu->data + apdu->datalen), out + 5, 3);
|
2002-02-10 18:04:03 +00:00
|
|
|
apdu->datalen += 3;
|
|
|
|
apdu->lc += 3;
|
2002-02-21 18:53:23 +00:00
|
|
|
apdu->le += 3;
|
2002-02-10 18:04:03 +00:00
|
|
|
if (crycks1)
|
|
|
|
memcpy(crycks1, out, 3);
|
2005-09-17 10:44:45 +00:00
|
|
|
sc_mem_clear(in, sizeof(in));
|
|
|
|
sc_mem_clear(out, sizeof(out));
|
|
|
|
sc_mem_clear(block, sizeof(block));
|
2007-01-06 13:23:52 +00:00
|
|
|
return r;
|
2002-02-10 18:04:03 +00:00
|
|
|
}
|
|
|
|
|
2002-02-21 18:53:23 +00:00
|
|
|
/*
|
|
|
|
* Verify secure messaging response
|
|
|
|
*/
|
|
|
|
static int
|
2005-03-08 20:59:35 +00:00
|
|
|
gpk_verify_crycks(sc_card_t *card, sc_apdu_t *apdu, u8 *crycks)
|
2002-02-21 18:53:23 +00:00
|
|
|
{
|
|
|
|
if (apdu->resplen < 3
|
|
|
|
|| memcmp(apdu->resp + apdu->resplen - 3, crycks, 3)) {
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
|
|
|
|
"Invalid secure messaging reply\n");
|
2002-04-06 12:02:28 +00:00
|
|
|
return SC_ERROR_UNKNOWN_DATA_RECEIVED;
|
2002-02-21 18:53:23 +00:00
|
|
|
}
|
|
|
|
apdu->resplen -= 3;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2002-02-10 18:04:03 +00:00
|
|
|
/*
|
|
|
|
* 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
|
2005-03-08 20:59:35 +00:00
|
|
|
gpk_create_file(sc_card_t *card, sc_file_t *file)
|
2002-02-10 18:04:03 +00:00
|
|
|
{
|
2002-02-24 19:32:14 +00:00
|
|
|
struct gpk_private_data *priv = DRVDATA(card);
|
2005-03-08 20:59:35 +00:00
|
|
|
sc_apdu_t apdu;
|
2002-02-21 18:53:23 +00:00
|
|
|
u8 data[28+3], crycks[3], resp[3];
|
2002-02-10 18:04:03 +00:00
|
|
|
size_t datalen, namelen;
|
|
|
|
int r;
|
|
|
|
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
|
|
|
|
"gpk_create_file(0x%04X)\n", file->id);
|
2002-02-21 18:53:23 +00:00
|
|
|
|
2002-02-10 18:04:03 +00:00
|
|
|
/* 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;
|
2002-02-21 18:53:23 +00:00
|
|
|
acl_to_ac(file, SC_AC_OP_CREATE, data + 6);
|
|
|
|
acl_to_ac(file, SC_AC_OP_CREATE, data + 8);
|
2002-02-10 18:04:03 +00:00
|
|
|
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;
|
2002-02-21 18:53:23 +00:00
|
|
|
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);
|
2002-02-10 18:04:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
|
2002-02-21 18:53:23 +00:00
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error");
|
2002-02-10 18:04:03 +00:00
|
|
|
|
2002-02-21 18:53:23 +00:00
|
|
|
/* verify secure messaging response */
|
|
|
|
if (priv->key_set)
|
|
|
|
r = gpk_verify_crycks(card, &apdu, crycks);
|
2002-02-10 18:04:03 +00:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the secure messaging key following a Select FileKey
|
|
|
|
*/
|
|
|
|
static int
|
2002-02-11 10:49:36 +00:00
|
|
|
gpk_set_filekey(const u8 *key, const u8 *challenge,
|
|
|
|
const u8 *r_rn, u8 *kats)
|
2002-02-10 18:04:03 +00:00
|
|
|
{
|
2007-01-06 13:23:52 +00:00
|
|
|
int r = SC_SUCCESS, outl;
|
2016-01-06 14:40:59 +00:00
|
|
|
EVP_CIPHER_CTX * ctx = NULL;
|
2007-01-06 13:23:52 +00:00
|
|
|
u8 out[16];
|
|
|
|
|
|
|
|
memcpy(out, key+8, 8);
|
|
|
|
memcpy(out+8, key, 8);
|
|
|
|
|
2016-01-06 14:40:59 +00:00
|
|
|
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))
|
2007-01-06 13:23:52 +00:00
|
|
|
r = SC_ERROR_INTERNAL;
|
2016-01-06 14:40:59 +00:00
|
|
|
|
|
|
|
if (!EVP_CIPHER_CTX_cleanup(ctx))
|
2007-01-06 13:23:52 +00:00
|
|
|
r = SC_ERROR_INTERNAL;
|
|
|
|
if (r == SC_SUCCESS) {
|
2016-01-06 14:40:59 +00:00
|
|
|
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))
|
2007-01-06 13:23:52 +00:00
|
|
|
r = SC_ERROR_INTERNAL;
|
2016-01-06 14:40:59 +00:00
|
|
|
if (!EVP_CIPHER_CTX_cleanup(ctx))
|
|
|
|
r = SC_ERROR_INTERNAL;
|
2007-01-06 13:23:52 +00:00
|
|
|
}
|
|
|
|
memset(out, 0, sizeof(out));
|
2002-02-10 18:04:03 +00:00
|
|
|
|
|
|
|
/* Verify Cryptogram presented by the card terminal
|
|
|
|
* XXX: what is the appropriate error code to return
|
|
|
|
* here? INVALID_ARGS doesn't seem quite right
|
|
|
|
*/
|
2007-01-06 13:23:52 +00:00
|
|
|
if (r == SC_SUCCESS) {
|
2016-01-06 14:40:59 +00:00
|
|
|
EVP_CIPHER_CTX_init(ctx);
|
|
|
|
EVP_EncryptInit_ex(ctx, EVP_des_ede(), NULL, kats, NULL);
|
|
|
|
if (!EVP_EncryptUpdate(ctx, out, &outl, challenge, 8))
|
2007-01-06 13:23:52 +00:00
|
|
|
r = SC_ERROR_INTERNAL;
|
|
|
|
if (memcmp(r_rn, out+4, 4) != 0)
|
|
|
|
r = SC_ERROR_INVALID_ARGUMENTS;
|
|
|
|
}
|
2002-02-10 18:04:03 +00:00
|
|
|
|
2016-01-06 14:40:59 +00:00
|
|
|
if (ctx)
|
|
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
|
|
|
2005-09-17 10:44:45 +00:00
|
|
|
sc_mem_clear(out, sizeof(out));
|
2002-02-10 18:04:03 +00:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Verify a key presented by the user for secure messaging
|
|
|
|
*/
|
|
|
|
static int
|
2005-03-08 20:59:35 +00:00
|
|
|
gpk_select_key(sc_card_t *card, int key_sfi, const u8 *buf, size_t buflen)
|
2002-02-10 18:04:03 +00:00
|
|
|
{
|
2002-02-24 19:32:14 +00:00
|
|
|
struct gpk_private_data *priv = DRVDATA(card);
|
2005-03-08 20:59:35 +00:00
|
|
|
sc_apdu_t apdu;
|
2004-12-15 19:59:59 +00:00
|
|
|
u8 rnd[8], resp[258];
|
2002-02-10 18:04:03 +00:00
|
|
|
int r;
|
|
|
|
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
|
2002-02-21 18:53:23 +00:00
|
|
|
|
2002-02-10 18:04:03 +00:00
|
|
|
if (buflen != 16)
|
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
|
|
|
2002-02-21 18:53:23 +00:00
|
|
|
/* now do the SelFk */
|
2016-01-06 14:40:59 +00:00
|
|
|
RAND_bytes(rnd, sizeof(rnd));
|
2002-02-10 18:04:03 +00:00
|
|
|
memset(&apdu, 0, sizeof(apdu));
|
|
|
|
apdu.cla = 0x80;
|
|
|
|
apdu.cse = SC_APDU_CASE_4_SHORT;
|
|
|
|
apdu.ins = 0x28;
|
2002-02-21 18:53:23 +00:00
|
|
|
apdu.p1 = 0;
|
2002-02-10 18:04:03 +00:00
|
|
|
apdu.p2 = key_sfi;
|
2004-12-15 19:59:59 +00:00
|
|
|
apdu.data = rnd;
|
|
|
|
apdu.datalen = sizeof(rnd);
|
2002-02-10 18:04:03 +00:00
|
|
|
apdu.lc = apdu.datalen;
|
|
|
|
apdu.resp = resp;
|
|
|
|
apdu.resplen = sizeof(resp);
|
2002-02-21 18:53:23 +00:00
|
|
|
apdu.le = 12;
|
2002-04-06 12:02:28 +00:00
|
|
|
|
2002-02-10 18:04:03 +00:00
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
|
2002-02-21 18:53:23 +00:00
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error");
|
2002-02-10 18:04:03 +00:00
|
|
|
|
|
|
|
if (apdu.resplen != 12) {
|
2002-04-06 12:02:28 +00:00
|
|
|
r = SC_ERROR_UNKNOWN_DATA_RECEIVED;
|
2002-02-10 18:04:03 +00:00
|
|
|
} else
|
2004-12-15 19:59:59 +00:00
|
|
|
if ((r = gpk_set_filekey(buf, rnd, resp, priv->key)) == 0) {
|
2002-02-10 18:04:03 +00:00
|
|
|
priv->key_set = 1;
|
2002-02-21 18:53:23 +00:00
|
|
|
priv->key_reference = key_sfi;
|
2002-02-10 18:04:03 +00:00
|
|
|
}
|
|
|
|
|
2005-09-17 10:44:45 +00:00
|
|
|
sc_mem_clear(resp, sizeof(resp));
|
2002-02-10 18:04:03 +00:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2002-02-25 18:48:34 +00:00
|
|
|
/*
|
|
|
|
* 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
|
2005-03-08 20:59:35 +00:00
|
|
|
gpk_set_security_env(sc_card_t *card,
|
|
|
|
const sc_security_env_t *env,
|
2002-02-25 18:48:34 +00:00
|
|
|
int se_num)
|
|
|
|
{
|
|
|
|
struct gpk_private_data *priv = DRVDATA(card);
|
2005-03-08 20:59:35 +00:00
|
|
|
sc_apdu_t apdu;
|
2002-02-25 18:48:34 +00:00
|
|
|
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) {
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Algorithm not supported.\n");
|
2002-02-25 18:48:34 +00:00
|
|
|
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)) {
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unknown key referenced.\n");
|
2002-02-25 18:48:34 +00:00
|
|
|
return SC_ERROR_NOT_SUPPORTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Right now, the OpenSC flags do not support any padding
|
|
|
|
* other than PKCS#1. */
|
2002-03-15 09:43:10 +00:00
|
|
|
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 {
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Padding algorithm not supported.\n");
|
2002-03-15 09:43:10 +00:00
|
|
|
return SC_ERROR_NOT_SUPPORTED;
|
|
|
|
}
|
2002-02-25 18:48:34 +00:00
|
|
|
|
|
|
|
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 :)
|
|
|
|
*/
|
2002-03-08 05:59:57 +00:00
|
|
|
if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) {
|
2002-08-20 08:59:58 +00:00
|
|
|
context = GPK_SIGN_RSA_SHA;
|
2002-02-25 18:48:34 +00:00
|
|
|
priv->sec_hash_len = 20;
|
2002-03-15 09:43:10 +00:00
|
|
|
} else
|
|
|
|
if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_MD5_SHA1) {
|
2002-08-20 08:59:58 +00:00
|
|
|
context = GPK_SIGN_RSA_SSL;
|
2002-03-15 09:43:10 +00:00
|
|
|
priv->sec_hash_len = 36;
|
|
|
|
} else
|
|
|
|
if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_MD5) {
|
2002-08-20 08:59:58 +00:00
|
|
|
context = GPK_SIGN_RSA_MD5;
|
2002-02-25 18:48:34 +00:00
|
|
|
priv->sec_hash_len = 16;
|
2002-03-15 09:43:10 +00:00
|
|
|
} else {
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unsupported signature algorithm");
|
2002-03-15 09:43:10 +00:00
|
|
|
return SC_ERROR_NOT_SUPPORTED;
|
2002-02-25 18:48:34 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SC_SEC_OPERATION_DECIPHER:
|
|
|
|
context = GPK_UNWRAP_RSA;
|
|
|
|
break;
|
|
|
|
default:
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Crypto operation not supported.\n");
|
2002-02-25 18:48:34 +00:00
|
|
|
return SC_ERROR_NOT_SUPPORTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get the file ID */
|
|
|
|
if (env->flags & SC_SEC_ENV_FILE_REF_PRESENT) {
|
|
|
|
if (env->file_ref.len != 2) {
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "File reference: invalid length.\n");
|
2002-02-25 18:48:34 +00:00
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
|
|
}
|
|
|
|
file_id = (env->file_ref.value[0] << 8)
|
|
|
|
| env->file_ref.value[1];
|
|
|
|
} else {
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "File reference missing.\n");
|
2002-02-25 18:48:34 +00:00
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
|
|
}
|
|
|
|
|
2002-04-11 15:12:41 +00:00
|
|
|
/* Select the PK file. The caller has already selected
|
|
|
|
* the DF. */
|
|
|
|
r = gpk_select_id(card, GPK_SEL_EF, file_id, NULL);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Failed to select PK file");
|
2002-04-11 15:12:41 +00:00
|
|
|
|
2002-02-25 18:48:34 +00:00
|
|
|
/* 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);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Failed to read PK sysrec");
|
2002-02-25 18:48:34 +00:00
|
|
|
if (r != 7 || sysrec[0] != 0) {
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "First record of file is not the sysrec");
|
2002-02-25 18:48:34 +00:00
|
|
|
return SC_ERROR_OBJECT_NOT_VALID;
|
|
|
|
}
|
|
|
|
if (sysrec[5] != 0x00) {
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Public key is not an RSA key");
|
2002-02-25 18:48:34 +00:00
|
|
|
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:
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unsupported modulus length");
|
2002-02-25 18:48:34 +00:00
|
|
|
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);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
|
2002-02-25 18:48:34 +00:00
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error");
|
2002-02-25 18:48:34 +00:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Restore security environment
|
|
|
|
* Not sure what this is supposed to do.
|
|
|
|
*/
|
|
|
|
static int
|
2005-03-08 20:59:35 +00:00
|
|
|
gpk_restore_security_env(sc_card_t *card, int se_num)
|
2002-02-25 18:48:34 +00:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2002-03-15 09:43:10 +00:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2002-02-25 18:48:34 +00:00
|
|
|
/*
|
|
|
|
* Use the card's on-board hashing functions to hash some data
|
|
|
|
*/
|
|
|
|
#ifdef dontuse
|
|
|
|
static int
|
2005-03-08 20:59:35 +00:00
|
|
|
gpk_hash(sc_card_t *card, const u8 *data, size_t datalen)
|
2002-02-25 18:48:34 +00:00
|
|
|
{
|
2005-03-08 20:59:35 +00:00
|
|
|
sc_apdu_t apdu;
|
2002-02-25 18:48:34 +00:00
|
|
|
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;
|
2002-08-20 08:59:58 +00:00
|
|
|
apdu.ins = 0xDA;
|
2002-02-25 18:48:34 +00:00
|
|
|
apdu.p1 = chain;
|
|
|
|
apdu.p2 = len;
|
|
|
|
apdu.lc = len + 2;
|
|
|
|
apdu.data= buffer;
|
|
|
|
apdu.datalen = len + 2;
|
|
|
|
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
|
2002-02-25 18:48:34 +00:00
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error");
|
2002-02-25 18:48:34 +00:00
|
|
|
chain = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Send the hashed data to the card.
|
|
|
|
*/
|
|
|
|
static int
|
2005-03-08 20:59:35 +00:00
|
|
|
gpk_init_hashed(sc_card_t *card, const u8 *digest, unsigned int len)
|
2002-02-25 18:48:34 +00:00
|
|
|
{
|
2005-03-08 20:59:35 +00:00
|
|
|
sc_apdu_t apdu;
|
2002-03-11 12:41:18 +00:00
|
|
|
u8 tsegid[64];
|
2002-02-25 18:48:34 +00:00
|
|
|
int r;
|
|
|
|
|
2002-03-15 09:43:10 +00:00
|
|
|
r = reverse(tsegid, sizeof(tsegid), digest, len);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Failed to reverse buffer");
|
2002-03-11 12:41:18 +00:00
|
|
|
|
2002-02-25 18:48:34 +00:00
|
|
|
memset(&apdu, 0, sizeof(apdu));
|
|
|
|
apdu.cse = SC_APDU_CASE_3_SHORT;
|
|
|
|
apdu.cla = 0x80;
|
|
|
|
apdu.ins = 0xEA;
|
|
|
|
apdu.lc = len;
|
2002-03-11 12:41:18 +00:00
|
|
|
apdu.data= tsegid;
|
2002-02-25 18:48:34 +00:00
|
|
|
apdu.datalen = len;
|
|
|
|
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
|
2002-02-25 18:48:34 +00:00
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error");
|
2002-02-25 18:48:34 +00:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Compute a signature.
|
|
|
|
* Note we hash everything manually and send it to the card.
|
|
|
|
*/
|
|
|
|
static int
|
2005-03-08 20:59:35 +00:00
|
|
|
gpk_compute_signature(sc_card_t *card, const u8 *data,
|
2002-02-25 18:48:34 +00:00
|
|
|
size_t data_len, u8 * out, size_t outlen)
|
|
|
|
{
|
|
|
|
struct gpk_private_data *priv = DRVDATA(card);
|
2005-03-08 20:59:35 +00:00
|
|
|
sc_apdu_t apdu;
|
2002-02-25 18:48:34 +00:00
|
|
|
u8 cardsig[1024/8];
|
|
|
|
int r;
|
|
|
|
|
2002-03-11 12:41:18 +00:00
|
|
|
if (data_len > priv->sec_mod_len) {
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
|
2017-03-14 19:02:30 +00:00
|
|
|
"Data length (%"SC_FORMAT_LEN_SIZE_T"u) does not match key modulus %u.\n",
|
|
|
|
data_len, priv->sec_mod_len);
|
2002-02-25 18:48:34 +00:00
|
|
|
return SC_ERROR_INTERNAL;
|
|
|
|
}
|
2002-06-18 12:20:26 +00:00
|
|
|
if (sizeof(cardsig) < priv->sec_mod_len)
|
|
|
|
return SC_ERROR_BUFFER_TOO_SMALL;
|
2002-02-25 18:48:34 +00:00
|
|
|
|
2002-03-11 12:41:18 +00:00
|
|
|
r = gpk_init_hashed(card, data, data_len);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Failed to send hash to card");
|
2002-02-25 18:48:34 +00:00
|
|
|
|
|
|
|
/* 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;
|
2002-08-20 08:59:58 +00:00
|
|
|
apdu.ins = 0x86;
|
2002-06-18 12:20:26 +00:00
|
|
|
apdu.p2 = priv->sec_padding;
|
2002-02-25 18:48:34 +00:00
|
|
|
apdu.resp= cardsig;
|
|
|
|
apdu.resplen = sizeof(cardsig);
|
|
|
|
apdu.le = priv->sec_mod_len;
|
|
|
|
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
|
2002-02-25 18:48:34 +00:00
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error");
|
2002-02-25 18:48:34 +00:00
|
|
|
|
|
|
|
/* The GPK returns the signature as little endian numbers.
|
|
|
|
* Need to revert these */
|
2002-03-15 09:43:10 +00:00
|
|
|
r = reverse(out, outlen, cardsig, apdu.resplen);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Failed to reverse signature");
|
2002-03-15 09:43:10 +00:00
|
|
|
|
|
|
|
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
|
2005-03-08 20:59:35 +00:00
|
|
|
gpk_decipher(sc_card_t *card, const u8 *in, size_t inlen,
|
2002-03-15 09:43:10 +00:00
|
|
|
u8 *out, size_t outlen)
|
|
|
|
{
|
|
|
|
struct gpk_private_data *priv = DRVDATA(card);
|
2005-03-08 20:59:35 +00:00
|
|
|
sc_apdu_t apdu;
|
2002-03-15 09:43:10 +00:00
|
|
|
u8 buffer[256];
|
|
|
|
int r;
|
|
|
|
|
|
|
|
if (inlen != priv->sec_mod_len) {
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
|
2017-03-14 19:02:30 +00:00
|
|
|
"Data length (%"SC_FORMAT_LEN_SIZE_T"u) does not match key modulus %u.\n",
|
|
|
|
inlen, priv->sec_mod_len);
|
2002-03-15 09:43:10 +00:00
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
2002-02-25 18:48:34 +00:00
|
|
|
}
|
|
|
|
|
2002-03-15 09:43:10 +00:00
|
|
|
/* First revert the cryptogram */
|
|
|
|
r = reverse(buffer, sizeof(buffer), in, inlen);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Cryptogram too large");
|
2002-03-15 09:43:10 +00:00
|
|
|
in = buffer;
|
|
|
|
|
2005-12-12 20:38:19 +00:00
|
|
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x1C, 0x00, 0x00);
|
|
|
|
apdu.cla |= 0x80;
|
|
|
|
apdu.lc = inlen;
|
|
|
|
apdu.data = in;
|
2002-03-15 09:43:10 +00:00
|
|
|
apdu.datalen = inlen;
|
2005-12-12 20:38:19 +00:00
|
|
|
apdu.le = 256; /* give me all you got :) */
|
|
|
|
apdu.resp = buffer;
|
2002-03-15 09:43:10 +00:00
|
|
|
apdu.resplen = sizeof(buffer);
|
|
|
|
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
|
2002-03-15 09:43:10 +00:00
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error");
|
2002-03-15 09:43:10 +00:00
|
|
|
|
|
|
|
/* Reverse the data we got back */
|
|
|
|
r = reverse(out, outlen, buffer, apdu.resplen);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Failed to reverse buffer");
|
2002-03-15 09:43:10 +00:00
|
|
|
|
|
|
|
return r;
|
2002-02-25 18:48:34 +00:00
|
|
|
}
|
2002-02-21 18:53:23 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Erase card
|
|
|
|
*/
|
|
|
|
static int
|
2005-03-08 20:59:35 +00:00
|
|
|
gpk_erase_card(sc_card_t *card)
|
2002-02-21 18:53:23 +00:00
|
|
|
{
|
2002-02-24 19:32:14 +00:00
|
|
|
struct gpk_private_data *priv = DRVDATA(card);
|
2005-03-08 20:59:35 +00:00
|
|
|
sc_apdu_t apdu;
|
2002-02-25 18:48:34 +00:00
|
|
|
u8 offset;
|
2002-02-21 18:53:23 +00:00
|
|
|
int r;
|
|
|
|
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
|
2005-02-06 19:40:40 +00:00
|
|
|
switch (card->type) {
|
2005-02-10 10:07:13 +00:00
|
|
|
case SC_CARD_TYPE_GPK_GPK4000_su256:
|
|
|
|
case SC_CARD_TYPE_GPK_GPK4000_sdo:
|
2002-02-21 18:53:23 +00:00
|
|
|
offset = 0x6B; /* courtesy gemplus hotline */
|
|
|
|
break;
|
|
|
|
|
2005-02-10 10:07:13 +00:00
|
|
|
case SC_CARD_TYPE_GPK_GPK4000_s:
|
2002-02-21 18:53:23 +00:00
|
|
|
offset = 7;
|
|
|
|
break;
|
|
|
|
|
2005-02-10 10:07:13 +00:00
|
|
|
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:
|
2002-02-21 18:53:23 +00:00
|
|
|
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);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
|
2002-02-21 18:53:23 +00:00
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error");
|
2002-02-21 18:53:23 +00:00
|
|
|
|
|
|
|
priv->key_set = 0;
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r);
|
2002-02-21 18:53:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
2005-03-08 20:59:35 +00:00
|
|
|
gpk_lock(sc_card_t *card, struct sc_cardctl_gpk_lock *args)
|
2002-02-21 18:53:23 +00:00
|
|
|
{
|
2002-02-24 19:32:14 +00:00
|
|
|
struct gpk_private_data *priv = DRVDATA(card);
|
2005-03-08 20:59:35 +00:00
|
|
|
sc_file_t *file = args->file;
|
|
|
|
sc_apdu_t apdu;
|
2002-02-21 18:53:23 +00:00
|
|
|
u8 data[8], crycks[3], resp[3];
|
|
|
|
int r;
|
|
|
|
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
|
|
|
|
"gpk_lock(0x%04X, %u)\n", file->id, args->operation);
|
2002-02-21 18:53:23 +00:00
|
|
|
|
|
|
|
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);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
|
2002-02-21 18:53:23 +00:00
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error");
|
2002-02-21 18:53:23 +00:00
|
|
|
|
|
|
|
if (priv->key_set)
|
|
|
|
r = gpk_verify_crycks(card, &apdu, crycks);
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2002-02-23 13:38:01 +00:00
|
|
|
/*
|
|
|
|
* Initialize the private portion of a public key file
|
|
|
|
*/
|
|
|
|
static int
|
2005-03-08 20:59:35 +00:00
|
|
|
gpk_pkfile_init(sc_card_t *card, struct sc_cardctl_gpk_pkinit *args)
|
2002-02-23 13:38:01 +00:00
|
|
|
{
|
2005-03-08 20:59:35 +00:00
|
|
|
sc_apdu_t apdu;
|
2002-02-23 13:38:01 +00:00
|
|
|
int r;
|
|
|
|
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
|
|
|
|
"gpk_pkfile_init(%u)\n", args->privlen);
|
2002-06-17 15:24:21 +00:00
|
|
|
|
2002-02-23 13:38:01 +00:00
|
|
|
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);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
|
2002-02-23 13:38:01 +00:00
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error");
|
2002-02-23 13:38:01 +00:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2003-12-16 14:32:16 +00:00
|
|
|
/*
|
|
|
|
* Initialize the private portion of a public key file
|
|
|
|
*/
|
|
|
|
static int
|
2005-03-08 20:59:35 +00:00
|
|
|
gpk_generate_key(sc_card_t *card, struct sc_cardctl_gpk_genkey *args)
|
2003-12-16 14:32:16 +00:00
|
|
|
{
|
2005-03-08 20:59:35 +00:00
|
|
|
sc_apdu_t apdu;
|
2003-12-16 14:32:16 +00:00
|
|
|
int r;
|
|
|
|
u8 buffer[256];
|
|
|
|
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
|
|
|
|
"gpk_generate_key(%u)\n", args->privlen);
|
2003-12-16 14:32:16 +00:00
|
|
|
if (args->privlen != 512 && args->privlen != 1024) {
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
|
2003-12-16 14:32:16 +00:00
|
|
|
"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;
|
2004-01-07 10:11:22 +00:00
|
|
|
apdu.le = args->privlen / 8 + 2;
|
2003-12-16 14:32:16 +00:00
|
|
|
apdu.resp = buffer;
|
|
|
|
apdu.resplen = 256;
|
|
|
|
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
|
2003-12-16 14:32:16 +00:00
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error");
|
2003-12-16 14:32:16 +00:00
|
|
|
|
2004-01-07 10:11:22 +00:00
|
|
|
/* 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);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Failed to reverse buffer");
|
2004-01-07 10:11:22 +00:00
|
|
|
args->pubkey_len = r;
|
|
|
|
}
|
2003-12-16 14:32:16 +00:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2002-02-23 13:38:01 +00:00
|
|
|
/*
|
|
|
|
* Store a privat key component
|
|
|
|
*/
|
|
|
|
static int
|
2005-03-08 20:59:35 +00:00
|
|
|
gpk_pkfile_load(sc_card_t *card, struct sc_cardctl_gpk_pkload *args)
|
2002-02-23 13:38:01 +00:00
|
|
|
{
|
2002-02-24 19:32:14 +00:00
|
|
|
struct gpk_private_data *priv = DRVDATA(card);
|
2005-03-08 20:59:35 +00:00
|
|
|
sc_apdu_t apdu;
|
2002-02-23 13:38:01 +00:00
|
|
|
unsigned int n;
|
|
|
|
u8 temp[256];
|
2007-01-06 13:23:52 +00:00
|
|
|
int r = SC_SUCCESS, outl;
|
2016-01-06 14:40:59 +00:00
|
|
|
EVP_CIPHER_CTX * ctx;
|
2002-02-23 13:38:01 +00:00
|
|
|
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "gpk_pkfile_load(fid=%04x, len=%d, datalen=%d)\n",
|
2002-06-17 15:24:21 +00:00
|
|
|
args->file->id, args->len, args->datalen);
|
|
|
|
|
2016-01-06 14:40:59 +00:00
|
|
|
ctx = EVP_CIPHER_CTX_new();
|
|
|
|
if (ctx == NULL)
|
|
|
|
return SC_ERROR_INTERNAL;
|
|
|
|
|
2010-03-15 12:17:13 +00:00
|
|
|
if (0) {
|
2017-11-13 14:11:58 +00:00
|
|
|
sc_log_hex(card->ctx, "Sending (cleartext)",
|
|
|
|
args->data, args->datalen);
|
2002-06-17 15:24:21 +00:00
|
|
|
}
|
|
|
|
|
2003-08-28 12:50:51 +00:00
|
|
|
memset(&apdu, 0, sizeof(apdu));
|
2002-02-23 13:38:01 +00:00
|
|
|
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) {
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "No secure messaging key set!\n");
|
2002-02-23 13:38:01 +00:00
|
|
|
return SC_ERROR_SECURITY_STATUS_NOT_SATISFIED;
|
|
|
|
}
|
2007-01-06 13:23:52 +00:00
|
|
|
|
2016-01-06 14:40:59 +00:00
|
|
|
EVP_EncryptInit_ex(ctx, EVP_des_ede(), NULL, priv->key, NULL);
|
2002-02-23 13:38:01 +00:00
|
|
|
for (n = 0; n < args->datalen; n += 8) {
|
2016-01-06 14:40:59 +00:00
|
|
|
if (!EVP_EncryptUpdate(ctx, temp+n, &outl, args->data + n, 8)) {
|
2007-01-06 13:23:52 +00:00
|
|
|
r = SC_ERROR_INTERNAL;
|
|
|
|
break;
|
|
|
|
}
|
2002-02-23 13:38:01 +00:00
|
|
|
}
|
2016-01-06 14:40:59 +00:00
|
|
|
if (ctx)
|
|
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
|
|
if (r != SC_SUCCESS)
|
2007-01-06 13:23:52 +00:00
|
|
|
return SC_ERROR_INTERNAL;
|
|
|
|
|
2002-02-23 13:38:01 +00:00
|
|
|
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);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
|
2002-02-23 13:38:01 +00:00
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error");
|
2002-02-23 13:38:01 +00:00
|
|
|
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
|
2002-02-23 13:38:01 +00:00
|
|
|
}
|
|
|
|
|
2002-12-04 09:26:40 +00:00
|
|
|
/*
|
|
|
|
* This function lets pkcs15init query for the transport key
|
|
|
|
*/
|
|
|
|
static int
|
2005-03-08 20:59:35 +00:00
|
|
|
gpk_get_default_key(sc_card_t *card, struct sc_cardctl_default_key *data)
|
2002-12-04 09:26:40 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2002-11-08 13:50:33 +00:00
|
|
|
/*
|
|
|
|
* GetInfo call
|
|
|
|
*/
|
2007-06-21 10:07:01 +00:00
|
|
|
static int gpk_get_info(sc_card_t *card, int p1, int p2, u8 *buf,
|
|
|
|
size_t buflen)
|
2002-11-08 13:50:33 +00:00
|
|
|
{
|
2005-03-08 20:59:35 +00:00
|
|
|
sc_apdu_t apdu;
|
2003-12-03 11:11:14 +00:00
|
|
|
int r, retry = 0;
|
2002-11-08 13:50:33 +00:00
|
|
|
|
2003-12-03 11:11:14 +00:00
|
|
|
/* 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);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "sc_lock() failed");
|
2003-12-03 11:11:14 +00:00
|
|
|
|
|
|
|
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) {
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "APDU transmit failed: %s",
|
2003-12-03 11:11:14 +00:00
|
|
|
sc_strerror(r));
|
|
|
|
sc_unlock(card);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
} while (apdu.sw1 == 0x6B && apdu.sw2 == 0x00 && retry++ < 1);
|
|
|
|
sc_unlock(card);
|
2002-11-08 13:50:33 +00:00
|
|
|
|
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error");
|
2002-11-08 13:50:33 +00:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2004-12-15 19:59:59 +00:00
|
|
|
static int gpk_get_serialnr(sc_card_t *card, sc_serial_number_t *serial)
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
u8 rbuf[10];
|
2005-03-08 20:59:35 +00:00
|
|
|
sc_apdu_t apdu;
|
2004-12-15 19:59:59 +00:00
|
|
|
|
2005-02-10 10:07:13 +00:00
|
|
|
if (card->type != SC_CARD_TYPE_GPK_GPK16000)
|
2004-12-15 19:59:59 +00:00
|
|
|
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);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
|
2004-12-15 19:59:59 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2002-02-21 18:53:23 +00:00
|
|
|
/*
|
|
|
|
* Dispatch card_ctl calls
|
|
|
|
*/
|
|
|
|
static int
|
2005-03-08 20:59:35 +00:00
|
|
|
gpk_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr)
|
2002-02-21 18:53:23 +00:00
|
|
|
{
|
|
|
|
switch (cmd) {
|
|
|
|
case SC_CARDCTL_ERASE_CARD:
|
|
|
|
return gpk_erase_card(card);
|
2002-12-04 09:26:40 +00:00
|
|
|
case SC_CARDCTL_GET_DEFAULT_KEY:
|
|
|
|
return gpk_get_default_key(card,
|
|
|
|
(struct sc_cardctl_default_key *) ptr);
|
2002-06-17 15:24:21 +00:00
|
|
|
case SC_CARDCTL_GPK_VARIANT:
|
2005-02-06 19:40:40 +00:00
|
|
|
*(int *) ptr = card->type;
|
2002-06-17 15:24:21 +00:00
|
|
|
return 0;
|
2002-02-21 18:53:23 +00:00
|
|
|
case SC_CARDCTL_GPK_LOCK:
|
|
|
|
return gpk_lock(card, (struct sc_cardctl_gpk_lock *) ptr);
|
2002-02-23 13:38:01 +00:00
|
|
|
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);
|
2002-11-08 13:50:33 +00:00
|
|
|
case SC_CARDCTL_GPK_IS_LOCKED:
|
|
|
|
*(int *) ptr = DRVDATA(card)->locked;
|
|
|
|
return 0;
|
2003-12-16 14:32:16 +00:00
|
|
|
case SC_CARDCTL_GPK_GENERATE_KEY:
|
|
|
|
return gpk_generate_key(card,
|
|
|
|
(struct sc_cardctl_gpk_genkey *) ptr);
|
2004-12-15 19:59:59 +00:00
|
|
|
case SC_CARDCTL_GET_SERIALNR:
|
|
|
|
return gpk_get_serialnr(card, (sc_serial_number_t *) ptr);
|
2002-02-21 18:53:23 +00:00
|
|
|
}
|
2003-12-16 14:32:16 +00:00
|
|
|
|
|
|
|
|
2002-02-21 18:53:23 +00:00
|
|
|
return SC_ERROR_NOT_SUPPORTED;
|
|
|
|
}
|
|
|
|
|
2002-12-23 18:47:27 +00:00
|
|
|
static int
|
|
|
|
gpk_build_pin_apdu(sc_card_t *card, sc_apdu_t *apdu, struct sc_pin_cmd_data *data)
|
|
|
|
{
|
|
|
|
static u8 sbuf[8];
|
2003-04-16 16:01:22 +00:00
|
|
|
int r;
|
2002-12-23 18:47:27 +00:00
|
|
|
|
|
|
|
if (data->pin_type != SC_AC_CHV)
|
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
|
|
|
|
|
|
/* XXX deal with secure messaging here */
|
|
|
|
memset(apdu, 0, sizeof(*apdu));
|
2003-02-16 20:25:41 +00:00
|
|
|
apdu->cse = SC_APDU_CASE_3_SHORT;
|
|
|
|
|
2003-06-23 12:56:36 +00:00
|
|
|
data->flags |= SC_PIN_CMD_NEED_PADDING;
|
|
|
|
|
2002-12-23 18:47:27 +00:00
|
|
|
switch (data->cmd) {
|
|
|
|
case SC_PIN_CMD_VERIFY:
|
|
|
|
/* Copy PIN to buffer and pad */
|
2003-07-30 11:07:25 +00:00
|
|
|
data->pin1.encoding = SC_PIN_ENCODING_ASCII;
|
|
|
|
data->pin1.pad_length = 8;
|
|
|
|
data->pin1.pad_char = 0x00;
|
2003-06-23 12:56:36 +00:00
|
|
|
data->pin1.offset = 5;
|
2002-12-23 18:47:27 +00:00
|
|
|
r = sc_build_pin(sbuf, 8, &data->pin1, 1);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2003-02-16 20:25:41 +00:00
|
|
|
apdu->cla = 0x00;
|
|
|
|
apdu->ins = 0x20;
|
|
|
|
apdu->p1 = 0x00;
|
2002-12-23 18:47:27 +00:00
|
|
|
break;
|
|
|
|
case SC_PIN_CMD_CHANGE:
|
|
|
|
case SC_PIN_CMD_UNBLOCK:
|
|
|
|
/* Copy PINs to buffer, BCD-encoded, and pad */
|
2003-07-30 11:07:25 +00:00
|
|
|
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;
|
2003-06-23 12:56:36 +00:00
|
|
|
data->pin2.offset = 5 + 4;
|
2002-12-23 18:47:27 +00:00
|
|
|
if ((r = sc_build_pin(sbuf, 4, &data->pin1, 1)) < 0
|
|
|
|
|| (r = sc_build_pin(sbuf + 4, 4, &data->pin2, 1)) < 0)
|
|
|
|
return r;
|
|
|
|
|
2003-02-16 20:25:41 +00:00
|
|
|
apdu->cla = 0x80;
|
|
|
|
apdu->ins = 0x24;
|
|
|
|
apdu->p1 = (data->cmd == SC_PIN_CMD_CHANGE)? 0x00 : 0x01;
|
2002-12-23 18:47:27 +00:00
|
|
|
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)
|
|
|
|
{
|
2005-03-08 20:59:35 +00:00
|
|
|
sc_apdu_t apdu;
|
2002-12-23 18:47:27 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2002-02-10 18:04:03 +00:00
|
|
|
/*
|
|
|
|
* Initialize the driver struct
|
|
|
|
*/
|
2003-02-20 12:51:07 +00:00
|
|
|
static struct sc_card_driver *
|
2007-06-21 10:07:01 +00:00
|
|
|
sc_get_driver(void)
|
2002-02-10 18:04:03 +00:00
|
|
|
{
|
2005-08-23 21:16:55 +00:00
|
|
|
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;
|
|
|
|
|
2002-02-10 18:04:03 +00:00
|
|
|
return &gpk_drv;
|
|
|
|
}
|
|
|
|
|
2003-02-20 12:51:07 +00:00
|
|
|
struct sc_card_driver *
|
2007-06-21 10:07:01 +00:00
|
|
|
sc_get_gpk_driver(void)
|
2002-02-10 18:04:03 +00:00
|
|
|
{
|
|
|
|
return sc_get_driver();
|
|
|
|
}
|
2008-03-06 16:06:59 +00:00
|
|
|
#endif /* ENABLE_OPENSSL */
|