opensc/src/libopensc/pkcs15-gemsafeGPK.c

526 lines
15 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
*/
#include "internal.h"
#include <opensc/pkcs15.h>
#include <opensc/log.h>
#include <opensc/cardctl.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
Complete rewrite of OpenSC build system. 1. Build system now supports MinGW (Windows) compilation using msys and cross compilation. 2. Ability to explicitly disable and enable dependencies of the package. 3. openct, pcsc and nsplugins features are disabled by default. 4. Modified pcsc driver to use pcsc dynamically, no compile time dependency is required. 5. --enable-pcsc-lite configuration option renamed to --enable-pcsc. 6. Install opensc.conf file (as opensc.conf.new if opensc.conf exists). 7. Add--enable-doc configuration option, allow installing documentation into target. 8. Add --disable-man configuration option, allow msys mingw32 users to build from svn without extra dependencies. 9. Add export files to each library in order to export only required symbols. Windows native build may use these files instead of scanning objects' symbols. 10. Add opensc-tool --info to display some general information about the build. 11. Create compatibility library to be linked against library instread of recompiling the same source files in different places. 12. Add different win32 version resource to each class of outputs. 13. Make xsl-stylesheets location selectable. 14. Some win32 fixups. 15. Some warning fixups. 16. Many other autoconf/automake cleanups. Alon Bar-Lev svn diff -r 3315:3399 https://www.opensc-project.org/svn/opensc/branches/alonbl/mingw _M . D configure.in _M src _M src/openssh M src/openssh/Makefile.am _M src/tools M src/tools/rutoken-tool.c M src/tools/opensc-tool.c M src/tools/cardos-info.c M src/tools/pkcs15-crypt.c M src/tools/pkcs15-init.c M src/tools/piv-tool.c M src/tools/netkey-tool.c M src/tools/eidenv.c M src/tools/cryptoflex-tool.c M src/tools/util.c M src/tools/pkcs11-tool.c M src/tools/pkcs15-tool.c M src/tools/util.h M src/tools/opensc-explorer.c M src/tools/Makefile.am _M src/pkcs11 M src/pkcs11/pkcs11-global.c M src/pkcs11/framework-pkcs15.c M src/pkcs11/mechanism.c M src/pkcs11/pkcs11-display.c M src/pkcs11/pkcs11-object.c A src/pkcs11/opensc-pkcs11.exports M src/pkcs11/sc-pkcs11.h M src/pkcs11/pkcs11-spy.c M src/pkcs11/openssl.c M src/pkcs11/Makefile.am A src/pkcs11/pkcs11-spy.exports _M src/tests _M src/tests/regression M src/tests/regression/Makefile.am M src/tests/sc-test.c M src/tests/pintest.c M src/tests/Makefile.am _M src/include _M src/include/opensc M src/include/opensc/Makefile.am A src/include/opensc/svnignore M src/include/Makefile.am _M src/signer _M src/signer/npinclude M src/signer/npinclude/Makefile.am M src/signer/Makefile.am A src/signer/signer.exports _M src/common A src/common/compat_dummy.c D src/common/getopt.txt D src/common/strlcpy.c D src/common/LICENSE A src/common/compat_getopt.txt A src/common/compat_strlcpy.c A src/common/LICENSE.compat_getopt A src/common/compat_getopt.c D src/common/strlcpy.h D src/common/ChangeLog D src/common/getpass.c D src/common/my_getopt.c A src/common/compat_strlcpy.h A src/common/compat_getpass.c A src/common/compat_getopt.h A src/common/ChangeLog.compat_getopt D src/common/README.strlcpy D src/common/my_getopt.h A src/common/compat_getpass.h A src/common/README.compat_strlcpy D src/common/strlcpy.3 A src/common/README.compat_getopt D src/common/getopt.3 D src/common/README.my_getopt A src/common/compat_strlcpy.3 A src/common/compat_getopt.3 M src/common/Makefile.am M src/Makefile.am _M src/pkcs15init M src/pkcs15init/pkcs15-oberthur.c M src/pkcs15init/profile.c M src/pkcs15init/pkcs15-lib.c M src/pkcs15init/pkcs15-rutoken.c A src/pkcs15init/pkcs15init.exports M src/pkcs15init/pkcs15-gpk.c M src/pkcs15init/Makefile.am _M src/scconf M src/scconf/Makefile.am M src/scconf/parse.c A src/scconf/scconf.exports _M src/libopensc M src/libopensc/card-rutoken.c M src/libopensc/compression.c M src/libopensc/sc.c M src/libopensc/card-piv.c M src/libopensc/pkcs15-openpgp.c M src/libopensc/pkcs15-postecert.c M src/libopensc/pkcs15-tcos.c M src/libopensc/opensc-config.in M src/libopensc/reader-pcsc.c A src/libopensc/internal-winscard.h M src/libopensc/ctx.c A src/libopensc/libopensc.exports M src/libopensc/pkcs15-piv.c M src/libopensc/pkcs15-infocamere.c M src/libopensc/internal.h M src/libopensc/pkcs15-actalis.c M src/libopensc/pkcs15-starcert.c M src/libopensc/card-oberthur.c M src/libopensc/pkcs15-atrust-acos.c M src/libopensc/p15card-helper.c D src/libopensc/part10.h M src/libopensc/ui.c M src/libopensc/card-gpk.c M src/libopensc/pkcs15-wrap.c M src/libopensc/pkcs15-gemsafeGPK.c M src/libopensc/log.c M src/libopensc/pkcs15-esteid.c M src/libopensc/pkcs15-prkey-rutoken.c M src/libopensc/log.h M src/libopensc/Makefile.am M src/libopensc/reader-openct.c _M aclocal M aclocal/Makefile.am _M win32 M win32/Makefile.am A win32/versioninfo.rc.in A win32/ltrc.inc A configure.ac _M doc _M doc/tools M doc/tools/pkcs15-profile.xml D doc/changelog.sh D doc/export-wiki.xsl _M doc/api _M doc/api/file M doc/api/man.xsl _M doc/api/asn1 _M doc/api/apps _M doc/api/init _M doc/api/types _M doc/api/card M doc/api/html.xsl _M doc/api/misc _M doc/api/util M doc/Makefile.am D doc/export-wiki.sh AM doc/nonpersistent A doc/nonpersistent/export-wiki.xsl A doc/nonpersistent/Makefile.am A doc/nonpersistent/export-wiki.sh A doc/nonpersistent/svn2cl.xsl D doc/generate-man.sh D doc/svn2cl.xsl M Makefile.am A svnignore _M etc M etc/opensc.conf.in M etc/Makefile.am D man _M solaris M solaris/Makefile git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@3405 c6295689-39f2-0310-b995-f0e70906c6a9
2008-03-06 16:06:59 +00:00
#include <compat_strlcpy.h>
#define MANU_ID "GemSAFE on GPK16000"
int sc_pkcs15emu_gemsafeGPK_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *);
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];
SC_FUNC_CALLED(card->ctx, 2);
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;
sc_debug(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, 2, 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, 1);
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[] = {
{ "1", "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[] = {
{ "1", "AUTH key", 1024, USAGE_AUT, "I0009",
0x00, "1", 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[8]; /* will loook for 8 keys */
u8 modulus_buf[ 1 + 1024 / 8]; /* tag+modulus */
u8 *cp;
char buf[256];
SC_FUNC_CALLED(card->ctx, 1);
/* need to limit to 248 */
if (card->max_send_size > 248)
card->max_send_size = 248;
if (card->max_recv_size > 248)
card->max_recv_size = 248;
/* could read this off card if needed */
p15card->label = strdup("GemSAFE");
p15card->manufacturer_id = strdup(MANU_ID);
/* get serial number */
r = sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serial);
r = sc_bin_to_hex(serial.value, serial.len, buf, sizeof(buf), 0);
if (r != SC_SUCCESS)
return SC_ERROR_INTERNAL;
p15card->serial_number = strdup(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;
free(file);
file = NULL;
sc_debug(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 = 7; i < 16; i++) {
path.value[0] = 0x00;
path.value[1] = i;
path.len = 2;
path.type = SC_PATH_TYPE_FILE_ID;
sc_ctx_suppress_errors_on(card->ctx); /* file may not exist, and not an error */
r = sc_select_file(card, &path, NULL);
sc_ctx_suppress_errors_off(card->ctx);
if (r < 0)
continue;
sc_ctx_suppress_errors_on(card->ctx);
r = sc_read_record(card, 1, sysrec, sizeof(sysrec), SC_RECORD_BY_REC_NR);
sc_ctx_suppress_errors_off(card->ctx);
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:
sc_error(card->ctx, "Unsupported modulus length");
continue;
}
kinfo[num_keyinfo].fileid = i;
sc_pkcs15_format_id("NONE", &kinfo[num_keyinfo].id);
sc_debug(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 skiping 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 */
gsdata = (unsigned char *) malloc(file->size);
if (!gsdata)
return SC_ERROR_OUT_OF_MEMORY;
/* set indcies 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 &&
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;
sc_debug(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);
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));
sc_debug(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_pin_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.reference = pins[i].ref;
pin_info.flags = pins[i].flags;
pin_info.type = pins[i].type;
pin_info.min_length = pins[i].minlen;
pin_info.stored_length = pins[i].storedlen;
pin_info.max_length = pins[i].maxlen;
pin_info.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;
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 mudulus 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)) {
sc_debug(card->ctx, "found key in file %d for id %d",
kinfo[j].fileid, prkey_info.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,
sc_pkcs15emu_opt_t *opts)
{
sc_card_t *card = p15card->card;
sc_context_t *ctx = card->ctx;
sc_debug(ctx, "Entering %s", __FUNCTION__);
if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK)
return sc_pkcs15emu_gemsafeGPK_init(p15card);
else {
int r = gemsafe_detect_card(p15card);
if (r)
return SC_ERROR_WRONG_CARD;
return sc_pkcs15emu_gemsafeGPK_init(p15card);
}
}