diff --git a/lib/nfc/protocols/mrtd.c b/lib/nfc/protocols/mrtd.c index 323b0440c..c0ac5215d 100644 --- a/lib/nfc/protocols/mrtd.c +++ b/lib/nfc/protocols/mrtd.c @@ -100,21 +100,29 @@ struct AIDSet AID = { bool mrtd_send_apdu(MrtdApplication* app, uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t lc, const void* data, int16_t le) { FuriHalNfcTxRxContext* tx_rx = app->tx_rx; + size_t idx = 0; FURI_LOG_D(TAG, "Send APDU, lc: %d, le: %d", lc, le); - size_t idx = 0; - tx_rx->tx_data[idx++] = cla; - tx_rx->tx_data[idx++] = ins; - tx_rx->tx_data[idx++] = p1; - tx_rx->tx_data[idx++] = p2; - if(lc > 0) { - tx_rx->tx_data[idx++] = lc; - memcpy(tx_rx->tx_data + idx, data, lc); - idx += lc; - } - if(le >= 0) { - tx_rx->tx_data[idx++] = le&0xff; + if(app->secure_messaging) { + FURI_LOG_D(TAG, "Protect APDU"); + + app->ssc_long++; + idx = mrtd_protect_apdu(cla, ins, p1, p2, lc, data, le, app->ksenc, app->ksmac, app->ssc_long, tx_rx->tx_data); + + } else { + tx_rx->tx_data[idx++] = cla; + tx_rx->tx_data[idx++] = ins; + tx_rx->tx_data[idx++] = p1; + tx_rx->tx_data[idx++] = p2; + if(lc > 0) { + tx_rx->tx_data[idx++] = lc; + memcpy(tx_rx->tx_data + idx, data, lc); + idx += lc; + } + if(le >= 0) { + tx_rx->tx_data[idx++] = le&0xff; + } } tx_rx->tx_bits = idx * 8; @@ -208,6 +216,7 @@ size_t mrtd_read_binary(MrtdApplication* app, uint8_t* buffer, size_t bufsize, s 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)) { return; } @@ -258,12 +267,22 @@ void parse_ef_dir(EF_DIR_contents* EF_DIR, const uint8_t* data, size_t length) { } } +void parse_ef_com(EF_COM_contents* EF_COM, const uint8_t* data, size_t length) { + UNUSED(EF_COM); //TODO + UNUSED(length); //TODO + size_t offset = 0; + + TlvInfo tlv = iso7816_tlv_parse(data + offset); + UNUSED(tlv); //TODO +} + //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.COM, "EF.COM"); 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"); @@ -294,6 +313,12 @@ void mrtd_test(MrtdApplication* app, MrtdData* mrtd_data) { default: break; } + + if(!mrtd_data->auth_success) { + return; + } + + mrtd_read_dump(app, EF.COM, "EF.COM"); } MrtdApplication* mrtd_alloc_init(FuriHalNfcTxRxContext* tx_rx) { @@ -388,5 +413,7 @@ bool mrtd_bac(MrtdApplication* app, MrtdAuthData* auth) { app->ssc_long = mrtd_ssc_from_data(rnd_ic, rnd_ifd); FURI_LOG_D(TAG, "SSC: %01llX", app->ssc_long); + app->secure_messaging = true; + return true; } diff --git a/lib/nfc/protocols/mrtd.h b/lib/nfc/protocols/mrtd.h index b44b26d99..f207fd97f 100644 --- a/lib/nfc/protocols/mrtd.h +++ b/lib/nfc/protocols/mrtd.h @@ -22,12 +22,20 @@ typedef struct { uint8_t applications_count; } EF_DIR_contents; +typedef struct { + uint16_t lds_version; + uint16_t unicode_version; + //TODO: taglist +} EF_COM_contents; + typedef struct { FuriHalNfcTxRxContext* tx_rx; uint16_t file_offset; uint8_t ksenc[16]; uint8_t ksmac[16]; - uint64_t ssc_long; + uint64_t ssc_long; // TODO: rename without _long + + bool secure_messaging; struct { EF_DIR_contents EF_DIR; diff --git a/lib/nfc/protocols/mrtd_helpers.c b/lib/nfc/protocols/mrtd_helpers.c index 3484010ad..22ce7e6c8 100644 --- a/lib/nfc/protocols/mrtd_helpers.c +++ b/lib/nfc/protocols/mrtd_helpers.c @@ -1,6 +1,7 @@ #include "mrtd_helpers.h" #include //TODO: remove +#include #include #include @@ -169,6 +170,96 @@ bool mrtd_bac_decrypt_verify(const uint8_t* data, size_t data_length, uint8_t* k return true; } +bool mrtd_bac_mac_init(mrtd_bac_mac_ctx* ctx, uint8_t key[16]) { + mbedtls_des_init(&ctx->des); + mbedtls_des_setkey_enc(&ctx->des, key); + memset(ctx->mac, 0, 8); + ctx->idx_in = 0; + memcpy(ctx->key, key, 16); + return true; +} + +bool mrtd_bac_mac_update(mrtd_bac_mac_ctx* ctx, const uint8_t* data, size_t data_length) { + size_t data_idx = 0; + //uint8_t* xormac = ctx->xormac; + + if(ctx->idx_in != 0) { + uint8_t buff_add = 8 - ctx->idx_in; + if(data_length < buff_add) { + buff_add = data_length; + } + memcpy(ctx->buffer_in + ctx->idx_in, data, buff_add); + ctx->idx_in = (ctx->idx_in + buff_add) % 8; + data_idx += buff_add; + + if(ctx->idx_in == 0) { // buffer_in filled + for(uint8_t j=0; j<8; ++j) { + ctx->xormac[j] = ctx->mac[j] ^ ctx->buffer_in[j]; + } + mbedtls_des_crypt_ecb(&ctx->des, ctx->xormac, ctx->mac); + + printf("DES1: %02X %02X %02X %02X %02X %02X %02X %02X\n", + ctx->buffer_in[0], ctx->buffer_in[1], ctx->buffer_in[2], ctx->buffer_in[3], + ctx->buffer_in[4], ctx->buffer_in[5], ctx->buffer_in[6], ctx->buffer_in[7]); + + //printf("DES1: %02X %02X %02X %02X %02X %02X %02X %02X\n", + //xormac[0], xormac[1], xormac[2], xormac[3], + //xormac[4], xormac[5], xormac[6], xormac[7]); + } + } + + while(true) { + if(data_idx + 8 > data_length) { + // Not a full block + break; + } + for(uint8_t j=0; j<8; ++j) { + ctx->xormac[j] = ctx->mac[j] ^ data[data_idx++]; + } + + mbedtls_des_crypt_ecb(&ctx->des, ctx->xormac, ctx->mac); + printf("DES1: %02X %02X %02X %02X %02X %02X %02X %02X\n", + data[data_idx - 8 + 0], data[data_idx - 8 + 1], data[data_idx - 8 + 2], data[data_idx - 8 + 3], + data[data_idx - 8 + 4], data[data_idx - 8 + 5], data[data_idx - 8 + 6], data[data_idx - 8 + 7]); + + //printf("DES1: %02X %02X %02X %02X %02X %02X %02X %02X\n", + //xormac[0], xormac[1], xormac[2], xormac[3], + //xormac[4], xormac[5], xormac[6], xormac[7]); + } + + if(data_idx < data_length) { + ctx->idx_in = data_length - data_idx; + memcpy(ctx->buffer_in, data + data_idx, ctx->idx_in); + } + + return true; +} + +bool mrtd_bac_mac_pad(mrtd_bac_mac_ctx* ctx) { + memset(ctx->buffer_in + ctx->idx_in, 0x00, 8 - ctx->idx_in); + ctx->buffer_in[ctx->idx_in] = 0x80; + ctx->idx_in = 8; + + mrtd_bac_mac_update(ctx, NULL, 0); // Force processing the buffer_in + return true; +} + +bool mrtd_bac_mac_finalize(mrtd_bac_mac_ctx* ctx, uint8_t output[8]) { + mrtd_bac_mac_pad(ctx); + + uint8_t tmp[8]; + mbedtls_des_init(&ctx->des); + mbedtls_des_setkey_dec(&ctx->des, ctx->key+8); + mbedtls_des_crypt_ecb(&ctx->des, ctx->mac, tmp); + + mbedtls_des_init(&ctx->des); + mbedtls_des_setkey_enc(&ctx->des, ctx->key); + mbedtls_des_crypt_ecb(&ctx->des, tmp, output); + + mbedtls_des_free(&ctx->des); + 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]; @@ -184,7 +275,11 @@ bool mrtd_bac_mac(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t for(uint8_t j=0; j<8; ++j) { xormac[j] = mac[j] ^ data[i * 8 + j]; } + mbedtls_des_crypt_ecb(&ctx, xormac, mac); + printf("DES1: %02X %02X %02X %02X %02X %02X %02X %02X\n", + xormac[0], xormac[1], xormac[2], xormac[3], + xormac[4], xormac[5], xormac[6], xormac[7]); } mbedtls_des_init(&ctx); @@ -201,6 +296,7 @@ bool mrtd_bac_mac(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t } bool mrtd_bac_padded_mac(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t* output) { + //TODO: bufferless padding should be possible with 3DES size_t newlength = ((data_length+8)/8)*8; // TODO: return this value too? uint8_t padded[newlength]; //TODO: input parameter memset(padded, 0, newlength); @@ -214,3 +310,75 @@ bool mrtd_bac_padded_mac(const uint8_t* data, size_t data_length, uint8_t* key, return true; } +size_t mrtd_protect_apdu(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t lc, const void* data, int16_t le, uint8_t* key_enc, uint8_t* key_mac, uint64_t ssc, uint8_t* output) { + //TODO: max size on output? + size_t idx = 0; + + // CC = MAC( SSC || CmdHeader || DO'87 ) + mrtd_bac_mac_ctx mac_ctx; + mrtd_bac_mac_init(&mac_ctx, key_mac); + uint64_t ssc_n = htonll(ssc); + mrtd_bac_mac_update(&mac_ctx, (uint8_t*)&ssc_n, 8); + + // Mask cla + output[idx++] = cla | 0x0c; + output[idx++] = ins; + output[idx++] = p1; + output[idx++] = p2; + + // Pad Header + mrtd_bac_mac_update(&mac_ctx, output, idx); + mrtd_bac_mac_pad(&mac_ctx); + + size_t idx_lc = idx; + output[idx++] = 0xff; // place holder for Lc + + // Build DO'87 + // TODO: condition on data presence + { + size_t newlength = ((lc+8)/8)*8; + uint8_t padded[newlength]; + size_t idx_do87 = idx; + + output[idx++] = 0x87; // Header + output[idx++] = newlength + 1; // Length + output[idx++] = 0x01; //TODO: check this value + + memset(padded, 0, newlength); + memcpy(padded, data, lc); + padded[lc] = 0x80; + + mrtd_bac_encrypt(padded, newlength, key_enc, output + idx); + idx += newlength; + + mrtd_bac_mac_update(&mac_ctx, output + idx_do87, idx - idx_do87); + } + + // Build DO'8E + // TODO: conditions? + { + output[idx++] = 0x8E; // Header + output[idx++] = 0x08; // Length + + printf("idx: %d\n", idx); + + uint8_t mac[8]; + mrtd_bac_mac_finalize(&mac_ctx, output + idx); + idx += 8; + printf("MAC: "); + for(uint8_t i=0; i<8; ++i) { + printf("%02X ", mac[i]); + } + printf("\n"); + } + + output[idx_lc] = idx - idx_lc - 1; // Set Lc + + output[idx++] = 0x00; + + if(le) { + //TODO: le? + } + + return idx; +} diff --git a/lib/nfc/protocols/mrtd_helpers.h b/lib/nfc/protocols/mrtd_helpers.h index 2c83e2cc6..1566be068 100644 --- a/lib/nfc/protocols/mrtd_helpers.h +++ b/lib/nfc/protocols/mrtd_helpers.h @@ -5,6 +5,8 @@ #include #include +#include + typedef struct { uint8_t year; uint8_t month; @@ -32,6 +34,16 @@ typedef struct { //TODO: PACE } MrtdAuthData; +typedef struct { + mbedtls_des_context des; + uint8_t key[16]; + uint8_t mac[8]; + uint8_t xormac[8]; + + uint8_t buffer_in[8]; + uint8_t idx_in; +} mrtd_bac_mac_ctx; + uint8_t mrtd_bac_check_digit(const char* input, const uint8_t length); //TODO: swap order, all other functions have output last @@ -47,12 +59,21 @@ bool mrtd_bac_encrypt(const uint8_t* data, size_t data_length, uint8_t* key, uin bool mrtd_bac_mac(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t* output); +bool mrtd_bac_mac_init(mrtd_bac_mac_ctx* ctx, uint8_t key[16]); + +bool mrtd_bac_mac_update(mrtd_bac_mac_ctx* ctx, const uint8_t* data, size_t data_length); + +bool mrtd_bac_mac_finalize(mrtd_bac_mac_ctx* ctx, uint8_t output[8]); + bool mrtd_bac_padded_mac(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t* output); bool mrtd_bac_decrypt(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t* output); bool mrtd_bac_decrypt_verify(const uint8_t* data, size_t data_length, uint8_t* key_enc, uint8_t* key_mac, uint8_t* output); +#include +#define htonll(x) ((((uint64_t)__htonl(x)) << 32) + __htonl((x) >> 32)) + static __inline uint64_t mrtd_ssc_from_data(const uint8_t* rnd_ic, const uint8_t* rnd_ifd) { #if _BYTE_ORDER == _LITTLE_ENDIAN return @@ -70,3 +91,5 @@ static __inline uint64_t mrtd_ssc_from_data(const uint8_t* rnd_ic, const uint8_t (*((uint64_t*)(rnd_ifd + 4)) * 0x100000000); #endif } + +size_t mrtd_protect_apdu(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t lc, const void* data, int16_t le, uint8_t* key_enc, uint8_t* key_mac, uint64_t ssc, uint8_t* output); diff --git a/test_mrtd_helpers.c b/test_mrtd_helpers.c index 6c16084cb..b3e5e462c 100644 --- a/test_mrtd_helpers.c +++ b/test_mrtd_helpers.c @@ -4,7 +4,7 @@ #include "lib/nfc/protocols/mrtd_helpers.h" -// 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 +// gcc -o test_mrtd_helpers -Wall -Ilib/mbedtls/include -Itoolchain/x86_64-linux/arm-none-eabi/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" @@ -152,7 +152,7 @@ void test_mrtd_bac_encrypt(uint8_t* data, size_t data_length, uint8_t* key, uint 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"); + printf("ERROR BAC MAC\n"); return; } @@ -161,6 +161,7 @@ void test_mrtd_bac_padded_mac(uint8_t* data, size_t data_length, uint8_t* key, u print_hex(exp_output, exp_output_length); printf(" is:\n"); print_hex(mac, 8); + printf(COLOR_RESET "\n"); return; } else { printf(COLOR_GREEN "SUCCESS - mrtd_bac_padded_mac output: "); @@ -169,6 +170,45 @@ void test_mrtd_bac_padded_mac(uint8_t* data, size_t data_length, uint8_t* key, u } } +void test_mrtd_bac_mac_calls(uint8_t* data, size_t data_length, uint8_t* key, uint8_t* exp_output, size_t exp_output_length) { + mrtd_bac_mac_ctx ctx; + + for(int break_at=0; break_at