opensc/src/libopensc/pkcs15-gemsafeGPK.c

521 lines
14 KiB
C
Raw Normal View History

/*
* partial PKCS15 emulation for gemsafe GPK cards
*
* Copyright (C) 2005, Douglas E. Engert <deengert@anl.gov>
* 2004, Nils Larsch <larsch@trustcenter.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
*/
2015-04-22 21:55:33 +00:00
#if HAVE_CONFIG_H
#include "config.h"
2015-04-22 21:55:33 +00:00
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "common/compat_strlcpy.h"
#include "internal.h"
#include "pkcs15.h"
#include "log.h"
#include "cardctl.h"
#define MANU_ID "GemSAFE on GPK16000"
static int (*pin_cmd_save)(struct sc_card *, struct sc_pin_cmd_data *,
int *tries_left);
typedef struct cdata_st {
const char *label;
int authority;
const char *path;
const char *id;
int obj_flags;
} cdata;
typedef struct pdata_st {
const char *id;
const char *label;
const char *path;
int ref;
int type;
unsigned int maxlen;
unsigned int minlen;
unsigned int storedlen;
int flags;
int tries_left;
const char pad_char;
int obj_flags;
} pindata;
typedef struct prdata_st {
const char *id;
const char *label;
unsigned int modulus_len;
int usage;
const char *path;
int ref;
const char *auth_id;
int obj_flags;
} prdata;
typedef struct keyinfo_st {
int fileid;
sc_pkcs15_id_t id;
unsigned int modulus_len;
u8 modulus[1024/8];
} keyinfo;
#define USAGE_NONREP SC_PKCS15_PRKEY_USAGE_NONREPUDIATION
#define USAGE_KE SC_PKCS15_PRKEY_USAGE_ENCRYPT | \
SC_PKCS15_PRKEY_USAGE_DECRYPT | \
SC_PKCS15_PRKEY_USAGE_WRAP | \
SC_PKCS15_PRKEY_USAGE_UNWRAP
#define USAGE_AUT SC_PKCS15_PRKEY_USAGE_ENCRYPT | \
SC_PKCS15_PRKEY_USAGE_DECRYPT | \
SC_PKCS15_PRKEY_USAGE_WRAP | \
SC_PKCS15_PRKEY_USAGE_UNWRAP | \
SC_PKCS15_PRKEY_USAGE_SIGN
static const u8 gemsafe_aid[] = {0xA0, 0x00, 0x00, 0x00, 0x18,
0x0F, 0x00, 0x00, 0x01, 0x63, 0x00, 0x01};
static int my_pin_cmd(sc_card_t * card, struct sc_pin_cmd_data * data,
int *tries_left)
{
/* GemSAFE pin uses a null terminated string with 0xFF */
/* so we need to add the 0x00 to the pin then pad with 0xFF */
int r;
const u8 *saved_data = NULL;
int saved_len = 0;
u8 newpin[8];
LOG_FUNC_CALLED(card->ctx);
memset(newpin, 0xff, sizeof(newpin));
if (data->pin1.data && data->pin1.len < 8 && data->pin1.len > 0) {
memcpy(newpin,data->pin1.data, (size_t)data->pin1.len);
newpin[data->pin1.len] = 0x00;
2018-11-22 08:31:29 +00:00
sc_log(card->ctx, "pin len=%d", data->pin1.len);
saved_data = data->pin1.data;
saved_len = data->pin1.len;
data->pin1.data = newpin;
data->pin1.len = sizeof(newpin);
}
r = pin_cmd_save(card, data, tries_left);
if (saved_data) {
data->pin1.data = saved_data;
data->pin1.len = saved_len;
}
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r);
}
static int is_seq(unsigned char * seq, unsigned int *seq_size, unsigned int *seq_len)
{
int i,j,k;
if (seq[0] != 0x30)
return 0; /* not a sequence */
if (seq[1] & 0x80) {
i = seq[1] & 0x7f;
if (i > 2 || i == 0)
return 0; /* cert would be bigger then 65k or zero */
if (seq[2] == 0)
return 0; /* DER would not have extra zero */
k = 0;
for (j = 0; j < i; j++) {
k = (k << 8) + seq[j + 2];
}
if (k < 128)
return 0; /* DER would have used single byte for len */
} else {
i = 0;
k = seq[1];
}
*seq_size = i + 2;
*seq_len = k;
return 1;
}
static int gemsafe_detect_card(sc_pkcs15_card_t *p15card)
{
sc_card_t *card = p15card->card;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
if (strcmp(card->name, "Gemplus GPK"))
return SC_ERROR_WRONG_CARD;
return SC_SUCCESS;
}
static int sc_pkcs15emu_gemsafeGPK_init(sc_pkcs15_card_t *p15card)
{
const cdata certs[] = {
{"User certificate",0, "","1", 0},
{NULL, 0, NULL, NULL, 0}
};
const pindata pins[] = {
{ "01", "pin", "3F000200", 0x00,
SC_PKCS15_PIN_TYPE_ASCII_NUMERIC,
8, 4, 8, SC_PKCS15_PIN_FLAG_NEEDS_PADDING |
SC_PKCS15_PIN_FLAG_LOCAL, -1, 0x00,
SC_PKCS15_CO_FLAG_PRIVATE },
{ NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0}
};
const prdata prkeys[] = {
{ "01", "AUTH key", 1024, USAGE_AUT, "I0009",
0x00, "01", 0},
{ NULL, NULL, 0, 0, NULL, 0, NULL, 0}
};
int r, i, j;
int dfpath;
sc_path_t path;
sc_file_t *file = NULL;
sc_card_t *card = p15card->card;
unsigned char *gsdata = NULL;
unsigned int idxlen, idx1, idx2, seq_len1, seq_len2, seq_size1, seq_size2;
sc_serial_number_t serial;
u8 sysrec[7];
int num_keyinfo = 0;
keyinfo kinfo[9]; /* will look for 9 keys */
u8 modulus_buf[ 1 + 1024 / 8]; /* tag+modulus */
u8 *cp;
char buf[256];
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
/* need to limit to 248 */
card->max_send_size = 248;
card->max_recv_size = 248;
/* could read this off card if needed */
set_string(&p15card->tokeninfo->label, "GemSAFE");
set_string(&p15card->tokeninfo->manufacturer_id, MANU_ID);
/* get serial number */
r = sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serial);
if (r != SC_SUCCESS)
return SC_ERROR_INTERNAL;
r = sc_bin_to_hex(serial.value, serial.len, buf, sizeof(buf), 0);
if (r != SC_SUCCESS)
return SC_ERROR_INTERNAL;
set_string(&p15card->tokeninfo->serial_number, buf);
/* test if we have a gemsafe app df */
memset(&path, 0, sizeof(path));
memcpy(path.value, gemsafe_aid, sizeof(gemsafe_aid));
path.len = sizeof(gemsafe_aid);
path.type = SC_PATH_TYPE_DF_NAME;
r = sc_select_file(card, &path, &file);
if (r < 0) {
/* OK, then lets try for 3f000200 */
sc_format_path("3F000200",&path);
path.type = SC_PATH_TYPE_PATH;
r = sc_select_file(card, &path, &file);
}
if (r < 0)
return SC_ERROR_WRONG_CARD;
/* we will use dfpath in all other references */
dfpath = file->id;
2015-10-14 20:37:12 +00:00
sc_file_free(file);
file = NULL;
2018-11-22 08:31:29 +00:00
sc_log(card->ctx, "GemSafe file found, id=%d",dfpath);
/* There may be more then one key in the directory. */
/* we need to find them so we can associate them with the */
/* the certificate. The files are 0007 to 000F */
for (i = 0x7; i <= 0xF; i++) {
path.value[0] = 0x00;
path.value[1] = i;
path.len = 2;
path.type = SC_PATH_TYPE_FILE_ID;
r = sc_select_file(card, &path, NULL);
if (r < 0)
continue;
r = sc_read_record(card, 1, sysrec, sizeof(sysrec), SC_RECORD_BY_REC_NR);
if (r != 7 || sysrec[0] != 0) {
continue;
}
if (sysrec[5] != 0x00) {
continue;
}
switch (sysrec[1]) {
case 0x00: kinfo[num_keyinfo].modulus_len = 512 / 8; break;
case 0x10: kinfo[num_keyinfo].modulus_len = 768 / 8; break;
case 0x11: kinfo[num_keyinfo].modulus_len = 1024 / 8; break;
default:
2018-11-22 08:31:29 +00:00
sc_log(card->ctx, "Unsupported modulus length");
continue;
}
kinfo[num_keyinfo].fileid = i;
sc_pkcs15_format_id("", &kinfo[num_keyinfo].id);
2018-11-22 08:31:29 +00:00
sc_log(card->ctx, "reading modulus");
r = sc_read_record(card, 2, modulus_buf,
kinfo[num_keyinfo].modulus_len+1, SC_RECORD_BY_REC_NR);
if (r < 0)
continue;
/* need to reverse the modulus skipping the tag */
j = kinfo[num_keyinfo].modulus_len;
cp = kinfo[num_keyinfo].modulus;
while (j--)
*cp++ = modulus_buf[j + 1];
num_keyinfo++;
}
/* Get the gemsafe data with the cert */
sc_format_path("3F000200004", &path);
/* file.id has the real DF of the GemSAFE file from above*/
path.value[2] = dfpath >> 8;
path.value[3] = dfpath & 0xff;
if (sc_select_file(card, &path, &file) < 0) {
return SC_ERROR_WRONG_CARD;
}
/* the GemSAFE file has our cert, but we do not know the format */
/* of the file. But we do know a cert has SEQ SEQ SEQOF INT 2 */
/* so we will look for that. We assume cert is larger then 127 bytes */
/* and less then 65K, and must be fit in the file->size */
/* There is a chance that we might find something that is not */
/* a cert, but the chances are low. If GemPlus ever publishes */
/* the format of the file, we can used that instead. */
/* For performance reasons we will only */
/* read part of the file , as it is about 6100 bytes */
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
gsdata = malloc(file->size);
if (!gsdata)
return SC_ERROR_OUT_OF_MEMORY;
/* set indices of data in gsdata */
idx1 = 0; /* start point */
idx2 = 0; /* index of last data read so far */
/* set certs We only have one we are interested in */
/* but the read loop is set up to allow for more in future */
for (i = 0; certs[i].label; i++) {
struct sc_pkcs15_cert_info cert_info;
struct sc_pkcs15_object cert_obj;
sc_pkcs15_cert_t *cert_out;
memset(&cert_info, 0, sizeof(cert_info));
memset(&cert_obj, 0, sizeof(cert_obj));
sc_pkcs15_format_id(certs[i].id, &cert_info.id);
cert_info.authority = certs[i].authority;
strlcpy(cert_obj.label, certs[i].label, sizeof(cert_obj.label));
cert_obj.flags = certs[i].obj_flags;
while (idx1 < file->size - 16) { /* actually 13 for all these tests */
if (idx1 + 16 > idx2 ) { /* need more data in buff */
idxlen = 248; /* read in next 248 bytes */
if (idxlen > file->size - idx2)
idxlen = file->size - idx2;
r = sc_read_binary(card, idx2, gsdata + idx2, idxlen, 0);
if (r < 0)
break;
idx2 = idx2 + idxlen;
}
if ( gsdata[idx1] == 0x30 &&
2020-02-20 13:07:58 +00:00
is_seq(gsdata + idx1, &seq_size1, &seq_len1) &&
is_seq(gsdata + idx1 + seq_size1, &seq_size2, &seq_len2) &&
gsdata[idx1 + seq_size1 + seq_size2 + 0] == 0xa0 &&
gsdata[idx1 + seq_size1 + seq_size2 + 1] == 0x03 &&
gsdata[idx1 + seq_size1 + seq_size2 + 2] == 0x02 &&
gsdata[idx1 + seq_size1 + seq_size2 + 3] == 0x01 &&
gsdata[idx1 + seq_size1 + seq_size2 + 4] == 0x02 &&
idx1 + 4 + seq_len1 < file->size) {
/* we have a cert (I hope) */
/* read in rest if needed */
idxlen = idx1 + seq_len1 + 4 - idx2;
if (idxlen > 0) {
idxlen = (idxlen + 3) & 0xfffffffc;
r = sc_read_binary(card, idx2, gsdata + idx2, idxlen, 0);
if (r < 0)
break; /* can not read cert */
idx2 = idx2 + idxlen;
}
cert_info.value.len = seq_len1 + 4;
2018-11-22 08:31:29 +00:00
sc_log(card->ctx, "Found cert at offset %d", idx1);
cert_info.value.value = (unsigned char *)
malloc(cert_info.value.len);
if (!cert_info.value.value)
return SC_ERROR_OUT_OF_MEMORY;
memcpy(cert_info.value.value, gsdata + idx1, cert_info.value.len);
2020-02-20 13:07:58 +00:00
idx1 = idx1 + cert_info.value.len;
break;
}
idx1++;
}
if (cert_info.value.value == NULL)
break; /* cert not found, no more certs */
r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info);
if (r < 0) {
free(gsdata);
return SC_ERROR_INTERNAL;
}
/* now lets see if we have a matching key for this cert */
r = sc_pkcs15_read_certificate(p15card, &cert_info, &cert_out);
if (r < 0) {
free(gsdata);
return SC_ERROR_INTERNAL;
}
for (j = 0; j < num_keyinfo; j++) {
if (cert_out->key->u.rsa.modulus.len == kinfo[j].modulus_len &&
memcmp(cert_out->key->u.rsa.modulus.data,
&kinfo[j].modulus, cert_out->key->u.rsa.modulus.len) == 0) {
memcpy(&kinfo[j].id, &cert_info.id, sizeof(sc_pkcs15_id_t));
2018-11-22 08:31:29 +00:00
sc_log(card->ctx, "found match");
}
}
sc_pkcs15_free_certificate(cert_out);
}
if (gsdata)
free(gsdata);
/* set pins */
/* GemSAFE uses different padding, so need to trap */
/* the pin_cmd and reset the padding */
pin_cmd_save = card->ops->pin_cmd;
card->ops->pin_cmd = my_pin_cmd;
for (i = 0; pins[i].label; i++) {
struct sc_pkcs15_auth_info pin_info;
struct sc_pkcs15_object pin_obj;
memset(&pin_info, 0, sizeof(pin_info));
memset(&pin_obj, 0, sizeof(pin_obj));
sc_pkcs15_format_id(pins[i].id, &pin_info.auth_id);
pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN;
pin_info.attrs.pin.reference = pins[i].ref;
pin_info.attrs.pin.flags = pins[i].flags;
pin_info.attrs.pin.type = pins[i].type;
pin_info.attrs.pin.min_length = pins[i].minlen;
pin_info.attrs.pin.stored_length = pins[i].storedlen;
pin_info.attrs.pin.max_length = pins[i].maxlen;
pin_info.attrs.pin.pad_char = pins[i].pad_char;
sc_format_path(pins[i].path, &pin_info.path);
pin_info.path.value[2] = dfpath >> 8;
pin_info.path.value[3] = dfpath & 0xff;
pin_info.tries_left = -1;
pin_info.logged_in = SC_PIN_STATE_UNKNOWN;
strlcpy(pin_obj.label, pins[i].label, sizeof(pin_obj.label));
pin_obj.flags = pins[i].obj_flags;
r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info);
if (r < 0)
return SC_ERROR_INTERNAL;
}
/* needs work, as we may want to add more then one key */
/* but not sure what the other keys do */
/* set private keys */
for (i = 0; prkeys[i].label; i++) {
struct sc_pkcs15_prkey_info prkey_info;
struct sc_pkcs15_object prkey_obj;
memset(&prkey_info, 0, sizeof(prkey_info));
memset(&prkey_obj, 0, sizeof(prkey_obj));
sc_pkcs15_format_id(prkeys[i].id, &prkey_info.id);
prkey_info.usage = prkeys[i].usage;
prkey_info.native = 1;
prkey_info.key_reference = prkeys[i].ref;
prkey_info.modulus_length= prkeys[i].modulus_len;
sc_format_path(prkeys[i].path, &prkey_info.path);
/*DEE need to look for them by reading and checking modulus vs cert */
/* will use the default path, unless we found a key with */
/* the same modulus as the cert(s) we already added */
/* This allows us to have a card with a key but no cert */
for (j = 0; j < num_keyinfo; j++) {
if (sc_pkcs15_compare_id(&kinfo[j].id, &prkey_info.id)) {
2018-11-22 08:31:29 +00:00
sc_log(card->ctx, "found key in file %d for id %s",
kinfo[j].fileid, prkeys[i].id);
prkey_info.path.value[0] = kinfo[j].fileid >> 8;
prkey_info.path.value[1] = kinfo[j].fileid & 0xff;
break;
}
}
strlcpy(prkey_obj.label, prkeys[i].label, sizeof(prkey_obj.label));
prkey_obj.flags = prkeys[i].obj_flags;
if (prkeys[i].auth_id)
sc_pkcs15_format_id(prkeys[i].auth_id, &prkey_obj.auth_id);
r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info);
if (r < 0)
return SC_ERROR_INTERNAL;
}
return SC_SUCCESS;
}
int sc_pkcs15emu_gemsafeGPK_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *aid)
{
sc_card_t *card = p15card->card;
sc_context_t *ctx = card->ctx;
2018-11-22 08:31:29 +00:00
sc_log(ctx, "Entering %s", __FUNCTION__);
if (gemsafe_detect_card(p15card))
return SC_ERROR_WRONG_CARD;
return sc_pkcs15emu_gemsafeGPK_init(p15card);
}