Skip to content

Commit

Permalink
card-piv.c,pkcs15-piv.c,piv-tool.c - Support for RSA 4096 and 25519
Browse files Browse the repository at this point in the history
Yubikey with firmware >= 5.7 supports RSA 4096, and EDDSA and XEDDSA
which is non standard PIV.

WIP  Only tested with 9A key and self signed certificate
created by Yubic-piv-tool.

Signature created with:
./pkcs11-tool -m EDDSA --login --sign --id 01 --input-file /tmp/data.txt --output-file /tmp/YK11-9A-signature.der
and signature verified via openssl 3.3.1
./openssl pkeyutl -verify -pubin -inkey /tmp/YK11-9A-pub.pem -rawin -in /tmp/data.txt -sigfile /tmp/YK11-9A-signature.der

 On branch X25519-improvements-2
 Changes to be committed:
	modified:   libopensc/card-piv.c
	modified:   libopensc/pkcs15-piv.c
	modified:   tools/piv-tool.c
  • Loading branch information
dengert committed Aug 22, 2024
1 parent 982adb5 commit 5af05d8
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 14 deletions.
79 changes: 67 additions & 12 deletions src/libopensc/card-piv.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* card-default.c: Support for cards with no driver
*
* Copyright (C) 2001, 2002 Juha Yrjölä <[email protected]>
* Copyright (C) 2005-2023 Douglas E. Engert <[email protected]>
* Copyright (C) 2005-2024 Douglas E. Engert <[email protected]>
* Copyright (C) 2006, Identity Alliance, Thomas Harning <[email protected]>
* Copyright (C) 2007, EMC, Russell Larner <[email protected]>
*
Expand Down Expand Up @@ -544,10 +544,13 @@ static const struct sc_atr_table piv_atrs[] = {
static struct piv_supported_ec_curves {
struct sc_object_id oid;
size_t size;
unsigned int key_type;
} ec_curves[] = {
{{{1, 2, 840, 10045, 3, 1, 7, -1}}, 256}, /* secp256r1, nistp256, prime256v1, ansiX9p256r1 */
{{{1, 3, 132, 0, 34, -1}}, 384}, /* secp384r1, nistp384, prime384v1, ansiX9p384r1 */
{{{-1}}, 0} /* This entry must not be touched. */
{{{1, 2, 840, 10045, 3, 1, 7, -1}}, 256, SC_ALGORITHM_EC}, /* secp256r1, nistp256, prime256v1, ansiX9p256r1 */
{{{1, 3, 132, 0, 34, -1}}, 384, SC_ALGORITHM_EC}, /* secp384r1, nistp384, prime384v1, ansiX9p384r1 */
{{{1, 3, 101, 112, -1}}, 255, SC_ALGORITHM_EDDSA}, /* RFC8410 OID equivalent to ed25519 */
{{{1, 3, 101, 110, -1}}, 255, SC_ALGORITHM_XEDDSA}, /* RFC8410 OID equivalent to curve25519 */
{{{-1}}, 0, 0} /* This entry must not be touched. */
};

/* all have same AID */
Expand All @@ -573,6 +576,8 @@ static struct piv_aid piv_aids[] = {
#define CI_NO_RSA2048 0x00010000U /* does not have RSA 2048 */
#define CI_NO_EC384 0x00020000U /* does not have EC 384 */
#define CI_NO_EC 0x00040000U /* No EC at all */
#define CI_RSA_4096 0x00080000U /* Card supports rsa 4096 */
#define CI_25519 0x00100000U /* Card supports ED25519 and X25519 */

/*
* Flags in the piv_object:
Expand Down Expand Up @@ -2720,14 +2725,21 @@ static int piv_generate_key(sc_card_t *card,
case 0x05: keydata->key_bits = 3072; break;
case 0x06: keydata->key_bits = 1024; break;
case 0x07: keydata->key_bits = 2048; break;
case 0x16: keydata->key_bits = 4096; break;
case 0x11: keydata->key_bits = 0;
keydata->ecparam = 0; /* we only support prime256v1 for 11 */
keydata->ecparam = 0; /* we only support prime256v1 */
keydata->ecparam_len =0;
break;
case 0x14: keydata->key_bits = 0;
keydata->ecparam = 0; /* we only support secp384r1 */
keydata->ecparam_len = 0;
break;
case 0xE0:
case 0xE1:
keydata->key_bits = 0;
keydata->ecparam = 0;
keydata->ecparam_len = 0;
break;
default:
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
}
Expand Down Expand Up @@ -2780,8 +2792,11 @@ static int piv_generate_key(sc_card_t *card,
keydata->pubkey_len = taglen;
memcpy (keydata->pubkey, tag, taglen);
}
}
else { /* must be EC */
// } else if (keydata->key_algid == 0xE0 || keydata->key_algid == 0xE1) {
// /* TODO DEE need to look at what gets returned */
/* TODO assume same as EC with tag 86 */

} else { /* must be EC */
tag = sc_asn1_find_tag(card->ctx, cp, in_len, 0x86, &taglen);
if (tag != NULL && taglen > 0) {
keydata->ecpoint = malloc(taglen);
Expand All @@ -2793,7 +2808,8 @@ static int piv_generate_key(sc_card_t *card,
}

/* TODO: -DEE Could add key to cache so could use engine to generate key,
* and sign req in single operation */
* and sign req in single operation or write temporary selfsigned
* certificate with new public key */
r = 0;
}

Expand Down Expand Up @@ -4512,6 +4528,12 @@ piv_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num)
}
} else
r = SC_ERROR_NO_CARD_SUPPORT;
} else if (env->algorithm == SC_ALGORITHM_EDDSA) {
priv->alg_id = 0xE0;
priv->key_size = 255;
} else if (env->algorithm == SC_ALGORITHM_XEDDSA) {
priv->alg_id = 0xE1;
priv->key_size = 255;
} else
r = SC_ERROR_NO_CARD_SUPPORT;
priv->key_ref = env->key_ref[0];
Expand Down Expand Up @@ -4541,6 +4563,7 @@ static int piv_validate_general_authentication(sc_card_t *card,
unsigned int cla, tag;
unsigned int real_alg_id, op_tag;

/* TODO check for 4096 keys */
u8 sbuf[4096]; /* needs work. for 3072 keys, needs 384+10 or so */
size_t sbuflen = sizeof(sbuf);
u8 rbuf[4096];
Expand All @@ -4561,6 +4584,7 @@ static int piv_validate_general_authentication(sc_card_t *card,
}
if (priv->operation == SC_SEC_OPERATION_DERIVE
&& priv->algorithm == SC_ALGORITHM_EC) {
/* TODO add code for X25519 */
op_tag = 0x85;
} else {
op_tag = 0x81;
Expand All @@ -4583,11 +4607,12 @@ static int piv_validate_general_authentication(sc_card_t *card,
case 128: real_alg_id = 0x06; break;
case 256: real_alg_id = 0x07; break;
case 384: real_alg_id = 0x05; break;
case 512: real_alg_id = 0x16; break;
default:
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NO_CARD_SUPPORT);
}
}
/* EC alg_id was already set */
/* EC and ED alg_id was already set */

r = piv_general_io(card, 0x87, real_alg_id, priv->key_ref,
sbuf, p - sbuf, rbuf, sizeof rbuf);
Expand Down Expand Up @@ -4649,6 +4674,18 @@ piv_compute_signature(sc_card_t *card, const u8 * data, size_t datalen,
goto err;

r = sc_asn1_decode_ecdsa_signature(card->ctx, rbuf, r, nLen, &out, outlen);
/* Yubikey 5.7.x supports ED25519 */
} else if (priv->alg_id == 0xE0) {
nLen = (priv->key_size + 7) / 8;
if (outlen < nLen) {
sc_log(card->ctx,
" output too small for ED signature %"SC_FORMAT_LEN_SIZE_T"u < %"SC_FORMAT_LEN_SIZE_T"u",
outlen, nLen);
r = SC_ERROR_INVALID_DATA;
goto err;
}
r = piv_validate_general_authentication(card, data, datalen, out, outlen);

} else { /* RSA is all set */
r = piv_validate_general_authentication(card, data, datalen, out, outlen);
}
Expand Down Expand Up @@ -5493,7 +5530,7 @@ static int piv_match_card_continued(sc_card_t *card)
apdu.resplen = sizeof(yubico_version_buf);
apdu.le = apdu.resplen;
r2 = sc_transmit_apdu(card, &apdu); /* on error yubico_version == 0 */
if (r2 >= 3) {
if (apdu.resplen == 3) {
priv->yubico_version = (yubico_version_buf[0]<<16) | (yubico_version_buf[1] <<8) | yubico_version_buf[2];
sc_log(card->ctx, "Yubico card->type=%d, r=0x%08x version=0x%08x", card->type, r, priv->yubico_version);
}
Expand Down Expand Up @@ -5608,6 +5645,9 @@ static int piv_match_card_continued(sc_card_t *card)
| CI_LEAKS_FILE_NOT_FOUND;
if (priv->yubico_version < 0x00040302)
priv->card_issues |= CI_VERIFY_LC0_FAIL;
/* TODO may need to relocate when I get card to test */
if (priv->yubico_version >= 0x00050700)
priv->card_issues |= CI_RSA_4096 | CI_25519;
break;

case SC_CARD_TYPE_PIV_II_GI_DE:
Expand Down Expand Up @@ -5691,6 +5731,8 @@ static int piv_init(sc_card_t *card)
int r = 0;
piv_private_data_t * priv = PIV_DATA(card);
unsigned long flags;
unsigned long flags_eddsa;
unsigned long flags_xeddsa;
unsigned long ext_flags;

SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
Expand Down Expand Up @@ -5739,15 +5781,28 @@ static int piv_init(sc_card_t *card)
_sc_card_add_rsa_alg(card, 1024, flags, 0); /* mandatory */
_sc_card_add_rsa_alg(card, 2048, flags, 0); /* optional */
_sc_card_add_rsa_alg(card, 3072, flags, 0); /* optional */
if (priv->card_issues & CI_RSA_4096)
_sc_card_add_rsa_alg(card, 4096, flags, 0); /* some Yubikeys support this */

if (!(priv->card_issues & CI_NO_EC)) {
int i;
flags = SC_ALGORITHM_ECDSA_RAW | SC_ALGORITHM_ECDH_CDH_RAW | SC_ALGORITHM_ECDSA_HASH_NONE;
ext_flags = SC_ALGORITHM_EXT_EC_NAMEDCURVE | SC_ALGORITHM_EXT_EC_UNCOMPRESES;
flags_eddsa = SC_ALGORITHM_EDDSA_RAW;
flags_xeddsa = SC_ALGORITHM_XEDDSA_RAW;

for (i = 0; ec_curves[i].oid.value[0] >= 0; i++) {
if (!(priv->card_issues & CI_NO_EC384 && ec_curves[i].size == 384))
_sc_card_add_ec_alg(card, ec_curves[i].size, flags, ext_flags, &ec_curves[i].oid);
if (ec_curves[i].key_type == SC_ALGORITHM_EC) {
if (!(priv->card_issues & CI_NO_EC384 && ec_curves[i].size == 384))
_sc_card_add_ec_alg(card, ec_curves[i].size, flags, ext_flags, &ec_curves[i].oid);

} else if (priv->card_issues & CI_25519) {
if (ec_curves[i].key_type == SC_ALGORITHM_EDDSA) {
_sc_card_add_eddsa_alg(card, ec_curves[i].size, flags_eddsa, ext_flags, &ec_curves[i].oid);
} else if (ec_curves[i].key_type == SC_ALGORITHM_XEDDSA) {
_sc_card_add_xeddsa_alg(card, ec_curves[i].size, flags_xeddsa, ext_flags, &ec_curves[i].oid);
}
}
}
}

Expand Down
25 changes: 23 additions & 2 deletions src/libopensc/pkcs15-piv.c
Original file line number Diff line number Diff line change
Expand Up @@ -886,6 +886,8 @@ static int sc_pkcs15emu_piv_init(sc_pkcs15_card_t *p15card)
break;

case SC_ALGORITHM_EC:
case SC_ALGORITHM_EDDSA:
case SC_ALGORITHM_XEDDSA:
ckis[i].pubkey_len = cert_out->key->u.ec.params.field_length;
if (ckis[i].cert_keyUsage_present) {
if (ckis[i].cert_keyUsage & SC_X509_DIGITAL_SIGNATURE) {
Expand Down Expand Up @@ -1082,7 +1084,9 @@ static int sc_pkcs15emu_piv_init(sc_pkcs15_card_t *p15card)
ckis[i].pubkey_from_file = 1;
break;
case SC_ALGORITHM_EC:
ckis[i].key_alg = SC_ALGORITHM_EC;
case SC_ALGORITHM_EDDSA:
case SC_ALGORITHM_XEDDSA:
ckis[i].key_alg = p15_key->algorithm;
ckis[i].pubkey_len = p15_key->u.ec.params.field_length;
ckis[i].pubkey_found = 1;
ckis[i].pubkey_from_file = 1;
Expand Down Expand Up @@ -1121,6 +1125,8 @@ static int sc_pkcs15emu_piv_init(sc_pkcs15_card_t *p15card)
ckis[i].pubkey_found = 1;
break;
case SC_ALGORITHM_EC:
case SC_ALGORITHM_EDDSA:
case SC_ALGORITHM_XEDDSA:
if (ckis[i].cert_keyUsage_present) {
pubkey_info.usage = ckis[i].pub_usage;
} else {
Expand All @@ -1131,7 +1137,14 @@ static int sc_pkcs15emu_piv_init(sc_pkcs15_card_t *p15card)
strncpy(pubkey_obj.label, pubkeys[i].label, SC_PKCS15_MAX_LABEL_SIZE - 1);

/* should not fail */
r = sc_pkcs15emu_add_ec_pubkey(p15card, &pubkey_obj, &pubkey_info);

if (ckis[i].key_alg == SC_ALGORITHM_EDDSA)
r = sc_pkcs15emu_add_eddsa_pubkey(p15card, &pubkey_obj, &pubkey_info);
else if (ckis[i].key_alg == SC_ALGORITHM_XEDDSA)
r = sc_pkcs15emu_add_xeddsa_pubkey(p15card, &pubkey_obj, &pubkey_info);
else
r = sc_pkcs15emu_add_ec_pubkey(p15card, &pubkey_obj, &pubkey_info);

LOG_TEST_GOTO_ERR(card->ctx, r, "Failed to add EC pubkey");

ckis[i].pubkey_found = 1;
Expand Down Expand Up @@ -1198,6 +1211,8 @@ static int sc_pkcs15emu_piv_init(sc_pkcs15_card_t *p15card)
r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info);
break;
case SC_ALGORITHM_EC:
case SC_ALGORITHM_EDDSA:
case SC_ALGORITHM_XEDDSA:
if (ckis[i].cert_keyUsage_present) {
prkey_info.usage |= ckis[i].priv_usage;
/* If retired key and non gov cert has NONREPUDIATION, treat as user_consent */
Expand All @@ -1210,6 +1225,12 @@ static int sc_pkcs15emu_piv_init(sc_pkcs15_card_t *p15card)
prkey_info.field_length = ckis[i].pubkey_len;
sc_log(card->ctx, "DEE added key_alg %2.2lx prkey_obj.flags %8.8x",
ckis[i].key_alg, prkey_obj.flags);

if (ckis[i].key_alg == SC_ALGORITHM_EDDSA)
r = sc_pkcs15emu_add_eddsa_prkey(p15card, &prkey_obj, &prkey_info);
else if (ckis[i].key_alg == SC_ALGORITHM_XEDDSA)
r = sc_pkcs15emu_add_xeddsa_prkey(p15card, &prkey_obj, &prkey_info);
else
r = sc_pkcs15emu_add_ec_prkey(p15card, &prkey_obj, &prkey_info);
break;
default:
Expand Down
29 changes: 29 additions & 0 deletions src/tools/piv-tool.c
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,14 @@ static int gen_key(const char * key_info)
case 0x14: keydata.key_bits = 0;
nid = NID_secp384r1;
break;
case 0xE0:
keydata.key_bits = 0;
nid = NID_ED25519;
break;
case 0xE1:
keydata.key_bits = 0;
nid = NID_X25519;
break;
#endif
default:
fprintf(stderr, "<keyref>:<algid> algid=RSA - 05, 06, 07 for 3072, 1024, 2048;EC - 11, 14 for 256, 384\n");
Expand Down Expand Up @@ -443,6 +451,27 @@ static int gen_key(const char * key_info)
EVP_PKEY_CTX_free(cctx);
OSSL_PARAM_free(params);
#endif

#ifdef EVP_PKEY_ED25519
} else if (nid == NID_ED25519 || nid == NID_X25519) {
#if OPENSSL_VERSION_NUMBER < 0x30000000L
fprintf(stderr, "This build of OpenSSL does not support ED25519 or X25519 keys\n");
return -1;
#else
evpkey = EVP_PKEY_new_raw_public_key(nid, NULL, keydata.ecpoint, keydata.ecpoint_len);
if (!evpkey) {
sc_log_openssl(ctx);
fprintf(stderr, "gen key failed ti copy 25519 pubkey\n");
return -1;
}

if (verbose)
EVP_PKEY_print_public_fp(stdout, evpkey, 0, NULL);
#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
#else
fprintf(stderr, "This build of OpenSSL does not support ED25519 or X25519 keys\n");
return -1;
#endif /* EVP_PKEY_ED25519 */
} else { /* EC key */
#if !defined(OPENSSL_NO_EC)
int i;
Expand Down

0 comments on commit 5af05d8

Please sign in to comment.