From b412843566cef7b1e4c551142d1e25b6bec2ebe8 Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Tue, 11 Oct 2022 22:13:10 +0200 Subject: [PATCH] MRTD BAC support and some other improvements --- lib/nfc/helpers/iso7816.c | 2 - lib/nfc/helpers/iso7816.h | 17 ++- lib/nfc/nfc_worker.c | 6 +- lib/nfc/protocols/mrtd.c | 179 ++++++++++++++++++++++++++-- lib/nfc/protocols/mrtd.h | 36 ++++-- lib/nfc/protocols/mrtd_helpers.c | 119 ++++++++++++++++++- lib/nfc/protocols/mrtd_helpers.h | 39 ++++++- test_iso7816_helpers.c | 70 +++++++++++ test_mrtd_helpers.c | 195 +++++++++++++++++++++++++++---- 9 files changed, 607 insertions(+), 56 deletions(-) diff --git a/lib/nfc/helpers/iso7816.c b/lib/nfc/helpers/iso7816.c index 5187aabbb..2cbff0809 100644 --- a/lib/nfc/helpers/iso7816.c +++ b/lib/nfc/helpers/iso7816.c @@ -6,8 +6,6 @@ TlvInfo iso7816_tlv_parse(const uint8_t* data) { TlvInfo tlv; - const uint8_t* org = data; - // Simple-TLV: tag can be any value from 1 to 254 (not '00' or 'FF') // BER-TLV: TODO describe // 00000 - 11110 => 0 - 30 (single byte) diff --git a/lib/nfc/helpers/iso7816.h b/lib/nfc/helpers/iso7816.h index 21e9bac34..883da0d33 100644 --- a/lib/nfc/helpers/iso7816.h +++ b/lib/nfc/helpers/iso7816.h @@ -5,8 +5,23 @@ #include #include +#define BER_CLASS_UNIVERSAL 0x0 +#define BER_CLASS_APPLICATION 0x1 +#define BER_CLASS_CONTEXT 0x2 +#define BER_CLASS_PRIVATE 0x3 + typedef struct { - uint16_t tag; + union { + uint16_t tag; + struct { + // LSB + uint8_t tag : 5; + uint8_t constructed : 1; + uint8_t class : 2; + // MSB + } ber; + //TODO: currently only works for 1-byte tags + }; size_t length; const uint8_t* value; diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 3012f121e..44a63c816 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -214,7 +214,7 @@ static bool nfc_worker_read_bank_card(NfcWorker* nfc_worker, FuriHalNfcTxRxConte return read_success; } -static bool nfc_worker_read_mrtd(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { +static bool nfc_worker_read_mrtd(NfcWorker* nfc_worker, MrtdData* mrtd_data, FuriHalNfcTxRxContext* tx_rx) { bool read_success = false; MrtdApplication* mrtd_app = mrtd_alloc_init(tx_rx); //EmvData* result = &nfc_worker->dev_data->emv_data; @@ -227,7 +227,7 @@ static bool nfc_worker_read_mrtd(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* t //mrtd_select(mrtd_app, EF.DIR); //mrtd_select_efcardaccess(mrtd_app); //mrtd_select_efdir(mrtd_app); - mrtd_test(mrtd_app); + mrtd_test(mrtd_app, mrtd_data); if(!mrtd_select_app(mrtd_app, AID.eMRTDApplication)) break; //TODO: read general informatie @@ -296,7 +296,7 @@ static bool nfc_worker_read_nfca(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* t furi_hal_nfc_sleep(); // Needed between checks FURI_LOG_D(TAG, "Try reading MRTD"); //TODO: support NFC-B? - if(nfc_worker_read_mrtd(nfc_worker, tx_rx)) { + if(nfc_worker_read_mrtd(nfc_worker, &nfc_worker->dev_data->mrtd_data, tx_rx)) { nfc_worker->dev_data->protocol = NfcDeviceProtocolMRTD; break; } diff --git a/lib/nfc/protocols/mrtd.c b/lib/nfc/protocols/mrtd.c index eefaa15d9..17fdfdf7a 100644 --- a/lib/nfc/protocols/mrtd.c +++ b/lib/nfc/protocols/mrtd.c @@ -1,5 +1,7 @@ #include +#include "../helpers/iso7816.h" + #include "mrtd.h" #define TAG "Mrtd" @@ -13,6 +15,16 @@ //- PACE (CONDITIONAL) //- BAC (CONDITIONAL) +static void hexdump(FuriLogLevel level, char* prefix, void* data, size_t length) { + if(furi_log_get_level() >= level) { + printf("%s ", prefix); + for(size_t i = 0; i < length; i++) { + printf("%02X ", ((uint8_t*)data)[i]); + } + printf("\r\n"); + } +} + static void mrtd_trace(MrtdApplication* app) { FuriHalNfcTxRxContext* tx_rx = app->tx_rx; if(furi_log_get_level() == FuriLogLevelTrace) { @@ -20,7 +32,7 @@ static void mrtd_trace(MrtdApplication* app) { for(size_t i = 0; i < tx_rx->tx_bits / 8; i++) { printf("%02X ", tx_rx->tx_data[i]); } - printf("\r\nRX: "); + printf("\r\nRX: ", tx_rx->rx_data); for(size_t i = 0; i < tx_rx->rx_bits / 8; i++) { printf("%02X ", tx_rx->rx_data[i]); } @@ -116,23 +128,53 @@ bool mrtd_send_apdu(MrtdApplication* app, uint8_t cla, uint8_t ins, uint8_t p1, if(ret_code == 0x9000) { return true; } else { - FURI_LOG_E(TAG, "APDU answer is not 0x9000, but 0x%04X", ret_code); + FURI_LOG_I(TAG, "APDU answer is not 0x9000, but 0x%04X", ret_code); return false; } } return false; } +//TODO: rename commands to "mrtd_cmd_..." bool mrtd_select_app(MrtdApplication* app, AIDValue aid) { FURI_LOG_D(TAG, "Send select App: %02X %02X %02X %02X %02X %02X %02X", aid[0], aid[1], aid[2], aid[3], aid[4], aid[5], aid[6]); if(!mrtd_send_apdu(app, 0x00, 0xA4, 0x04, 0x0C, 0x07, aid, -1)) { - FURI_LOG_E(TAG, "Failed select App"); + FURI_LOG_W(TAG, "Failed select App"); return false; } return true; } +bool mrtd_get_challenge(MrtdApplication* app, uint8_t challenge[8]) { + FURI_LOG_D(TAG, "Send Get Challenge"); + if(!mrtd_send_apdu(app, 0x00, 0x84, 0x00, 0x00, 0x00, NULL, 0x08)) { + FURI_LOG_W(TAG, "Failed get challenge"); + return false; + } + + FuriHalNfcTxRxContext* tx_rx = app->tx_rx; + memcpy(challenge, tx_rx->rx_data, 8); + + return true; +} + +bool mrtd_external_authenticate(MrtdApplication* app, uint8_t* cmd_data, size_t cmd_size, uint8_t* out_data, size_t out_size) { + furi_assert(cmd_size == 0x28); + furi_assert(out_size >= 0x28); + + FURI_LOG_D(TAG, "Send External Authenticate"); + if(!mrtd_send_apdu(app, 0x00, 0x82, 0x00, 0x00, cmd_size, cmd_data, 0x28)) { + FURI_LOG_W(TAG, "Failed External Authenticate"); + return false; + } + + FuriHalNfcTxRxContext* tx_rx = app->tx_rx; + memcpy(out_data, tx_rx->rx_data, 0x28); + + return true; +} + bool mrtd_select_file(MrtdApplication* app, EFFile file) { uint8_t data[] = {file.file_id >> 8, file.file_id & 0xff}; FURI_LOG_D(TAG, "Send select EF: 0x%04X", file.file_id); @@ -144,6 +186,7 @@ bool mrtd_select_file(MrtdApplication* app, EFFile file) { return true; } +//TODO: use out parameter to point to rx_data buffer instead of require allocating another size_t mrtd_read_binary(MrtdApplication* app, uint8_t* buffer, size_t bufsize, size_t offset) { UNUSED(buffer); UNUSED(bufsize); @@ -160,6 +203,9 @@ size_t mrtd_read_binary(MrtdApplication* app, uint8_t* buffer, size_t bufsize, s return 0; } +//TODO: use short id to read, because it's mandatory for eMRTD +//TODO: check for support of extended length in EF.ATR/INFO, see ISO7816-4 + void mrtd_read_dump(MrtdApplication* app, EFFile file, const char* descr) { FURI_LOG_D(TAG, "Read and dump %s:", descr); if(!mrtd_select_file(app, file)) { @@ -168,16 +214,70 @@ void mrtd_read_dump(MrtdApplication* app, EFFile file, const char* descr) { uint8_t data[2048]; size_t read = 0; do { - read = mrtd_read_binary(app, data+read, sizeof(data)-read, read); + read = mrtd_read_binary(app, data + read, sizeof(data) - read, read); } while(read > 0); } -void mrtd_test(MrtdApplication* app) { +void parse_ef_dir(EF_DIR_contents* EF_DIR, const uint8_t* data, size_t length) { + size_t offset = 0; + uint8_t app_idx = 0; + + memset(EF_DIR->applications, 0x00, sizeof(EF_DIR->applications)); + EF_DIR->applications_count = 0; + + while(offset < length) { + TlvInfo tlv = iso7816_tlv_parse(data + offset); + + if(tlv.tag != 0x61 || tlv.length != 0x09) { + FURI_LOG_E(TAG, "Invalid EF.DIR, tag at offset %d must be '61' and length 9. Got '%02X' and %d", offset, tlv.tag, tlv.length); + return; + } + + tlv = iso7816_tlv_parse(tlv.value); + if(tlv.tag != 0x4F || tlv.length != 0x07) { + FURI_LOG_E(TAG, "Invalid EF.DIR, subtag at offset %d must be '4F' and length 7", offset); + return; + } + + memcpy(EF_DIR->applications[app_idx], tlv.value, tlv.length); + EF_DIR->applications_count = ++app_idx; + + offset = tlv.next - data; + } + + //TODO: remove testing block: + FURI_LOG_D(TAG, "EF.DIR applications: %d", EF_DIR->applications_count); + if(furi_log_get_level() >= FuriLogLevelDebug) { + for(uint8_t i=0; iapplications_count; ++i) { + printf("- "); + for(uint8_t n=0; napplications[i][n]); + } + printf("\r\n"); + } + } +} + +//TODO: remove testing function +void mrtd_test(MrtdApplication* app, MrtdData* mrtd_data) { + FuriHalNfcTxRxContext* tx_rx = app->tx_rx; + FURI_LOG_D(TAG, "Mrtd Test"); mrtd_read_dump(app, EF.ATR, "EF.ATR"); mrtd_read_dump(app, EF.DIR, "EF.DIR"); + parse_ef_dir(&app->files.EF_DIR, tx_rx->rx_data, tx_rx->rx_bits / 8 - 2); // bits to bytes, and exclude the 2 byte return code mrtd_read_dump(app, EF.CardAccess, "EF.CardAccess"); mrtd_read_dump(app, EF.CardSecurity, "EF.CardSecurity"); + + mrtd_select_app(app, AID.eMRTDApplication); + + //TODO: remove details + /* + mrtd_data->auth.birth_date = (MrtdDate){.year=69, .month=8, .day=6}; + mrtd_data->auth.expiry_date = (MrtdDate){.year=94, .month=6, .day=23}; + memcpy(mrtd_data->auth.doc_number, "L898902C<", 9); + */ + mrtd_bac(app, &mrtd_data->auth); } MrtdApplication* mrtd_alloc_init(FuriHalNfcTxRxContext* tx_rx) { @@ -193,21 +293,84 @@ void mrtd_free(MrtdApplication* app) { free(app); } -bool mrtd_bac(MrtdApplication* app) { +bool mrtd_bac(MrtdApplication* app, MrtdAuthData* auth) { UNUSED(app); static bool rand_generator_inited = false; + uint8_t rnd_ic[8]; uint8_t rnd_ifd[8]; uint8_t k_ifd[16]; if(!rand_generator_inited) { - // TODO: should this maybe be system wide? + // TODO: should random initialization maybe be system wide? srand(DWT->CYCCNT); rand_generator_inited = true; } + mrtd_get_challenge(app, rnd_ic); + //TODO: remove memcpy rnd_ic + //memcpy(rnd_ic, "\x46\x08\xF9\x19\x88\x70\x22\x12", 8); + furi_hal_random_fill_buf(rnd_ifd, 8); furi_hal_random_fill_buf(k_ifd, 16); + //TODO: remove testing code: + //memcpy(rnd_ifd, "\x78\x17\x23\x86\x0C\x06\xC2\x26", 8); + //memcpy(k_ifd, "\x0B\x79\x52\x40\xCB\x70\x49\xB0\x1C\x19\xB3\x3E\x32\x80\x4F\x0B", 16); - return false; //TODO: return true + uint8_t kenc[16]; + uint8_t kmac[16]; + + if(!mrtd_bac_keys(auth, kenc, kmac)) { + FURI_LOG_E(TAG, "Failed to calculate BAC keys"); + return false; + } + + uint8_t S[32]; + memcpy(S, rnd_ifd, 8); + memcpy(S+8, rnd_ic, 8); + memcpy(S+16, k_ifd, 16); + + hexdump(FuriLogLevelDebug, "S:", S, 32); + + uint8_t cmd_data[40]; + uint8_t *eifd = cmd_data; + uint8_t *kifd = cmd_data+32; + mrtd_bac_encrypt(S, 32, kenc, eifd); + mrtd_bac_padded_mac(eifd, 32, kmac, kifd); + + uint8_t response[40]; + if(!mrtd_external_authenticate(app, cmd_data, 40, response, 40)) { + FURI_LOG_E(TAG, "BAC External Authenticate failed"); + return false; + } + + uint8_t buffer[32]; // Received R = RND.IC (8) || RND.IFD (8) || KIC (16) + if(!mrtd_bac_decrypt_verify(response, 40, kenc, kmac, buffer)) { + FURI_LOG_W(TAG, "BAC DecryptVerify failed"); + } + + uint8_t *rnd_ifd_recv = buffer + 8; + uint8_t *kic = buffer + 16; + + if(memcmp(rnd_ifd, rnd_ifd_recv, 8)) { + FURI_LOG_W(TAG, "BAC RND.IFD sent and received mismatch."); + } + + uint8_t kseed[16]; + for(uint8_t i=0; i<16; ++i) { + kseed[i] = kifd[i] ^ kic[i]; + } + + if(!mrtd_bac_keys_from_seed(kseed, app->ksenc, app->ksmac)) { + FURI_LOG_E(TAG, "BAC error, could not derive KSenc and KSmac"); + return false; + } + + hexdump(FuriLogLevelTrace, "RND.IC:", rnd_ic, 8); + hexdump(FuriLogLevelTrace, "RND.IFS:", rnd_ifd, 8); + + app->ssc_long = mrtd_ssc_from_data(rnd_ic, rnd_ifd); + FURI_LOG_D(TAG, "SSC: %01llX", app->ssc_long); + + return true; } diff --git a/lib/nfc/protocols/mrtd.h b/lib/nfc/protocols/mrtd.h index 7d8c19bf9..81210c986 100644 --- a/lib/nfc/protocols/mrtd.h +++ b/lib/nfc/protocols/mrtd.h @@ -4,12 +4,34 @@ #include "mrtd_helpers.h" +#define MAX_EFDIR_APPS 4 + +typedef uint8_t AIDValue[7]; + +struct AIDSet { + AIDValue eMRTDApplication; + AIDValue TravelRecords; + AIDValue VisaRecords; + AIDValue AdditionalBiometrics; +}; + +extern struct AIDSet AID; + +typedef struct { + AIDValue applications[MAX_EFDIR_APPS]; + uint8_t applications_count; +} EF_DIR_contents; + typedef struct { FuriHalNfcTxRxContext* tx_rx; uint16_t file_offset; uint8_t ksenc[16]; uint8_t ksmac[16]; uint64_t ssc_long; + + struct { + EF_DIR_contents EF_DIR; + } files; } MrtdApplication; typedef struct { @@ -51,19 +73,9 @@ struct EFFormat { extern struct EFFormat EF; -typedef uint8_t AIDValue[7]; - -struct AIDSet { - AIDValue eMRTDApplication; - AIDValue TravelRecords; - AIDValue VisaRecords; - AIDValue AdditionalBiometrics; -}; - -extern struct AIDSet AID; - //TODO: description MrtdApplication* mrtd_alloc_init(FuriHalNfcTxRxContext* tx_rx); bool mrtd_select_app(MrtdApplication* app, AIDValue aid); bool mrtd_select_file(MrtdApplication* app, EFFile file); -void mrtd_test(MrtdApplication* app); +void mrtd_test(MrtdApplication* app, MrtdData* mrtd_data); +bool mrtd_bac(MrtdApplication* app, MrtdAuthData* auth); diff --git a/lib/nfc/protocols/mrtd_helpers.c b/lib/nfc/protocols/mrtd_helpers.c index f8adb0cf7..d5b529734 100644 --- a/lib/nfc/protocols/mrtd_helpers.c +++ b/lib/nfc/protocols/mrtd_helpers.c @@ -1,18 +1,20 @@ #include "mrtd_helpers.h" +#include //TODO: remove + #include #include static inline unsigned char *ucstr(const char *str) { return (unsigned char *)str; } -uint8_t mrtd_bac_check_digit(const uint8_t* input, const uint8_t length) { +uint8_t mrtd_bac_check_digit(const char* input, const uint8_t length) { const uint8_t num_weights = 3; uint8_t weights[] = {7, 3, 1}; uint8_t check_digit = 0; uint8_t idx; for(uint8_t i=0; i= 'A' && c <= 'Z') { idx = c - 'A' + 10; } else if(c >= 'a' && c <= 'z') { @@ -27,7 +29,7 @@ uint8_t mrtd_bac_check_digit(const uint8_t* input, const uint8_t length) { return check_digit; } -void mrtd_print_date(uint8_t* output, MrtdDate* date) { +void mrtd_print_date(char* output, MrtdDate* date) { output[0] = (date->year / 10) + '0'; output[1] = (date->year % 10) + '0'; output[2] = (date->month / 10) + '0'; @@ -36,7 +38,7 @@ void mrtd_print_date(uint8_t* output, MrtdDate* date) { output[5] = (date->day % 10) + '0'; } -bool mrtd_bac_get_kmrz(MrtdAuthData* auth, uint8_t* output, uint8_t output_size) { +bool mrtd_bac_get_kmrz(MrtdAuthData* auth, char* output, uint8_t output_size) { uint8_t idx = 0; uint8_t docnr_length = strlen(auth->doc_number); uint8_t cd_idx = 0; @@ -68,7 +70,7 @@ bool mrtd_bac_get_kmrz(MrtdAuthData* auth, uint8_t* output, uint8_t output_size) return true; } -bool mrtd_bac_keys(const uint8_t kseed[16], uint8_t ksenc[16], uint8_t ksmac[16]) { +bool mrtd_bac_keys_from_seed(const uint8_t kseed[16], uint8_t ksenc[16], uint8_t ksmac[16]) { uint8_t hash[20]; mbedtls_sha1_context ctx; mbedtls_sha1_init(&ctx); @@ -99,3 +101,110 @@ bool mrtd_bac_keys(const uint8_t kseed[16], uint8_t ksenc[16], uint8_t ksmac[16] mbedtls_sha1_free(&ctx); return true; } + +bool mrtd_bac_keys(MrtdAuthData* auth, uint8_t ksenc[16], uint8_t ksmac[16]) { + uint8_t kmrz_max_length = MRTD_DOCNR_MAX_LENGTH + 16; + char kmrz[kmrz_max_length]; + if(!mrtd_bac_get_kmrz(auth, kmrz, kmrz_max_length)) { + return false; + } + + printf("kmrz: %s\r\n", kmrz); //TODO: remove + + uint8_t hash[20]; + mbedtls_sha1((uint8_t*)kmrz, strlen(kmrz), hash); + + if(!mrtd_bac_keys_from_seed(hash, ksenc, ksmac)) { + return false; + } + + return true; +} + +//NOTE: output size will be ((data_length+8)/8)*8 +bool mrtd_bac_encrypt(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t* output) { + uint8_t IV[8] = "\x00\x00\x00\x00\x00\x00\x00\x00"; + + mbedtls_des3_context ctx; + mbedtls_des3_init(&ctx); + mbedtls_des3_set2key_enc(&ctx, key); + if(mbedtls_des3_crypt_cbc(&ctx, MBEDTLS_DES_ENCRYPT, data_length, IV, data, output)) { + return false; + } + mbedtls_des3_free(&ctx); + + return true; +} + +bool mrtd_bac_decrypt(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t* output) { + uint8_t IV[8] = "\x00\x00\x00\x00\x00\x00\x00\x00"; + + mbedtls_des3_context ctx; + mbedtls_des3_init(&ctx); + mbedtls_des3_set2key_dec(&ctx, key); + if(mbedtls_des3_crypt_cbc(&ctx, MBEDTLS_DES_DECRYPT, data_length, IV, data, output)) { + return false; + } + mbedtls_des3_free(&ctx); + + return true; +} + +bool mrtd_bac_decrypt_verify(const uint8_t* data, size_t data_length, uint8_t* key_enc, uint8_t* key_mac, uint8_t* output) { + mrtd_bac_decrypt(data, data_length - 8, key_enc, output); + + uint8_t mac_calc[8]; + mrtd_bac_padded_mac(data, data_length - 8, key_mac, mac_calc); + + if(memcmp(mac_calc, data + data_length - 8, 8)) { + printf( "MAC failed\n"); + return false; + } + return true; +} + +bool mrtd_bac_mac(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t* output) { + // MAC + uint8_t mac[8]; + uint8_t xormac[8]; + uint8_t tmp[8]; + mbedtls_des_context ctx; + + mbedtls_des_init(&ctx); + mbedtls_des_setkey_enc(&ctx, key); + + memset(mac, 0, 8); + for(size_t i=0; i #include +#include #include "lib/nfc/protocols/mrtd_helpers.h" -// gcc -o test_mrtd_helpers -Ilib/mbedtls/include lib/nfc/protocols/mrtd_helpers.c lib/mbedtls/library/sha1.c lib/mbedtls/library/platform_util.c test_mrtd_helpers.c +// gcc -o test_mrtd_helpers -Wall -Ilib/mbedtls/include lib/nfc/protocols/mrtd_helpers.c lib/mbedtls/library/sha1.c lib/mbedtls/library/des.c lib/mbedtls/library/platform_util.c test_mrtd_helpers.c #define COLOR_RED "\033[0;31m" #define COLOR_GREEN "\033[0;32m" @@ -15,7 +16,7 @@ void print_hex(const uint8_t* data, size_t length) { } } -void test_mrtd_bac_check_digit(const uint8_t* input, const uint8_t exp_output) { +void test_mrtd_bac_check_digit(const char* input, const uint8_t exp_output) { uint8_t output = mrtd_bac_check_digit(input, strlen(input)); if(output != exp_output) { printf(COLOR_RED "FAILED - mrtd_bac_check_digit for %s is not %d, but %d\n" COLOR_RESET, @@ -27,9 +28,9 @@ void test_mrtd_bac_check_digit(const uint8_t* input, const uint8_t exp_output) { input, output); } -void test_bac_get_kmrz(MrtdAuthData* auth, const uint8_t* exp_output) { +void test_bac_get_kmrz(MrtdAuthData* auth, const char* exp_output) { bool result; - uint8_t buffer[255]; + char buffer[255]; result = mrtd_bac_get_kmrz(auth, buffer, 255); if(!result) { @@ -49,7 +50,7 @@ void test_bac_get_kmrz(MrtdAuthData* auth, const uint8_t* exp_output) { void test_sha1(const uint8_t* data, const uint8_t* exp_output) { uint8_t hash[20]; - mbedtls_sha1(data, strlen(data), hash); + mbedtls_sha1(data, strlen((char*)data), hash); if(memcmp(hash, exp_output, 20)) { printf(COLOR_RED "FAILED - sha1 of %s, expected:\n", data); @@ -63,18 +64,18 @@ void test_sha1(const uint8_t* data, const uint8_t* exp_output) { printf("\n" COLOR_RESET); } -void test_mrtd_bac_keys(const uint8_t kseed[16], const uint8_t exp_ksenc[16], const uint8_t exp_ksmac[16]) { +void test_mrtd_bac_keys_from_seed(const uint8_t kseed[16], const uint8_t exp_ksenc[16], const uint8_t exp_ksmac[16]) { uint8_t ksenc[16]; uint8_t ksmac[16]; - if(!mrtd_bac_keys(kseed, ksenc, ksmac)) { - printf(COLOR_RED "FAILED - mrtd_bac_keys returned FALSE for "); + if(!mrtd_bac_keys_from_seed(kseed, ksenc, ksmac)) { + printf(COLOR_RED "FAILED - mrtd_bac_keys_from_seed returned FALSE for "); print_hex(kseed, 16); printf(COLOR_RESET "\n"); return; } if(memcmp(exp_ksenc, ksenc, 16)) { - printf(COLOR_RED "FAILED - mrtd_bac_keys of "); + printf(COLOR_RED "FAILED - mrtd_bac_keys_from_seed of "); print_hex(kseed, 16); printf(", expected ksenc:\n"); print_hex(exp_ksenc, 16); @@ -82,7 +83,7 @@ void test_mrtd_bac_keys(const uint8_t kseed[16], const uint8_t exp_ksenc[16], co print_hex(ksenc, 16); return; } else if(memcmp(exp_ksmac, ksmac, 16)) { - printf(COLOR_RED "FAILED - mrtd_bac_keys of "); + printf(COLOR_RED "FAILED - mrtd_bac_keys_from_seed of "); print_hex(kseed, 16); printf(", expected ksmac:\n"); print_hex(exp_ksmac, 16); @@ -90,7 +91,7 @@ void test_mrtd_bac_keys(const uint8_t kseed[16], const uint8_t exp_ksenc[16], co print_hex(ksmac, 16); return; } else { - printf(COLOR_GREEN "SUCCESS - mrtd_bac_keys of "); + printf(COLOR_GREEN "SUCCESS - mrtd_bac_keys_from_seed of "); print_hex(kseed, 16); printf(" ksenc: "); print_hex(ksenc, 16); @@ -100,6 +101,107 @@ void test_mrtd_bac_keys(const uint8_t kseed[16], const uint8_t exp_ksenc[16], co } } +void test_mrtd_bac_keys(MrtdAuthData* auth, const uint8_t exp_ksenc[16], const uint8_t exp_ksmac[16]) { + uint8_t ksenc[16]; + uint8_t ksmac[16]; + if(!mrtd_bac_keys(auth, ksenc, ksmac)) { + printf(COLOR_RED "FAILED - mrtd_bac_keys returned FALSE\n" COLOR_RESET); + return; + } + + if(memcmp(exp_ksenc, ksenc, 16)) { + printf(COLOR_RED "FAILED - mrtd_bac_keys, expected ksenc:\n"); + print_hex(exp_ksenc, 16); + printf(" is:\n"); + print_hex(ksenc, 16); + return; + } else if(memcmp(exp_ksmac, ksmac, 16)) { + printf(COLOR_RED "FAILED - mrtd_bac_keys, expected ksmac:\n"); + print_hex(exp_ksmac, 16); + printf(" is:\n"); + print_hex(ksmac, 16); + return; + } else { + printf(COLOR_GREEN "SUCCESS - mrtd_bac_keys ksenc: "); + print_hex(ksenc, 16); + printf(" ksmac: "); + print_hex(ksmac, 16); + printf(COLOR_RESET "\n"); + } +} + +void test_mrtd_bac_encrypt(uint8_t* data, size_t data_length, uint8_t* key, uint8_t* exp_output, size_t exp_output_length) { + uint8_t buffer[256]; + + // buffer size must be at least ((data_length+8)/8)*8 + mrtd_bac_encrypt(data, data_length, key, buffer); + + if(memcmp(exp_output, buffer, exp_output_length)) { + printf(COLOR_RED "FAILED - mrtd_bac_encrypt, expected output:\n"); + print_hex(exp_output, exp_output_length); + printf(" is:\n"); + print_hex(buffer, exp_output_length); + return; + } else { + printf(COLOR_GREEN "SUCCESS - mrtd_bac_encrypt output: "); + print_hex(buffer, exp_output_length); + printf(COLOR_RESET "\n"); + } +} + +void test_mrtd_bac_padded_mac(uint8_t* data, size_t data_length, uint8_t* key, uint8_t* exp_output, size_t exp_output_length) { + uint8_t mac[8]; + if(!mrtd_bac_padded_mac(data, data_length, key, mac)) { + printf("ERROR BAC MAC"); + return; + } + + if(memcmp(exp_output, mac, exp_output_length)) { + printf(COLOR_RED "FAILED - mrtd_bac_padded_mac, expected output:\n"); + print_hex(exp_output, exp_output_length); + printf(" is:\n"); + print_hex(mac, 8); + return; + } else { + printf(COLOR_GREEN "SUCCESS - mrtd_bac_padded_mac output: "); + print_hex(mac, 8); + printf(COLOR_RESET "\n"); + } +} + +void test_mrtd_bac_decrypt_verify(const uint8_t* data, size_t data_length, uint8_t* key_enc, uint8_t* key_mac, uint8_t* exp_output, size_t exp_output_length, bool should_verify) { + uint8_t buffer[256]; + + bool result = mrtd_bac_decrypt_verify(data, data_length, key_enc, key_mac, buffer); + if(result != should_verify) { + printf(COLOR_RED "FAILED - mrtd_bac_decrypt_verify, expected verify: %d, but is: %d\n", should_verify, result); + return; + } + + if(memcmp(exp_output, buffer, exp_output_length)) { + printf(COLOR_RED "FAILED - mrtd_bac_decrypt_verify, expected output:\n"); + print_hex(exp_output, exp_output_length); + printf(" is:\n"); + print_hex(buffer, 32); + return; + } + + printf(COLOR_GREEN "SUCCESS - mrtd_bac_decrypt_verify output: "); + print_hex(buffer, exp_output_length); + printf(COLOR_RESET "\n"); +} + +void test_mrtd_ssc_from_data(const uint8_t* rnd_ic, const uint8_t* rnd_ifd, uint64_t exp_ssc) { + uint64_t ssc_long = mrtd_ssc_from_data(rnd_ic, rnd_ifd); + + if(ssc_long != exp_ssc) { + printf(COLOR_RED "FAILED - mrtd_ssc_from_data, expected ssc: %016lx, but is: %016lx\n" COLOR_RESET, exp_ssc, ssc_long); + return; + } + + printf(COLOR_GREEN "SUCCESS - mrtd_ssc_from_data output: %016lx\n" COLOR_RESET, ssc_long); +} + int main(int argc, char** argv) { test_mrtd_bac_check_digit("D23145890734", 9); test_mrtd_bac_check_digit("340712", 7); @@ -109,19 +211,70 @@ int main(int argc, char** argv) { .doc_number = "D23145890734", .birth_date = {34, 7, 12}, .expiry_date = {95, 7, 12}, - }, "D23145890734934071279507122"); + }, "D23145890734934071279507122" + ); test_bac_get_kmrz(&(MrtdAuthData){ - .doc_number = "L898902C", - .birth_date = {69, 8, 6}, - .expiry_date = {94, 6, 23}, - }, "L898902C<369080619406236"); + .doc_number = "L898902C", + .birth_date = {69, 8, 6}, + .expiry_date = {94, 6, 23}, + }, "L898902C<369080619406236" + ); - test_sha1("L898902C<369080619406236", "\x23\x9a\xb9\xcb\x28\x2d\xaf\x66\x23\x1d\xc5\xa4\xdf\x6b\xfb\xae\xdf\x47\x75\x65"); + test_sha1((uint8_t*)"L898902C<369080619406236", (uint8_t*)"\x23\x9a\xb9\xcb\x28\x2d\xaf\x66\x23\x1d\xc5\xa4\xdf\x6b\xfb\xae\xdf\x47\x75\x65"); - test_mrtd_bac_keys( - "\x23\x9a\xb9\xcb\x28\x2d\xaf\x66\x23\x1d\xc5\xa4\xdf\x6b\xfb\xae", - "\xab\x94\xfd\xec\xf2\x67\x4f\xdf\xb9\xb3\x91\xf8\x5d\x7f\x76\xf2", - "\x79\x62\xd9\xec\xe0\x3d\x1a\xcd\x4c\x76\x08\x9d\xce\x13\x15\x43"); + test_mrtd_bac_keys_from_seed( + (uint8_t*)"\x23\x9a\xb9\xcb\x28\x2d\xaf\x66\x23\x1d\xc5\xa4\xdf\x6b\xfb\xae", + (uint8_t*)"\xab\x94\xfd\xec\xf2\x67\x4f\xdf\xb9\xb3\x91\xf8\x5d\x7f\x76\xf2", + (uint8_t*)"\x79\x62\xd9\xec\xe0\x3d\x1a\xcd\x4c\x76\x08\x9d\xce\x13\x15\x43" + ); + + test_mrtd_bac_keys(&(MrtdAuthData){ + .doc_number = "L898902C", + .birth_date = {69, 8, 6}, + .expiry_date = {94, 6, 23}, + }, + (uint8_t*)"\xab\x94\xfd\xec\xf2\x67\x4f\xdf\xb9\xb3\x91\xf8\x5d\x7f\x76\xf2", + (uint8_t*)"\x79\x62\xd9\xec\xe0\x3d\x1a\xcd\x4c\x76\x08\x9d\xce\x13\x15\x43" + ); + + test_mrtd_bac_encrypt( + /*input*/ (uint8_t*)"\x78\x17\x23\x86\x0C\x06\xC2\x26\x46\x08\xF9\x19\x88\x70\x22\x12\x0B\x79\x52\x40\xCB\x70\x49\xB0\x1C\x19\xB3\x3E\x32\x80\x4F\x0B", + /*size*/ 32, + /*key*/ (uint8_t*)"\xAB\x94\xFD\xEC\xF2\x67\x4F\xDF\xB9\xB3\x91\xF8\x5D\x7F\x76\xF2", + /*exp output*/ (uint8_t*)"\x72\xC2\x9C\x23\x71\xCC\x9B\xDB\x65\xB7\x79\xB8\xE8\xD3\x7B\x29\xEC\xC1\x54\xAA\x56\xA8\x79\x9F\xAE\x2F\x49\x8F\x76\xED\x92\xF2", + /*exp_output_size*/ 32 + ); + + test_mrtd_bac_padded_mac( + /*input*/ (uint8_t*)"\x72\xC2\x9C\x23\x71\xCC\x9B\xDB\x65\xB7\x79\xB8\xE8\xD3\x7B\x29\xEC\xC1\x54\xAA\x56\xA8\x79\x9F\xAE\x2F\x49\x8F\x76\xED\x92\xF2", + /*size*/ 32, + /*key*/ (uint8_t*)"\x79\x62\xD9\xEC\xE0\x3D\x1A\xCD\x4C\x76\x08\x9D\xCE\x13\x15\x43", + /*exp output*/ (uint8_t*)"\x5F\x14\x48\xEE\xA8\xAD\x90\xA7", + /*exp_output_size*/ 8 + ); + + test_mrtd_bac_decrypt_verify( + /*input*/ (uint8_t*)"\x46\xB9\x34\x2A\x41\x39\x6C\xD7\x38\x6B\xF5\x80\x31\x04\xD7\xCE\xDC\x12\x2B\x91\x32\x13\x9B\xAF\x2E\xED\xC9\x4E\xE1\x78\x53\x4F\x2F\x2D\x23\x5D\x07\x4D\x74\x49", + /*size*/ 40, + /*key_enc*/ (uint8_t*)"\xAB\x94\xFD\xEC\xF2\x67\x4F\xDF\xB9\xB3\x91\xF8\x5D\x7F\x76\xF2", + /*key_mac*/ (uint8_t*)"\x79\x62\xD9\xEC\xE0\x3D\x1A\xCD\x4C\x76\x08\x9D\xCE\x13\x15\x43", + /*exp output*/ (uint8_t*)"\x46\x08\xF9\x19\x88\x70\x22\x12\x78\x17\x23\x86\x0C\x06\xC2\x26\x0B\x4F\x80\x32\x3E\xB3\x19\x1C\xB0\x49\x70\xCB\x40\x52\x79\x0B", + /*exp_output_size*/ 32, + /*should_verify*/ 1 + ); + + //TODO: test that does not verify + + test_mrtd_bac_keys_from_seed( + (uint8_t*)"\x00\x36\xD2\x72\xF5\xC3\x50\xAC\xAC\x50\xC3\xF5\x72\xD2\x36\x00", + (uint8_t*)"\x97\x9E\xC1\x3B\x1C\xBF\xE9\xDC\xD0\x1A\xB0\xFE\xD3\x07\xEA\xE5", + (uint8_t*)"\xF1\xCB\x1F\x1F\xB5\xAD\xF2\x08\x80\x6B\x89\xDC\x57\x9D\xC1\xF8" + ); + + uint8_t* rnd_ic = (uint8_t*)"\x46\x08\xF9\x19\x88\x70\x22\x12"; + uint8_t* rnd_ifd = (uint8_t*)"\x78\x17\x23\x86\x0C\x06\xC2\x26"; + + test_mrtd_ssc_from_data(rnd_ic, rnd_ifd, 0x887022120C06C226); return 0; }