From 3ef24f98546300dc7dbd5608507bbbf99e7ebc3d Mon Sep 17 00:00:00 2001 From: Doug Engert Date: Wed, 14 Aug 2024 19:08:28 -0500 Subject: [PATCH] card-piv.c,pkcs15-piv.c,piv-tool.c - Support for RSA 4096 and 25519 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 --- src/libopensc/card-piv.c | 79 ++++++++++++++++++++++++++++++++------ src/libopensc/pkcs15-piv.c | 25 +++++++++++- src/tools/piv-tool.c | 29 ++++++++++++++ 3 files changed, 119 insertions(+), 14 deletions(-) diff --git a/src/libopensc/card-piv.c b/src/libopensc/card-piv.c index ca071c58edd..e5b8c2941ea 100644 --- a/src/libopensc/card-piv.c +++ b/src/libopensc/card-piv.c @@ -3,7 +3,7 @@ * card-default.c: Support for cards with no driver * * Copyright (C) 2001, 2002 Juha Yrjölä - * Copyright (C) 2005-2023 Douglas E. Engert + * Copyright (C) 2005-2024 Douglas E. Engert * Copyright (C) 2006, Identity Alliance, Thomas Harning * Copyright (C) 2007, EMC, Russell Larner * @@ -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 */ @@ -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: @@ -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); } @@ -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); @@ -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; } @@ -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]; @@ -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]; @@ -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; @@ -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); @@ -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); } @@ -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); } @@ -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: @@ -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); @@ -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); + } + } } } diff --git a/src/libopensc/pkcs15-piv.c b/src/libopensc/pkcs15-piv.c index fad10899e12..e41d571726e 100644 --- a/src/libopensc/pkcs15-piv.c +++ b/src/libopensc/pkcs15-piv.c @@ -900,6 +900,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) { @@ -1099,7 +1101,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; @@ -1138,6 +1142,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 { @@ -1148,7 +1154,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; @@ -1219,6 +1232,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 */ @@ -1231,6 +1246,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: diff --git a/src/tools/piv-tool.c b/src/tools/piv-tool.c index b9e3d4a5c30..935df25eb2d 100644 --- a/src/tools/piv-tool.c +++ b/src/tools/piv-tool.c @@ -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, ": algid=RSA - 05, 06, 07 for 3072, 1024, 2048;EC - 11, 14 for 256, 384\n"); @@ -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;