From 2f24c3d9a89b04d3fabd68f50c53391fac3bb6ab Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Tue, 19 Mar 2024 17:56:36 +0100 Subject: [PATCH] Added support for PIV signature. Signed-off-by: Pol Henarejos --- src/openpgp/openpgp.h | 14 +++- src/openpgp/piv.c | 151 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 144 insertions(+), 21 deletions(-) diff --git a/src/openpgp/openpgp.h b/src/openpgp/openpgp.h index 92f4d1d..e068736 100644 --- a/src/openpgp/openpgp.h +++ b/src/openpgp/openpgp.h @@ -31,9 +31,21 @@ extern bool has_pw1; extern bool has_pw3; -extern int store_keys(void *key_ctx, int type, uint16_t key_id); +extern int store_keys(void *key_ctx, int type, uint16_t key_id, bool use_kek); extern void make_rsa_response(mbedtls_rsa_context *rsa); extern void make_ecdsa_response(mbedtls_ecdsa_context *ecdsa); +extern int ecdsa_sign(mbedtls_ecdsa_context *ctx, + const uint8_t *data, + size_t data_len, + uint8_t *out, + size_t *out_len); +extern int rsa_sign(mbedtls_rsa_context *ctx, + const uint8_t *data, + size_t data_len, + uint8_t *out, + size_t *out_len); +extern int load_private_key_rsa(mbedtls_rsa_context *ctx, file_t *fkey, bool use_dek); +extern int load_private_key_ecdsa(mbedtls_ecdsa_context *ctx, file_t *fkey, bool use_dek); #define ALGO_RSA 0x01 #define ALGO_ECDH 0x12 diff --git a/src/openpgp/piv.c b/src/openpgp/piv.c index d1f9db9..e6f3085 100644 --- a/src/openpgp/piv.c +++ b/src/openpgp/piv.c @@ -141,8 +141,8 @@ static void scan_files() { } } bool reset_dek = false; - if ((ef = search_by_fid(EF_DEK, NULL, SPECIFY_ANY)) || true) { - if (file_get_size(ef) == 0 || file_get_size(ef) == IV_SIZE+32*3 || true) { + if ((ef = search_by_fid(EF_DEK, NULL, SPECIFY_ANY))) { + if (file_get_size(ef) == 0 || file_get_size(ef) == IV_SIZE+32*3) { printf("DEK is empty or older\r\n"); const uint8_t defpin[6] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36 }; const uint8_t *dek = random_bytes_get(IV_SIZE + 32); @@ -173,7 +173,7 @@ static void scan_files() { if ((ef = search_by_fid(EF_PIV_PIN, NULL, SPECIFY_ANY))) { if (!ef->data || reset_dek) { printf("PIV PIN is empty. Initializing with default password\r\n"); - const uint8_t def[6] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36 }; + const uint8_t def[8] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0xFF, 0xFF }; uint8_t dhash[33]; dhash[0] = sizeof(def); double_hash_pin(def, sizeof(def), dhash + 1); @@ -379,6 +379,7 @@ static int cmd_get_metadata() { } uint8_t challenge[16]; bool has_challenge = false; +bool has_mgm = false; static int cmd_authenticate() { uint8_t algo = P1(apdu), key_ref = P2(apdu); if (apdu.nc == 0) { @@ -387,6 +388,19 @@ static int cmd_authenticate() { if (apdu.data[0] != 0x7C) { return SW_WRONG_DATA(); } + if (key_ref == EF_PIV_KEY_CARDMGM) { + if (algo != PIV_ALGO_AES128 && algo != PIV_ALGO_AES192 && algo != PIV_ALGO_AES256) { + return SW_INCORRECT_P1P2(); + } + file_t *ef_mgm = search_by_fid(key_ref, NULL, SPECIFY_EF); + if (!file_has_data(ef_mgm)) { + return SW_MEMORY_FAILURE(); + } + uint16_t mgm_len = file_get_size(ef_mgm); + if ((algo == PIV_ALGO_AES128 && mgm_len != 16) || (algo == PIV_ALGO_AES192 && mgm_len != 24) || (algo == PIV_ALGO_AES256 && mgm_len != 32)) { + return SW_INCORRECT_P1P2(); + } + } asn1_ctx_t ctxi, a7c = { 0 }; asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi); if (!asn1_find_tag(&ctxi, 0x7C, &a7c) || asn1_len(&ctxi) == 0) { @@ -403,14 +417,11 @@ static int cmd_authenticate() { if (key_ref != EF_PIV_KEY_CARDMGM) { return SW_INCORRECT_P1P2(); } - file_t *ef_mgm = search_by_fid(EF_PIV_KEY_CARDMGM, NULL, SPECIFY_EF); + file_t *ef_mgm = search_by_fid(key_ref, NULL, SPECIFY_EF); if (!file_has_data(ef_mgm)) { return SW_MEMORY_FAILURE(); } uint16_t mgm_len = file_get_size(ef_mgm); - if ((algo == PIV_ALGO_AES128 && mgm_len != 16) || (algo == PIV_ALGO_AES192 && mgm_len != 24) || (algo == PIV_ALGO_AES256 && mgm_len != 32)) { - return SW_INCORRECT_P1P2(); - } mbedtls_aes_context ctx; mbedtls_aes_init(&ctx); int r = mbedtls_aes_setkey_enc(&ctx, file_get_data(ef_mgm), mgm_len * 8); @@ -428,8 +439,8 @@ static int cmd_authenticate() { if (r != 0) { return SW_EXEC_ERROR(); } + has_challenge = true; } - has_challenge = true; } else { if (!has_challenge) { @@ -442,19 +453,79 @@ static int cmd_authenticate() { return SW_INCORRECT_PARAMS(); } if (key_ref != EF_PIV_KEY_CARDMGM) { - return SW_INCORRECT_P1P2(); + return SW_INCORRECT_P1P2(); + } + has_mgm = true; + } + } + if (a81.data) { + if (!a81.len) { + memcpy(challenge, random_bytes_get(sizeof(challenge)), sizeof(challenge)); + res_APDU[res_APDU_size++] = 0x7C; + res_APDU[res_APDU_size++] = 10; + res_APDU[res_APDU_size++] = 0x81; + res_APDU[res_APDU_size++] = sizeof(challenge); + memcpy(res_APDU + res_APDU_size, challenge, sizeof(challenge)); + res_APDU_size += sizeof(challenge); + has_challenge = true; + } + else { + file_t *ef_key = search_by_fid(key_ref, NULL, SPECIFY_EF); + if (!file_has_data(ef_key)) { + return SW_MEMORY_FAILURE(); + } + if (algo == PIV_ALGO_RSA1024 || algo == PIV_ALGO_RSA2048) { + mbedtls_rsa_context ctx; + mbedtls_rsa_init(&ctx); + int r = load_private_key_rsa(&ctx, ef_key, false); + if (r != CCID_OK) { + mbedtls_rsa_free(&ctx); + return SW_EXEC_ERROR(); } - file_t *ef_mgm = search_by_fid(EF_PIV_KEY_CARDMGM, NULL, SPECIFY_EF); - if (!file_has_data(ef_mgm)) { - return SW_MEMORY_FAILURE(); + size_t olen = 0; + res_APDU[res_APDU_size++] = 0x7C; + res_APDU[res_APDU_size++] = 10; + res_APDU[res_APDU_size++] = 0x82; + res_APDU[res_APDU_size++] = 0x82; + r = rsa_sign(&ctx, a81.data, a81.len, res_APDU + res_APDU_size + 2, &olen); + mbedtls_rsa_free(&ctx); + res_APDU[res_APDU_size++] = olen >> 8; + res_APDU[res_APDU_size++] = olen & 0xFF; + res_APDU_size += olen; + if (r != 0) { + return SW_EXEC_ERROR(); } - uint16_t mgm_len = file_get_size(ef_mgm); - if ((algo == PIV_ALGO_AES128 && mgm_len != 16) || (algo == PIV_ALGO_AES192 && mgm_len != 24) || (algo == PIV_ALGO_AES256 && mgm_len != 32)) { - return SW_INCORRECT_P1P2(); + } + else if (algo == PIV_ALGO_ECCP256 || algo == PIV_ALGO_ECCP384) { + mbedtls_ecdsa_context ctx; + mbedtls_ecdsa_init(&ctx); + int r = load_private_key_ecdsa(&ctx, ef_key, false); + if (r != CCID_OK) { + mbedtls_ecdsa_free(&ctx); + return SW_EXEC_ERROR(); + } + size_t olen = 0; + res_APDU[res_APDU_size++] = 0x7C; + res_APDU[res_APDU_size++] = 10; + res_APDU[res_APDU_size++] = 0x82; + res_APDU[res_APDU_size++] = 0x82; + r = ecdsa_sign(&ctx, a81.data, a81.len, res_APDU + res_APDU_size + 2, &olen); + mbedtls_ecdsa_free(&ctx); + res_APDU[res_APDU_size++] = olen >> 8; + res_APDU[res_APDU_size++] = olen & 0xFF; + res_APDU_size += olen; + if (r != 0) { + return SW_EXEC_ERROR(); + } + } + else if (algo == PIV_ALGO_AES128 || algo == PIV_ALGO_AES192 || algo == PIV_ALGO_AES256) { + uint16_t key_len = file_get_size(ef_key); + if (a81.len % 16 != 0) { + return SW_DATA_INVALID(); } mbedtls_aes_context ctx; mbedtls_aes_init(&ctx); - int r = mbedtls_aes_setkey_enc(&ctx, file_get_data(ef_mgm), mgm_len * 8); + int r = mbedtls_aes_setkey_enc(&ctx, file_get_data(ef_key), key_len * 8); if (r != 0) { mbedtls_aes_free(&ctx); return SW_EXEC_ERROR(); @@ -462,13 +533,50 @@ static int cmd_authenticate() { res_APDU[res_APDU_size++] = 0x7C; res_APDU[res_APDU_size++] = 10; res_APDU[res_APDU_size++] = 0x82; - res_APDU[res_APDU_size++] = 16; + res_APDU[res_APDU_size++] = a81.len; r = mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, a81.data, res_APDU + res_APDU_size); - res_APDU_size += 16; mbedtls_aes_free(&ctx); + res_APDU_size += a81.len; if (r != 0) { return SW_EXEC_ERROR(); } + } + } + } + if (a82.data) { + if (!a82.len) { + // Should be handled by a81 or a80 + } + else { + if (key_ref != EF_PIV_KEY_CARDMGM) { + return SW_INCORRECT_P1P2(); + } + if (!has_challenge) { + return SW_COMMAND_NOT_ALLOWED(); + } + if (sizeof(challenge) != a82.len) { + return SW_DATA_INVALID(); + } + file_t *ef_key = search_by_fid(key_ref, NULL, SPECIFY_EF); + if (!file_has_data(ef_key)) { + return SW_MEMORY_FAILURE(); + } + uint16_t key_len = file_get_size(ef_key); + mbedtls_aes_context ctx; + mbedtls_aes_init(&ctx); + int r = mbedtls_aes_setkey_dec(&ctx, file_get_data(ef_key), key_len * 8); + if (r != 0) { + mbedtls_aes_free(&ctx); + return SW_EXEC_ERROR(); + } + r = mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_DECRYPT, a82.data, res_APDU); + mbedtls_aes_free(&ctx); + if (r != 0) { + return SW_EXEC_ERROR(); + } + if (memcmp(res_APDU, challenge, sizeof(challenge)) != 0) { + return SW_DATA_INVALID(); + } } } return SW_OK(); @@ -485,6 +593,9 @@ static int cmd_asym_keygen() { if (P1(apdu) != 0x0) { return SW_INCORRECT_P1P2(); } + if (!has_mgm) { + return SW_SECURITY_STATUS_NOT_SATISFIED(); + } asn1_ctx_t ctxi, aac = {0}; asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi); if (!asn1_find_tag(&ctxi, 0xAC, &aac) || asn1_len(&aac) == 0) { @@ -510,7 +621,7 @@ static int cmd_asym_keygen() { mbedtls_rsa_free(&rsa); return SW_EXEC_ERROR(); } - r = store_keys(&rsa, ALGO_RSA, key_ref); + r = store_keys(&rsa, ALGO_RSA, key_ref, false); make_rsa_response(&rsa); mbedtls_rsa_free(&rsa); if (r != CCID_OK) { @@ -528,7 +639,7 @@ static int cmd_asym_keygen() { mbedtls_ecdsa_free(&ecdsa); return SW_EXEC_ERROR(); } - r = store_keys(&ecdsa, ALGO_ECDSA, key_ref); + r = store_keys(&ecdsa, ALGO_ECDSA, key_ref, false); make_ecdsa_response(&ecdsa); mbedtls_ecdsa_free(&ecdsa); if (r != CCID_OK) {