mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-07-28 01:58:11 -07:00
MRTD BAC support and some other improvements
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -5,8 +5,23 @@
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+171
-8
@@ -1,5 +1,7 @@
|
||||
#include <furi_hal_random.h>
|
||||
|
||||
#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; i<EF_DIR->applications_count; ++i) {
|
||||
printf("- ");
|
||||
for(uint8_t n=0; n<sizeof(AIDValue); ++n) {
|
||||
printf("%02X ", EF_DIR->applications[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;
|
||||
}
|
||||
|
||||
+24
-12
@@ -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);
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
#include "mrtd_helpers.h"
|
||||
|
||||
#include <stdio.h> //TODO: remove
|
||||
|
||||
#include <mbedtls/sha1.h>
|
||||
#include <mbedtls/des.h>
|
||||
|
||||
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<length; ++i) {
|
||||
uint8_t c = input[i];
|
||||
char c = input[i];
|
||||
if(c >= '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<data_length / 8; ++i) {
|
||||
for(uint8_t j=0; j<8; ++j) {
|
||||
xormac[j] = mac[j] ^ data[i * 8 + j];
|
||||
}
|
||||
mbedtls_des_crypt_ecb(&ctx, xormac, mac);
|
||||
}
|
||||
|
||||
mbedtls_des_init(&ctx);
|
||||
mbedtls_des_setkey_dec(&ctx, key+8);
|
||||
mbedtls_des_crypt_ecb(&ctx, mac, tmp);
|
||||
|
||||
mbedtls_des_init(&ctx);
|
||||
mbedtls_des_setkey_enc(&ctx, key);
|
||||
mbedtls_des_crypt_ecb(&ctx, tmp, output);
|
||||
|
||||
mbedtls_des_free(&ctx);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mrtd_bac_padded_mac(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t* output) {
|
||||
size_t newlength = ((data_length+8)/8)*8; // TODO: return this value too?
|
||||
uint8_t padded[newlength]; //TODO: input parameter
|
||||
memset(padded, 0, newlength);
|
||||
memcpy(padded, data, data_length);
|
||||
padded[data_length] = 0x80;
|
||||
|
||||
if(!mrtd_bac_mac(padded, newlength, key, output)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,10 +30,41 @@ typedef struct {
|
||||
//TODO: PACE
|
||||
} MrtdAuthData;
|
||||
|
||||
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);
|
||||
|
||||
void mrtd_print_date(uint8_t* output, MrtdDate* date);
|
||||
//TODO: swap order, all other functions have output last
|
||||
void mrtd_print_date(char* output, MrtdDate* date);
|
||||
|
||||
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);
|
||||
|
||||
bool mrtd_bac_keys(const uint8_t* kseed, uint8_t* ksenc, uint8_t* ksmac);
|
||||
bool mrtd_bac_keys_from_seed(const uint8_t* kseed, uint8_t* ksenc, uint8_t* ksmac);
|
||||
|
||||
bool mrtd_bac_keys(MrtdAuthData* auth, uint8_t ksenc[16], uint8_t ksmac[16]);
|
||||
|
||||
bool mrtd_bac_encrypt(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t* output);
|
||||
|
||||
bool mrtd_bac_mac(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t* output);
|
||||
|
||||
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);
|
||||
|
||||
static __inline uint64_t mrtd_ssc_from_data(const uint8_t* rnd_ic, const uint8_t* rnd_ifd) {
|
||||
#if _BYTE_ORDER == _LITTLE_ENDIAN
|
||||
return
|
||||
(((uint64_t)rnd_ic[4] << 56) & 0xff00000000000000) |
|
||||
(((uint64_t)rnd_ic[5] << 48) & 0x00ff000000000000) |
|
||||
(((uint64_t)rnd_ic[6] << 40) & 0x0000ff0000000000) |
|
||||
(((uint64_t)rnd_ic[7] << 32) & 0x000000ff00000000) |
|
||||
(((uint64_t)rnd_ifd[4] << 24) & 0x00000000ff000000) |
|
||||
(((uint64_t)rnd_ifd[5] << 16) & 0x0000000000ff0000) |
|
||||
(((uint64_t)rnd_ifd[6] << 8) & 0x000000000000ff00) |
|
||||
(((uint64_t)rnd_ifd[7]) & 0x00000000000000ff);
|
||||
#else
|
||||
#error Using untested code, please verify first!
|
||||
return (*((uint64_t*)(rnd_ic + 4)) & 0xffffffff) +
|
||||
(*((uint64_t*)(rnd_ifd + 4)) * 0x100000000);
|
||||
#endif
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user