MyEID: implement support for 4K RSA keys (MyEID 4.5+)

MyEID starting version 4.5 supports 4K RSA keys. The card also
now supports proper APDU chainging (for sending large APDUs) and
receiving large responses via GET_RESPONSE splitting.

This updates the following:
* detection code properly announces 3K and 4K RSA support
  when available
* APDU chaining is used when possible
* use ISO GET_RESPONSE handling for large responses
* max_recv_size is set to 256 which it always was supposed to be
  as the old cards respond with that large responses too
* use the 2K signing kludge only on cards that need it
* unwrap and decipher code paths unified to the extent possible

Signed-off-by: Timo Teräs <timo.teras@iki.fi>
This commit is contained in:
Timo Teräs 2019-03-30 18:25:02 +02:00 committed by Frank Morgner
parent 0e25c1d2a6
commit 3f832ca6da
2 changed files with 155 additions and 202 deletions

View File

@ -1,7 +1,7 @@
/*
* card-myeid.c
*
* Copyright (C) 2008-2009 Aventra Ltd.
* Copyright (C) 2008-2019 Aventra Ltd.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -60,7 +60,9 @@
#define MYEID_CARD_CAP_PIV_EMU 0x20
#define MYEID_MAX_APDU_DATA_LEN 0xFF
#define MYEID_MAX_RSA_KEY_LEN 2048
#define MYEID_MAX_RSA_KEY_LEN 4096
#define MYEID_MAX_EXT_APDU_BUFFER_SIZE (MYEID_MAX_RSA_KEY_LEN/8+16)
static const char *myeid_card_name = "MyEID";
static char card_name_buf[MYEID_CARD_NAME_MAX_LEN];
@ -79,6 +81,7 @@ typedef struct myeid_private_data {
int card_state;
unsigned short change_counter;
unsigned char cap_chaining;
/* the driver sets sec_env pointer in myeid_set_security_env and
it is used immediately in myeid_decipher to differentiate between RSA decryption and
ECDH key agreement. Note that this pointer is usually not valid
@ -161,7 +164,7 @@ static int myeid_init(struct sc_card *card)
myeid_private_data_t *priv;
u8 appletInfo[20];
size_t appletInfoLen;
myeid_card_caps_t card_caps;
myeid_card_caps_t card_caps;
size_t resp_len = 0;
static struct sc_aid myeid_aid = { "\xA0\x00\x00\x00\x63\x50\x4B\x43\x53\x2D\x31\x35", 0x0C };
int rv = 0;
@ -191,15 +194,6 @@ static int myeid_init(struct sc_card *card)
priv->change_counter = appletInfo[19] | appletInfo[18] << 8;
flags = SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_ONBOARD_KEY_GEN;
flags |= SC_ALGORITHM_RSA_HASH_NONE | SC_ALGORITHM_RSA_HASH_SHA1;
_sc_card_add_rsa_alg(card, 512, flags, 0);
_sc_card_add_rsa_alg(card, 768, flags, 0);
_sc_card_add_rsa_alg(card, 1024, flags, 0);
_sc_card_add_rsa_alg(card, 1536, flags, 0);
_sc_card_add_rsa_alg(card, 2048, flags, 0);
memset(&card_caps, 0, sizeof(myeid_card_caps_t));
card_caps.max_ecc_key_length = 256;
card_caps.max_rsa_key_length = 2048;
@ -212,6 +206,22 @@ static int myeid_init(struct sc_card *card)
}
}
flags = SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_ONBOARD_KEY_GEN;
flags |= SC_ALGORITHM_RSA_HASH_NONE | SC_ALGORITHM_RSA_HASH_SHA1;
_sc_card_add_rsa_alg(card, 512, flags, 0);
_sc_card_add_rsa_alg(card, 768, flags, 0);
_sc_card_add_rsa_alg(card, 1024, flags, 0);
_sc_card_add_rsa_alg(card, 1536, flags, 0);
_sc_card_add_rsa_alg(card, 2048, flags, 0);
if (card_caps.card_supported_features & MYEID_CARD_CAP_RSA) {
if (card_caps.max_rsa_key_length >= 3072)
_sc_card_add_rsa_alg(card, 3072, flags, 0);
if (card_caps.max_rsa_key_length >= 4096)
_sc_card_add_rsa_alg(card, 4096, flags, 0);
}
/* show ECC algorithms if the applet version of the inserted card supports them */
if (card->version.fw_major >= 35) {
int i;
@ -251,7 +261,10 @@ static int myeid_init(struct sc_card *card)
card->caps |= SC_CARD_CAP_WRAP_KEY | SC_CARD_CAP_UNWRAP_KEY
| SC_CARD_CAP_ONCARD_SESSION_OBJECTS;
card->max_recv_size = 255;
if (card->version.fw_major >= 45)
priv->cap_chaining = 1;
card->max_recv_size = 256;
card->max_send_size = 255;
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
@ -969,12 +982,10 @@ myeid_convert_ec_signature(struct sc_context *ctx, size_t s_len, unsigned char *
free(buf);
return buflen;
}
/*
MyEID does not support RAW RSA signature for 2048 bit key.
(Source: MyEID reference manual 2.1.4)
This function uses decipher operation for calculating RAW 2048 bit signature.
*/
/* MyEID cards before version 4.5 do not support RAW RSA signature for 2048 bit RSA keys.
* (Source: MyEID reference manual 2.1.4)
*
* This function uses decipher operation for calculating RAW 2048 bit signature. */
static int
myeid_compute_raw_2048_signature(struct sc_card *card, const u8 * data, size_t datalen,
u8 * out, size_t outlen)
@ -1044,8 +1055,8 @@ myeid_compute_signature(struct sc_card *card, const u8 * data, size_t datalen,
{
struct sc_context *ctx;
struct sc_apdu apdu;
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
u8 rbuf[MYEID_MAX_EXT_APDU_BUFFER_SIZE];
u8 sbuf[MYEID_MAX_EXT_APDU_BUFFER_SIZE];
struct myeid_private_data* priv;
int r;
size_t field_length = 0;
@ -1071,16 +1082,17 @@ myeid_compute_signature(struct sc_card *card, const u8 * data, size_t datalen,
}
}
if ((datalen + pad_chars) > 256)
if ((datalen + pad_chars) > sizeof(sbuf))
LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
if (datalen == 256 && priv->sec_env->algorithm == SC_ALGORITHM_RSA)
if (priv->sec_env->algorithm == SC_ALGORITHM_RSA && datalen == 256 && !priv->cap_chaining)
return myeid_compute_raw_2048_signature(card, data, datalen, out, outlen);
/* 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.flags |= SC_APDU_FLAGS_CHAINING;
apdu.resp = rbuf;
apdu.resplen = sizeof(rbuf);
apdu.le = 256;
@ -1094,7 +1106,7 @@ myeid_compute_signature(struct sc_card *card, const u8 * data, size_t datalen,
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
LOG_TEST_RET(ctx, r, "compute_signature failed");
if (priv->sec_env->algorithm == SC_ALGORITHM_EC) {
if (priv->sec_env->algorithm == SC_ALGORITHM_EC) {
r = myeid_convert_ec_signature(ctx, priv->sec_env->algorithm_ref, apdu.resp, apdu.resplen);
LOG_TEST_RET(ctx, r, "compute_signature convert signature failed");
apdu.resplen = r;
@ -1177,15 +1189,101 @@ int myeid_ecdh_derive(struct sc_card *card, const u8* pubkey, size_t pubkey_len,
LOG_FUNC_RETURN(card->ctx, apdu.resplen);
}
static int myeid_transmit_decipher_pi_split(struct sc_card *card, struct sc_apdu *apdu, u8 *sbuf)
{
/* MyEID before 4.5.x does not support APDU chaining. The payload
* is split to two regular APDUs and Padding Indicator field is used to
* describe which slice it is. */
size_t crgram_len = apdu->lc - 1;
size_t crgram_half = crgram_len / 2;
size_t resplen = apdu->resplen;
unsigned char *resp = apdu->resp;
int r;
LOG_FUNC_CALLED(card->ctx);
/* Send 1st part, no response */
apdu->cse = SC_APDU_CASE_3_SHORT;
apdu->data = &sbuf[0];
apdu->datalen = apdu->lc = crgram_half + 1;
apdu->resp = 0;
apdu->resplen = 0;
apdu->le = 0;
sbuf[0] = 0x81; /* Padding Indicator, 0x81 = First half */
r = sc_transmit_apdu(card, apdu);
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
if (apdu->sw1 != 0x90 || apdu->sw2 != 0x00)
return 0;
/* Send 2nd part, expect response */
apdu->cse = resplen ? SC_APDU_CASE_4_SHORT : SC_APDU_CASE_3_SHORT;
apdu->data = &sbuf[crgram_half];
apdu->datalen = apdu->lc = crgram_len - crgram_half + 1;
apdu->resp = resp;
apdu->resplen = resplen;
apdu->le = resplen ? MIN(card->max_recv_size, crgram_len) : 0;
sbuf[crgram_half] = 0x82; /* Padding Indicator, 0x82 = Second half */
r = sc_transmit_apdu(card, apdu);
LOG_FUNC_RETURN(card->ctx, r);
}
static int myeid_transmit_decipher(struct sc_card *card, u8 p1, u8 p2,
const u8 * crgram, size_t crgram_len, u8 * out, size_t outlen)
{
myeid_private_data_t *priv = card->drv_data;
struct sc_apdu apdu;
u8 rbuf[SC_MAX_EXT_APDU_BUFFER_SIZE];
u8 sbuf[SC_MAX_EXT_APDU_BUFFER_SIZE];
int r;
LOG_FUNC_CALLED(card->ctx);
/* INS: 0x2A PERFORM SECURITY OPERATION
* P1: 0x00 Resp: No response (unwrapping)
* P1: 0x80 Resp: Plain value
* P2: 0x84 Cmd: Cryptogram (no padding byte)
* P2: 0x86 Cmd: Padding indicator byte followed by cryptogram */
sc_format_apdu(card, &apdu, p1 ? SC_APDU_CASE_4_SHORT : SC_APDU_CASE_3_SHORT, 0x2A, p1, p2);
if (p2 == 0x86) {
if (crgram_len+1 > sizeof(sbuf))
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
sbuf[0] = 0; /* Padding indicator: 0x00 = No further indication */
memcpy(sbuf + 1, crgram, crgram_len);
apdu.data = sbuf;
apdu.datalen = apdu.lc = crgram_len + 1;
} else {
apdu.data = crgram;
apdu.datalen = apdu.lc = crgram_len;
}
if (p1 != 0x00) {
apdu.resp = rbuf;
apdu.resplen = sizeof(rbuf);
apdu.le = MIN(card->max_recv_size, crgram_len);
}
if (p2 == 0x86 && crgram_len == 256 && !priv->cap_chaining) {
r = myeid_transmit_decipher_pi_split(card, &apdu, sbuf);
} else {
apdu.flags |= SC_APDU_FLAGS_CHAINING;
r = sc_transmit_apdu(card, &apdu);
}
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
LOG_TEST_RET(card->ctx, r, "DECIPHER returned error");
outlen = MIN(apdu.resplen, outlen);
memcpy(out, apdu.resp, outlen);
LOG_FUNC_RETURN(card->ctx, outlen);
}
static int myeid_decipher(struct sc_card *card, const u8 * crgram,
size_t crgram_len, u8 * out, size_t outlen)
{
int r;
myeid_private_data_t* priv;
struct sc_apdu apdu;
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
LOG_FUNC_CALLED(card->ctx);
@ -1202,77 +1300,8 @@ static int myeid_decipher(struct sc_card *card, const u8 * crgram,
LOG_FUNC_RETURN(card->ctx, r);
}
if (crgram_len > 256)
LOG_FUNC_RETURN(card->ctx, 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;
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);
LOG_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;
/* 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);
LOG_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);
LOG_FUNC_RETURN(card->ctx, len);
}
}
else
{
int len = apdu.resplen > outlen ? outlen : apdu.resplen;
memcpy(out, apdu.resp, len);
LOG_FUNC_RETURN(card->ctx, len);
}
}
LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2));
r = myeid_transmit_decipher(card, 0x80, 0x86, crgram, crgram_len, out, outlen);
LOG_FUNC_RETURN(card->ctx, r);
}
@ -1309,111 +1338,35 @@ static int myeid_wrap_key(struct sc_card *card, u8 *out, size_t outlen)
static int myeid_unwrap_key(struct sc_card *card, const u8 *crgram, size_t crgram_len)
{
int r;
struct sc_apdu apdu;
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
myeid_private_data_t* priv = card->drv_data;
u8 p2 = 0x86; /* init P2 for asymmetric crypto by default.*/
myeid_private_data_t* priv;
int symmetric_operation = 0;
int r;
if (card == NULL || crgram == NULL) {
if (card == NULL || crgram == NULL)
return SC_ERROR_INVALID_ARGUMENTS;
}
LOG_FUNC_CALLED(card->ctx);
LOG_FUNC_CALLED(card->ctx);
if (crgram_len > MYEID_MAX_RSA_KEY_LEN / 8)
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
if (card->drv_data)
if (priv && priv->sec_env)
{
priv = (myeid_private_data_t*) card->drv_data;
if (priv->sec_env)
{
if (priv->sec_env->algorithm == SC_ALGORITHM_AES ||
priv->sec_env->algorithm == SC_ALGORITHM_3DES ||
priv->sec_env->algorithm == SC_ALGORITHM_DES)
symmetric_operation = 1;
}
if (priv->sec_env->algorithm == SC_ALGORITHM_AES ||
priv->sec_env->algorithm == SC_ALGORITHM_3DES ||
priv->sec_env->algorithm == SC_ALGORITHM_DES)
p2 = 0x84;
}
if (symmetric_operation)
{
p2 = 0x84; /* Set correct P2 for symmetric crypto */
if (crgram_len > MYEID_MAX_APDU_DATA_LEN)
{
LOG_TEST_RET(card->ctx, SC_ERROR_WRONG_LENGTH, "Unwrapping symmetric data longer that 255 bytes is not supported\n");
}
}
if (p2 == 0x84 && crgram_len > MYEID_MAX_APDU_DATA_LEN)
LOG_TEST_RET(card->ctx, SC_ERROR_WRONG_LENGTH, "Unwrapping symmetric data longer that 255 bytes is not supported\n");
/* INS: 0x2A PERFORM SECURITY OPERATION
* P1: 0x00 Do not expect response - the deciphered data will be placed into the target key EF.
* P2: 0x86 Cmd: Padding indicator byte followed by cryptogram
* P2: 0x84 Cmd: AES/3DES Cryptogram (plain value encoded in BER-TLV DO, but not including SM DOs)
If crgram_len == 256 (2048 bit RSA), we split the cryptogram in two and send two APDUs.
*/
sc_format_apdu(card, &apdu,
SC_APDU_CASE_3_SHORT,
0x2A, 0x00, p2);
apdu.resp = rbuf;
apdu.resplen = sizeof(rbuf);
apdu.le = 0;
if (symmetric_operation)
{
/* symmetric crypto, no padding indicator byte */
memcpy(sbuf, crgram, crgram_len);
apdu.lc = crgram_len;
}
else
{
if (crgram_len >= MYEID_MAX_APDU_DATA_LEN)
{
/* 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);
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00)
{
if (crgram_len >= MYEID_MAX_APDU_DATA_LEN)
{
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT,
0x2A, 0x00, p2);
apdu.resp = rbuf;
apdu.resplen = sizeof(rbuf);
apdu.le = 0;
/* 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);
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
}
}
LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2));
* P1: 0x00 Do not expect response - the deciphered data will be placed into the target key EF.
* P2: 0x86 Cmd: Padding indicator byte followed by cryptogram
* P2: 0x84 Cmd: AES/3DES Cryptogram (plain value encoded in BER-TLV DO, but not including SM DOs) */
r = myeid_transmit_decipher(card, 0x00, p2, crgram, crgram_len, 0, 0);
LOG_FUNC_RETURN(card->ctx, r);
}
@ -1482,22 +1435,22 @@ static int myeid_getdata(struct sc_card *card, struct sc_cardctl_myeid_data_obj*
static int myeid_loadkey(sc_card_t *card, int mode, u8* value, int value_len)
{
myeid_private_data_t *priv = (myeid_private_data_t *) card->drv_data;
sc_apdu_t apdu;
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
u8 sbuf[MYEID_MAX_EXT_APDU_BUFFER_SIZE];
int r, len;
LOG_FUNC_CALLED(card->ctx);
len = 0;
if(value_len == 0 || value == NULL)
if (value_len == 0 || value == NULL)
return 0;
if(value != NULL &&
value[0] != 0x0 &&
mode != LOAD_KEY_PUBLIC_EXPONENT &&
mode != LOAD_KEY_SYMMETRIC)
if (value[0] != 0x0 &&
mode != LOAD_KEY_PUBLIC_EXPONENT &&
mode != LOAD_KEY_SYMMETRIC)
sbuf[len++] = 0x0;
if(mode == LOAD_KEY_MODULUS && value_len >= 256)
if(mode == LOAD_KEY_MODULUS && value_len == 256 && !priv->cap_chaining)
{
if((value_len % 2) > 0 && value[0] == 0x00)
{
@ -1517,11 +1470,10 @@ static int myeid_loadkey(sc_card_t *card, int mode, u8* value, int value_len)
apdu.lc = len;
r = sc_transmit_apdu(card, &apdu);
if(r < 0)
return r;
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
if(r < 0)
return r;
LOG_TEST_RET(card->ctx, r, "LOAD KEY returned error");
mode = 0x89;
len = value_len - 128;
@ -1546,7 +1498,7 @@ static int myeid_loadkey(sc_card_t *card, int mode, u8* value, int value_len)
memset(&apdu, 0, sizeof(apdu));
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xDA, 0x01, mode);
apdu.flags = SC_APDU_FLAGS_CHAINING;
apdu.cla = 0x00;
apdu.data = sbuf;
apdu.datalen = len;
@ -1824,6 +1776,7 @@ static struct sc_card_driver * sc_get_driver(void)
myeid_ops.append_record = NULL;
myeid_ops.update_record = NULL;
myeid_ops.select_file = myeid_select_file;
myeid_ops.get_response = iso_ops->get_response;
myeid_ops.create_file = myeid_create_file;
myeid_ops.delete_file = myeid_delete_file;
myeid_ops.list_files = myeid_list_files;

View File

@ -36,7 +36,7 @@
#undef KEEP_AC_NONE_FOR_INIT_APPLET
#define MYEID_MAX_PINS 14
#define MYEID_MAX_RSA_KEY_LEN 2048
#define MYEID_MAX_RSA_KEY_LEN 4096
unsigned char MYEID_DEFAULT_PUBKEY[] = {0x01, 0x00, 0x01};
#define MYEID_DEFAULT_PUBKEY_LEN sizeof(MYEID_DEFAULT_PUBKEY)