Add myeid driver by Aventra.
git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@3740 c6295689-39f2-0310-b995-f0e70906c6a9
This commit is contained in:
parent
2a6c41be27
commit
2763c08d09
|
@ -40,7 +40,7 @@ libopensc_la_SOURCES = \
|
||||||
card-oberthur.c card-belpic.c card-atrust-acos.c card-entersafe.c \
|
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-incrypto34.c card-piv.c card-muscle.c card-acos5.c \
|
||||||
card-asepcos.c card-akis.c card-gemsafeV1.c card-rutoken.c \
|
card-asepcos.c card-akis.c card-gemsafeV1.c card-rutoken.c \
|
||||||
card-rtecp.c card-westcos.c \
|
card-rtecp.c card-westcos.c card-myeid.c \
|
||||||
\
|
\
|
||||||
pkcs15-openpgp.c pkcs15-infocamere.c pkcs15-starcert.c \
|
pkcs15-openpgp.c pkcs15-infocamere.c pkcs15-starcert.c \
|
||||||
pkcs15-tcos.c pkcs15-esteid.c pkcs15-postecert.c pkcs15-gemsafeGPK.c \
|
pkcs15-tcos.c pkcs15-esteid.c pkcs15-postecert.c pkcs15-gemsafeGPK.c \
|
||||||
|
|
|
@ -29,7 +29,7 @@ OBJECTS = \
|
||||||
card-oberthur.obj card-belpic.obj card-atrust-acos.obj card-entersafe.obj \
|
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-incrypto34.obj card-piv.obj card-muscle.obj card-acos5.obj \
|
||||||
card-asepcos.obj card-akis.obj card-gemsafeV1.obj card-rutoken.obj \
|
card-asepcos.obj card-akis.obj card-gemsafeV1.obj card-rutoken.obj \
|
||||||
card-rtecp.obj \
|
card-rtecp.obj card-myeid.obj \
|
||||||
\
|
\
|
||||||
p15emu-westcos.obj \
|
p15emu-westcos.obj \
|
||||||
pkcs15-openpgp.obj pkcs15-infocamere.obj pkcs15-starcert.obj \
|
pkcs15-openpgp.obj pkcs15-infocamere.obj pkcs15-starcert.obj \
|
||||||
|
|
|
@ -0,0 +1,981 @@
|
||||||
|
/*
|
||||||
|
* card-myeid.c
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008-2009 Aventra Ltd.
|
||||||
|
*
|
||||||
|
* 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 "asn1.h"
|
||||||
|
#include "types.h"
|
||||||
|
#include "cardctl.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
|
static enum LOAD_KEY {
|
||||||
|
LOAD_KEY_MODULUS = 0x80,
|
||||||
|
LOAD_KEY_PUBLIC_EXPONENT = 0x81,
|
||||||
|
LOAD_KEY_PRIME_P = 0x83,
|
||||||
|
LOAD_KEY_PRIME_Q = 0x84,
|
||||||
|
LOAD_KEY_DP1 = 0x85,
|
||||||
|
LOAD_KEY_DQ1 = 0x86,
|
||||||
|
LOAD_KEY_INVQ = 0x87
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct sc_card_operations myeid_ops;
|
||||||
|
static struct sc_card_driver myeid_drv = {
|
||||||
|
"MyEID cards with PKCS#15 applet",
|
||||||
|
"myeid",
|
||||||
|
&myeid_ops
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *myeid_atrs[] = {
|
||||||
|
"3B:F5:18:00:FF:81:31:FE:45:4D:79:45:49:44:65",
|
||||||
|
"3B:F5:18:00:00:81:31:FE:45:4D:79:45:49:44:9A",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static int myeid_finish(struct sc_card *card)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int myeid_match_card(struct sc_card *card)
|
||||||
|
{
|
||||||
|
int i, match = -1;
|
||||||
|
|
||||||
|
for (i = 0; myeid_atrs[i] != NULL; i++)
|
||||||
|
{
|
||||||
|
u8 defatr[SC_MAX_ATR_SIZE];
|
||||||
|
size_t len = sizeof(defatr);
|
||||||
|
const char *atrp = myeid_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;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (match == -1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int myeid_init(struct sc_card *card)
|
||||||
|
{
|
||||||
|
unsigned long flags =0;
|
||||||
|
|
||||||
|
flags = SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_PAD_PKCS1;
|
||||||
|
flags |= SC_ALGORITHM_RSA_HASH_NONE | SC_ALGORITHM_RSA_HASH_SHA1;
|
||||||
|
|
||||||
|
_sc_card_add_rsa_alg(card, 1024, flags, 0);
|
||||||
|
_sc_card_add_rsa_alg(card, 2048, flags, 0);
|
||||||
|
|
||||||
|
/* State that we have an RNG */
|
||||||
|
card->caps |= SC_CARD_CAP_RNG;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct sc_card_operations *iso_ops = NULL;
|
||||||
|
|
||||||
|
static int acl_to_byte(const struct sc_acl_entry *e)
|
||||||
|
{
|
||||||
|
switch (e->method) {
|
||||||
|
case SC_AC_NONE:
|
||||||
|
return 0x00;
|
||||||
|
case SC_AC_CHV:
|
||||||
|
case SC_AC_TERM:
|
||||||
|
case SC_AC_AUT:
|
||||||
|
if (e->key_ref == SC_AC_KEY_REF_NONE)
|
||||||
|
return -1;
|
||||||
|
if (e->key_ref < 1 || e->key_ref > 14)
|
||||||
|
return -1;
|
||||||
|
return e->key_ref;
|
||||||
|
case SC_AC_NEVER:
|
||||||
|
return 0x0F;
|
||||||
|
}
|
||||||
|
return 0x00;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add_acl_entry(struct sc_file *file, int op, u8 byte)
|
||||||
|
{
|
||||||
|
unsigned int method, key_ref = SC_AC_KEY_REF_NONE;
|
||||||
|
|
||||||
|
switch (byte)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
method = SC_AC_NONE;
|
||||||
|
break;
|
||||||
|
case 15:
|
||||||
|
method = SC_AC_NEVER;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
method = SC_AC_CHV;
|
||||||
|
key_ref = byte;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sc_file_add_acl_entry(file, op, method, key_ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parse_sec_attr(struct sc_file *file, const u8 *buf, size_t len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
const int df_ops[4] =
|
||||||
|
{ SC_AC_OP_CREATE, SC_AC_OP_CREATE, SC_AC_OP_DELETE, -1 };
|
||||||
|
const int ef_ops[4] =
|
||||||
|
{ SC_AC_OP_READ, SC_AC_OP_UPDATE, SC_AC_OP_DELETE, -1 };
|
||||||
|
const int key_ops[4] =
|
||||||
|
{ SC_AC_OP_CRYPTO, SC_AC_OP_UPDATE, SC_AC_OP_DELETE, SC_AC_OP_CRYPTO };
|
||||||
|
|
||||||
|
const int *ops;
|
||||||
|
|
||||||
|
if (len < 2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (file->type) {
|
||||||
|
case SC_FILE_TYPE_WORKING_EF:
|
||||||
|
ops = ef_ops;
|
||||||
|
break;
|
||||||
|
case SC_FILE_TYPE_INTERNAL_EF:
|
||||||
|
ops = key_ops;
|
||||||
|
break;
|
||||||
|
case SC_FILE_TYPE_DF:
|
||||||
|
ops = df_ops;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ops = key_ops;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
if (ops[i] == -1)
|
||||||
|
continue;
|
||||||
|
if ((i & 1) == 0)
|
||||||
|
add_acl_entry(file, ops[i], (u8)(buf[i / 2] >> 4));
|
||||||
|
else
|
||||||
|
add_acl_entry(file, ops[i], (u8)(buf[i / 2] & 0x0F));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int myeid_select_file(struct sc_card *card, const struct sc_path *in_path,
|
||||||
|
struct sc_file **file)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = iso_ops->select_file(card, in_path, file);
|
||||||
|
if (r)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if (file != NULL) {
|
||||||
|
parse_sec_attr(*file, (*file)->sec_attr, (*file)->sec_attr_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int myeid_read_binary(struct sc_card *card, unsigned int idx,
|
||||||
|
u8 * buf, size_t count, unsigned long flags)
|
||||||
|
{
|
||||||
|
return iso_ops->read_binary(card, idx, buf, count, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int myeid_list_files(struct sc_card *card, u8 *buf, size_t buflen)
|
||||||
|
{
|
||||||
|
struct sc_apdu apdu;
|
||||||
|
int r,i;
|
||||||
|
|
||||||
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xCA, 0x01, 0xA1);
|
||||||
|
apdu.resp = buf;
|
||||||
|
apdu.resplen = buflen;
|
||||||
|
apdu.le = buflen > 256 ? 256 : buflen;
|
||||||
|
|
||||||
|
r = sc_transmit_apdu(card, &apdu);
|
||||||
|
|
||||||
|
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||||
|
if (apdu.resplen == 0)
|
||||||
|
return sc_check_sw(card, apdu.sw1, apdu.sw2);
|
||||||
|
return apdu.resplen;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int myeid_process_fci(struct sc_card *card, struct sc_file *file,
|
||||||
|
const u8 *buf, size_t buflen)
|
||||||
|
{
|
||||||
|
size_t taglen = 0;
|
||||||
|
const u8 *tag = NULL;
|
||||||
|
int r ;
|
||||||
|
|
||||||
|
r = iso_ops->process_fci(card, file, buf, buflen);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if(file->type == SC_FILE_EF_UNKNOWN)
|
||||||
|
{
|
||||||
|
tag = sc_asn1_find_tag(NULL, buf, buflen, 0x82, &taglen);
|
||||||
|
if (tag != NULL && taglen > 0 && *tag == 17)
|
||||||
|
{
|
||||||
|
file->type = SC_FILE_TYPE_INTERNAL_EF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int encode_file_structure(sc_card_t *card, const sc_file_t *file,
|
||||||
|
u8 *out, size_t *outlen)
|
||||||
|
{
|
||||||
|
u8 buf[40];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* PrivateKey
|
||||||
|
* 0E0000019 6217 81020400 820111 83024B01 8603000000 85028000 8A0100 RESULT 6984
|
||||||
|
* 6217 81020400 820111 83024B01 8603000000 85021000 8A0100 */
|
||||||
|
memset(buf,0x0,sizeof(buf));
|
||||||
|
|
||||||
|
buf[0] = 0x62;
|
||||||
|
buf[1] = 0x17;
|
||||||
|
/* File size */
|
||||||
|
buf[2] = (SC_FILE_TYPE_WORKING_EF == file->type ?0x80:0x81);
|
||||||
|
buf[3] = 0x02;
|
||||||
|
buf[4] = (file->size >> 8) & 0xFF;
|
||||||
|
buf[5] = file->size & 0xFF;
|
||||||
|
|
||||||
|
/* File Description tag */
|
||||||
|
buf[6] = 0x82;
|
||||||
|
buf[7] = 0x01;
|
||||||
|
buf[8] = 0x01;
|
||||||
|
|
||||||
|
/* File Identifier tag */
|
||||||
|
buf[9] = 0x83;
|
||||||
|
buf[10] = 0x02;
|
||||||
|
buf[11] = (file->id >> 8) & 0xFF;
|
||||||
|
buf[12] = file->id & 0xFF;
|
||||||
|
|
||||||
|
/* Security Attributes Tag */
|
||||||
|
buf[13] = 0x86;
|
||||||
|
buf[14] = 0x03;
|
||||||
|
buf[15] = 0x0;
|
||||||
|
buf[16] = 0x0;
|
||||||
|
buf[17] = 0x0;
|
||||||
|
|
||||||
|
if (file->sec_attr_len == 3 && file->sec_attr)
|
||||||
|
{
|
||||||
|
buf[15] = file->sec_attr[0];
|
||||||
|
buf[16] = file->sec_attr[1];
|
||||||
|
buf[17] = file->sec_attr[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Proprietary Information */
|
||||||
|
buf[18] = 0x85;
|
||||||
|
buf[19] = 0x02;
|
||||||
|
/* AC right to clear default 0 */
|
||||||
|
buf[20] = (SC_FILE_TYPE_INTERNAL_EF == file->type ?0x0:0x80);
|
||||||
|
buf[21] = 0x0;
|
||||||
|
|
||||||
|
/* Life Cycle Status tag */
|
||||||
|
buf[22] = 0x8A;
|
||||||
|
buf[23] = 0x01;
|
||||||
|
buf[24] = 0x0; /* RFU */
|
||||||
|
|
||||||
|
switch (file->type)
|
||||||
|
{
|
||||||
|
case SC_FILE_TYPE_WORKING_EF:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SC_FILE_TYPE_INTERNAL_EF:
|
||||||
|
buf[8] = 0x11;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SC_FILE_TYPE_DF:
|
||||||
|
buf[8] = 0x38;
|
||||||
|
if(file->namelen > 0 && file->namelen <= 16)
|
||||||
|
{
|
||||||
|
buf[25] = 0x84;
|
||||||
|
buf[26] = (u8)file->namelen;
|
||||||
|
for(i=0;i < (int)file->namelen;i++)
|
||||||
|
{
|
||||||
|
buf[i + 26] = file->name[i];
|
||||||
|
}
|
||||||
|
buf[1] = 0x19 + file->namelen + 2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sc_error(card->ctx, "Unknown file type\n");
|
||||||
|
return SC_ERROR_INVALID_ARGUMENTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
*outlen = buf[1]+2;
|
||||||
|
memcpy(out, buf, *outlen);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int myeid_create_file(struct sc_card *card, struct sc_file *file)
|
||||||
|
{
|
||||||
|
sc_apdu_t apdu;
|
||||||
|
u8 sbuf[32];
|
||||||
|
size_t buflen;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = encode_file_structure(card, file, sbuf, &buflen);
|
||||||
|
if (r)
|
||||||
|
return r;
|
||||||
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x00, 0x00);
|
||||||
|
apdu.data = sbuf;
|
||||||
|
apdu.datalen = buflen;
|
||||||
|
apdu.lc = buflen;
|
||||||
|
|
||||||
|
r = sc_transmit_apdu(card, &apdu);
|
||||||
|
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||||
|
if (apdu.sw1 == 0x6A && apdu.sw2 == 0x89)
|
||||||
|
return SC_ERROR_FILE_ALREADY_EXISTS;
|
||||||
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
||||||
|
SC_TEST_RET(card->ctx, r, "Card returned error");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* no record oriented file services */
|
||||||
|
static int myeid_read_record_unsupp(struct sc_card *card, unsigned int rec_nr,
|
||||||
|
u8 *buf, size_t count, unsigned long flags)
|
||||||
|
{
|
||||||
|
return SC_ERROR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int myeid_wrupd_record_unsupp(struct sc_card *card, unsigned int rec_nr,
|
||||||
|
const u8 *buf, size_t count, unsigned long flags)
|
||||||
|
{
|
||||||
|
return SC_ERROR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int myeid_append_record_unsupp(struct sc_card *card, const u8 *buf,
|
||||||
|
size_t count, unsigned long flags)
|
||||||
|
{
|
||||||
|
return SC_ERROR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int myeid_write_binary(struct sc_card *card, unsigned int idx,
|
||||||
|
const u8 *buf, size_t count, unsigned long flags)
|
||||||
|
{
|
||||||
|
return iso_ops->write_binary(card, idx, buf, count, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int myeid_update_binary(struct sc_card *card, unsigned int idx,
|
||||||
|
const u8 *buf, size_t count, unsigned long flags)
|
||||||
|
{
|
||||||
|
return iso_ops->update_binary(card, idx, buf, count, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int myeid_delete_file(struct sc_card *card, const struct sc_path *path)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
struct sc_apdu apdu;
|
||||||
|
|
||||||
|
SC_FUNC_CALLED(card->ctx, 1);
|
||||||
|
if (path->type != SC_PATH_TYPE_FILE_ID && path->len != 2)
|
||||||
|
{
|
||||||
|
sc_error(card->ctx, "File type has to be SC_PATH_TYPE_FILE_ID\n");
|
||||||
|
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INVALID_ARGUMENTS);
|
||||||
|
}
|
||||||
|
r = sc_select_file(card, path, NULL);
|
||||||
|
SC_TEST_RET(card->ctx, r, "Unable to select file to be deleted");
|
||||||
|
|
||||||
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xE4, 0x00, 0x00);
|
||||||
|
apdu.cla = 0xA0;
|
||||||
|
|
||||||
|
r = sc_transmit_apdu(card, &apdu);
|
||||||
|
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||||
|
|
||||||
|
return sc_check_sw(card, apdu.sw1, apdu.sw2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int myeid_set_security_env2(sc_card_t *card, const sc_security_env_t *env,
|
||||||
|
int se_num)
|
||||||
|
{
|
||||||
|
sc_apdu_t apdu;
|
||||||
|
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
|
||||||
|
u8 *p;
|
||||||
|
int r, locked = 0;
|
||||||
|
|
||||||
|
assert(card != NULL && env != NULL);
|
||||||
|
|
||||||
|
|
||||||
|
if (env->flags & SC_SEC_ENV_KEY_REF_ASYMMETRIC)
|
||||||
|
{
|
||||||
|
sc_error(card->ctx, "asymmetric keyref not supported.\n");
|
||||||
|
return SC_ERROR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
if (se_num > 0)
|
||||||
|
{
|
||||||
|
sc_error(card->ctx, "restore security environment not supported.\n");
|
||||||
|
return SC_ERROR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0, 0);
|
||||||
|
switch (env->operation)
|
||||||
|
{
|
||||||
|
case SC_SEC_OPERATION_DECIPHER:
|
||||||
|
apdu.p1 = 0x41;
|
||||||
|
apdu.p2 = 0xB8;
|
||||||
|
break;
|
||||||
|
case SC_SEC_OPERATION_SIGN:
|
||||||
|
apdu.p1 = 0x41;
|
||||||
|
apdu.p2 = 0xB6;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return SC_ERROR_INVALID_ARGUMENTS;
|
||||||
|
}
|
||||||
|
apdu.le = 0;
|
||||||
|
p = sbuf;
|
||||||
|
if (env->flags & SC_SEC_ENV_ALG_REF_PRESENT)
|
||||||
|
{
|
||||||
|
*p++ = 0x80; /* algorithm reference */
|
||||||
|
*p++ = 0x01;
|
||||||
|
*p++ = env->algorithm_ref & 0xFF;
|
||||||
|
}
|
||||||
|
if (env->flags & SC_SEC_ENV_FILE_REF_PRESENT)
|
||||||
|
{
|
||||||
|
*p++ = 0x81;
|
||||||
|
*p++ = 2;
|
||||||
|
memcpy(p, env->file_ref.value, 2);
|
||||||
|
p += 2;
|
||||||
|
}
|
||||||
|
if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT)
|
||||||
|
{
|
||||||
|
*p++ = 0x84;
|
||||||
|
*p++ = 1;
|
||||||
|
*p++ = 0;
|
||||||
|
}
|
||||||
|
r = p - sbuf;
|
||||||
|
apdu.lc = r;
|
||||||
|
apdu.datalen = r;
|
||||||
|
apdu.data = sbuf;
|
||||||
|
apdu.resplen = 0;
|
||||||
|
if (se_num > 0) {
|
||||||
|
r = sc_lock(card);
|
||||||
|
SC_TEST_RET(card->ctx, r, "sc_lock() failed");
|
||||||
|
locked = 1;
|
||||||
|
}
|
||||||
|
if (apdu.datalen != 0)
|
||||||
|
{
|
||||||
|
r = sc_transmit_apdu(card, &apdu);
|
||||||
|
if (r)
|
||||||
|
{
|
||||||
|
sc_perror(card->ctx, r, "APDU transmit failed");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
||||||
|
if (r)
|
||||||
|
{
|
||||||
|
sc_perror(card->ctx, r, "Card returned error");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (se_num <= 0)
|
||||||
|
return 0;
|
||||||
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0xF2, se_num);
|
||||||
|
r = sc_transmit_apdu(card, &apdu);
|
||||||
|
sc_unlock(card);
|
||||||
|
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||||
|
return sc_check_sw(card, apdu.sw1, apdu.sw2);
|
||||||
|
err:
|
||||||
|
if (locked)
|
||||||
|
sc_unlock(card);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int myeid_set_security_env(struct sc_card *card,
|
||||||
|
const struct sc_security_env *env, int se_num)
|
||||||
|
{
|
||||||
|
if (env->flags & SC_SEC_ENV_ALG_PRESENT)
|
||||||
|
{
|
||||||
|
sc_security_env_t tmp;
|
||||||
|
|
||||||
|
tmp = *env;
|
||||||
|
tmp.flags &= ~SC_SEC_ENV_ALG_PRESENT;
|
||||||
|
tmp.flags |= SC_SEC_ENV_ALG_REF_PRESENT;
|
||||||
|
if (tmp.algorithm != SC_ALGORITHM_RSA)
|
||||||
|
{
|
||||||
|
sc_error(card->ctx, "Only RSA algorithm supported.\n");
|
||||||
|
return SC_ERROR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp.algorithm_ref = 0x00;
|
||||||
|
/* potential FIXME: return an error, if an unsupported
|
||||||
|
* pad or hash was requested, although this shouldn't happen */
|
||||||
|
if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1)
|
||||||
|
tmp.algorithm_ref = 0x02;
|
||||||
|
if (tmp.algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1)
|
||||||
|
tmp.algorithm_ref |= 0x10;
|
||||||
|
return myeid_set_security_env2(card, &tmp, se_num);
|
||||||
|
}
|
||||||
|
return myeid_set_security_env2(card, env, se_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int myeid_compute_signature(struct sc_card *card, const u8 * data,
|
||||||
|
size_t datalen, u8 * out, size_t outlen)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
if (datalen > 256)
|
||||||
|
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_4_SHORT, 0x2A, 0x9E, 0x9A);
|
||||||
|
apdu.resp = rbuf;
|
||||||
|
apdu.resplen = sizeof(rbuf);
|
||||||
|
apdu.le = 256;
|
||||||
|
if (datalen == 256)
|
||||||
|
{
|
||||||
|
apdu.p2 = data[0];
|
||||||
|
memcpy(sbuf, data+1, datalen-1);
|
||||||
|
apdu.lc = datalen - 1;
|
||||||
|
apdu.datalen = datalen - 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy(sbuf, data, datalen);
|
||||||
|
apdu.lc = datalen;
|
||||||
|
apdu.datalen = datalen;
|
||||||
|
}
|
||||||
|
|
||||||
|
apdu.data = sbuf;
|
||||||
|
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;
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int myeid_decipher(struct sc_card *card, const u8 * crgram,
|
||||||
|
size_t crgram_len, u8 * out, size_t outlen)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
struct sc_apdu apdu;
|
||||||
|
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
|
||||||
|
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
|
||||||
|
|
||||||
|
assert(card != NULL && crgram != NULL && out != NULL);
|
||||||
|
SC_FUNC_CALLED(card->ctx, 2);
|
||||||
|
if (crgram_len > 256)
|
||||||
|
SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_INVALID_ARGUMENTS);
|
||||||
|
|
||||||
|
/* INS: 0x2A PERFORM SECURITY OPERATION
|
||||||
|
* P1: 0x80 Resp: Plain value
|
||||||
|
* P2: 0x86 Cmd: Padding indicator byte followed by cryptogram */
|
||||||
|
sc_format_apdu(card, &apdu,
|
||||||
|
(crgram_len < 256) ? SC_APDU_CASE_4_SHORT : SC_APDU_CASE_3_SHORT,
|
||||||
|
0x2A, 0x80, 0x86);
|
||||||
|
|
||||||
|
apdu.resp = rbuf;
|
||||||
|
apdu.resplen = sizeof(rbuf);
|
||||||
|
apdu.le = crgram_len;
|
||||||
|
apdu.sensitive = 1;
|
||||||
|
|
||||||
|
if (crgram_len == 256)
|
||||||
|
{
apdu.le = 0;
|
||||||
|
/* padding indicator byte, 0x81 = first half of 2048 bit cryptogram */
|
||||||
|
sbuf[0] = 0x81;
|
||||||
|
memcpy(sbuf + 1, crgram, crgram_len / 2);
|
||||||
|
apdu.lc = crgram_len / 2 + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sbuf[0] = 0; /* padding indicator byte, 0x00 = No further indication */
|
||||||
|
memcpy(sbuf + 1, crgram, crgram_len);
|
||||||
|
apdu.lc = crgram_len + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
apdu.datalen = apdu.lc;
apdu.data = sbuf;
|
||||||
|
|
||||||
|
r = sc_transmit_apdu(card, &apdu);
|
||||||
|
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||||
|
if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00)
|
||||||
|
{
|
||||||
|
if (crgram_len == 256)
|
||||||
|
{
|
||||||
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT,
|
||||||
|
0x2A, 0x80, 0x86);
|
||||||
|
apdu.resp = rbuf;
|
||||||
|
apdu.resplen = sizeof(rbuf);
|
||||||
|
apdu.le = crgram_len;
|
||||||
|
apdu.sensitive = 1;
|
||||||
|
/* padding indicator byte,
|
||||||
|
* 0x82 = Second half of 2048 bit cryptogram */
|
||||||
|
sbuf[0] = 0x82;
|
||||||
|
memcpy(sbuf + 1, crgram + crgram_len / 2, crgram_len / 2);
|
||||||
|
apdu.lc = crgram_len / 2 + 1;
|
||||||
|
apdu.datalen = apdu.lc;
|
||||||
|
apdu.data = sbuf;
|
||||||
|
|
||||||
|
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;
|
||||||
|
memcpy(out, apdu.resp, len);
|
||||||
|
SC_FUNC_RETURN(card->ctx, 2, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int len = apdu.resplen > outlen ? outlen : apdu.resplen;
|
||||||
|
|
||||||
|
memcpy(out, apdu.resp, len);
|
||||||
|
SC_FUNC_RETURN(card->ctx, 2, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SC_FUNC_RETURN(card->ctx, 2, sc_check_sw(card, apdu.sw1, apdu.sw2));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write internal data, e.g. add default pin-records to pin */
|
||||||
|
static int myeid_putdata(struct sc_card *card, struct sc_cardctl_myeid_data_obj* data_obj)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
struct sc_apdu apdu;
|
||||||
|
|
||||||
|
SC_FUNC_CALLED(card->ctx, 1);
|
||||||
|
|
||||||
|
memset(&apdu, 0, sizeof(apdu));
|
||||||
|
apdu.cse = SC_APDU_CASE_3_SHORT;
|
||||||
|
apdu.cla = 0x00;
|
||||||
|
apdu.ins = 0xDA;
|
||||||
|
apdu.p1 = data_obj->P1;
|
||||||
|
apdu.p2 = data_obj->P2;
|
||||||
|
apdu.lc = data_obj->DataLen;
|
||||||
|
apdu.datalen = data_obj->DataLen;
|
||||||
|
apdu.data = data_obj->Data;
|
||||||
|
|
||||||
|
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, "PUT_DATA returned error");
|
||||||
|
|
||||||
|
SC_FUNC_RETURN(card->ctx, 1, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read internal data, e.g. get RSA public key */
|
||||||
|
static int myeid_getdata(struct sc_card *card, struct sc_cardctl_myeid_data_obj* data_obj)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
struct sc_apdu apdu;
|
||||||
|
|
||||||
|
SC_FUNC_CALLED(card->ctx, 1);
|
||||||
|
|
||||||
|
memset(&apdu, 0, sizeof(apdu));
|
||||||
|
apdu.cse = SC_APDU_CASE_2_SHORT;
|
||||||
|
apdu.cla = 0x00;
|
||||||
|
apdu.ins = 0xCA; /* GET DATA */
|
||||||
|
apdu.p1 = data_obj->P1;
|
||||||
|
apdu.p2 = data_obj->P2;
|
||||||
|
apdu.lc = 0;
|
||||||
|
apdu.datalen = 0;
|
||||||
|
apdu.data = data_obj->Data;
|
||||||
|
|
||||||
|
apdu.le = 256;
|
||||||
|
apdu.resp = data_obj->Data;
|
||||||
|
apdu.resplen = data_obj->DataLen;
|
||||||
|
|
||||||
|
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, "GET_DATA returned error");
|
||||||
|
|
||||||
|
if (apdu.resplen > data_obj->DataLen)
|
||||||
|
r = SC_ERROR_WRONG_LENGTH;
|
||||||
|
else
|
||||||
|
data_obj->DataLen = apdu.resplen;
|
||||||
|
|
||||||
|
SC_FUNC_RETURN(card->ctx, 1, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int myeid_loadkey(sc_card_t *card, int mode, u8* value, int value_len)
|
||||||
|
{
|
||||||
|
sc_apdu_t apdu;
|
||||||
|
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
|
||||||
|
int r, len;
|
||||||
|
|
||||||
|
len = 0;
|
||||||
|
if(value_len == 0 || value == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if(value != NULL &&
|
||||||
|
value[0] != 0x0 &&
|
||||||
|
mode != LOAD_KEY_PUBLIC_EXPONENT)
|
||||||
|
sbuf[len++] = 0x0;
|
||||||
|
|
||||||
|
SC_FUNC_CALLED(card->ctx, 1);
|
||||||
|
|
||||||
|
if(mode == LOAD_KEY_MODULUS && value_len >= 256)
|
||||||
|
{
|
||||||
|
r=0;
|
||||||
|
if((value_len % 2) > 0 && value[0] == 0x00)
|
||||||
|
{
|
||||||
|
value_len--;
|
||||||
|
memmove(value, value + 1, value_len);
|
||||||
|
}
|
||||||
|
mode = 0x88;
|
||||||
|
len = 128;
|
||||||
|
memcpy(sbuf,value, 128);
|
||||||
|
|
||||||
|
memset(&apdu, 0, sizeof(apdu));
|
||||||
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xDA, 0x01, mode);
|
||||||
|
|
||||||
|
apdu.cla = 0x00;
|
||||||
|
apdu.data = sbuf;
|
||||||
|
apdu.datalen = len;
|
||||||
|
apdu.lc = len;
|
||||||
|
|
||||||
|
r = sc_transmit_apdu(card, &apdu);
|
||||||
|
if(r < 0)
|
||||||
|
return r;
|
||||||
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
||||||
|
if(r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
mode = 0x89;
|
||||||
|
len = value_len - 128;
|
||||||
|
memset(&sbuf, 0, SC_MAX_APDU_BUFFER_SIZE);
|
||||||
|
memcpy(sbuf,value + 128, value_len - 128);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy(sbuf + len, value, value_len);
|
||||||
|
len += value_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&apdu, 0, sizeof(apdu));
|
||||||
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xDA, 0x01, mode);
|
||||||
|
|
||||||
|
apdu.cla = 0x00;
|
||||||
|
apdu.data = sbuf;
|
||||||
|
apdu.datalen = len;
|
||||||
|
apdu.lc = len;
|
||||||
|
|
||||||
|
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_FUNC_RETURN(card->ctx, 1, r);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Generate or store a key */
|
||||||
|
static int myeid_generate_store_key(struct sc_card *card,
|
||||||
|
struct sc_cardctl_myeid_gen_store_key_info *data)
|
||||||
|
{
|
||||||
|
struct sc_apdu apdu;
|
||||||
|
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
|
||||||
|
int r,len;
|
||||||
|
|
||||||
|
/* Setup key-generation paramters */
|
||||||
|
if (data->op_type == OP_TYPE_GENERATE)
|
||||||
|
{
|
||||||
|
len = 0;
|
||||||
|
|
||||||
|
sbuf[len++] = 0x30;
|
||||||
|
sbuf[len++] = 0x05;
|
||||||
|
sbuf[len++] = 0x81;
|
||||||
|
sbuf[len++] = data->pubexp_len;
|
||||||
|
|
||||||
|
memcpy(sbuf + len, data->pubexp, data->pubexp_len);
|
||||||
|
len += data->pubexp_len;
|
||||||
|
|
||||||
|
memset(&apdu, 0, sizeof(apdu));
|
||||||
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x46, 0x00, 0x00);
|
||||||
|
apdu.cla = 0x00;
|
||||||
|
apdu.data = sbuf;
|
||||||
|
apdu.datalen = len;
|
||||||
|
apdu.lc = len;
|
||||||
|
|
||||||
|
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, "GENERATE_KEY returned error");
|
||||||
|
|
||||||
|
SC_FUNC_RETURN(card->ctx, 1, r);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if((r=myeid_loadkey(card, LOAD_KEY_PRIME_P,
|
||||||
|
data->primep, data->primep_len)) >= 0 &&
|
||||||
|
(r=myeid_loadkey(card, LOAD_KEY_PRIME_Q,
|
||||||
|
data->primeq, data->primeq_len)) >= 0 &&
|
||||||
|
(r=myeid_loadkey(card, LOAD_KEY_DP1,
|
||||||
|
data->dp1, data->dp1_len)) >= 0 &&
|
||||||
|
(r=myeid_loadkey(card, LOAD_KEY_DQ1,
|
||||||
|
data->dq1, data->dq1_len)) >= 0 &&
|
||||||
|
(r=myeid_loadkey(card, LOAD_KEY_INVQ,
|
||||||
|
data->invq, data->invq_len)) >= 0 &&
|
||||||
|
(r=myeid_loadkey(card, LOAD_KEY_MODULUS,
|
||||||
|
data->mod, data->mod_len)) >= 0 &&
|
||||||
|
(r=myeid_loadkey(card, LOAD_KEY_PUBLIC_EXPONENT,
|
||||||
|
data->pubexp, data->pubexp_len)) >= 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int myeid_activate_card(struct sc_card *card)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
u8 sbuf[] ="\xA0\x00\x00\x00\x63\x50\x4B\x43\x53\x2D\x31\x35";
|
||||||
|
sc_apdu_t apdu;
|
||||||
|
|
||||||
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x44, 0x04, 0x00);
|
||||||
|
apdu.cla = 0x00;
|
||||||
|
apdu.data = sbuf;
|
||||||
|
apdu.datalen = 0x0C;
|
||||||
|
apdu.lc = 0x0C;
|
||||||
|
|
||||||
|
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, "ACTIVATE_APPLET returned error");
|
||||||
|
|
||||||
|
SC_FUNC_RETURN(card->ctx, 1, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int myeid_get_serialnr(sc_card_t *card, sc_serial_number_t *serial)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
sc_apdu_t apdu;
|
||||||
|
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
|
||||||
|
|
||||||
|
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0xA0);
|
||||||
|
apdu.resp = rbuf;
|
||||||
|
apdu.resplen = sizeof(rbuf);
|
||||||
|
apdu.le = 256;
|
||||||
|
|
||||||
|
r = sc_transmit_apdu(card, &apdu);
|
||||||
|
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||||
|
|
||||||
|
if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00)
|
||||||
|
return SC_ERROR_INTERNAL;
|
||||||
|
|
||||||
|
if (apdu.resplen != 20)
|
||||||
|
{
|
||||||
|
sc_debug(card->ctx, "unexpected response to GET DATA serial number\n");
|
||||||
|
return SC_ERROR_INTERNAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* cache serial number */
|
||||||
|
memcpy(card->serialnr.value, &rbuf[10], 8);
|
||||||
|
card->serialnr.len = 8;
|
||||||
|
|
||||||
|
/* copy and return serial number */
|
||||||
|
memcpy(serial, &card->serialnr, sizeof(*serial));
|
||||||
|
|
||||||
|
return SC_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int myeid_card_ctl(struct sc_card *card, unsigned long cmd, void *ptr)
|
||||||
|
{
|
||||||
|
switch(cmd) {
|
||||||
|
case SC_CARDCTL_MYEID_PUTDATA:
|
||||||
|
return myeid_putdata(card,
|
||||||
|
(struct sc_cardctl_myeid_data_obj*) ptr);
|
||||||
|
case SC_CARDCTL_MYEID_GETDATA:
|
||||||
|
return myeid_getdata(card,
|
||||||
|
(struct sc_cardctl_myeid_data_obj*) ptr);
|
||||||
|
case SC_CARDCTL_MYEID_GENERATE_KEY:
|
||||||
|
return myeid_generate_store_key(card,
|
||||||
|
(struct sc_cardctl_myeid_gen_store_key_info *) ptr);
|
||||||
|
case SC_CARDCTL_MYEID_ACTIVATE_CARD:
|
||||||
|
return myeid_activate_card(card);
|
||||||
|
case SC_CARDCTL_GET_SERIALNR:
|
||||||
|
return myeid_get_serialnr(card, (sc_serial_number_t *)ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SC_ERROR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* "The PINs are "global" in a PKCS#15 sense, meaning that they remain valid
|
||||||
|
* until card reset! Selecting another applet doesn't invalidate the PINs,
|
||||||
|
* you need to reset the card." - javacard@zurich.ibm.com, when asked about
|
||||||
|
* how to invalidate logged in pins.
|
||||||
|
*/
|
||||||
|
static int myeid_logout(struct sc_card *card)
|
||||||
|
{
|
||||||
|
return 0; /* Can't */
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct sc_card_driver * sc_get_driver(void)
|
||||||
|
{
|
||||||
|
struct sc_card_driver *iso_drv = sc_get_iso7816_driver();
|
||||||
|
|
||||||
|
myeid_ops = *iso_drv->ops;
|
||||||
|
myeid_ops.match_card = myeid_match_card;
|
||||||
|
myeid_ops.init = myeid_init;
|
||||||
|
myeid_ops.finish = myeid_finish;
|
||||||
|
if (iso_ops == NULL)
|
||||||
|
iso_ops = iso_drv->ops;
|
||||||
|
myeid_ops.read_binary = myeid_read_binary;
|
||||||
|
myeid_ops.read_record = myeid_read_record_unsupp;
|
||||||
|
myeid_ops.write_record = myeid_wrupd_record_unsupp;
|
||||||
|
myeid_ops.append_record = myeid_append_record_unsupp;
|
||||||
|
myeid_ops.update_record = myeid_wrupd_record_unsupp;
|
||||||
|
myeid_ops.write_binary = myeid_write_binary;
|
||||||
|
myeid_ops.update_binary = myeid_update_binary;
|
||||||
|
myeid_ops.select_file = myeid_select_file;
|
||||||
|
myeid_ops.create_file = myeid_create_file;
|
||||||
|
myeid_ops.delete_file = myeid_delete_file;
|
||||||
|
myeid_ops.list_files = myeid_list_files;
|
||||||
|
myeid_ops.set_security_env = myeid_set_security_env;
|
||||||
|
myeid_ops.compute_signature = myeid_compute_signature;
|
||||||
|
myeid_ops.decipher = myeid_decipher;
|
||||||
|
myeid_ops.logout = myeid_logout;
|
||||||
|
myeid_ops.process_fci = myeid_process_fci;
|
||||||
|
myeid_ops.card_ctl = myeid_card_ctl;
|
||||||
|
|
||||||
|
return &myeid_drv;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sc_card_driver * sc_get_myeid_driver(void)
|
||||||
|
{
|
||||||
|
return sc_get_driver();
|
||||||
|
}
|
||||||
|
|
|
@ -188,6 +188,14 @@ enum {
|
||||||
SC_CARDCTL_WESTCOS_SET_DEFAULT_KEY,
|
SC_CARDCTL_WESTCOS_SET_DEFAULT_KEY,
|
||||||
SC_CARDCTL_WESTCOS_LOAD_DATA,
|
SC_CARDCTL_WESTCOS_LOAD_DATA,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MyEID specific calls
|
||||||
|
*/
|
||||||
|
SC_CARDCTL_MYEID_BASE = _CTL_PREFIX('M', 'Y', 'E'),
|
||||||
|
SC_CARDCTL_MYEID_PUTDATA,
|
||||||
|
SC_CARDCTL_MYEID_GETDATA,
|
||||||
|
SC_CARDCTL_MYEID_GENERATE_KEY,
|
||||||
|
SC_CARDCTL_MYEID_ACTIVATE_CARD,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
@ -690,6 +698,35 @@ typedef struct sc_rtecp_genkey_data {
|
||||||
size_t modulus_len;
|
size_t modulus_len;
|
||||||
} sc_rtecp_genkey_data_t;
|
} sc_rtecp_genkey_data_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MyEID stuff
|
||||||
|
*/
|
||||||
|
struct sc_cardctl_myeid_data_obj {
|
||||||
|
int P1;
|
||||||
|
int P2;
|
||||||
|
u8 * Data;
|
||||||
|
size_t DataLen;
|
||||||
|
int LengthMax;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sc_cardctl_myeid_gen_store_key_info {
|
||||||
|
int op_type;
|
||||||
|
unsigned int mod_len;
|
||||||
|
unsigned char *mod;
|
||||||
|
unsigned int pubexp_len;
|
||||||
|
unsigned char *pubexp;
|
||||||
|
unsigned int primep_len;
|
||||||
|
unsigned char *primep;
|
||||||
|
unsigned int primeq_len;
|
||||||
|
unsigned char *primeq;
|
||||||
|
unsigned int dp1_len;
|
||||||
|
unsigned char *dp1;
|
||||||
|
unsigned int dq1_len;
|
||||||
|
unsigned char *dq1;
|
||||||
|
unsigned int invq_len;
|
||||||
|
unsigned char *invq;
|
||||||
|
};
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -146,6 +146,10 @@ enum {
|
||||||
SC_CARD_TYPE_ENTERSAFE_BASE = 19000,
|
SC_CARD_TYPE_ENTERSAFE_BASE = 19000,
|
||||||
SC_CARD_TYPE_ENTERSAFE_3K,
|
SC_CARD_TYPE_ENTERSAFE_3K,
|
||||||
SC_CARD_TYPE_ENTERSAFE_FTCOS_PK_01C,
|
SC_CARD_TYPE_ENTERSAFE_FTCOS_PK_01C,
|
||||||
|
|
||||||
|
/* MyEID cards */
|
||||||
|
SC_CARD_TYPE_MYEID_BASE = 20000,
|
||||||
|
SC_CARD_TYPE_MYEID_GENERIC,
|
||||||
};
|
};
|
||||||
|
|
||||||
extern sc_card_driver_t *sc_get_default_driver(void);
|
extern sc_card_driver_t *sc_get_default_driver(void);
|
||||||
|
@ -175,6 +179,7 @@ extern sc_card_driver_t *sc_get_entersafe_driver(void);
|
||||||
extern sc_card_driver_t *sc_get_rutoken_driver(void);
|
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_rtecp_driver(void);
|
||||||
extern sc_card_driver_t *sc_get_westcos_driver(void);
|
extern sc_card_driver_t *sc_get_westcos_driver(void);
|
||||||
|
extern sc_card_driver_t *sc_get_myeid_driver(void);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,6 +85,7 @@ static const struct _sc_driver_entry internal_card_drivers[] = {
|
||||||
{ "rutoken", (void *(*)(void)) sc_get_rutoken_driver },
|
{ "rutoken", (void *(*)(void)) sc_get_rutoken_driver },
|
||||||
{ "rutoken_ecp",(void *(*)(void)) sc_get_rtecp_driver },
|
{ "rutoken_ecp",(void *(*)(void)) sc_get_rtecp_driver },
|
||||||
{ "westcos", (void *(*)(void)) sc_get_westcos_driver },
|
{ "westcos", (void *(*)(void)) sc_get_westcos_driver },
|
||||||
|
{ "myeid", (void *(*)(void)) sc_get_myeid_driver },
|
||||||
/* emv is not really used, not sure if it works, but it conflicts with
|
/* emv is not really used, not sure if it works, but it conflicts with
|
||||||
muscle and rutoken driver, thus has to be after them */
|
muscle and rutoken driver, thus has to be after them */
|
||||||
{ "emv", (void *(*)(void)) sc_get_emv_driver },
|
{ "emv", (void *(*)(void)) sc_get_emv_driver },
|
||||||
|
|
|
@ -25,7 +25,8 @@ dist_pkgdata_DATA = \
|
||||||
asepcos.profile \
|
asepcos.profile \
|
||||||
entersafe.profile \
|
entersafe.profile \
|
||||||
rutoken_ecp.profile \
|
rutoken_ecp.profile \
|
||||||
westcos.profile
|
westcos.profile \
|
||||||
|
myeid.profile
|
||||||
|
|
||||||
AM_CPPFLAGS = -DSC_PKCS15_PROFILE_DIRECTORY=\"$(pkgdatadir)\"
|
AM_CPPFLAGS = -DSC_PKCS15_PROFILE_DIRECTORY=\"$(pkgdatadir)\"
|
||||||
AM_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) $(LTLIB_CFLAGS)
|
AM_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) $(LTLIB_CFLAGS)
|
||||||
|
@ -38,7 +39,7 @@ libpkcs15init_la_SOURCES = \
|
||||||
pkcs15-cardos.c pkcs15-jcop.c pkcs15-starcos.c \
|
pkcs15-cardos.c pkcs15-jcop.c pkcs15-starcos.c \
|
||||||
pkcs15-oberthur.c pkcs15-setcos.c pkcs15-incrypto34.c \
|
pkcs15-oberthur.c pkcs15-setcos.c pkcs15-incrypto34.c \
|
||||||
pkcs15-muscle.c pkcs15-asepcos.c pkcs15-rutoken.c \
|
pkcs15-muscle.c pkcs15-asepcos.c pkcs15-rutoken.c \
|
||||||
pkcs15-entersafe.c pkcs15-rtecp.c \
|
pkcs15-entersafe.c pkcs15-rtecp.c pkcs15-myeid.c \
|
||||||
pkcs15init.exports
|
pkcs15init.exports
|
||||||
if WIN32
|
if WIN32
|
||||||
libpkcs15init_la_SOURCES += versioninfo.rc
|
libpkcs15init_la_SOURCES += versioninfo.rc
|
||||||
|
|
|
@ -11,6 +11,7 @@ OBJECTS = pkcs15-lib.obj profile.obj keycache.obj \
|
||||||
pkcs15-oberthur.obj pkcs15-setcos.obj pkcs15-incrypto34.obj \
|
pkcs15-oberthur.obj pkcs15-setcos.obj pkcs15-incrypto34.obj \
|
||||||
pkcs15-muscle.obj pkcs15-asepcos.obj pkcs15-rutoken.obj \
|
pkcs15-muscle.obj pkcs15-asepcos.obj pkcs15-rutoken.obj \
|
||||||
pkcs15-entersafe.obj pkcs15-rtecp.obj pkcs15-westcos.obj \
|
pkcs15-entersafe.obj pkcs15-rtecp.obj pkcs15-westcos.obj \
|
||||||
|
pkcs15-myeid.obj \
|
||||||
versioninfo.res
|
versioninfo.res
|
||||||
|
|
||||||
all: install-headers $(TARGET)
|
all: install-headers $(TARGET)
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
#
|
||||||
|
# PKCS15 r/w profile for MyEID cards
|
||||||
|
#
|
||||||
|
cardinfo {
|
||||||
|
max-pin-length = 8;
|
||||||
|
pin-encoding = ascii-numeric;
|
||||||
|
pin-pad-char = 0x00;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Define reasonable limits for PINs and PUK
|
||||||
|
# Note that we do not set a file path or reference
|
||||||
|
# here; that is done dynamically.
|
||||||
|
PIN user-pin {
|
||||||
|
attempts = 3;
|
||||||
|
}
|
||||||
|
PIN user-puk {
|
||||||
|
attempts = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Additional filesystem info.
|
||||||
|
# This is added to the file system info specified in the
|
||||||
|
# main profile.
|
||||||
|
filesystem {
|
||||||
|
DF MF {
|
||||||
|
DF PKCS15-AppDF {
|
||||||
|
EF template-private-key {
|
||||||
|
type = internal-ef;
|
||||||
|
file-id = 4B01; # This is the base FileID
|
||||||
|
size = 266; # 266 is enough for 1024-bit keys
|
||||||
|
ACL = *=NEVER, CRYPTO=$PIN, UPDATE=$PIN;
|
||||||
|
}
|
||||||
|
EF template-public-key {
|
||||||
|
file-id = 5501;
|
||||||
|
ACL = *=NEVER, READ=NONE, UPDATE=$PIN;
|
||||||
|
}
|
||||||
|
EF template-certificate {
|
||||||
|
file-id = 4301;
|
||||||
|
ACL = *=NEVER, READ=NONE, UPDATE=$PIN;
|
||||||
|
}
|
||||||
|
EF template-extractable-key {
|
||||||
|
file-id = 7000;
|
||||||
|
ACL = *=NEVER, READ=$PIN, UPDATE=$PIN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Define an SO pin
|
||||||
|
# This PIN is not used yet.
|
||||||
|
#PIN sopin {
|
||||||
|
# file = sopinfile;
|
||||||
|
# reference = 0;
|
||||||
|
#}
|
|
@ -405,6 +405,7 @@ extern struct sc_pkcs15init_operations *sc_pkcs15init_get_rutoken_ops(void);
|
||||||
extern struct sc_pkcs15init_operations *sc_pkcs15init_get_entersafe_ops(void);
|
extern struct sc_pkcs15init_operations *sc_pkcs15init_get_entersafe_ops(void);
|
||||||
extern struct sc_pkcs15init_operations *sc_pkcs15init_get_rtecp_ops(void);
|
extern struct sc_pkcs15init_operations *sc_pkcs15init_get_rtecp_ops(void);
|
||||||
extern struct sc_pkcs15init_operations *sc_pkcs15init_get_westcos_ops(void);
|
extern struct sc_pkcs15init_operations *sc_pkcs15init_get_westcos_ops(void);
|
||||||
|
extern struct sc_pkcs15init_operations *sc_pkcs15init_get_myeid_ops(void);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,6 +166,7 @@ static struct profile_operations {
|
||||||
{ "entersafe",(void*) sc_pkcs15init_get_entersafe_ops },
|
{ "entersafe",(void*) sc_pkcs15init_get_entersafe_ops },
|
||||||
{ "rutoken_ecp", (void *) sc_pkcs15init_get_rtecp_ops },
|
{ "rutoken_ecp", (void *) sc_pkcs15init_get_rtecp_ops },
|
||||||
{ "westcos", (void *) sc_pkcs15init_get_westcos_ops },
|
{ "westcos", (void *) sc_pkcs15init_get_westcos_ops },
|
||||||
|
{ "myeid", (void *) sc_pkcs15init_get_myeid_ops },
|
||||||
{ NULL, NULL },
|
{ NULL, NULL },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,426 @@
|
||||||
|
/*
|
||||||
|
* MyEID specific operations for PKCS15 initialization
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008-2009 Aventra Ltd.
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <opensc/opensc.h>
|
||||||
|
#include <opensc/cardctl.h>
|
||||||
|
#include <opensc/log.h>
|
||||||
|
#include "pkcs15-init.h"
|
||||||
|
#include "keycache.h"
|
||||||
|
#include "profile.h"
|
||||||
|
|
||||||
|
#define MYEID_MAX_PINS 5
|
||||||
|
|
||||||
|
unsigned char MYEID_DEFAULT_PUBKEY[] = {0x01, 0x00, 0x01};
|
||||||
|
#define MYEID_DEFAULT_PUBKEY_LEN sizeof(MYEID_DEFAULT_PUBKEY)
|
||||||
|
|
||||||
|
static int myeid_generate_store_key( sc_profile_t *, sc_card_t *,
|
||||||
|
unsigned int, unsigned int, sc_pkcs15_pubkey_t *,
|
||||||
|
sc_pkcs15_prkey_t *, sc_pkcs15_prkey_info_t *);
|
||||||
|
|
||||||
|
static int myeid_create_pin_internal(sc_profile_t *, sc_card_t *,
|
||||||
|
int, sc_pkcs15_pin_info_t *, const u8 *, size_t,
|
||||||
|
const u8 *, size_t);
|
||||||
|
|
||||||
|
static int myeid_puk_retries(sc_profile_t *, int);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Erase the card.
|
||||||
|
*/
|
||||||
|
static int myeid_erase_card(sc_profile_t *profile, sc_card_t *card)
|
||||||
|
{
|
||||||
|
struct sc_cardctl_myeid_data_obj data_obj;
|
||||||
|
sc_pkcs15_pin_info_t pin_info;
|
||||||
|
u8 data[8];
|
||||||
|
int r;
|
||||||
|
|
||||||
|
/* Just delete the entire MF */
|
||||||
|
|
||||||
|
/* The SO pin has pin reference 1 -- not that it matters much
|
||||||
|
* because pkcs15-init will ask to enter all pins, even if we
|
||||||
|
* did a --so-pin on the command line. */
|
||||||
|
sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &pin_info);
|
||||||
|
sc_keycache_set_pin_name(NULL, pin_info.reference, SC_PKCS15INIT_SO_PIN);
|
||||||
|
|
||||||
|
/* Select parent DF and verify PINs/key as necessary */
|
||||||
|
r = sc_pkcs15init_authenticate(profile, card, profile->mf_info->file, SC_AC_OP_DELETE);
|
||||||
|
if (r < 0)
|
||||||
|
return r == SC_ERROR_FILE_NOT_FOUND ? 0 : r;
|
||||||
|
|
||||||
|
data[0]= 0xFF;
|
||||||
|
data[1]= 0xFF;
|
||||||
|
data[2]= 0x33;
|
||||||
|
data[3]= 0x3F;
|
||||||
|
data[4]= 0xFF;
|
||||||
|
data[5]= 0x33;
|
||||||
|
data[6]= 0x3F;
|
||||||
|
data[7]= 0xFF;
|
||||||
|
|
||||||
|
data_obj.P1 = 0x01;
|
||||||
|
data_obj.P2 = 0xE0;
|
||||||
|
data_obj.Data = data;
|
||||||
|
data_obj.DataLen = 0x08;
|
||||||
|
|
||||||
|
r = sc_card_ctl(card, SC_CARDCTL_MYEID_PUTDATA, &data_obj);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Select the PIN reference
|
||||||
|
*/
|
||||||
|
static int myeid_select_pin_reference(sc_profile_t *profile, sc_card_t *card,
|
||||||
|
sc_pkcs15_pin_info_t *pin_info)
|
||||||
|
{
|
||||||
|
sc_pkcs15_pin_info_t pin_info_prof;
|
||||||
|
|
||||||
|
pin_info_prof.reference = 1; /* Default SO PIN ref. */
|
||||||
|
sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &pin_info_prof);
|
||||||
|
|
||||||
|
/* For the SO pin, we take the first available pin reference = 1 */
|
||||||
|
if (pin_info->flags & SC_PKCS15_PIN_FLAG_SO_PIN)
|
||||||
|
pin_info->reference = pin_info_prof.reference;
|
||||||
|
/* sc_pkcs15init_create_pin() starts checking if 0 is an acceptable
|
||||||
|
* pin reference, which isn't for the myeid cards. And since the
|
||||||
|
* value 1 has been assigned to the SO pin, we'll jump to 2. */
|
||||||
|
else if (pin_info->reference == 0)
|
||||||
|
pin_info->reference = pin_info_prof.reference + 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a new PIN
|
||||||
|
*/
|
||||||
|
static int myeid_create_pin(sc_profile_t *profile, sc_card_t *card,
|
||||||
|
sc_file_t *df, sc_pkcs15_object_t *pin_obj,
|
||||||
|
const u8 *pin, size_t pin_len,
|
||||||
|
const u8 *puk, size_t puk_len)
|
||||||
|
{
|
||||||
|
return myeid_create_pin_internal(profile, card,
|
||||||
|
0, (sc_pkcs15_pin_info_t *) pin_obj->data,
|
||||||
|
pin, pin_len,
|
||||||
|
puk, puk_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Setup file struct & path: get correct template from the profile, construct full path
|
||||||
|
* num = number of objects of this type already on the card
|
||||||
|
*/
|
||||||
|
static int myeid_new_file(sc_profile_t *profile, sc_card_t *card,
|
||||||
|
unsigned int type, unsigned int num,
|
||||||
|
sc_file_t **out)
|
||||||
|
{
|
||||||
|
sc_file_t *file;
|
||||||
|
sc_path_t *p;
|
||||||
|
char name[64], *tag;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (type == SC_PKCS15_TYPE_PRKEY_RSA)
|
||||||
|
tag = "private-key";
|
||||||
|
else if (type == SC_PKCS15_TYPE_PUBKEY_RSA)
|
||||||
|
tag = "public-key";
|
||||||
|
else if ((type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_CERT)
|
||||||
|
tag = "certificate";
|
||||||
|
else if ((type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_DATA_OBJECT)
|
||||||
|
tag = "data";
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sc_error(card->ctx, "Unsupported file type");
|
||||||
|
return SC_ERROR_INVALID_ARGUMENTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get template from profile */
|
||||||
|
snprintf(name, sizeof(name), "template-%s", tag);
|
||||||
|
if (sc_profile_get_file(profile, name, &file) < 0)
|
||||||
|
{
|
||||||
|
sc_error(card->ctx, "Profile doesn't define %s", name);
|
||||||
|
return SC_ERROR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Auto-increment FID for next object */
|
||||||
|
file->id += num;
|
||||||
|
p = &file->path;
|
||||||
|
*p = profile->df_info->file->path;
|
||||||
|
p->value[p->len++] = (u8) (file->id / 256);
|
||||||
|
p->value[p->len++] = (u8) (file->id % 256);
|
||||||
|
|
||||||
|
/* Increment FID until there's no file with such path */
|
||||||
|
r = sc_select_file(card, p, NULL);
|
||||||
|
while(r == 0)
|
||||||
|
{
|
||||||
|
file->id++;
|
||||||
|
p->value[p->len - 2] = (u8) (file->id / 256);
|
||||||
|
p->value[p->len - 1] = (u8) (file->id % 256);
|
||||||
|
r = sc_select_file(card, p, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
*out = file;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int myeid_encode_private_key(sc_profile_t *profile, sc_card_t *card,
|
||||||
|
struct sc_pkcs15_prkey_rsa *rsa, u8 *key,
|
||||||
|
size_t *keysize, int key_ref)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int myeid_encode_public_key(sc_profile_t *profile, sc_card_t *card,
|
||||||
|
struct sc_pkcs15_prkey_rsa *rsa, u8 *key,
|
||||||
|
size_t *keysize, int key_ref)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generate RSA key
|
||||||
|
*/
|
||||||
|
static int myeid_generate_key(sc_profile_t *profile, sc_card_t *card,
|
||||||
|
unsigned int index, /* keyref: 0 for 1st key, ... */
|
||||||
|
unsigned int keybits,
|
||||||
|
sc_pkcs15_pubkey_t *pubkey,
|
||||||
|
struct sc_pkcs15_prkey_info *info)
|
||||||
|
{
|
||||||
|
return myeid_generate_store_key(profile, card, index, keybits,
|
||||||
|
pubkey, NULL, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Store RSA key
|
||||||
|
*/
|
||||||
|
static int myeid_new_key(sc_profile_t *profile, sc_card_t *card,
|
||||||
|
struct sc_pkcs15_prkey *key, unsigned int index,
|
||||||
|
struct sc_pkcs15_prkey_info *info)
|
||||||
|
{
|
||||||
|
return myeid_generate_store_key(profile, card, index,
|
||||||
|
key->u.rsa.modulus.len * 8, NULL, key, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Common code for generating or storing a private key.
|
||||||
|
* If pubkey == NULL and prkey != NULL, we have to store a private key
|
||||||
|
* In the oposite case, we have to generate a private key
|
||||||
|
*/
|
||||||
|
static int myeid_generate_store_key(sc_profile_t *profile, sc_card_t *card,
|
||||||
|
unsigned int index, /* keynumber: 0 for 1st priv key, ... */
|
||||||
|
unsigned int keybits,
|
||||||
|
sc_pkcs15_pubkey_t *pubkey,
|
||||||
|
sc_pkcs15_prkey_t *prkey,
|
||||||
|
sc_pkcs15_prkey_info_t *info)
|
||||||
|
{
|
||||||
|
struct sc_cardctl_myeid_gen_store_key_info args;
|
||||||
|
struct sc_cardctl_myeid_data_obj data_obj;
|
||||||
|
unsigned char raw_pubkey[256];
|
||||||
|
int r;
|
||||||
|
unsigned int mod_len;
|
||||||
|
sc_file_t *prkf = NULL;
|
||||||
|
|
||||||
|
/* Parameter check */
|
||||||
|
if ( (keybits < 1024) || (keybits > 2048) || (keybits & 0X7)) {
|
||||||
|
sc_error(card->ctx,
|
||||||
|
"Unsupported key size [%u]: 1024-2048 bit + 8-multiple\n", keybits);
|
||||||
|
return SC_ERROR_INVALID_ARGUMENTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the private key file */
|
||||||
|
r = myeid_new_file(profile, card, SC_PKCS15_TYPE_PRKEY_RSA, index, &prkf);
|
||||||
|
if (r < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
/* Take enough room for a 1024 bit key */
|
||||||
|
if (prkf->size < 1024)
|
||||||
|
prkf->size = 1024;
|
||||||
|
|
||||||
|
/* Now create the key file */
|
||||||
|
r = sc_pkcs15init_create_file(profile, card, prkf);
|
||||||
|
if (r < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
/* Fill in data structure */
|
||||||
|
memset(&args, 0, sizeof(args));
|
||||||
|
args.mod_len = keybits;
|
||||||
|
if (prkey == NULL)
|
||||||
|
{
|
||||||
|
args.op_type = OP_TYPE_GENERATE;
|
||||||
|
args.pubexp_len = MYEID_DEFAULT_PUBKEY_LEN;
|
||||||
|
args.pubexp = MYEID_DEFAULT_PUBKEY;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
args.op_type = OP_TYPE_STORE;
|
||||||
|
args.pubexp_len = prkey->u.rsa.exponent.len;
|
||||||
|
args.pubexp = prkey->u.rsa.exponent.data;
|
||||||
|
args.primep_len = prkey->u.rsa.p.len;
|
||||||
|
args.primep = prkey->u.rsa.p.data;
|
||||||
|
args.primeq_len = prkey->u.rsa.q.len;
|
||||||
|
args.primeq = prkey->u.rsa.q.data;
|
||||||
|
|
||||||
|
args.dp1_len = prkey->u.rsa.dmp1.len;
|
||||||
|
args.dp1 = prkey->u.rsa.dmp1.data;
|
||||||
|
args.dq1_len = prkey->u.rsa.dmq1.len;
|
||||||
|
args.dq1 = prkey->u.rsa.dmq1.data;
|
||||||
|
args.invq_len = prkey->u.rsa.iqmp.len;
|
||||||
|
args.invq = prkey->u.rsa.iqmp.data;
|
||||||
|
|
||||||
|
args.mod_len = prkey->u.rsa.modulus.len;
|
||||||
|
args.mod = prkey->u.rsa.modulus.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Authenticate */
|
||||||
|
r = sc_pkcs15init_authenticate(profile, card, prkf, SC_AC_OP_UPDATE);
|
||||||
|
if (r < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
/* Generate/store rsa key */
|
||||||
|
r = sc_card_ctl(card, SC_CARDCTL_MYEID_GENERATE_KEY, &args);
|
||||||
|
if (r < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
info->key_reference = 0;
|
||||||
|
info->path = prkf->path;
|
||||||
|
|
||||||
|
done:
|
||||||
|
if (prkf)
|
||||||
|
sc_file_free(prkf);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a new PIN
|
||||||
|
*/
|
||||||
|
static int myeid_create_pin_internal(sc_profile_t *profile, sc_card_t *card,
|
||||||
|
int ignore_ac, sc_pkcs15_pin_info_t *pin_info,
|
||||||
|
const u8 *pin, size_t pin_len,
|
||||||
|
const u8 *puk, size_t puk_len)
|
||||||
|
{
|
||||||
|
u8 data[19];
|
||||||
|
int so_pin_ref;
|
||||||
|
int r;
|
||||||
|
struct sc_cardctl_myeid_data_obj data_obj;
|
||||||
|
sc_file_t *pinfile = NULL;
|
||||||
|
|
||||||
|
if (pin_info->reference >= MYEID_MAX_PINS)
|
||||||
|
return SC_ERROR_INVALID_ARGUMENTS;
|
||||||
|
if (pin == NULL || puk == NULL || pin_len < 4 || puk_len < 4)
|
||||||
|
return SC_ERROR_INVALID_PIN_LENGTH;
|
||||||
|
|
||||||
|
/* Verify required access rights if needed (i.e. if the
|
||||||
|
* pin file isn't in the CREATE life cycle state). */
|
||||||
|
if (!ignore_ac)
|
||||||
|
{
|
||||||
|
/* Re-ink the SO pin to the MF because there is the pin file */
|
||||||
|
so_pin_ref = sc_keycache_find_named_pin(&profile->df_info->file->path,
|
||||||
|
SC_PKCS15INIT_SO_PIN);
|
||||||
|
if (so_pin_ref >= 0)
|
||||||
|
sc_keycache_set_pin_name(&profile->mf_info->file->path,
|
||||||
|
so_pin_ref, SC_PKCS15INIT_SO_PIN);
|
||||||
|
|
||||||
|
r = sc_profile_get_file(profile, "pinfile", &pinfile);
|
||||||
|
if (r >= 0)
|
||||||
|
r = sc_pkcs15init_authenticate(profile, card, pinfile, SC_AC_OP_UPDATE);
|
||||||
|
sc_file_free(pinfile);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make command to add a pin-record */
|
||||||
|
data_obj.P1 = 01;
|
||||||
|
data_obj.P2 = pin_info->reference; /* myeid pin number */
|
||||||
|
|
||||||
|
memcpy(&data[0], (u8 *)pin, pin_len); /* copy pin*/
|
||||||
|
memcpy(&data[8], (u8 *)puk, puk_len); /* copy puk */
|
||||||
|
|
||||||
|
|
||||||
|
/* Optional PIN locking
|
||||||
|
* data[17] = pin_info->tries_left & 0x0F;
|
||||||
|
* data[18] = myeid_puk_retries(profile, pin_info->reference) & 0x0F;
|
||||||
|
*/
|
||||||
|
|
||||||
|
data[17] = 0x00;
|
||||||
|
data[18] = 0x00;
|
||||||
|
data[19] = 0x00;
|
||||||
|
|
||||||
|
data_obj.Data = data;
|
||||||
|
data_obj.DataLen = 0x10;
|
||||||
|
|
||||||
|
r = sc_card_ctl(card, SC_CARDCTL_MYEID_PUTDATA, &data_obj);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int myeid_puk_retries(sc_profile_t *profile, int pin_ref)
|
||||||
|
{
|
||||||
|
sc_pkcs15_pin_info_t pin_info;
|
||||||
|
|
||||||
|
pin_info.reference = 1; /* Default SO PIN ref. */
|
||||||
|
sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &pin_info);
|
||||||
|
|
||||||
|
/* If pin_ref is the SO PIN, get the SO PUK info, otherwise the User PUK info */
|
||||||
|
sc_profile_get_pin_info(profile,
|
||||||
|
pin_ref == pin_info.reference ?
|
||||||
|
SC_PKCS15INIT_SO_PUK : SC_PKCS15INIT_USER_PUK,
|
||||||
|
&pin_info);
|
||||||
|
|
||||||
|
if ((pin_info.tries_left < 0) || (pin_info.tries_left > 15))
|
||||||
|
return 3; /* Little extra safety */
|
||||||
|
return pin_info.tries_left;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For Myeid, all objects are files that can be deleted in any order */
|
||||||
|
static int myeid_delete_object(struct sc_profile *profile,
|
||||||
|
struct sc_card *card, unsigned int type,
|
||||||
|
const void *data, const sc_path_t *path)
|
||||||
|
{
|
||||||
|
return sc_pkcs15init_delete_by_path(profile, card, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct sc_pkcs15init_operations sc_pkcs15init_myeid_operations = {
|
||||||
|
myeid_erase_card,
|
||||||
|
NULL, /* init_card */
|
||||||
|
NULL, /* create_dir */
|
||||||
|
NULL, /* create_domain */
|
||||||
|
myeid_select_pin_reference,
|
||||||
|
myeid_create_pin,
|
||||||
|
NULL, /* select_key_reference */
|
||||||
|
NULL, /* create_key */
|
||||||
|
NULL, /* store_key */
|
||||||
|
NULL, /* generate_key */
|
||||||
|
myeid_encode_private_key,
|
||||||
|
myeid_encode_public_key,
|
||||||
|
NULL, /* finalize_card */
|
||||||
|
NULL,
|
||||||
|
NULL, /* style api */
|
||||||
|
myeid_new_key,
|
||||||
|
myeid_new_file,
|
||||||
|
myeid_generate_key,
|
||||||
|
myeid_delete_object
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sc_pkcs15init_operations *sc_pkcs15init_get_myeid_ops(void)
|
||||||
|
{
|
||||||
|
return &sc_pkcs15init_myeid_operations;
|
||||||
|
}
|
Loading…
Reference in New Issue