Add support for Portugese eID on IAS and Gemsafe cards, by João Poupino.
git-svn-id: https://www.opensc-project.org/svnp/opensc/branches/martin/0.12@3755 c6295689-39f2-0310-b995-f0e70906c6a9
This commit is contained in:
parent
a8dc97e0dc
commit
9c7eb8122a
@ -40,12 +40,12 @@ libopensc_la_SOURCES = \
|
||||
card-oberthur.c card-belpic.c card-atrust-acos.c card-entersafe.c \
|
||||
card-incrypto34.c card-piv.c card-muscle.c card-acos5.c \
|
||||
card-asepcos.c card-akis.c card-gemsafeV1.c card-rutoken.c \
|
||||
card-rtecp.c card-westcos.c card-myeid.c \
|
||||
card-rtecp.c card-westcos.c card-myeid.c card-ias.c\
|
||||
\
|
||||
pkcs15-openpgp.c pkcs15-infocamere.c pkcs15-starcert.c \
|
||||
pkcs15-tcos.c pkcs15-esteid.c pkcs15-postecert.c pkcs15-gemsafeGPK.c \
|
||||
pkcs15-actalis.c pkcs15-atrust-acos.c pkcs15-tccardos.c pkcs15-piv.c \
|
||||
pkcs15-esinit.c p15emu-westcos.c \
|
||||
pkcs15-esinit.c p15emu-westcos.c pkcs15-pteid.c \
|
||||
compression.c p15card-helper.c \
|
||||
\
|
||||
libopensc.exports
|
||||
|
@ -29,13 +29,13 @@ OBJECTS = \
|
||||
card-oberthur.obj card-belpic.obj card-atrust-acos.obj card-entersafe.obj \
|
||||
card-incrypto34.obj card-piv.obj card-muscle.obj card-acos5.obj \
|
||||
card-asepcos.obj card-akis.obj card-gemsafeV1.obj card-rutoken.obj \
|
||||
card-rtecp.obj card-myeid.obj \
|
||||
card-rtecp.obj card-myeid.obj card-ias.obj \
|
||||
\
|
||||
p15emu-westcos.obj \
|
||||
pkcs15-openpgp.obj pkcs15-infocamere.obj pkcs15-starcert.obj \
|
||||
pkcs15-tcos.obj pkcs15-esteid.obj pkcs15-postecert.obj pkcs15-gemsafeGPK.obj \
|
||||
pkcs15-actalis.obj pkcs15-atrust-acos.obj pkcs15-tccardos.obj pkcs15-piv.obj \
|
||||
pkcs15-esinit.obj \
|
||||
pkcs15-esinit.obj pkcs15-pteid.c \
|
||||
compression.obj p15card-helper.obj \
|
||||
versioninfo.res
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
/* Initially written by David Mattes (david.mattes@boeing.com) */
|
||||
/* Portuguese eID card support by Joao Poupino (joao.poupino@ist.utl.pt) */
|
||||
|
||||
#include "internal.h"
|
||||
#include "cardctl.h"
|
||||
@ -32,20 +33,27 @@ static struct sc_card_driver gemsafe_drv = {
|
||||
NULL, 0, NULL
|
||||
};
|
||||
|
||||
static const char *gemexpresso_atrs[] = {
|
||||
/* standard version */
|
||||
"3B:7B:94:00:00:80:65:B0:83:01:01:74:83:00:90:00",
|
||||
"3B:6B:00:00:80:65:B0:83:01:01:74:83:00:90:00",
|
||||
/* fips 140 version */
|
||||
"3B:6B:00:00:80:65:B0:83:01:03:74:83:00:90:00",
|
||||
/* TODO: add more ATRs */
|
||||
"3B:7A:94:00:00:80:65:A2:01:01:01:3D:72:D6:43",
|
||||
"3B:7D:94:00:00:80:31:80:65:B0:83:01:01:90:83:00:90:00",
|
||||
NULL
|
||||
/* Known ATRs */
|
||||
static struct sc_atr_table gemsafe_atrs[] = {
|
||||
/* standard version */
|
||||
{"3B:7B:94:00:00:80:65:B0:83:01:01:74:83:00:90:00", NULL, NULL, SC_CARD_TYPE_GEMSAFEV1_GENERIC, 0, NULL},
|
||||
{"3B:6B:00:00:80:65:B0:83:01:01:74:83:00:90:00", NULL, NULL, SC_CARD_TYPE_GEMSAFEV1_GENERIC, 0, NULL},
|
||||
/* fips 140 version */
|
||||
{"3B:6B:00:00:80:65:B0:83:01:03:74:83:00:90:00", NULL, NULL, SC_CARD_TYPE_GEMSAFEV1_GENERIC, 0, NULL},
|
||||
/* Undefined */
|
||||
{"3B:7A:94:00:00:80:65:A2:01:01:01:3D:72:D6:43", NULL, NULL, SC_CARD_TYPE_GEMSAFEV1_GENERIC, 0, NULL},
|
||||
{"3B:7D:94:00:00:80:31:80:65:B0:83:01:01:90:83:00:90:00", NULL, NULL, SC_CARD_TYPE_GEMSAFEV1_GENERIC, 0, NULL},
|
||||
/* Portuguese eID cards */
|
||||
{"3B:7D:95:00:00:80:31:80:65:B0:83:11:C0:A9:83:00", NULL, NULL, SC_CARD_TYPE_GEMSAFEV1_PTEID, 0, NULL},
|
||||
{"3B:7D:95:00:00:80:31:80:65:B0:83:11:C0:A9:83:00:90:00", NULL, NULL, SC_CARD_TYPE_GEMSAFEV1_PTEID, 0, NULL},
|
||||
{NULL, NULL, NULL, 0, 0, NULL}
|
||||
};
|
||||
|
||||
static const u8 gemsafe_def_aid[] = {0xA0, 0x00, 0x00, 0x00, 0x18, 0x0A,
|
||||
0x00, 0x00, 0x01, 0x63, 0x42, 0x00};
|
||||
|
||||
static const u8 gemsafe_pteid_aid[] = {0x60, 0x46, 0x32, 0xFF, 0x00, 0x00, 0x02};
|
||||
|
||||
/*
|
||||
static const u8 gemsafe_def_aid[] = {0xA0, 0x00, 0x00, 0x00, 0x63, 0x50,
|
||||
0x4B, 0x43, 0x53, 0x2D, 0x31, 0x35};
|
||||
@ -113,27 +121,12 @@ static int gp_select_applet(sc_card_t *card, const u8 *aid, size_t aid_len)
|
||||
return SC_SUCCESS;
|
||||
}
|
||||
|
||||
static int gemsafe_match_card(struct sc_card *card)
|
||||
static int gemsafe_match_card(sc_card_t *card)
|
||||
{
|
||||
int i, match = -1;
|
||||
int i;
|
||||
|
||||
SC_FUNC_CALLED(card->ctx, 1);
|
||||
|
||||
for (i = 0; gemexpresso_atrs[i] != NULL; i++) {
|
||||
u8 defatr[SC_MAX_ATR_SIZE];
|
||||
size_t len = sizeof(defatr);
|
||||
const char *atrp = gemexpresso_atrs[i];
|
||||
|
||||
if (sc_hex_to_bin(atrp, defatr, &len))
|
||||
continue;
|
||||
if (len != card->atr_len)
|
||||
continue;
|
||||
if (memcmp(card->atr, defatr, len) != 0)
|
||||
continue;
|
||||
match = i + 1;
|
||||
break;
|
||||
}
|
||||
if (match == -1)
|
||||
i = _sc_match_atr(card, gemsafe_atrs, &card->type);
|
||||
if (i < 0)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
@ -153,12 +146,17 @@ static int gemsafe_init(struct sc_card *card)
|
||||
if (!exdata)
|
||||
return SC_ERROR_OUT_OF_MEMORY;
|
||||
exdata->aid_len = sizeof(exdata->aid);
|
||||
/* try to get a AID from the config file */
|
||||
r = get_conf_aid(card, exdata->aid, &exdata->aid_len);
|
||||
if (r < 0) {
|
||||
/* failed, use default value */
|
||||
memcpy(exdata->aid, gemsafe_def_aid, sizeof(gemsafe_def_aid));
|
||||
exdata->aid_len = sizeof(gemsafe_def_aid);
|
||||
if(card->type == SC_CARD_TYPE_GEMSAFEV1_GENERIC) {
|
||||
/* try to get a AID from the config file */
|
||||
r = get_conf_aid(card, exdata->aid, &exdata->aid_len);
|
||||
if (r < 0) {
|
||||
/* failed, use default value */
|
||||
memcpy(exdata->aid, gemsafe_def_aid, sizeof(gemsafe_def_aid));
|
||||
exdata->aid_len = sizeof(gemsafe_def_aid);
|
||||
}
|
||||
} else if (card->type == SC_CARD_TYPE_GEMSAFEV1_PTEID) {
|
||||
memcpy(exdata->aid, gemsafe_pteid_aid, sizeof(gemsafe_pteid_aid));
|
||||
exdata->aid_len = sizeof(gemsafe_pteid_aid);
|
||||
}
|
||||
|
||||
/* increase lock_count here to prevent sc_unlock to select
|
||||
@ -343,18 +341,18 @@ static int gemsafe_process_fci(struct sc_card *card, struct sc_file *file,
|
||||
return SC_SUCCESS;
|
||||
}
|
||||
|
||||
static u8 gemsafe_flags2algref(const struct sc_security_env *env)
|
||||
static u8 gemsafe_flags2algref(struct sc_card *card, const struct sc_security_env *env)
|
||||
{
|
||||
u8 ret = 0;
|
||||
|
||||
if (env->operation == SC_SEC_OPERATION_SIGN) {
|
||||
if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1)
|
||||
ret = 0x12;
|
||||
ret = card->type == SC_CARD_TYPE_GEMSAFEV1_PTEID ? 0x02 : 0x12;
|
||||
else if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_ISO9796)
|
||||
ret = 0x11;
|
||||
} else if (env->operation == SC_SEC_OPERATION_DECIPHER) {
|
||||
if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1)
|
||||
ret = 0x12;
|
||||
ret = card->type == SC_CARD_TYPE_GEMSAFEV1_PTEID ? 0x02 : 0x12;
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -388,7 +386,7 @@ static int gemsafe_set_security_env(struct sc_card *card,
|
||||
|
||||
if (!(se_env.flags & SC_SEC_ENV_ALG_REF_PRESENT)) {
|
||||
/* set the algorithm reference */
|
||||
alg_ref = gemsafe_flags2algref(&se_env);
|
||||
alg_ref = gemsafe_flags2algref(card, &se_env);
|
||||
if (alg_ref) {
|
||||
se_env.algorithm_ref = alg_ref;
|
||||
se_env.flags |= SC_SEC_ENV_ALG_REF_PRESENT;
|
||||
@ -417,11 +415,16 @@ static int gemsafe_compute_signature(struct sc_card *card, const u8 * data,
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
}
|
||||
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x9E, 0xAC);
|
||||
apdu.cla |= 0x80;
|
||||
apdu.resp = rbuf;
|
||||
apdu.resplen = sizeof(rbuf);
|
||||
apdu.le = 256;
|
||||
/* the Portuguese eID card requires a two-phase exchange */
|
||||
if(card->type == SC_CARD_TYPE_GEMSAFEV1_PTEID) {
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x2A, 0x90, 0xA0);
|
||||
} else {
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x9E, 0xAC);
|
||||
apdu.cla |= 0x80;
|
||||
apdu.resp = rbuf;
|
||||
apdu.resplen = sizeof(rbuf);
|
||||
apdu.le = 256;
|
||||
}
|
||||
/* we sign a digestInfo object => tag 0x90 */
|
||||
sbuf[0] = 0x90;
|
||||
sbuf[1] = (u8)data_len;
|
||||
@ -433,6 +436,17 @@ static int gemsafe_compute_signature(struct sc_card *card, const u8 * data,
|
||||
r = sc_transmit_apdu(card, &apdu);
|
||||
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||
if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) {
|
||||
if(card->type == SC_CARD_TYPE_GEMSAFEV1_PTEID) {
|
||||
/* finalize the exchange */
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x2A, 0x9E, 0x9A);
|
||||
apdu.le = 128; /* 1024 bit keys */
|
||||
apdu.resp = rbuf;
|
||||
apdu.resplen = sizeof(rbuf);
|
||||
r = sc_transmit_apdu(card, &apdu);
|
||||
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||
if(apdu.sw1 != 0x90 || apdu.sw2 != 0x00)
|
||||
SC_FUNC_RETURN(card->ctx, 2, sc_check_sw(card, apdu.sw1, apdu.sw2));
|
||||
}
|
||||
int len = apdu.resplen > outlen ? outlen : apdu.resplen;
|
||||
|
||||
memcpy(out, apdu.resp, len);
|
||||
@ -473,6 +487,25 @@ static int gemsafe_decipher(struct sc_card *card, const u8 * crgram,
|
||||
SC_FUNC_RETURN(card->ctx, 2, sc_check_sw(card, apdu.sw1, apdu.sw2));
|
||||
}
|
||||
|
||||
static int gemsafe_get_challenge(sc_card_t *card, u8 *rnd, size_t len)
|
||||
{
|
||||
int prev_cla, r;
|
||||
|
||||
prev_cla = card->cla;
|
||||
if(card->type == SC_CARD_TYPE_GEMSAFEV1_PTEID) {
|
||||
/* Warning: this depends on iso7816_get_challenge not
|
||||
* changing the value of the card's CLA
|
||||
*/
|
||||
card->cla = 0x80;
|
||||
}
|
||||
r = iso_ops->get_challenge(card, rnd, len);
|
||||
/* Restore the CLA value if needed */
|
||||
if(card->cla != prev_cla)
|
||||
card->cla = prev_cla;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int gemsafe_build_pin_apdu(struct sc_card *card,
|
||||
struct sc_apdu *apdu,
|
||||
struct sc_pin_cmd_data *data)
|
||||
@ -622,7 +655,7 @@ static struct sc_card_driver *sc_get_driver(void)
|
||||
iso_ops = iso_drv->ops;
|
||||
/* use the standard iso operations as default */
|
||||
gemsafe_ops = *iso_drv->ops;
|
||||
/* gemsafe specfic functions */
|
||||
/* gemsafe specific functions */
|
||||
gemsafe_ops.match_card = gemsafe_match_card;
|
||||
gemsafe_ops.init = gemsafe_init;
|
||||
gemsafe_ops.finish = gemsafe_finish;
|
||||
@ -631,6 +664,7 @@ static struct sc_card_driver *sc_get_driver(void)
|
||||
gemsafe_ops.set_security_env = gemsafe_set_security_env;
|
||||
gemsafe_ops.decipher = gemsafe_decipher;
|
||||
gemsafe_ops.compute_signature = gemsafe_compute_signature;
|
||||
gemsafe_ops.get_challenge = gemsafe_get_challenge;
|
||||
gemsafe_ops.process_fci = gemsafe_process_fci;
|
||||
gemsafe_ops.pin_cmd = gemsafe_pin_cmd;
|
||||
|
||||
|
550
src/libopensc/card-ias.c
Normal file
550
src/libopensc/card-ias.c
Normal file
@ -0,0 +1,550 @@
|
||||
/*
|
||||
* Driver for IAS based cards, e.g. Portugal's eID card.
|
||||
*
|
||||
* Copyright (C) 2009, Joao Poupino <joao.poupino@ist.utl.pt>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* Partially based on the ISO7816 driver.
|
||||
*
|
||||
* Thanks to Andre Cruz, Jorge Ferreira and Paulo F. Andrade
|
||||
*/
|
||||
|
||||
#include "internal.h"
|
||||
#include "cardctl.h"
|
||||
#include "asn1.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Portugal eID uses 1024 bit keys */
|
||||
#define PTEID_RSA_KEYSIZE 128
|
||||
|
||||
#define DRVDATA(card) ((struct ias_priv_data *) ((card)->drv_data))
|
||||
|
||||
static struct sc_card_operations ias_ops;
|
||||
static struct sc_card_operations *iso_ops = NULL;
|
||||
|
||||
static struct sc_card_driver ias_drv = {
|
||||
"IAS",
|
||||
"ias",
|
||||
&ias_ops,
|
||||
NULL, 0, NULL
|
||||
};
|
||||
|
||||
/* Known ATRs */
|
||||
static struct sc_atr_table ias_atrs[] = {
|
||||
/* Portugal eID cards */
|
||||
{"3B:65:00:00:D0:00:54:01:31", NULL, NULL, SC_CARD_TYPE_IAS_PTEID, 0, NULL},
|
||||
{"3B:65:00:00:D0:00:54:01:32", NULL, NULL, SC_CARD_TYPE_IAS_PTEID, 0, NULL},
|
||||
{"3B:95:95:40:FF:D0:00:54:01:31", NULL, NULL, SC_CARD_TYPE_IAS_PTEID, 0, NULL},
|
||||
{"3B:95:95:40:FF:D0:00:54:01:32", NULL, NULL, SC_CARD_TYPE_IAS_PTEID, 0, NULL},
|
||||
{NULL, NULL, NULL, 0, 0, NULL}
|
||||
};
|
||||
|
||||
/* Known AIDs */
|
||||
static const u8 ias_aid_pteid[] = {0x60, 0x46, 0x32, 0xFF, 0x00, 0x01, 0x02};
|
||||
|
||||
static int ias_select_applet(sc_card_t *card, const u8 *aid, size_t aid_len)
|
||||
{
|
||||
int r;
|
||||
sc_path_t tpath;
|
||||
|
||||
tpath.type = SC_PATH_TYPE_DF_NAME;
|
||||
tpath.len = aid_len;
|
||||
memcpy(tpath.value, aid, aid_len);
|
||||
r = iso_ops->select_file(card, &tpath, NULL);
|
||||
if (r != SC_SUCCESS) {
|
||||
sc_debug(card->ctx, "unable to select applet");
|
||||
return r;
|
||||
}
|
||||
|
||||
return SC_SUCCESS;
|
||||
}
|
||||
|
||||
static int ias_init(sc_card_t *card)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
assert(card != NULL);
|
||||
|
||||
SC_FUNC_CALLED(card->ctx, 1);
|
||||
card->name = "IAS";
|
||||
card->cla = 0x00;
|
||||
|
||||
/* Card version detection */
|
||||
if (card->type == SC_CARD_TYPE_IAS_PTEID) {
|
||||
int r = ias_select_applet(card, ias_aid_pteid, sizeof(ias_aid_pteid));
|
||||
if (r != SC_SUCCESS)
|
||||
return r;
|
||||
/* Add other cards if necessary */
|
||||
} else {
|
||||
return SC_ERROR_INTERNAL;
|
||||
}
|
||||
|
||||
/* Set card capabilities */
|
||||
card->caps |= SC_CARD_CAP_RNG;
|
||||
|
||||
/* Set the supported algorithms */
|
||||
flags = SC_ALGORITHM_RSA_PAD_PKCS1 |
|
||||
SC_ALGORITHM_RSA_HASH_NONE;
|
||||
|
||||
/* Only 1024 bit key sizes were tested */
|
||||
_sc_card_add_rsa_alg(card, 1024, flags, 0);
|
||||
|
||||
return SC_SUCCESS;
|
||||
}
|
||||
|
||||
static int ias_finish(sc_card_t *card)
|
||||
{
|
||||
return SC_SUCCESS;
|
||||
}
|
||||
|
||||
static int ias_match_card(sc_card_t *card)
|
||||
{
|
||||
int i;
|
||||
|
||||
i = _sc_match_atr(card, ias_atrs, &card->type);
|
||||
if (i < 0)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ias_build_pin_apdu(sc_card_t *card,
|
||||
sc_apdu_t *apdu,
|
||||
struct sc_pin_cmd_data *data)
|
||||
{
|
||||
static u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
|
||||
int r, len, pad, use_pin_pad, ins, p1;
|
||||
|
||||
r = len = pad = use_pin_pad = ins = p1 = 0;
|
||||
assert(card != NULL);
|
||||
|
||||
switch (data->pin_type) {
|
||||
case SC_AC_CHV:
|
||||
break;
|
||||
default:
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
}
|
||||
|
||||
if (data->flags & SC_PIN_CMD_USE_PINPAD)
|
||||
use_pin_pad = 1;
|
||||
/* "needs-padding" necessary for the PTEID card,
|
||||
* but not defined in the pin structure
|
||||
*/
|
||||
if ((data->flags & SC_PIN_CMD_NEED_PADDING) ||
|
||||
card->type == SC_CARD_TYPE_IAS_PTEID)
|
||||
pad = 1;
|
||||
|
||||
data->pin1.offset = 5;
|
||||
|
||||
switch (data->cmd) {
|
||||
case SC_PIN_CMD_VERIFY:
|
||||
ins = 0x20;
|
||||
if ( (r = sc_build_pin(sbuf, sizeof(sbuf), &data->pin1, pad)) < 0)
|
||||
return r;
|
||||
len = r;
|
||||
break;
|
||||
case SC_PIN_CMD_CHANGE:
|
||||
ins = 0x24;
|
||||
if ((data->flags & SC_PIN_CMD_IMPLICIT_CHANGE) == 0 &&
|
||||
(data->pin1.len != 0 || use_pin_pad)) {
|
||||
if ( (r = sc_build_pin(sbuf, sizeof(sbuf), &data->pin1, pad)) < 0)
|
||||
return r;
|
||||
len += r;
|
||||
} else {
|
||||
/* implicit test */
|
||||
p1 = 1;
|
||||
}
|
||||
data->pin2.offset = data->pin1.offset + len;
|
||||
if ( (r = sc_build_pin(sbuf+len, sizeof(sbuf)-len, &data->pin2, pad)) < 0)
|
||||
return r;
|
||||
len += r;
|
||||
break;
|
||||
case SC_PIN_CMD_UNBLOCK:
|
||||
ins = 0x2C;
|
||||
if (data->pin1.len != 0 || use_pin_pad) {
|
||||
if ( (r = sc_build_pin(sbuf, sizeof(sbuf), &data->pin1, pad)) < 0)
|
||||
return r;
|
||||
len += r;
|
||||
} else {
|
||||
p1 |= 0x02;
|
||||
}
|
||||
if (data->pin2.len != 0 || use_pin_pad) {
|
||||
data->pin2.offset = data->pin1.offset + len;
|
||||
if ( (r = sc_build_pin(sbuf+len, sizeof(sbuf)-len, &data->pin2, pad)) < 0)
|
||||
return r;
|
||||
len += r;
|
||||
} else {
|
||||
p1 |= 0x01;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return SC_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
sc_format_apdu(card, apdu, SC_APDU_CASE_3_SHORT, ins, p1, data->pin_reference);
|
||||
apdu->lc = len;
|
||||
apdu->datalen = len;
|
||||
apdu->data = sbuf;
|
||||
apdu->resplen = 0;
|
||||
|
||||
return SC_SUCCESS;
|
||||
}
|
||||
|
||||
static int ias_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data,
|
||||
int *tries_left)
|
||||
{
|
||||
int r;
|
||||
sc_apdu_t local_apdu;
|
||||
|
||||
SC_FUNC_CALLED(card->ctx, 1);
|
||||
|
||||
/* Check if a PIN change operation is being requested,
|
||||
* as it requires sending two separate APDUs
|
||||
*/
|
||||
if (data->cmd == SC_PIN_CMD_CHANGE) {
|
||||
/* Build a SC_PIN_CMD_VERIFY APDU */
|
||||
data->cmd = SC_PIN_CMD_VERIFY;
|
||||
r = ias_build_pin_apdu(card, &local_apdu, data);
|
||||
if (r < 0)
|
||||
return r;
|
||||
data->apdu = &local_apdu;
|
||||
r = iso_ops->pin_cmd(card, data, tries_left);
|
||||
if (r < 0)
|
||||
return r;
|
||||
/* Continue processing */
|
||||
data->cmd = SC_PIN_CMD_CHANGE;
|
||||
/* The IAS spec mandates an implicit change PIN operation */
|
||||
data->flags |= SC_PIN_CMD_IMPLICIT_CHANGE;
|
||||
}
|
||||
|
||||
r = ias_build_pin_apdu(card, &local_apdu, data);
|
||||
if (r < 0)
|
||||
return r;
|
||||
data->apdu = &local_apdu;
|
||||
|
||||
return iso_ops->pin_cmd(card, data, tries_left);
|
||||
}
|
||||
|
||||
static int ias_set_security_env(sc_card_t *card,
|
||||
const sc_security_env_t *env, int se_num)
|
||||
{
|
||||
int r;
|
||||
sc_apdu_t apdu;
|
||||
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
|
||||
|
||||
sc_debug(card->ctx, "ias_set_security_env, keyRef = 0x%0x, algo = 0x%0x\n",
|
||||
*env->key_ref, env->algorithm_flags);
|
||||
|
||||
assert(card != NULL && env != NULL);
|
||||
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0);
|
||||
switch (env->operation) {
|
||||
case SC_SEC_OPERATION_DECIPHER:
|
||||
apdu.p2 = 0xB8; /* confidentiality template */
|
||||
sbuf[0] = 0x95; /* tag for usage qualifier byte */
|
||||
sbuf[1] = 0x01; /* tag length */
|
||||
sbuf[2] = 0x40; /* data decryption */
|
||||
sbuf[3] = 0x84; /* tag for private key reference */
|
||||
sbuf[4] = 0x01; /* tag length */
|
||||
sbuf[5] = *env->key_ref; /* key reference */
|
||||
sbuf[6] = 0x80; /* tag for algorithm reference */
|
||||
sbuf[7] = 0x01; /* tag length */
|
||||
if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1)
|
||||
sbuf[8] = 0x1A; /* RSA PKCS#1 with no data formatting */
|
||||
else {
|
||||
sc_debug(card->ctx, "Set Sec Env: unsupported algo 0X%0X\n",
|
||||
env->algorithm_flags);
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
}
|
||||
apdu.lc = 9;
|
||||
apdu.datalen = 9;
|
||||
break;
|
||||
case SC_SEC_OPERATION_SIGN:
|
||||
apdu.p2 = 0xA4; /* authentication template */
|
||||
sbuf[0] = 0x95; /* tag for usage qualifier byte */
|
||||
sbuf[1] = 0x01; /* tag length */
|
||||
sbuf[2] = 0x40; /* internal authentication */
|
||||
sbuf[3] = 0x84; /* tag for private key reference */
|
||||
sbuf[4] = 0x01; /* tag length */
|
||||
sbuf[5] = *env->key_ref; /* key reference */
|
||||
sbuf[6] = 0x80; /* tag for algorithm reference */
|
||||
sbuf[7] = 0x01; /* tag length */
|
||||
if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1)
|
||||
sbuf[8] = 0x02; /* RSA PKCS#1 with no data formatting */
|
||||
else {
|
||||
sc_debug(card->ctx, "Set Sec Env: unsupported algo 0X%0X\n",
|
||||
env->algorithm_flags);
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
}
|
||||
apdu.lc = 9;
|
||||
apdu.datalen = 9;
|
||||
break;
|
||||
default:
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
}
|
||||
apdu.le = 0;
|
||||
apdu.data = sbuf;
|
||||
apdu.resplen = 0;
|
||||
|
||||
r = sc_transmit_apdu(card, &apdu);
|
||||
SC_TEST_RET(card->ctx, r, "Set Security Env APDU transmit failed");
|
||||
|
||||
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
||||
SC_TEST_RET(card->ctx, r, "Card's Set Security Env command returned error");
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int ias_compute_signature(sc_card_t *card, const u8 * data,
|
||||
size_t data_len, u8 * out, size_t outlen)
|
||||
{
|
||||
int r;
|
||||
size_t len = 0;
|
||||
sc_apdu_t apdu;
|
||||
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
|
||||
sc_context_t *ctx = card->ctx;
|
||||
|
||||
SC_FUNC_CALLED(ctx, 1);
|
||||
|
||||
if (data_len > 64) {
|
||||
sc_debug(ctx, "error: input data too long: %lu bytes\n", data_len);
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
}
|
||||
|
||||
/* Send the data */
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x88, 0x02, 0x00);
|
||||
memcpy(sbuf, data, data_len);
|
||||
apdu.data = sbuf;
|
||||
apdu.lc = data_len;
|
||||
apdu.datalen = data_len;
|
||||
|
||||
r = sc_transmit_apdu(card, &apdu);
|
||||
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||
|
||||
/* Get the result */
|
||||
if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) {
|
||||
len = card->type == SC_CARD_TYPE_IAS_PTEID ? PTEID_RSA_KEYSIZE : outlen;
|
||||
r = iso_ops->get_response(card, &len, out);
|
||||
if (r == 0)
|
||||
SC_FUNC_RETURN(card->ctx, 2, len);
|
||||
else
|
||||
SC_FUNC_RETURN(card->ctx, 2, r);
|
||||
}
|
||||
|
||||
SC_FUNC_RETURN(card->ctx, 2, sc_check_sw(card, apdu.sw1, apdu.sw2));
|
||||
}
|
||||
|
||||
static int ias_select_file(sc_card_t *card, const sc_path_t *in_path,
|
||||
sc_file_t **file_out)
|
||||
{
|
||||
int r, pathlen, stripped_len, offset;
|
||||
u8 buf[SC_MAX_APDU_BUFFER_SIZE];
|
||||
u8 pathbuf[SC_MAX_PATH_SIZE], *path;
|
||||
sc_context_t *ctx;
|
||||
sc_apdu_t apdu, rapdu;
|
||||
sc_file_t *file;
|
||||
|
||||
r = pathlen = stripped_len = offset = 0;
|
||||
path = pathbuf;
|
||||
file = NULL;
|
||||
|
||||
assert(card != NULL && in_path != NULL);
|
||||
ctx = card->ctx;
|
||||
|
||||
if (in_path->len > SC_MAX_PATH_SIZE)
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
memcpy(path, in_path->value, in_path->len);
|
||||
pathlen = in_path->len;
|
||||
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0, 0);
|
||||
apdu.p2 = 0; /* First record, return FCI */
|
||||
|
||||
switch (in_path->type) {
|
||||
case SC_PATH_TYPE_FILE_ID:
|
||||
apdu.p1 = 2;
|
||||
if (pathlen != 2)
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
break;
|
||||
case SC_PATH_TYPE_DF_NAME:
|
||||
apdu.p1 = 4;
|
||||
break;
|
||||
case SC_PATH_TYPE_PATH:
|
||||
apdu.p1 = 9;
|
||||
/* Strip the MF */
|
||||
if (pathlen >= 2 && memcmp(path, "\x3f\x00", 2) == 0) {
|
||||
if (pathlen == 2) { /* Only 3f00 provided */
|
||||
apdu.p1 = 0;
|
||||
break;
|
||||
}
|
||||
path += 2;
|
||||
pathlen -= 2;
|
||||
}
|
||||
/* Optimization based on the normal Portuguese eID usage pattern:
|
||||
* paths with len >= 4 shall be stripped - this avoids unnecessary
|
||||
* "file not found" errors. Other cards may benefit from this also.
|
||||
*
|
||||
* This works perfectly for the Portuguese eID card, but if you
|
||||
* are adapting this driver to another card, "false positives" may
|
||||
* occur depending, of course, on the file structure of the card.
|
||||
*
|
||||
* Please have this in mind if adapting this driver to another card.
|
||||
*/
|
||||
if (pathlen >= 4) {
|
||||
stripped_len = pathlen - 2;
|
||||
path += stripped_len;
|
||||
pathlen = 2;
|
||||
} else if (pathlen == 2) {
|
||||
apdu.p1 = 0;
|
||||
}
|
||||
break;
|
||||
case SC_PATH_TYPE_FROM_CURRENT:
|
||||
apdu.p1 = 9;
|
||||
break;
|
||||
case SC_PATH_TYPE_PARENT:
|
||||
apdu.p1 = 3;
|
||||
apdu.p2 = 0x0C;
|
||||
pathlen = 0;
|
||||
apdu.cse = SC_APDU_CASE_2_SHORT;
|
||||
break;
|
||||
default:
|
||||
SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_INVALID_ARGUMENTS);
|
||||
}
|
||||
|
||||
apdu.lc = pathlen;
|
||||
apdu.data = path;
|
||||
apdu.datalen = pathlen;
|
||||
|
||||
if (file_out != NULL) {
|
||||
apdu.resp = buf;
|
||||
apdu.resplen = sizeof(buf);
|
||||
apdu.le = 256;
|
||||
} else {
|
||||
apdu.p2 = 0x0C;
|
||||
apdu.cse = (apdu.lc == 0) ? SC_APDU_CASE_1 : SC_APDU_CASE_3_SHORT;
|
||||
}
|
||||
|
||||
r = sc_transmit_apdu(card, &apdu);
|
||||
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||
if (file_out == NULL) {
|
||||
if (apdu.sw1 == 0x61)
|
||||
SC_FUNC_RETURN(card->ctx, 2, 0);
|
||||
SC_FUNC_RETURN(card->ctx, 2, sc_check_sw(card, apdu.sw1, apdu.sw2));
|
||||
}
|
||||
|
||||
/* A "file not found" error was received, this can mean two things:
|
||||
* 1) the file does not exist
|
||||
* 2) the current DF may be incorrect due to the optimization applied
|
||||
* earlier. If the path was previously stripped, select the first DF
|
||||
* and try to re-select the path with the full value.
|
||||
*/
|
||||
if (stripped_len > 0 && apdu.sw1 == 0x6A && apdu.sw2 == 0x82) {
|
||||
sc_file_t *file = NULL;
|
||||
sc_path_t tpath;
|
||||
int i;
|
||||
|
||||
/* Restore original path value */
|
||||
path -= stripped_len;
|
||||
pathlen += stripped_len;
|
||||
|
||||
memset(&tpath, 0, sizeof(sc_path_t));
|
||||
tpath.type = SC_PATH_TYPE_PATH;
|
||||
tpath.len = 2;
|
||||
if(path[0] == 0x3f && path[1] == 0x00)
|
||||
offset = 2;
|
||||
else
|
||||
offset = 0;
|
||||
tpath.value[0] = path[offset];
|
||||
tpath.value[1] = path[offset + 1];
|
||||
|
||||
/* Go up in the hierarchy to the correct DF */
|
||||
r = ias_select_file(card, &tpath, &file);
|
||||
SC_TEST_RET(card->ctx, r, "Error selecting parent.");
|
||||
if(file->type != SC_FILE_TYPE_DF)
|
||||
return SC_ERROR_FILE_NOT_FOUND;
|
||||
|
||||
/* We're now in the right place, reconstruct the APDU and retry */
|
||||
path += offset + 2;
|
||||
pathlen -= offset + 2;
|
||||
apdu.lc = pathlen;
|
||||
apdu.data = path;
|
||||
apdu.datalen = pathlen;
|
||||
|
||||
if (file_out != NULL)
|
||||
apdu.resplen = sizeof(buf);
|
||||
|
||||
r = sc_transmit_apdu(card, &apdu);
|
||||
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||
if (file_out == NULL) {
|
||||
if (apdu.sw1 == 0x61)
|
||||
SC_FUNC_RETURN(card->ctx, 2, 0);
|
||||
SC_FUNC_RETURN(card->ctx, 2, sc_check_sw(card, apdu.sw1, apdu.sw2));
|
||||
}
|
||||
}
|
||||
|
||||
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
||||
if (r)
|
||||
SC_FUNC_RETURN(card->ctx, 2, r);
|
||||
|
||||
if (apdu.resplen < 2)
|
||||
SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_UNKNOWN_DATA_RECEIVED);
|
||||
switch (apdu.resp[0]) {
|
||||
case 0x6F:
|
||||
file = sc_file_new();
|
||||
if (file == NULL)
|
||||
SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_OUT_OF_MEMORY);
|
||||
file->path = *in_path;
|
||||
if (card->ops->process_fci == NULL) {
|
||||
sc_file_free(file);
|
||||
SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_NOT_SUPPORTED);
|
||||
}
|
||||
if ((size_t)apdu.resp[1] + 2 <= apdu.resplen)
|
||||
card->ops->process_fci(card, file, apdu.resp+2, apdu.resp[1]);
|
||||
*file_out = file;
|
||||
break;
|
||||
case 0x00: /* proprietary coding */
|
||||
SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_UNKNOWN_DATA_RECEIVED);
|
||||
default:
|
||||
SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_UNKNOWN_DATA_RECEIVED);
|
||||
}
|
||||
|
||||
return SC_SUCCESS;
|
||||
}
|
||||
|
||||
static struct sc_card_driver *sc_get_driver(void)
|
||||
{
|
||||
struct sc_card_driver *iso_drv = sc_get_iso7816_driver();
|
||||
|
||||
if (iso_ops == NULL)
|
||||
iso_ops = iso_drv->ops;
|
||||
/* Use the standard iso operations as default */
|
||||
ias_ops = *iso_drv->ops;
|
||||
/* IAS specific functions */
|
||||
ias_ops.select_file = ias_select_file;
|
||||
ias_ops.match_card = ias_match_card;
|
||||
ias_ops.init = ias_init;
|
||||
ias_ops.finish = ias_finish;
|
||||
ias_ops.set_security_env = ias_set_security_env;
|
||||
ias_ops.compute_signature = ias_compute_signature;
|
||||
ias_ops.pin_cmd = ias_pin_cmd;
|
||||
|
||||
return &ias_drv;
|
||||
}
|
||||
|
||||
struct sc_card_driver *sc_get_ias_driver(void)
|
||||
{
|
||||
return sc_get_driver();
|
||||
}
|
@ -151,6 +151,15 @@ enum {
|
||||
/* MyEID cards */
|
||||
SC_CARD_TYPE_MYEID_BASE = 20000,
|
||||
SC_CARD_TYPE_MYEID_GENERIC,
|
||||
|
||||
/* GemsafeV1 cards */
|
||||
SC_CARD_TYPE_GEMSAFEV1_BASE = 21000,
|
||||
SC_CARD_TYPE_GEMSAFEV1_GENERIC,
|
||||
SC_CARD_TYPE_GEMSAFEV1_PTEID,
|
||||
|
||||
/* IAS cards */
|
||||
SC_CARD_TYPE_IAS_BASE = 22000,
|
||||
SC_CARD_TYPE_IAS_PTEID,
|
||||
};
|
||||
|
||||
extern sc_card_driver_t *sc_get_default_driver(void);
|
||||
@ -181,6 +190,7 @@ extern sc_card_driver_t *sc_get_rutoken_driver(void);
|
||||
extern sc_card_driver_t *sc_get_rtecp_driver(void);
|
||||
extern sc_card_driver_t *sc_get_westcos_driver(void);
|
||||
extern sc_card_driver_t *sc_get_myeid_driver(void);
|
||||
extern sc_card_driver_t *sc_get_ias_driver(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -71,6 +71,7 @@ static const struct _sc_driver_entry internal_card_drivers[] = {
|
||||
{ "oberthur", (void *(*)(void)) sc_get_oberthur_driver },
|
||||
#endif
|
||||
{ "belpic", (void *(*)(void)) sc_get_belpic_driver },
|
||||
{ "ias", (void *(*)(void)) sc_get_ias_driver },
|
||||
{ "atrust-acos",(void *(*)(void)) sc_get_atrust_acos_driver },
|
||||
{ "muscle", (void *(*)(void)) sc_get_muscle_driver },
|
||||
{ "incrypto34", (void *(*)(void)) sc_get_incrypto34_driver },
|
||||
|
@ -32,7 +32,9 @@ struct app_entry {
|
||||
|
||||
static const struct app_entry apps[] = {
|
||||
{ (const u8 *) "\xA0\x00\x00\x00\x63PKCS-15", 12, "PKCS #15" },
|
||||
{ (const u8 *) "\xA0\x00\x00\x01\x77PKCS-15", 12, "Belgian eID" },
|
||||
{ (const u8 *) "\xA0\x00\x00\x01\x77PKCS-15", 12, "Belgian eID" }
|
||||
/* Needed for the normal PKCS#15 processing of the Portugal eID card */
|
||||
/* { (const u8 *) "\x44\x46\x20\x69\x73\x73\x75\x65\x72", 9, "Portugal eID" } */
|
||||
};
|
||||
|
||||
static const struct app_entry * find_app_entry(const u8 * aid, size_t aid_len)
|
||||
|
295
src/libopensc/pkcs15-pteid.c
Normal file
295
src/libopensc/pkcs15-pteid.c
Normal file
@ -0,0 +1,295 @@
|
||||
/*
|
||||
* PKCS15 emulation layer for Portugal eID card.
|
||||
*
|
||||
* Copyright (C) 2009, Joao Poupino <joao.poupino@ist.utl.pt>
|
||||
* Copyright (C) 2004, Martin Paljak <martin@paljak.pri.ee>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* Based on the PKCS#15 emulation layer for EstEID card by Martin Paljak
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* The card has a valid PKCS#15 file system. However, the private keys
|
||||
* are missing the SC_PKCS15_CO_FLAG_PRIVATE flag and this causes problems
|
||||
* with some applications (i.e. they don't work).
|
||||
*
|
||||
* The three main objectives of the emulation layer are:
|
||||
*
|
||||
* 1. Add the necessary SC_PKCS15_CO_FLAG_PRIVATE flag to private keys.
|
||||
* 2. Hide "superfluous" PKCS#15 objects, e.g. PUKs (the user can't use them).
|
||||
* 3. Improve usability by providing more descriptive names for the PINs, Keys, etc.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "internal.h"
|
||||
#include "pkcs15.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <compat_strlcpy.h>
|
||||
|
||||
#define IAS_CARD 0
|
||||
#define GEMSAFE_CARD 1
|
||||
|
||||
int sc_pkcs15emu_pteid_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *);
|
||||
|
||||
static int sc_pkcs15emu_pteid_init(sc_pkcs15_card_t * p15card)
|
||||
{
|
||||
int r, i, type;
|
||||
unsigned char *buf;
|
||||
size_t len;
|
||||
sc_pkcs15_tokeninfo_t tokeninfo;
|
||||
sc_path_t tmppath;
|
||||
sc_card_t *card = p15card->card;
|
||||
sc_context_t *ctx = card->ctx;
|
||||
|
||||
/* Parse the TokenInfo EF */
|
||||
sc_format_path("3f004f005032", &tmppath);
|
||||
r = sc_select_file(card, &tmppath, &p15card->file_tokeninfo);
|
||||
if (r)
|
||||
goto end;
|
||||
if ( (len = p15card->file_tokeninfo->size) == 0) {
|
||||
sc_error(card->ctx, "EF(TokenInfo) is empty\n");
|
||||
goto end;
|
||||
}
|
||||
buf = malloc(len);
|
||||
if (buf == NULL)
|
||||
return SC_ERROR_OUT_OF_MEMORY;
|
||||
r = sc_read_binary(card, 0, buf, len, 0);
|
||||
if (r < 0)
|
||||
goto end;
|
||||
if (r <= 2) {
|
||||
r = SC_ERROR_PKCS15_APP_NOT_FOUND;
|
||||
goto end;
|
||||
}
|
||||
memset(&tokeninfo, 0, sizeof(tokeninfo));
|
||||
r = sc_pkcs15_parse_tokeninfo(ctx, &tokeninfo, buf, (size_t) r);
|
||||
if (r != SC_SUCCESS)
|
||||
goto end;
|
||||
p15card->version = tokeninfo.version;
|
||||
p15card->label = tokeninfo.label;
|
||||
p15card->serial_number = tokeninfo.serial_number;
|
||||
p15card->manufacturer_id = tokeninfo.manufacturer_id;
|
||||
p15card->last_update = tokeninfo.last_update;
|
||||
p15card->flags = tokeninfo.flags;
|
||||
p15card->preferred_language = tokeninfo.preferred_language;
|
||||
p15card->seInfo = tokeninfo.seInfo;
|
||||
p15card->num_seInfo = tokeninfo.num_seInfo;
|
||||
|
||||
/* Card type detection */
|
||||
if (card->type == SC_CARD_TYPE_IAS_PTEID)
|
||||
type = IAS_CARD;
|
||||
else if (card->type == SC_CARD_TYPE_GEMSAFEV1_PTEID)
|
||||
type = GEMSAFE_CARD;
|
||||
else {
|
||||
r = SC_ERROR_INTERNAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
p15card->flags = SC_PKCS15_CARD_FLAG_PRN_GENERATION
|
||||
| SC_PKCS15_CARD_FLAG_EID_COMPLIANT
|
||||
| SC_PKCS15_CARD_FLAG_READONLY;
|
||||
|
||||
/* TODO: Use the cardholder's name? */
|
||||
/* TODO: Use Portuguese descriptions? */
|
||||
|
||||
/* Add X.509 Certificates */
|
||||
static const char *pteid_cert_names[4] = {
|
||||
"AUTHENTICATION CERTIFICATE",
|
||||
"SIGNATURE CERTIFICATE",
|
||||
"SIGNATURE SUB CA",
|
||||
"AUTHENTICATION SUB CA"
|
||||
};
|
||||
/* X.509 Certificate Paths */
|
||||
static const char *pteid_cert_paths[4] = {
|
||||
"3f005f00ef09", /* Authentication Certificate path */
|
||||
"3f005f00ef08", /* Digital Signature Certificate path */
|
||||
"3f005f00ef0f", /* Signature sub CA path */
|
||||
"3f005f00ef10" /* Authentication sub CA path */
|
||||
};
|
||||
/* X.509 Certificate IDs */
|
||||
static const int pteid_cert_ids[4] = {0x45, 0x46, 0x51, 0x52};
|
||||
struct sc_pkcs15_cert_info cert_info;
|
||||
struct sc_pkcs15_object cert_obj;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
memset(&cert_info, 0, sizeof(cert_info));
|
||||
memset(&cert_obj, 0, sizeof(cert_obj));
|
||||
|
||||
cert_info.id.value[0] = pteid_cert_ids[i];
|
||||
cert_info.id.len = 1;
|
||||
sc_format_path(pteid_cert_paths[i], &cert_info.path);
|
||||
strlcpy(cert_obj.label, pteid_cert_names[i], sizeof(cert_obj.label));
|
||||
r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info);
|
||||
if (r < 0) {
|
||||
r = SC_ERROR_INTERNAL;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add PINs */
|
||||
static const char *pteid_pin_names[3] = {
|
||||
"Auth PIN",
|
||||
"Sign PIN",
|
||||
"Address PIN"
|
||||
};
|
||||
/* PIN References */
|
||||
static const int pteid_pin_ref[2][3] = { {1, 130, 131}, {129, 130, 131} };
|
||||
/* PIN Authentication IDs */
|
||||
static const int pteid_pin_authid[3] = {1, 2, 3};
|
||||
/* PIN Paths */
|
||||
static const char *pteid_pin_paths[2][3] = { {NULL, "3f005f00", NULL},
|
||||
{NULL, NULL, NULL} };
|
||||
struct sc_pkcs15_pin_info pin_info;
|
||||
struct sc_pkcs15_object pin_obj;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
memset(&pin_info, 0, sizeof(pin_info));
|
||||
memset(&pin_obj, 0, sizeof(pin_obj));
|
||||
|
||||
pin_info.auth_id.len = 1;
|
||||
pin_info.auth_id.value[0] = pteid_pin_authid[i];
|
||||
pin_info.reference = pteid_pin_ref[type][i];
|
||||
pin_info.flags = SC_PKCS15_PIN_FLAG_NEEDS_PADDING
|
||||
| SC_PKCS15_PIN_FLAG_INITIALIZED
|
||||
| SC_PKCS15_PIN_FLAG_CASE_SENSITIVE;
|
||||
pin_info.type = SC_PKCS15_PIN_TYPE_ASCII_NUMERIC;
|
||||
pin_info.min_length = 4;
|
||||
pin_info.stored_length = 8;
|
||||
pin_info.max_length = 8;
|
||||
pin_info.pad_char = type == IAS_CARD ? 0x2F : 0xFF;
|
||||
pin_info.tries_left = -1;
|
||||
if (pteid_pin_paths[type][i] != NULL)
|
||||
sc_format_path(pteid_pin_paths[type][i], &pin_info.path);
|
||||
strlcpy(pin_obj.label, pteid_pin_names[i], sizeof(pin_obj.label));
|
||||
pin_obj.flags = 0;
|
||||
r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info);
|
||||
if (r < 0) {
|
||||
r = SC_ERROR_INTERNAL;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add Private Keys */
|
||||
|
||||
/* Key reference */
|
||||
static const int pteid_prkey_keyref[2][2] = { {1, 130}, {2, 1} };
|
||||
/* RSA Private Key usage */
|
||||
static int pteid_prkey_usage[2] = {
|
||||
SC_PKCS15_PRKEY_USAGE_SIGN,
|
||||
SC_PKCS15_PRKEY_USAGE_NONREPUDIATION};
|
||||
/* RSA Private Key IDs */
|
||||
static const int pteid_prkey_ids[2] = {0x45, 0x46};
|
||||
static const char *pteid_prkey_names[2] = {
|
||||
"CITIZEN AUTHENTICATION KEY",
|
||||
"CITIZEN SIGNATURE KEY"};
|
||||
/* RSA Private Key Paths */
|
||||
static const char *pteid_prkey_paths[2][2] = { {NULL, "3f005f00"}, {NULL, NULL} };
|
||||
struct sc_pkcs15_prkey_info prkey_info;
|
||||
struct sc_pkcs15_object prkey_obj;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
memset(&prkey_info, 0, sizeof(prkey_info));
|
||||
memset(&prkey_obj, 0, sizeof(prkey_obj));
|
||||
|
||||
prkey_info.id.len = 1;
|
||||
prkey_info.id.value[0] = pteid_prkey_ids[i];
|
||||
prkey_info.usage = pteid_prkey_usage[i];
|
||||
prkey_info.native = 1;
|
||||
prkey_info.key_reference = pteid_prkey_keyref[type][i];
|
||||
prkey_info.modulus_length = 1024;
|
||||
if (pteid_prkey_paths[type][i] != NULL)
|
||||
sc_format_path(pteid_prkey_paths[type][i], &prkey_info.path);
|
||||
strlcpy(prkey_obj.label, pteid_prkey_names[i], sizeof(prkey_obj.label));
|
||||
prkey_obj.auth_id.len = 1;
|
||||
prkey_obj.auth_id.value[0] = i + 1;
|
||||
prkey_obj.user_consent = (i == 1) ? 1 : 0;
|
||||
prkey_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE;
|
||||
|
||||
r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info);
|
||||
if (r < 0) {
|
||||
r = SC_ERROR_INTERNAL;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add objects */
|
||||
static const char *object_ids[3] = {"1", "2", "3"};
|
||||
static const char *object_oids[3] = {"-1", "-1", "-1"};
|
||||
static const char *object_labels[3] = {"Citizen Data",
|
||||
"Citizen Address Data",
|
||||
"Citizen Notepad"};
|
||||
static const char *object_authids[3] = {"3", "3", "1"};
|
||||
static const char *object_paths[3] = {"3f005f00ef02",
|
||||
"3f005f00ef05",
|
||||
"3f005f00ef07"};
|
||||
static const int object_flags[3] = {0,
|
||||
SC_PKCS15_CO_FLAG_PRIVATE,
|
||||
SC_PKCS15_CO_FLAG_MODIFIABLE};
|
||||
struct sc_pkcs15_data_info obj_info;
|
||||
struct sc_pkcs15_object obj_obj;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
memset(&obj_info, 0, sizeof(obj_info));
|
||||
memset(&obj_obj, 0, sizeof(obj_obj));
|
||||
|
||||
sc_pkcs15_format_id(object_ids[i], &obj_info.id);
|
||||
sc_format_path(object_paths[i], &obj_info.path);
|
||||
r = sc_format_oid(&obj_info.app_oid, object_oids[i]);
|
||||
if (r != SC_SUCCESS)
|
||||
goto end;
|
||||
strlcpy(obj_info.app_label, object_labels[i], SC_PKCS15_MAX_LABEL_SIZE);
|
||||
if (object_authids[i] != NULL)
|
||||
sc_pkcs15_format_id(object_authids[i], &obj_obj.auth_id);
|
||||
strlcpy(obj_obj.label, object_labels[i], SC_PKCS15_MAX_LABEL_SIZE);
|
||||
obj_obj.flags = object_flags[i];
|
||||
|
||||
r = sc_pkcs15emu_object_add(p15card, SC_PKCS15_TYPE_DATA_OBJECT, &obj_obj, &obj_info);
|
||||
if (r < 0)
|
||||
goto end;
|
||||
}
|
||||
end:
|
||||
if (buf != NULL) {
|
||||
free(buf);
|
||||
buf = NULL;
|
||||
}
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return SC_SUCCESS;
|
||||
}
|
||||
|
||||
static int pteid_detect_card(sc_pkcs15_card_t *p15card)
|
||||
{
|
||||
if (p15card->card->type == SC_CARD_TYPE_IAS_PTEID ||
|
||||
p15card->card->type == SC_CARD_TYPE_GEMSAFEV1_PTEID)
|
||||
return SC_SUCCESS;
|
||||
return SC_ERROR_WRONG_CARD;
|
||||
}
|
||||
|
||||
int sc_pkcs15emu_pteid_init_ex(sc_pkcs15_card_t *p15card, sc_pkcs15emu_opt_t *opts)
|
||||
{
|
||||
if (opts != NULL && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK)
|
||||
return sc_pkcs15emu_pteid_init(p15card);
|
||||
else {
|
||||
int r = pteid_detect_card(p15card);
|
||||
if (r)
|
||||
return SC_ERROR_WRONG_CARD;
|
||||
return sc_pkcs15emu_pteid_init(p15card);
|
||||
}
|
||||
}
|
@ -56,6 +56,8 @@ extern int sc_pkcs15emu_tccardos_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t
|
||||
|
||||
extern int sc_pkcs15emu_entersafe_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *);
|
||||
|
||||
extern int sc_pkcs15emu_pteid_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *);
|
||||
|
||||
static struct {
|
||||
const char * name;
|
||||
int (*handler)(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *);
|
||||
@ -74,6 +76,7 @@ static struct {
|
||||
{ "atrust-acos",sc_pkcs15emu_atrust_acos_init_ex},
|
||||
{ "tccardos", sc_pkcs15emu_tccardos_init_ex },
|
||||
{ "entersafe", sc_pkcs15emu_entersafe_init_ex },
|
||||
{ "pteid", sc_pkcs15emu_pteid_init_ex },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
@ -90,6 +93,8 @@ int sc_pkcs15_is_emulation_only(sc_card_t *card)
|
||||
{
|
||||
switch (card->type) {
|
||||
case SC_CARD_TYPE_MCRD_ESTEID:
|
||||
case SC_CARD_TYPE_IAS_PTEID:
|
||||
case SC_CARD_TYPE_GEMSAFEV1_PTEID:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
|
Loading…
Reference in New Issue
Block a user