From 9fe59a551a91104ed3832ad7e7815389ea9b2512 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Tue, 26 Mar 2024 12:13:51 +0100 Subject: [PATCH] Added support for ATTESTATION. Signed-off-by: Pol Henarejos --- src/openpgp/piv.c | 178 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 167 insertions(+), 11 deletions(-) diff --git a/src/openpgp/piv.c b/src/openpgp/piv.c index a6cc47b..3d05e70 100644 --- a/src/openpgp/piv.c +++ b/src/openpgp/piv.c @@ -29,6 +29,7 @@ #include "asn1.h" #include "mbedtls/aes.h" #include "mbedtls/des.h" +#include "mbedtls/x509_crt.h" #include "openpgp.h" #define PIV_ALGO_3DES 0x03 @@ -55,6 +56,10 @@ #define ORIGIN_GENERATED 0x01 #define ORIGIN_IMPORTED 0x02 +#define IS_RETIRED(x) ((x) >= EF_PIV_KEY_RETIRED1 && (x) <= EF_PIV_KEY_RETIRED20) +#define IS_ACTIVE(x) ((x) >= EF_PIV_KEY_AUTHENTICATION && (x) <= EF_PIV_KEY_CARDAUTH) +#define IS_KEY(x) ((IS_ACTIVE((x))) || (IS_RETIRED((x)))) + uint8_t piv_aid[] = { 5, 0xA0, 0x00, 0x00, 0x03, 0x8, @@ -72,6 +77,67 @@ bool has_pwpiv = false; uint8_t session_pwpiv[32]; int piv_process_apdu(); + + +static int x509_create_cert(void *pk_ctx, uint8_t algo, uint8_t slot, bool attestation, uint8_t *buffer, size_t buffer_size) { + mbedtls_x509write_cert ctx; + mbedtls_x509write_crt_init(&ctx); + mbedtls_x509write_crt_set_version(&ctx, MBEDTLS_X509_CRT_VERSION_3); + mbedtls_x509write_crt_set_validity(&ctx, "20240325000000", "20741231235959"); + uint8_t serial[20]; + random_gen(NULL, serial, sizeof(serial)); + mbedtls_x509write_crt_set_serial_raw(&ctx, serial, sizeof(serial)); + mbedtls_pk_context skey, ikey; + mbedtls_ecdsa_context actx; // attestation key + mbedtls_pk_init(&skey); + mbedtls_pk_init(&ikey); + if (algo == PIV_ALGO_RSA1024 || algo == PIV_ALGO_RSA2048) { + mbedtls_pk_setup(&skey, mbedtls_pk_info_from_type(MBEDTLS_PK_RSA)); + } + else if (algo == PIV_ALGO_ECCP256 || algo == PIV_ALGO_ECCP384) { + mbedtls_pk_setup(&skey, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY)); + } + skey.pk_ctx = pk_ctx; + mbedtls_x509write_crt_set_subject_key(&ctx, &skey); + char buf_sname[256]; + if (attestation) { + sprintf(buf_sname, "C=ES,O=Pico Keys,CN=Pico OpenPGP PIV Attestation %X", slot); + mbedtls_x509write_crt_set_subject_name(&ctx, buf_sname); + sprintf(buf_sname, "C=ES,O=Pico Keys,CN=Pico OpenPGP PIV Slot %X", slot); + mbedtls_x509write_crt_set_issuer_name(&ctx, buf_sname); + file_t *ef_key = search_by_fid(EF_PIV_KEY_ATTESTATION, NULL, SPECIFY_EF); + mbedtls_ecdsa_init(&actx); + load_private_key_ecdsa(&actx, ef_key, false); + mbedtls_pk_setup(&ikey, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY)); + ikey.pk_ctx = &actx; + mbedtls_x509write_crt_set_issuer_key(&ctx, &ikey); + } + else { + sprintf(buf_sname, "C=ES,O=Pico Keys,CN=Pico OpenPGP PIV Slot %X", slot); + mbedtls_x509write_crt_set_issuer_name(&ctx, buf_sname); + mbedtls_x509write_crt_set_subject_name(&ctx, buf_sname); + mbedtls_x509write_crt_set_issuer_key(&ctx, &skey); + } + if (algo == PIV_ALGO_ECCP384) { + mbedtls_x509write_crt_set_md_alg(&ctx, MBEDTLS_MD_SHA384); + } + else { + mbedtls_x509write_crt_set_md_alg(&ctx, MBEDTLS_MD_SHA256); + } + mbedtls_x509write_crt_set_basic_constraints(&ctx, 0, 0); + mbedtls_x509write_crt_set_subject_key_identifier(&ctx); + mbedtls_x509write_crt_set_authority_key_identifier(&ctx); + mbedtls_x509write_crt_set_key_usage(&ctx, + MBEDTLS_X509_KU_DIGITAL_SIGNATURE | + MBEDTLS_X509_KU_KEY_CERT_SIGN); + int ret = mbedtls_x509write_crt_der(&ctx, buffer, buffer_size, random_gen, NULL); + /* skey cannot be freed, as it is freed later */ + if (attestation) { + mbedtls_ecdsa_free(&actx); + } + return ret; +} + static void scan_files() { scan_flash(); file_t *ef = search_by_fid(EF_PIV_KEY_CARDMGM, NULL, SPECIFY_EF); @@ -153,6 +219,20 @@ static void scan_files() { flash_write_data_to_file(ef, dhash, sizeof(dhash)); } } + if ((ef = search_by_fid(EF_PIV_KEY_ATTESTATION, NULL, SPECIFY_ANY))) { + if (!ef->data) { + printf("ATTESTATION key is empty. Initializing with random one\r\n"); + mbedtls_ecdsa_context ecdsa; + mbedtls_ecdsa_init(&ecdsa); + int r = mbedtls_ecdsa_genkey(&ecdsa, MBEDTLS_ECP_DP_SECP384R1, random_gen, NULL); + r = store_keys(&ecdsa, ALGO_ECDSA, EF_PIV_KEY_ATTESTATION, false); + uint8_t cert[2048]; + r = x509_create_cert(&ecdsa, PIV_ALGO_ECCP384, 0xF9, false, cert, sizeof(cert)); + ef = search_by_fid(EF_PIV_ATTESTATION, NULL, SPECIFY_ANY); + flash_write_data_to_file(ef, cert + sizeof(cert) - r, r); + mbedtls_ecdsa_free(&ecdsa); + } + } low_flash_available(); } @@ -708,8 +788,8 @@ static int cmd_asym_keygen() { if (key_ref == 0x93) { key_ref = EF_PIV_KEY_RETIRED18; } - else if (key_ref == 0xF9) { - key_ref = EF_PIV_KEY_ATTESTATION; + if (key_ref != EF_PIV_KEY_AUTHENTICATION && key_ref != EF_PIV_KEY_SIGNATURE && key_ref != EF_PIV_KEY_KEYMGM && key_ref != EF_PIV_KEY_CARDAUTH && !(key_ref >= EF_PIV_KEY_RETIRED1 && key_ref <= EF_PIV_KEY_RETIRED20)) { + return SW_INCORRECT_P1P2(); } asn1_ctx_t ctxi, aac = {0}; asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi); @@ -723,24 +803,43 @@ static int cmd_asym_keygen() { if (asn1_len(&a80) == 0) { return SW_WRONG_DATA(); } + uint16_t key_cert = 0; + if (key_ref == EF_PIV_KEY_AUTHENTICATION) { + key_cert = EF_PIV_AUTHENTICATION; + } + else if (key_ref == EF_PIV_KEY_SIGNATURE) { + key_cert = EF_PIV_SIGNATURE; + } + else if (key_ref == EF_PIV_KEY_KEYMGM) { + key_cert = EF_PIV_KEY_MANAGEMENT; + } + else if (key_ref == EF_PIV_KEY_CARDAUTH) { + key_cert = EF_PIV_CARD_AUTH; + } + else { + key_cert = key_ref + 0xC08B; + } if (a80.data[0] == PIV_ALGO_RSA1024 || a80.data[0] == PIV_ALGO_RSA2048) { printf("KEYPAIR RSA\r\n"); asn1_ctx_t a81 = {0}; asn1_find_tag(&aac, 0x81, &a81); mbedtls_rsa_context rsa; mbedtls_rsa_init(&rsa); - uint8_t index = 0; int exponent = 65537, nlen = (a80.data[0] == PIV_ALGO_RSA1024 ? 1024 : 2048); if (asn1_len(&a81)) { exponent = (int)asn1_get_uint(&a81); } - int r = mbedtls_rsa_gen_key(&rsa, random_gen, &index, nlen, exponent); + int r = mbedtls_rsa_gen_key(&rsa, random_gen, NULL, nlen, exponent); if (r != 0) { mbedtls_rsa_free(&rsa); return SW_EXEC_ERROR(); } - r = store_keys(&rsa, ALGO_RSA, key_ref, false); make_rsa_response(&rsa); + uint8_t cert[2048]; + r = x509_create_cert(&rsa, a80.data[0], key_ref, false, cert, sizeof(cert)); + file_t *ef = search_by_fid(key_cert, NULL, SPECIFY_ANY); + flash_write_data_to_file(ef, cert + sizeof(cert) - r, r); + r = store_keys(&rsa, ALGO_RSA, key_ref, false); mbedtls_rsa_free(&rsa); if (r != CCID_OK) { return SW_EXEC_ERROR(); @@ -751,14 +850,17 @@ static int cmd_asym_keygen() { mbedtls_ecp_group_id gid = a80.data[0] == PIV_ALGO_ECCP256 ? MBEDTLS_ECP_DP_SECP256R1 : MBEDTLS_ECP_DP_SECP384R1; mbedtls_ecdsa_context ecdsa; mbedtls_ecdsa_init(&ecdsa); - uint8_t index = 0; - int r = mbedtls_ecdsa_genkey(&ecdsa, gid, random_gen, &index); + int r = mbedtls_ecdsa_genkey(&ecdsa, gid, random_gen, NULL); if (r != 0) { mbedtls_ecdsa_free(&ecdsa); return SW_EXEC_ERROR(); } - r = store_keys(&ecdsa, ALGO_ECDSA, key_ref, false); make_ecdsa_response(&ecdsa); + uint8_t cert[2048]; + r = x509_create_cert(&ecdsa, a80.data[0], key_ref, false, cert, sizeof(cert)); + file_t *ef = search_by_fid(key_cert, NULL, SPECIFY_ANY); + flash_write_data_to_file(ef, cert + sizeof(cert) - r, r); + r = store_keys(&ecdsa, ALGO_ECDSA, key_ref, false); mbedtls_ecdsa_free(&ecdsa); if (r != CCID_OK) { return SW_EXEC_ERROR(); @@ -836,14 +938,12 @@ static int cmd_set_mgmkey() { return SW_OK(); } -#define IS_RETIRED(x) ((x) >= EF_PIV_KEY_RETIRED1 && (x) <= EF_PIV_KEY_RETIRED20) -#define IS_ACTIVE(x) ((x) >= EF_PIV_KEY_AUTHENTICATION && (x) <= EF_PIV_KEY_CARDAUTH) static int cmd_move_key() { if (apdu.nc != 0) { return SW_WRONG_LENGTH(); } uint8_t to = P1(apdu), from = P2(apdu); - if ((!IS_RETIRED(from) && !IS_ACTIVE(from)) || (!IS_RETIRED(to) && !IS_ACTIVE(to))) { + if (!IS_KEY(to) || !IS_KEY(from)) { return SW_INCORRECT_P1P2(); } if (from == 0x93) { @@ -957,6 +1057,60 @@ static int cmd_reset() { return SW_OK(); } +static int cmd_attestation() { + uint8_t key_ref = P1(apdu); + if (P2(apdu) != 0x00) { + return SW_INCORRECT_P1P2(); + } + if (!IS_KEY(key_ref)) { + return SW_REFERENCE_NOT_FOUND(); + } + file_t *ef_key = NULL; + int meta_len = 0; + uint8_t *meta = NULL; + if (!(ef_key = search_by_fid(key_ref, NULL, SPECIFY_EF)) || !file_has_data(ef_key)) { + return SW_REFERENCE_NOT_FOUND(); + } + if ((meta_len = meta_find(key_ref, &meta)) <= 0) { + return SW_REFERENCE_NOT_FOUND(); + } + if (meta[3] != ORIGIN_GENERATED) { + return SW_COMMAND_NOT_ALLOWED(); + } + int r = 0; + if (meta[0] == PIV_ALGO_RSA1024 || meta[0] == PIV_ALGO_RSA2048) { + mbedtls_rsa_context ctx; + mbedtls_rsa_init(&ctx); + r = load_private_key_rsa(&ctx, ef_key, false); + if (r != CCID_OK) { + mbedtls_rsa_free(&ctx); + return SW_EXEC_ERROR(); + } + r = x509_create_cert(&ctx, meta[0], key_ref, true, res_APDU, 2048); + mbedtls_rsa_free(&ctx); + } + else if (meta[0] == PIV_ALGO_ECCP256 || meta[0] == PIV_ALGO_ECCP384) { + mbedtls_ecdsa_context ctx; + mbedtls_ecdsa_init(&ctx); + r = load_private_key_ecdsa(&ctx, ef_key, false); + if (r != CCID_OK) { + mbedtls_ecdsa_free(&ctx); + return SW_EXEC_ERROR(); + } + r = x509_create_cert(&ctx, meta[0], key_ref, true, res_APDU, 2048); + mbedtls_ecdsa_free(&ctx); + } + else { + return SW_WRONG_DATA(); + } + if (r <= 0) { + return SW_EXEC_ERROR(); + } + memmove(res_APDU, res_APDU + 2048 - r, r); + res_APDU_size = r; + return SW_OK(); +} + #define INS_VERIFY 0x20 #define INS_VERSION 0xFD #define INS_SELECT 0xA4 @@ -973,6 +1127,7 @@ static int cmd_reset() { #define INS_RESET_RETRY 0x2C #define INS_SET_RETRIES 0xFA #define INS_RESET 0xFB +#define INS_ATTESTATION 0xF9 static const cmd_t cmds[] = { { INS_VERSION, cmd_version }, @@ -990,6 +1145,7 @@ static const cmd_t cmds[] = { { INS_RESET_RETRY, cmd_reset_retry }, { INS_SET_RETRIES, cmd_set_retries }, { INS_RESET, cmd_reset }, + { INS_ATTESTATION, cmd_attestation }, { 0x00, 0x0 } };