Add support for Estonian ID card. Written by Martin Paljak.
git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@1834 c6295689-39f2-0310-b995-f0e70906c6a9
This commit is contained in:
parent
78d32814a1
commit
268a302950
@ -30,7 +30,7 @@ libopensc_la_SOURCES = \
|
||||
card-oberthur.c card-oberthur.h \
|
||||
\
|
||||
pkcs15-openpgp.c pkcs15-infocamere.c pkcs15-starcert.c \
|
||||
pkcs15-netkey.c
|
||||
pkcs15-netkey.c pkcs15-esteid.c
|
||||
libopensc_la_LDFLAGS = -version-info @OPENSC_LT_CURRENT@:@OPENSC_LT_REVISION@:@OPENSC_LT_AGE@
|
||||
libopensc_la_LIBADD = @LIBSCCONF@ ../scdl/libscdl.la @LIBDL@ @LIBCRYPTO@ @OPENCT_LIBS@ @LIBPCSC@
|
||||
|
||||
@ -39,7 +39,7 @@ include_HEADERS = \
|
||||
cardctl.h asn1.h log.h ui.h \
|
||||
errors.h types.h
|
||||
|
||||
noinst_HEADERS = ctbcs.h internal.h
|
||||
noinst_HEADERS = ctbcs.h internal.h esteid.h
|
||||
|
||||
pkgconfigdir = @libdir@/pkgconfig
|
||||
pkgconfig_DATA = libopensc.pc
|
||||
|
@ -28,7 +28,7 @@ OBJECTS = \
|
||||
card-oberthur.obj \
|
||||
\
|
||||
pkcs15-openpgp.obj pkcs15-infocamere.obj pkcs15-starcert.obj \
|
||||
pkcs15-netkey.obj \
|
||||
pkcs15-netkey.obj pkcs15-esteid.c \
|
||||
\
|
||||
$(TOPDIR)\win32\version.res
|
||||
|
||||
|
@ -1,6 +1,10 @@
|
||||
/*
|
||||
* card-mcrd.c: Support for MICARDO cards
|
||||
*
|
||||
* Copyright (C) 2004 Martin Paljak <martin@paljak.pri.ee>
|
||||
* Copyright (C) 2004 Priit Randla <priit.randla@eyp.ee>
|
||||
* Copyright (C) 2003 Marie Fischer <marie@vtl.ee>
|
||||
|
||||
* Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi>
|
||||
* Copyright (C) 2002 g10 Code GmbH
|
||||
*
|
||||
@ -26,12 +30,26 @@
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
static const char *mcrd_atrs[] = {
|
||||
"3B:FF:94:00:FF:80:B1:FE:45:1F:03:00:68:D2:76:00:00:28:FF"
|
||||
":05:1E:31:80:00:90:00:23", /* German BMI card */
|
||||
"3B:FE:94:00:FF:80:B1:FA:45:1F:03:45:73:74:45:49:44:20:76"
|
||||
":65:72:20:31:2E:30:43", /* EstEID (Estonian Big Brother card) */
|
||||
NULL
|
||||
#include "esteid.h"
|
||||
|
||||
#define TYPE_UNKNOWN 0
|
||||
#define TYPE_ANY 1
|
||||
#define TYPE_ESTEID 2
|
||||
|
||||
|
||||
/* this structure should be somewhere else. Copied from other card source.
|
||||
* I need to make sure a 'variant' of a card to make decisions.
|
||||
*/
|
||||
struct sc_card_atrs {
|
||||
const char *atr;
|
||||
const int type;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
static struct sc_card_atrs mcrd_atrs[] = {
|
||||
{"3B:FF:94:00:FF:80:B1:FE:45:1F:03:00:68:D2:76:00:00:28:FF:05:1E:31:80:00:90:00:23", TYPE_ANY, "German BMI"},
|
||||
{"3B:FE:94:00:FF:80:B1:FA:45:1F:03:45:73:74:45:49:44:20:76:65:72:20:31:2E:30:43", TYPE_ESTEID, "EstEID"},
|
||||
{NULL, TYPE_UNKNOWN, NULL}
|
||||
};
|
||||
|
||||
static struct sc_card_operations mcrd_ops;
|
||||
@ -85,6 +103,7 @@ struct mcrd_priv_data {
|
||||
size_t curpathlen; /* Length of this path or 0 if unknown. */
|
||||
int is_ef; /* True if the path points to an EF. */
|
||||
struct df_info_s *df_infos;
|
||||
sc_security_env_t sec_env; /* current security environment */
|
||||
};
|
||||
|
||||
#define DRVDATA(card) ((struct mcrd_priv_data *) ((card)->drv_data))
|
||||
@ -145,30 +164,128 @@ static void clear_special_files (struct df_info_s *dfi)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int mcrd_match_card(struct sc_card *card)
|
||||
// this function should be somewhere else in opensc code, too
|
||||
static int
|
||||
sc_card_identify (struct sc_card *card, struct sc_card_atrs *atr_list)
|
||||
{
|
||||
int i, match = -1;
|
||||
|
||||
for (i = 0; mcrd_atrs[i] != NULL; i++) {
|
||||
int i;
|
||||
for (i = 0; atr_list[i].atr != NULL; i++)
|
||||
{
|
||||
u8 defatr[SC_MAX_ATR_SIZE];
|
||||
size_t len = sizeof(defatr);
|
||||
const char *atrp = mcrd_atrs[i];
|
||||
|
||||
if (sc_hex_to_bin(atrp, defatr, &len))
|
||||
size_t len = sizeof (defatr);
|
||||
const char *atrp = atr_list[i].atr;
|
||||
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;
|
||||
break;
|
||||
if (memcmp (card->atr, defatr, len) == 0)
|
||||
return atr_list[i].type;
|
||||
}
|
||||
if (match == -1)
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Some functionality straight from the EstEID manual.
|
||||
* Official notice: Refer to the Micardo 2.1 Public manual.
|
||||
* Sad side: not available without a NDA.
|
||||
*/
|
||||
|
||||
return 1;
|
||||
int
|
||||
mcrd_delete_ref_to_authkey (struct sc_card *card)
|
||||
{
|
||||
struct sc_apdu apdu;
|
||||
int r;
|
||||
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
|
||||
|
||||
assert (card != NULL);
|
||||
sc_format_apdu (card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0xA4);
|
||||
|
||||
sbuf[0] = 0x83;
|
||||
sbuf[1] = 0x00;
|
||||
apdu.data = sbuf;
|
||||
apdu.lc = 2;
|
||||
apdu.datalen = 2;
|
||||
r = sc_transmit_apdu (card, &apdu);
|
||||
SC_TEST_RET (card->ctx, r, "APDU transmit failed");
|
||||
SC_FUNC_RETURN (card->ctx, 2, sc_check_sw (card, apdu.sw1, apdu.sw2));
|
||||
}
|
||||
|
||||
int
|
||||
mcrd_delete_ref_to_signkey (struct sc_card *card)
|
||||
{
|
||||
struct sc_apdu apdu;
|
||||
int r;
|
||||
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
|
||||
assert (card != NULL);
|
||||
|
||||
sc_format_apdu (card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0xB6);
|
||||
|
||||
sbuf[0] = 0x83;
|
||||
sbuf[1] = 0x00;
|
||||
apdu.data = sbuf;
|
||||
apdu.lc = 2;
|
||||
apdu.datalen = 2;
|
||||
r = sc_transmit_apdu (card, &apdu);
|
||||
SC_TEST_RET (card->ctx, r, "APDU transmit failed");
|
||||
SC_FUNC_RETURN (card->ctx, 2, sc_check_sw (card, apdu.sw1, apdu.sw2));
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
mcrd_set_decipher_key_ref (struct sc_card *card, int key_reference)
|
||||
{
|
||||
struct sc_apdu apdu;
|
||||
struct sc_path path;
|
||||
int r;
|
||||
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
|
||||
u8 keyref_data[SC_ESTEID_KEYREF_FILE_RECLEN];
|
||||
assert (card != NULL);
|
||||
|
||||
sc_format_apdu (card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0xB8);
|
||||
/* track the active keypair */
|
||||
sc_format_path("0033", &path);
|
||||
r = sc_select_file(card, &path, NULL);
|
||||
SC_TEST_RET(card->ctx, r,
|
||||
"Can't select keyref info file 0x0033");
|
||||
r = sc_read_record(card, 1, keyref_data,
|
||||
SC_ESTEID_KEYREF_FILE_RECLEN,
|
||||
SC_RECORD_BY_REC_NR);
|
||||
SC_TEST_RET(card->ctx, r,
|
||||
"Can't read keyref info file!");
|
||||
|
||||
sc_debug(card->ctx,
|
||||
"authkey reference 0x%02x%02x\n",
|
||||
keyref_data[9], keyref_data[10]);
|
||||
|
||||
sc_debug(card->ctx,
|
||||
"signkey reference 0x%02x%02x\n",
|
||||
keyref_data[19], keyref_data[20]);
|
||||
|
||||
|
||||
sbuf[0] = 0x83;
|
||||
sbuf[1] = 0x03;
|
||||
sbuf[2] = 0x80;
|
||||
switch (key_reference)
|
||||
{
|
||||
case 1:
|
||||
sbuf[3] = keyref_data[9];
|
||||
sbuf[4] = keyref_data[10];
|
||||
break;
|
||||
case 2:
|
||||
sbuf[3] = keyref_data[19];
|
||||
sbuf[4] = keyref_data[20];
|
||||
break;
|
||||
}
|
||||
apdu.data = sbuf;
|
||||
apdu.lc = 5;
|
||||
apdu.datalen = 5;
|
||||
r = sc_transmit_apdu (card, &apdu);
|
||||
SC_TEST_RET (card->ctx, r, "APDU transmit failed");
|
||||
SC_FUNC_RETURN (card->ctx, 2, sc_check_sw (card, apdu.sw1, apdu.sw2));
|
||||
}
|
||||
|
||||
static int mcrd_match_card(struct sc_card *card)
|
||||
{
|
||||
return sc_card_identify(card, mcrd_atrs) != 0;
|
||||
}
|
||||
|
||||
static int mcrd_init(struct sc_card *card)
|
||||
@ -193,8 +310,8 @@ static int mcrd_init(struct sc_card *card)
|
||||
|
||||
priv->curpath[0] = MFID;
|
||||
priv->curpathlen = 1;
|
||||
load_special_files (card);
|
||||
|
||||
if (sc_card_identify(card, mcrd_atrs) != TYPE_ESTEID)
|
||||
load_special_files (card);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -979,7 +1096,7 @@ mcrd_select_file(struct sc_card *card, const struct sc_path *path,
|
||||
|
||||
/* Crypto operations */
|
||||
|
||||
static int restore_se (struct sc_card *card, int se_num)
|
||||
static int mcrd_enable_se (struct sc_card *card, int se_num)
|
||||
{
|
||||
struct sc_apdu apdu;
|
||||
int r;
|
||||
@ -994,19 +1111,59 @@ static int restore_se (struct sc_card *card, int se_num)
|
||||
|
||||
/* It seems that MICARDO does not fully comply with ISO, so I use
|
||||
values gathered from peeking actual signing opeations using a
|
||||
different system. */
|
||||
different system.
|
||||
It has been generalized [?] and modified by information coming from
|
||||
openpgp card implementation, EstEID 'manual' and some other sources. -mp
|
||||
*/
|
||||
static int mcrd_set_security_env(struct sc_card *card,
|
||||
const struct sc_security_env *env,
|
||||
int se_num)
|
||||
{
|
||||
struct mcrd_priv_data *priv = DRVDATA(card);
|
||||
struct sc_apdu apdu;
|
||||
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
|
||||
u8 *p;
|
||||
int r, locked = 0;
|
||||
|
||||
assert(card != NULL && env != NULL);
|
||||
if (se_num)
|
||||
SC_FUNC_CALLED(card->ctx, 2);
|
||||
|
||||
/* if (se_num)
|
||||
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INVALID_ARGUMENTS);
|
||||
*/
|
||||
/* special environemnt handling for esteid, stolen from openpgp */
|
||||
if (sc_card_identify(card, mcrd_atrs) == TYPE_ESTEID) {
|
||||
/* some sanity checks */
|
||||
if (env->flags & SC_SEC_ENV_ALG_PRESENT) {
|
||||
if (env->algorithm != SC_ALGORITHM_RSA)
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
}
|
||||
if (!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT)
|
||||
|| env->key_ref_len != 1)
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
|
||||
select_esteid_df(card); // is it needed?
|
||||
switch (env->operation) {
|
||||
case SC_SEC_OPERATION_DECIPHER:
|
||||
sc_debug(card->ctx,
|
||||
"Using keyref %d to dechiper\n",
|
||||
env->key_ref[0]);
|
||||
mcrd_enable_se(card, 6);
|
||||
mcrd_delete_ref_to_authkey(card);
|
||||
mcrd_delete_ref_to_signkey(card);
|
||||
mcrd_set_decipher_key_ref(card, env->key_ref[0]);
|
||||
break;
|
||||
case SC_SEC_OPERATION_SIGN:
|
||||
sc_debug(card->ctx, "Using keyref %d to sign\n",
|
||||
env->key_ref[0]);
|
||||
mcrd_enable_se(card, 1);
|
||||
break;
|
||||
default:
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
}
|
||||
priv->sec_env = *env;
|
||||
return 0;
|
||||
}
|
||||
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0, 0);
|
||||
apdu.le = 0;
|
||||
@ -1037,8 +1194,8 @@ static int mcrd_set_security_env(struct sc_card *card,
|
||||
if (num != -1) {
|
||||
/* Need to restore the security environmnet. */
|
||||
if (num) {
|
||||
r = restore_se (card, num);
|
||||
SC_TEST_RET(card->ctx, r, "restore_se failed");
|
||||
r = mcrd_enable_se (card, num);
|
||||
SC_TEST_RET(card->ctx, r, "mcrd_enable_se failed");
|
||||
}
|
||||
p += 2;
|
||||
}
|
||||
@ -1080,61 +1237,118 @@ err:
|
||||
return r;
|
||||
}
|
||||
|
||||
/* heavily modified by -mp */
|
||||
static int mcrd_compute_signature(struct sc_card *card,
|
||||
const u8 * data, size_t datalen,
|
||||
u8 * out, size_t outlen)
|
||||
{
|
||||
struct mcrd_priv_data *priv = DRVDATA(card);
|
||||
sc_security_env_t *env = &priv->sec_env;
|
||||
int r;
|
||||
struct sc_apdu apdu;
|
||||
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
|
||||
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
|
||||
|
||||
assert(card != NULL && data != NULL && out != NULL);
|
||||
SC_FUNC_CALLED(card->ctx, 2);
|
||||
if (env->operation != SC_SEC_OPERATION_SIGN)
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
if (datalen > 255)
|
||||
SC_FUNC_RETURN(card->ctx, 4, SC_ERROR_INVALID_ARGUMENTS);
|
||||
|
||||
/* INS: 0x2A PERFORM SECURITY OPERATION
|
||||
* P1: 0x9E Resp: Digital Signature
|
||||
* P2: 0x9A Cmd: Input for Digital Signature */
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x2A, 0x9E,
|
||||
0x9A);
|
||||
apdu.resp = rbuf;
|
||||
apdu.resplen = sizeof(rbuf); /* FIXME */
|
||||
sc_debug(card->ctx,
|
||||
"Will compute signature for %d (0x%02x) bytes using key %d\n",
|
||||
datalen, datalen, env->key_ref[0]);
|
||||
|
||||
memcpy(sbuf, data, datalen);
|
||||
apdu.data = sbuf;
|
||||
switch (env->key_ref[0]) {
|
||||
case SC_ESTEID_AUTH: /* authentication key */
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT,
|
||||
0x88, 0, 0);
|
||||
break;
|
||||
default:
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT,
|
||||
0x2A, 0x9E, 0x9A);
|
||||
|
||||
}
|
||||
apdu.lc = datalen;
|
||||
apdu.data = data;
|
||||
apdu.datalen = datalen;
|
||||
apdu.le = 0x80;
|
||||
apdu.resp = out;
|
||||
apdu.resplen = outlen;
|
||||
|
||||
r = sc_transmit_apdu(card, &apdu);
|
||||
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
||||
SC_TEST_RET(card->ctx, r, "Card returned error");
|
||||
|
||||
SC_FUNC_RETURN(card->ctx, 4, apdu.resplen);
|
||||
}
|
||||
|
||||
/* added by -mp */
|
||||
int mcrd_decipher(struct sc_card *card,
|
||||
const u8 * crgram, size_t crgram_len, u8 * out,
|
||||
size_t out_len)
|
||||
{
|
||||
|
||||
int r;
|
||||
struct sc_apdu apdu;
|
||||
struct mcrd_priv_data *priv = DRVDATA(card);
|
||||
sc_security_env_t *env = &priv->sec_env;
|
||||
u8 *temp;
|
||||
|
||||
sc_debug(card->ctx,
|
||||
"Will dechiper %d (0x%02x) bytes using key %d\n",
|
||||
crgram_len, crgram_len, env->key_ref[0]);
|
||||
|
||||
/* saniti check */
|
||||
if (env->operation != SC_SEC_OPERATION_DECIPHER)
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
|
||||
if (!(temp = (u8 *) malloc(crgram_len + 1)))
|
||||
return SC_ERROR_OUT_OF_MEMORY;
|
||||
temp[0] = '\0';
|
||||
memcpy(temp + 1, crgram, crgram_len);
|
||||
crgram = temp;
|
||||
crgram_len += 1;
|
||||
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x2A, 0x80,
|
||||
0x86);
|
||||
|
||||
apdu.resp = out;
|
||||
apdu.resplen = out_len;
|
||||
|
||||
apdu.data = crgram;
|
||||
apdu.datalen = crgram_len;
|
||||
|
||||
apdu.lc = crgram_len;
|
||||
apdu.sensitive = 1;
|
||||
r = sc_transmit_apdu(card, &apdu);
|
||||
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||
if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) {
|
||||
int len = apdu.resplen > outlen ? outlen : apdu.resplen;
|
||||
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
||||
SC_TEST_RET(card->ctx, r, "Card returned error");
|
||||
|
||||
memcpy(out, apdu.resp, len);
|
||||
SC_FUNC_RETURN(card->ctx, 4, len);
|
||||
}
|
||||
else if (apdu.sw1 == 0x60 && apdu.sw2 == 0x61) {
|
||||
/* This might be a problem with by Cardman driver.
|
||||
Status codes 60xx should never been seen at this
|
||||
layer, so I assume 0x6180 here. FIXME! */
|
||||
int len;
|
||||
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT,
|
||||
0xC0, 0, 0);
|
||||
apdu.le = 0x80;
|
||||
apdu.resp = rbuf;
|
||||
apdu.resplen = 0x80;
|
||||
r = sc_transmit_apdu(card, &apdu);
|
||||
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||
len = apdu.resplen > outlen ? outlen : apdu.resplen;
|
||||
memcpy(out, apdu.resp, len);
|
||||
SC_FUNC_RETURN(card->ctx, 4, len);
|
||||
}
|
||||
|
||||
SC_FUNC_RETURN(card->ctx, 4, sc_check_sw(card, apdu.sw1, apdu.sw2));
|
||||
SC_FUNC_RETURN(card->ctx, 4, apdu.resplen);
|
||||
}
|
||||
|
||||
/* added by -mp */
|
||||
int mcrd_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data,
|
||||
int *tries_left)
|
||||
{
|
||||
|
||||
SC_FUNC_CALLED(card->ctx, 3);
|
||||
if (sc_card_identify(card, mcrd_atrs) == TYPE_ESTEID)
|
||||
if (data->cmd == SC_PIN_CMD_UNBLOCK) {
|
||||
int r;
|
||||
/* FIXME: the code in iso pin_cmd sets tries_left to -1 at once.
|
||||
I guess it is a thing that need implementing. set it to NULL now -mp */
|
||||
r = sc_verify(card, SC_AC_CHV, 0, data->pin1.data,
|
||||
data->pin1.len, NULL);
|
||||
SC_TEST_RET(card->ctx, r,
|
||||
"PUK verification failed!");
|
||||
}
|
||||
SC_FUNC_RETURN(card->ctx, 4, iso_ops->pin_cmd(card, data, tries_left));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -1142,17 +1356,19 @@ static int mcrd_compute_signature(struct sc_card *card,
|
||||
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;
|
||||
|
||||
mcrd_ops = *iso_drv->ops;
|
||||
mcrd_ops.match_card = mcrd_match_card;
|
||||
mcrd_ops.init = mcrd_init;
|
||||
mcrd_ops.finish = mcrd_finish;
|
||||
if (iso_ops == NULL)
|
||||
iso_ops = iso_drv->ops;
|
||||
mcrd_ops.select_file = mcrd_select_file;
|
||||
mcrd_ops.set_security_env = mcrd_set_security_env;
|
||||
mcrd_ops.compute_signature = mcrd_compute_signature;
|
||||
|
||||
mcrd_ops.decipher = mcrd_decipher;
|
||||
mcrd_ops.pin_cmd = mcrd_pin_cmd;
|
||||
|
||||
return &mcrd_drv;
|
||||
}
|
||||
|
||||
|
31
src/libopensc/esteid.h
Normal file
31
src/libopensc/esteid.h
Normal file
@ -0,0 +1,31 @@
|
||||
#ifndef _OPENSC_ESTEID_H
|
||||
#define _OPENSC_ESTEID_H
|
||||
|
||||
#define SC_ESTEID_AUTH 1
|
||||
#define SC_ESTEID_SIGN 2
|
||||
|
||||
|
||||
/* personal data file record numbers */
|
||||
|
||||
#define SC_ESTEID_PD_SURNAME 1
|
||||
#define SC_ESTEID_PD_GIVEN_NAMES1 2
|
||||
#define SC_ESTEID_PD_GIVEN_NAMES2 3
|
||||
#define SC_ESTEID_PD_SEX 4
|
||||
#define SC_ESTEID_PD_CITIZENSHIP 5
|
||||
#define SC_ESTEID_PD_DATE_OF_BIRTH 6
|
||||
#define SC_ESTEID_PD_PERSONAL_ID 7
|
||||
#define SC_ESTEID_PD_DOCUMENT_NR 8
|
||||
#define SC_ESTEID_PD_EXPIRY_DATE 9
|
||||
#define SC_ESTEID_PD_PLACE_OF_BIRTH 10
|
||||
#define SC_ESTEID_PD_ISSUING_DATE 11
|
||||
#define SC_ESTEID_PD_PERMIT_TYPE 12
|
||||
#define SC_ESTEID_PD_REMARK1 13
|
||||
#define SC_ESTEID_PD_REMARK2 14
|
||||
#define SC_ESTEID_PD_REMARK3 15
|
||||
#define SC_ESTEID_PD_REMARK4 16
|
||||
|
||||
/* i love constants */
|
||||
#define SC_ESTEID_KEYREF_FILE_RECLEN 21
|
||||
|
||||
int select_esteid_df(sc_card_t * card);
|
||||
#endif
|
199
src/libopensc/pkcs15-esteid.c
Normal file
199
src/libopensc/pkcs15-esteid.c
Normal file
@ -0,0 +1,199 @@
|
||||
/*
|
||||
* PKCS15 emulation layer for EstEID card.
|
||||
*
|
||||
* Copyright (C) 2004, Martin Paljak <martin@paljak.pri.ee>
|
||||
* Copyright (C) 2004, Bud P. Bruegger <bud@comune.grosseto.it>
|
||||
* Copyright (C) 2004, Antonino Iacono <ant_iacono@tin.it>
|
||||
* Copyright (C) 2003, Olaf Kirch <okir@suse.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 "pkcs15.h"
|
||||
#include "asn1.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "esteid.h"
|
||||
|
||||
static void
|
||||
set_string (char **strp, const char *value)
|
||||
{
|
||||
if (*strp)
|
||||
free (strp);
|
||||
*strp = value ? strdup (value) : NULL;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
select_esteid_df (sc_card_t * card)
|
||||
{
|
||||
int r;
|
||||
sc_path_t tmppath;
|
||||
sc_format_path ("3F00EEEE", &tmppath);
|
||||
tmppath.type = SC_PATH_TYPE_PATH;
|
||||
r = sc_select_file (card, &tmppath, NULL);
|
||||
SC_TEST_RET (card->ctx, r, "esteid select DF failed");
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
sc_pkcs15emu_esteid_init (sc_pkcs15_card_t * p15card)
|
||||
{
|
||||
sc_card_t *card = p15card->card;
|
||||
unsigned char buff[256];
|
||||
int r, i, flags;
|
||||
sc_path_t tmppath;
|
||||
sc_pkcs15_id_t id;
|
||||
|
||||
set_string (&p15card->label, "EstEID isikutunnistus");
|
||||
set_string (&p15card->manufacturer_id, "AS Sertifitseerimiskeskus");
|
||||
|
||||
select_esteid_df (card);
|
||||
|
||||
/* read the serial (document number) */
|
||||
sc_format_path ("5044", &tmppath);
|
||||
tmppath.type = SC_PATH_TYPE_PATH;
|
||||
r = sc_select_file (card, &tmppath, NULL);
|
||||
SC_TEST_RET (card->ctx, r, "select esteid PD failed");
|
||||
r = sc_read_record (card, SC_ESTEID_PD_DOCUMENT_NR, buff, 8,
|
||||
SC_RECORD_BY_REC_NR);
|
||||
SC_TEST_RET (card->ctx, r, "read document number failed");
|
||||
// null-terminate
|
||||
buff[r] = '\0';
|
||||
set_string (&p15card->serial_number, buff);
|
||||
|
||||
p15card->flags =
|
||||
SC_PKCS15_CARD_FLAG_PRN_GENERATION |
|
||||
SC_PKCS15_CARD_FLAG_EID_COMPLIANT | SC_PKCS15_CARD_FLAG_READONLY;
|
||||
|
||||
/* EstEEID uses 1024b RSA */
|
||||
card->algorithm_count = 0;
|
||||
flags = SC_ALGORITHM_RSA_PAD_PKCS1;
|
||||
_sc_card_add_rsa_alg (card, 1024, flags, 0);
|
||||
|
||||
/* add certificates */
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
static char *esteid_cert_names[2] = {
|
||||
"Autentimissertifikaat",
|
||||
"Allkirjasertifikaat"
|
||||
};
|
||||
static char *esteid_cert_paths[2] = {
|
||||
"3f00eeeeaace",
|
||||
"3f00eeeeddce"
|
||||
};
|
||||
static int esteid_cert_ids[2] = {
|
||||
SC_ESTEID_AUTH,
|
||||
SC_ESTEID_SIGN
|
||||
};
|
||||
sc_path_t path;
|
||||
sc_pkcs15_id_t auth_id;
|
||||
|
||||
sc_format_path (esteid_cert_paths[i], &path);
|
||||
path.type = SC_PATH_TYPE_PATH;
|
||||
auth_id.value[0] = esteid_cert_ids[i];
|
||||
auth_id.len = 1;
|
||||
|
||||
r = sc_pkcs15emu_add_cert (p15card,
|
||||
SC_PKCS15_TYPE_CERT_X509, 0,
|
||||
&path, &auth_id, esteid_cert_names[i], 0);
|
||||
}
|
||||
|
||||
/* the file with key pin info (tries left) */
|
||||
sc_format_path ("3f000016", &tmppath);
|
||||
sc_select_file (card, &tmppath, NULL);
|
||||
|
||||
/* add pins */
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
char tries_left;
|
||||
static char *esteid_pin_names[3] = {
|
||||
"PIN1 - Autentiseerimine",
|
||||
"PIN2 - Allkirjastamine",
|
||||
"PUK"
|
||||
};
|
||||
|
||||
static int esteid_pin_min[3] = {
|
||||
4,
|
||||
5,
|
||||
8
|
||||
};
|
||||
|
||||
static int esteid_pin_ref[3] = {
|
||||
1,
|
||||
2,
|
||||
0
|
||||
};
|
||||
|
||||
static int esteid_pin_flags[3] = {
|
||||
0,
|
||||
0,
|
||||
SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN
|
||||
};
|
||||
|
||||
r = sc_read_record (card, i + 1, buff, 128, SC_RECORD_BY_REC_NR);
|
||||
tries_left = buff[5];
|
||||
|
||||
id.len = 1;
|
||||
id.value[0] = i + 1;
|
||||
|
||||
|
||||
|
||||
|
||||
sc_pkcs15emu_add_pin (p15card, &id,
|
||||
esteid_pin_names[i], NULL,
|
||||
esteid_pin_ref[i],
|
||||
SC_PKCS15_PIN_TYPE_ASCII_NUMERIC,
|
||||
esteid_pin_min[i], 12,
|
||||
esteid_pin_flags[i], tries_left, '\0', 0);
|
||||
}
|
||||
|
||||
/* add private keys */
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
static int prkey_pin[2] = { SC_ESTEID_AUTH, SC_ESTEID_SIGN };
|
||||
static int prkey_usage[2] = {
|
||||
SC_PKCS15_PRKEY_USAGE_ENCRYPT |
|
||||
SC_PKCS15_PRKEY_USAGE_DECRYPT |
|
||||
SC_PKCS15_PRKEY_USAGE_SIGN |
|
||||
SC_PKCS15_PRKEY_USAGE_SIGNRECOVER |
|
||||
SC_PKCS15_PRKEY_USAGE_WRAP | SC_PKCS15_PRKEY_USAGE_UNWRAP,
|
||||
|
||||
SC_PKCS15_PRKEY_USAGE_NONREPUDIATION
|
||||
};
|
||||
static char *prkey_name[2] = {
|
||||
"Autentiseerimise v\365ti",
|
||||
"Allkirjastamise v\365ti"
|
||||
};
|
||||
sc_pkcs15_id_t id, auth_id;
|
||||
|
||||
id.value[0] = prkey_pin[i];
|
||||
id.len = 1;
|
||||
auth_id.value[0] = prkey_pin[i];
|
||||
auth_id.len = 1;
|
||||
|
||||
// NULL may be a path.... ?
|
||||
r = sc_pkcs15emu_add_prkey (p15card, &id,
|
||||
prkey_name[i],
|
||||
SC_PKCS15_TYPE_PRKEY_RSA,
|
||||
1024, prkey_usage[i], NULL,
|
||||
i + 1, &auth_id, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
@ -33,6 +33,7 @@ extern int sc_pkcs15emu_openpgp_init(sc_pkcs15_card_t *);
|
||||
extern int sc_pkcs15emu_infocamere_init(sc_pkcs15_card_t *);
|
||||
extern int sc_pkcs15emu_starcert_init(sc_pkcs15_card_t *);
|
||||
extern int sc_pkcs15emu_netkey_init(sc_pkcs15_card_t *);
|
||||
extern int sc_pkcs15emu_esteid_init(sc_pkcs15_card_t *);
|
||||
|
||||
static struct {
|
||||
const char * name;
|
||||
@ -42,6 +43,7 @@ static struct {
|
||||
{ "infocamere", sc_pkcs15emu_infocamere_init },
|
||||
{ "starcert", sc_pkcs15emu_starcert_init },
|
||||
{ "netkey", sc_pkcs15emu_netkey_init },
|
||||
{ "esteid", sc_pkcs15emu_esteid_init },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user