MRTD WIP secure messaging

This commit is contained in:
Chris van Marle
2022-10-11 22:13:27 +02:00
parent c221c8947e
commit ab5e564410
5 changed files with 320 additions and 17 deletions
+39 -12
View File
@@ -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;
}
+9 -1
View File
@@ -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;
+168
View File
@@ -1,6 +1,7 @@
#include "mrtd_helpers.h"
#include <stdio.h> //TODO: remove
#include <stdlib.h>
#include <mbedtls/sha1.h>
#include <mbedtls/des.h>
@@ -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;
}
+23
View File
@@ -5,6 +5,8 @@
#include <stddef.h>
#include <string.h>
#include <mbedtls/des.h>
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 <machine/_endian.h>
#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);
+81 -4
View File
@@ -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<data_length; break_at += 3) {
if(!mrtd_bac_mac_init(&ctx, key)) {
printf("ERROR mrtd_bac_mac_init (break_at: %d)\n", break_at);
return;
}
uint8_t mac[8];
if(!mrtd_bac_mac_update(&ctx, data, break_at)) {
printf("ERROR mrtd_bac_mac_update 1 (break_at: %d)\n", break_at);
}
if(!mrtd_bac_mac_update(&ctx, data + break_at, data_length - break_at)) {
printf("ERROR mrtd_bac_mac_update 2 (break_at: %d)\n", break_at);
}
if(!mrtd_bac_mac_finalize(&ctx, mac)) {
printf("ERROR mrtd_bac_mac_finalize (break_at: %d)\n", break_at);
return;
}
if(memcmp(exp_output, mac, exp_output_length)) {
printf(COLOR_RED "FAILED - mrtd_bac_mac calls (break_at: %d), expected output:\n", break_at);
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_mac calls (break_at: %d) output: ", break_at);
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];
@@ -202,6 +242,30 @@ void test_mrtd_ssc_from_data(const uint8_t* rnd_ic, const uint8_t* rnd_ifd, uint
printf(COLOR_GREEN "SUCCESS - mrtd_ssc_from_data output: %016lx\n" COLOR_RESET, ssc_long);
}
void test_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* ks_enc, uint8_t* ks_mac, uint64_t ssc, uint8_t* exp_output, size_t exp_output_length) {
uint8_t buffer[4096];
size_t ret = mrtd_protect_apdu(cla, ins, p1, p2, lc, data, le, ks_enc, ks_mac, ssc, buffer);
if(!ret) {
printf(COLOR_RED "FAILED - mrtd_protect_apdu returned an error\n" COLOR_RESET);
return;
}
if(memcmp(buffer, exp_output, ret)) {
printf(COLOR_RED "FAILED - mrtd_protect_apdu, expected output:\n" COLOR_RESET);
print_hex(exp_output, exp_output_length);
printf(" is:\n");
print_hex(buffer, ret);
return;
}
printf(COLOR_GREEN "SUCCESS - mrtd_protect_apdu output: ");
print_hex(buffer, ret);
printf("\n" COLOR_RESET);
//TODO: hexdump
}
int main(int argc, char** argv) {
test_mrtd_bac_check_digit("D23145890734", 9);
test_mrtd_bac_check_digit("340712", 7);
@@ -253,6 +317,14 @@ int main(int argc, char** argv) {
/*exp_output_size*/ 8
);
test_mrtd_bac_mac_calls(
/*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,
@@ -265,10 +337,13 @@ int main(int argc, char** argv) {
//TODO: test that does not verify
uint8_t* ks_enc = (uint8_t*)"\x97\x9E\xC1\x3B\x1C\xBF\xE9\xDC\xD0\x1A\xB0\xFE\xD3\x07\xEA\xE5";
uint8_t* ks_mac = (uint8_t*)"\xF1\xCB\x1F\x1F\xB5\xAD\xF2\x08\x80\x6B\x89\xDC\x57\x9D\xC1\xF8";
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"
ks_enc,
ks_mac
);
uint8_t* rnd_ic = (uint8_t*)"\x46\x08\xF9\x19\x88\x70\x22\x12";
@@ -276,5 +351,7 @@ int main(int argc, char** argv) {
test_mrtd_ssc_from_data(rnd_ic, rnd_ifd, 0x887022120C06C226);
test_mrtd_protect_apdu(0x00, 0xA4, 0x02, 0x0C, 0x02, "\x01\x1e", -1, ks_enc, ks_mac, 0x887022120C06C227, (uint8_t*)"\x0C\xA4\x02\x0C\x15\x87\x09\x01\x63\x75\x43\x29\x08\xC0\x44\xF6\x8E\x08\xBF\x8B\x92\xD6\x35\xFF\x24\xF8\x00", 27);
return 0;
}