mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-04-25 03:29:58 -07:00
Merge branch 'pr/401' into 420
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
#include <furi_hal_random.h>
|
||||
#include <storage/storage.h>
|
||||
#include <dialogs/dialogs.h>
|
||||
#include <nfc/nfc_device.h>
|
||||
|
||||
#include "../helpers/iso7816.h"
|
||||
|
||||
@@ -19,6 +22,9 @@
|
||||
|
||||
#define num_elements(A) (sizeof(A) / sizeof(A[0]))
|
||||
|
||||
static const char* mrtd_auth_file_header = "Flipper MRTD params";
|
||||
static const uint32_t mrtd_auth_file_version = 1;
|
||||
|
||||
static void hexdump(FuriLogLevel level, char* prefix, void* data, size_t length) {
|
||||
if(furi_log_get_level() >= level) {
|
||||
printf("%s ", prefix);
|
||||
@@ -425,7 +431,7 @@ bool parse_ef_dg1(EF_DG1_contents* DG1, const uint8_t* data, size_t length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mrtd_read_parse_file(MrtdApplication* app, MrtdData* mrtd_data, EFFile file) {
|
||||
bool mrtd_read_parse_file(MrtdApplication* app, EFFile file) {
|
||||
uint8_t buffer[100];
|
||||
size_t buf_len;
|
||||
|
||||
@@ -450,13 +456,13 @@ bool mrtd_read_parse_file(MrtdApplication* app, MrtdData* mrtd_data, EFFile file
|
||||
bool result = false;
|
||||
|
||||
if(file.file_id == EF.COM.file_id) {
|
||||
result = parse_ef_com(&mrtd_data->files.EF_COM, buffer, buf_len);
|
||||
result = parse_ef_com(&app->mrtd_data->files.EF_COM, buffer, buf_len);
|
||||
FURI_LOG_D(TAG, "Parsed EF.COM");
|
||||
} else if(file.file_id == EF.DIR.file_id) {
|
||||
result = parse_ef_dir(&mrtd_data->files.EF_DIR, buffer, buf_len);
|
||||
result = parse_ef_dir(&app->mrtd_data->files.EF_DIR, buffer, buf_len);
|
||||
FURI_LOG_D(TAG, "Parsed EF.DIR");
|
||||
} else if(file.file_id == EF.DG1.file_id) {
|
||||
result = parse_ef_dg1(&mrtd_data->files.DG1, buffer, buf_len);
|
||||
result = parse_ef_dg1(&app->mrtd_data->files.DG1, buffer, buf_len);
|
||||
} else {
|
||||
FURI_LOG_W(TAG, "Don't know how to parse file with id 0x%04X", file.file_id);
|
||||
}
|
||||
@@ -464,52 +470,11 @@ bool mrtd_read_parse_file(MrtdApplication* app, MrtdData* mrtd_data, EFFile file
|
||||
return result;
|
||||
}
|
||||
|
||||
//TODO: remove testing function
|
||||
void mrtd_test(MrtdApplication* app, MrtdData* mrtd_data) {
|
||||
FURI_LOG_D(TAG, "Mrtd Test");
|
||||
//mrtd_read_dump(app, EF.ATR);
|
||||
//mrtd_read_dump(app, EF.COM);
|
||||
//mrtd_read_dump(app, EF.DIR);
|
||||
//mrtd_read_dump(app, EF.CardAccess);
|
||||
//mrtd_read_dump(app, EF.CardSecurity);
|
||||
|
||||
mrtd_select_app(app, AID.eMRTDApplication);
|
||||
|
||||
MrtdAuthMethod method = mrtd_data->auth.method;
|
||||
mrtd_data->auth_success = false;
|
||||
FURI_LOG_D(TAG, "Auth method: %d", method);
|
||||
switch(method) {
|
||||
case MrtdAuthMethodAny:
|
||||
//TODO: try PACE, then BAC
|
||||
case MrtdAuthMethodBac:
|
||||
mrtd_data->auth_success = mrtd_bac(app, &mrtd_data->auth);
|
||||
break;
|
||||
case MrtdAuthMethodPace:
|
||||
FURI_LOG_E(TAG, "Auth method PACE not implemented");
|
||||
break;
|
||||
case MrtdAuthMethodNone:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if(!mrtd_data->auth_success) {
|
||||
return;
|
||||
}
|
||||
|
||||
mrtd_read_parse_file(app, mrtd_data, EF.COM);
|
||||
//mrtd_read_parse_file(app, mrtd_data, EF.DIR);
|
||||
|
||||
mrtd_read_parse_file(app, mrtd_data, EF.DG1);
|
||||
|
||||
//mrtd_read_dump(app, EF.DG2);
|
||||
//mrtd_read_dump(app, EF.DG14);
|
||||
//mrtd_read_dump(app, EF.DG15);
|
||||
}
|
||||
|
||||
MrtdApplication* mrtd_alloc_init(FuriHalNfcTxRxContext* tx_rx) {
|
||||
MrtdApplication* mrtd_alloc_init(FuriHalNfcTxRxContext* tx_rx, MrtdData* mrtd_data) {
|
||||
MrtdApplication* app = malloc(sizeof(MrtdApplication));
|
||||
|
||||
app->tx_rx = tx_rx;
|
||||
app->mrtd_data = mrtd_data;
|
||||
|
||||
return app;
|
||||
}
|
||||
@@ -612,3 +577,168 @@ bool mrtd_bac(MrtdApplication* app, MrtdAuthData* auth) {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mrtd_authenticate(MrtdApplication* app) {
|
||||
MrtdAuthMethod method = app->mrtd_data->auth.method;
|
||||
app->mrtd_data->auth_success = false;
|
||||
app->mrtd_data->auth_method_used = MrtdAuthMethodNone;
|
||||
FURI_LOG_D(TAG, "Auth method: %d", method);
|
||||
switch(method) {
|
||||
case MrtdAuthMethodAny:
|
||||
//TODO: try PACE, then BAC. For now, fall through to just BAC
|
||||
case MrtdAuthMethodBac:
|
||||
app->mrtd_data->auth_success = mrtd_bac(app, &app->mrtd_data->auth);
|
||||
app->mrtd_data->auth_method_used = MrtdAuthMethodBac;
|
||||
break;
|
||||
case MrtdAuthMethodPace:
|
||||
FURI_LOG_E(TAG, "Auth method PACE not implemented");
|
||||
break;
|
||||
case MrtdAuthMethodNone:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if(!app->mrtd_data->auth_success) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mrtd_auth_params_save(Storage* storage, DialogsApp* dialogs, MrtdAuthData* auth_data, const char* file_name) {
|
||||
return mrtd_auth_params_save_file(storage, dialogs, auth_data, file_name, MRTD_APP_FOLDER, MRTD_APP_EXTENSION);
|
||||
}
|
||||
|
||||
void mrtd_date_prepare_format_string(MrtdDate date, FuriString* format_string) {
|
||||
furi_string_printf(
|
||||
format_string, "%02u%02u%02u",
|
||||
date.year,
|
||||
date.month,
|
||||
date.day);
|
||||
}
|
||||
|
||||
bool mrtd_date_parse_format_string(MrtdDate* date, FuriString* format_string) {
|
||||
int year;
|
||||
int month;
|
||||
int day;
|
||||
|
||||
int ret = sscanf(furi_string_get_cstr(format_string), "%02d%02d%02d", &year, &month, &day);
|
||||
if(ret != 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
date->year = year;
|
||||
date->month = month;
|
||||
date->day = day;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mrtd_auth_params_save_file(Storage* storage, DialogsApp* dialogs, MrtdAuthData* auth_data, const char* file_name, const char* folder, const char* extension) {
|
||||
furi_assert(auth_data);
|
||||
|
||||
bool saved = false;
|
||||
FlipperFormat* file = flipper_format_file_alloc(storage);
|
||||
FuriString* temp_str;
|
||||
temp_str = furi_string_alloc();
|
||||
|
||||
do {
|
||||
// Create mrtd directory if necessary
|
||||
if(!storage_simply_mkdir(storage, MRTD_APP_FOLDER)) break;
|
||||
|
||||
furi_string_printf(temp_str, "%s/%s%s", folder, file_name, extension);
|
||||
|
||||
// Open file
|
||||
if(!flipper_format_file_open_always(file, furi_string_get_cstr(temp_str))) break;
|
||||
// Write header
|
||||
if(!flipper_format_write_header_cstr(file, mrtd_auth_file_header, mrtd_auth_file_version)) break;
|
||||
|
||||
// Write auth method
|
||||
furi_string_set(temp_str, mrtd_auth_method_string(auth_data->method));
|
||||
if(!flipper_format_write_string(file, "Method", temp_str)) break;
|
||||
|
||||
// Write birth date
|
||||
mrtd_date_prepare_format_string(auth_data->birth_date, temp_str);
|
||||
if(!flipper_format_write_string(file, "BirthDate", temp_str)) break;
|
||||
|
||||
// Write expiry date
|
||||
mrtd_date_prepare_format_string(auth_data->expiry_date, temp_str);
|
||||
if(!flipper_format_write_string(file, "ExpiryDate", temp_str)) break;
|
||||
|
||||
// Write docnr
|
||||
furi_string_set(temp_str, auth_data->doc_number);
|
||||
if(!flipper_format_write_string(file, "DocNr", temp_str)) break;
|
||||
|
||||
saved = true;
|
||||
} while(false);
|
||||
|
||||
if(!saved) {
|
||||
dialog_message_show_storage_error(dialogs, "Can not save\nparams file");
|
||||
}
|
||||
furi_string_free(temp_str);
|
||||
flipper_format_free(file);
|
||||
return saved;
|
||||
}
|
||||
|
||||
bool mrtd_auth_params_load(Storage* storage, DialogsApp* dialogs, MrtdAuthData* auth_data, const char* file_path, bool show_dialog) {
|
||||
furi_assert(storage);
|
||||
furi_assert(dialogs);
|
||||
furi_assert(auth_data);
|
||||
furi_assert(file_path);
|
||||
|
||||
bool parsed = false;
|
||||
FlipperFormat* file = flipper_format_file_alloc(storage);
|
||||
bool deprecated_version = false;
|
||||
|
||||
FuriString* temp_str;
|
||||
temp_str = furi_string_alloc();
|
||||
|
||||
MrtdAuthData copy;
|
||||
|
||||
FURI_LOG_D(TAG, "Load auth params");
|
||||
|
||||
do {
|
||||
if(!flipper_format_file_open_existing(file, file_path)) break;
|
||||
|
||||
uint32_t version = 0;
|
||||
if(!flipper_format_read_header(file, temp_str, &version)) break;
|
||||
FURI_LOG_D(TAG, "Version: %s", furi_string_get_cstr(temp_str));
|
||||
if(furi_string_cmp_str(temp_str, mrtd_auth_file_header) || (version != mrtd_auth_file_version)) {
|
||||
deprecated_version = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_format_read_string(file, "Method", temp_str)) break;
|
||||
FURI_LOG_D(TAG, "Method: %s", furi_string_get_cstr(temp_str));
|
||||
if(!mrtd_auth_method_parse_string(©.method, furi_string_get_cstr(temp_str))) break;
|
||||
|
||||
if(!flipper_format_read_string(file, "BirthDate", temp_str)) break;
|
||||
FURI_LOG_D(TAG, "BirthDate: %s", furi_string_get_cstr(temp_str));
|
||||
if(!mrtd_date_parse_format_string(©.birth_date, temp_str)) break;
|
||||
|
||||
if(!flipper_format_read_string(file, "ExpiryDate", temp_str)) break;
|
||||
FURI_LOG_D(TAG, "ExpiryDate: %s", furi_string_get_cstr(temp_str));
|
||||
if(!mrtd_date_parse_format_string(©.expiry_date, temp_str)) break;
|
||||
|
||||
if(!flipper_format_read_string(file, "DocNr", temp_str)) break;
|
||||
FURI_LOG_D(TAG, "DocNr: %s", furi_string_get_cstr(temp_str));
|
||||
strlcpy(copy.doc_number, furi_string_get_cstr(temp_str), MRTD_DOCNR_MAX_LENGTH);
|
||||
|
||||
// Everything went fine. Save copy to pointed auth data
|
||||
*auth_data = copy;
|
||||
parsed = true;
|
||||
} while(false);
|
||||
|
||||
FURI_LOG_D(TAG, "Load done, success: %d", parsed);
|
||||
|
||||
if(!parsed && show_dialog) {
|
||||
if(deprecated_version) {
|
||||
dialog_message_show_storage_error(dialogs, "File format deprecated");
|
||||
} else {
|
||||
dialog_message_show_storage_error(dialogs, "Can not parse\nfile");
|
||||
}
|
||||
}
|
||||
|
||||
furi_string_free(temp_str);
|
||||
flipper_format_free(file);
|
||||
return parsed;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi_hal_nfc.h>
|
||||
#include <helpers/mrtd_helpers.h>
|
||||
|
||||
#include "mrtd_helpers.h"
|
||||
#define MRTD_APP_FOLDER NFC_APP_FOLDER "/mrtd"
|
||||
#define MRTD_APP_EXTENSION ".mrtd"
|
||||
|
||||
typedef struct {
|
||||
FuriHalNfcTxRxContext* tx_rx;
|
||||
MrtdData* mrtd_data;
|
||||
uint16_t file_offset;
|
||||
uint8_t ksenc[16];
|
||||
uint8_t ksmac[16];
|
||||
@@ -14,20 +17,13 @@ typedef struct {
|
||||
bool secure_messaging;
|
||||
} MrtdApplication;
|
||||
|
||||
typedef struct {
|
||||
MrtdAuthData auth;
|
||||
bool auth_success; //TODO: register (and display) method used BAC/PACE
|
||||
|
||||
struct {
|
||||
EF_DIR_contents EF_DIR;
|
||||
EF_COM_contents EF_COM;
|
||||
EF_DG1_contents DG1;
|
||||
} files;
|
||||
} MrtdData;
|
||||
|
||||
//TODO: description
|
||||
MrtdApplication* mrtd_alloc_init(FuriHalNfcTxRxContext* tx_rx);
|
||||
MrtdApplication* mrtd_alloc_init(FuriHalNfcTxRxContext* tx_rx, MrtdData* mrtd_data);
|
||||
bool mrtd_select_app(MrtdApplication* app, AIDValue aid);
|
||||
bool mrtd_select_file(MrtdApplication* app, EFFile file);
|
||||
void mrtd_test(MrtdApplication* app, MrtdData* mrtd_data);
|
||||
bool mrtd_bac(MrtdApplication* app, MrtdAuthData* auth);
|
||||
bool mrtd_authenticate(MrtdApplication* app);
|
||||
bool mrtd_read_parse_file(MrtdApplication* app, EFFile file);
|
||||
|
||||
bool mrtd_auth_params_save(Storage* storage, DialogsApp* dialogs, MrtdAuthData* auth_data, const char* file_name);
|
||||
bool mrtd_auth_params_save_file(Storage* storage, DialogsApp* dialogs, MrtdAuthData* auth_data, const char* file_name, const char* folder, const char* extension);
|
||||
|
||||
bool mrtd_auth_params_load(Storage* storage, DialogsApp* dialogs, MrtdAuthData* auth_data, const char* file_path, bool show_dialog);
|
||||
|
||||
@@ -1,623 +0,0 @@
|
||||
#include "mrtd_helpers.h"
|
||||
#include "../helpers/iso7816.h"
|
||||
|
||||
#include <stdio.h> //TODO: remove
|
||||
#include <stdlib.h>
|
||||
|
||||
#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 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) {
|
||||
char c = input[i];
|
||||
if(c >= 'A' && c <= 'Z') {
|
||||
idx = c - 'A' + 10;
|
||||
} else if(c >= 'a' && c <= 'z') {
|
||||
idx = c - 'a' + 10;
|
||||
} else if(c >= '0' && c <= '9') {
|
||||
idx = c - '0';
|
||||
} else {
|
||||
idx = 0;
|
||||
}
|
||||
check_digit = (check_digit + idx * weights[i % num_weights]) % 10;
|
||||
}
|
||||
return check_digit;
|
||||
}
|
||||
|
||||
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';
|
||||
output[3] = (date->month % 10) + '0';
|
||||
output[4] = (date->day / 10) + '0';
|
||||
output[5] = (date->day % 10) + '0';
|
||||
}
|
||||
|
||||
uint8_t charval(char c) {
|
||||
if(c >= '0' && c <= '9') {
|
||||
return c - '0';
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mrtd_parse_date(MrtdDate* date, const unsigned char* input) {
|
||||
date->year = charval(input[0]) * 10 + charval(input[1]);
|
||||
date->month = charval(input[2]) * 10 + charval(input[3]);
|
||||
date->day = charval(input[4]) * 10 + charval(input[5]);
|
||||
}
|
||||
|
||||
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;
|
||||
if(output_size < docnr_length + 16) {
|
||||
return false;
|
||||
}
|
||||
|
||||
cd_idx = idx;
|
||||
for(uint8_t i = 0; i < docnr_length; ++i) {
|
||||
char c = auth->doc_number[i];
|
||||
if(c >= 'a' && c <= 'z') {
|
||||
c = c - 'a' + 'A';
|
||||
}
|
||||
output[idx++] = c;
|
||||
}
|
||||
|
||||
if(docnr_length < 9) {
|
||||
memset(output + idx, '<', 9 - docnr_length);
|
||||
idx += 9 - docnr_length;
|
||||
}
|
||||
|
||||
output[idx++] = mrtd_bac_check_digit(output + cd_idx, docnr_length) + '0';
|
||||
|
||||
cd_idx = idx;
|
||||
mrtd_print_date(output + idx, &auth->birth_date);
|
||||
idx += 6;
|
||||
output[idx++] = mrtd_bac_check_digit(output + cd_idx, 6) + '0';
|
||||
|
||||
cd_idx = idx;
|
||||
mrtd_print_date(output + idx, &auth->expiry_date);
|
||||
idx += 6;
|
||||
output[idx++] = mrtd_bac_check_digit(output + cd_idx, 6) + '0';
|
||||
|
||||
output[idx++] = '\x00';
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
do {
|
||||
for(uint8_t i = 1; i <= 2; ++i) {
|
||||
if(mbedtls_sha1_starts(&ctx)) break;
|
||||
if(mbedtls_sha1_update(&ctx, kseed, 16)) break;
|
||||
if(mbedtls_sha1_update(&ctx, ucstr("\x00\x00\x00"), 3)) break;
|
||||
if(mbedtls_sha1_update(&ctx, &i, 1)) break;
|
||||
if(mbedtls_sha1_finish(&ctx, hash)) break;
|
||||
|
||||
switch(i) {
|
||||
case 1:
|
||||
memcpy(ksenc, hash, 16);
|
||||
mbedtls_des_key_set_parity(ksenc);
|
||||
mbedtls_des_key_set_parity(ksenc + 8);
|
||||
break;
|
||||
case 2:
|
||||
memcpy(ksmac, hash, 16);
|
||||
mbedtls_des_key_set_parity(ksmac);
|
||||
mbedtls_des_key_set_parity(ksmac + 8);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while(false);
|
||||
|
||||
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, const 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\r\n");
|
||||
for(uint8_t i = 0; i < 8; ++i) {
|
||||
printf("%02X <=> %02X\r\n", mac_calc[i], data[data_length - 8 + i]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// If output or output_written are NULL-pointers, no output is written
|
||||
// Otherwise, and if DO'87 is present, data is written to *output
|
||||
// output should have enough room for additional padding (rounded up by 8 bytes)
|
||||
// output_written will be the length without padding
|
||||
uint16_t mrtd_bac_decrypt_verify_sm(
|
||||
const uint8_t* data,
|
||||
size_t data_length,
|
||||
uint8_t* key_enc,
|
||||
uint8_t* key_mac,
|
||||
uint64_t ssc,
|
||||
uint8_t* output,
|
||||
size_t* output_written) {
|
||||
// Message: [DO'85 or DO'87] || [DO'99] || DO'8E
|
||||
// Lengths: Var 1+1+2=4 1+1+8=10
|
||||
|
||||
//TODO: check for DO'99 presence, instead of assuming
|
||||
uint16_t ret_code = data[data_length - 10 - 2] << 8 | data[data_length - 10 - 1];
|
||||
//ntohs(data + data_length - 10 - 2);
|
||||
|
||||
TlvInfo do87 = iso7816_tlv_select(data, data_length, (uint16_t[]){0x87}, 1);
|
||||
//printf("DO87.Tag: %X\n", do87.tag);
|
||||
//printf("DO87.Length: %ld\n", do87.length);
|
||||
//printf("DO87.Value: ");
|
||||
//for(uint8_t i=1; i<do87.length; ++i) { printf("%02X ", do87.value[i]); }
|
||||
//printf("\r\n");
|
||||
|
||||
if(do87.tag) {
|
||||
if(output_written != NULL && output != NULL) {
|
||||
// Skip the first byte '01'
|
||||
const uint8_t* encdata = do87.value + 1;
|
||||
size_t enclength = do87.length - 1;
|
||||
|
||||
mrtd_bac_decrypt(encdata, enclength, key_enc, output);
|
||||
printf("Decrypted: ");
|
||||
for(uint8_t i = 0; i < enclength; ++i) printf("%02X ", output[i]);
|
||||
printf("\r\n");
|
||||
|
||||
//TODO: function mrtd_bac_unpad?
|
||||
int padidx;
|
||||
for(padidx = enclength - 1; padidx >= 0; --padidx) {
|
||||
if(output[padidx] == 0x00) {
|
||||
continue;
|
||||
} else if(output[padidx] == 0x80) {
|
||||
break;
|
||||
} else {
|
||||
printf("Invalid padding\r\n");
|
||||
return 0xff01;
|
||||
}
|
||||
}
|
||||
printf(" ");
|
||||
for(int i = 0; i < padidx; ++i) {
|
||||
printf(" ");
|
||||
}
|
||||
printf("^^\r\n");
|
||||
printf("Pad starts at: %d\r\n", padidx);
|
||||
|
||||
*output_written = padidx - 1;
|
||||
}
|
||||
} else {
|
||||
if(output_written != NULL) {
|
||||
*output_written = 0;
|
||||
}
|
||||
}
|
||||
|
||||
mrtd_bac_mac_ctx ctx;
|
||||
mrtd_bac_mac_init(&ctx, key_mac);
|
||||
uint64_t ssc_n = htonll(ssc);
|
||||
mrtd_bac_mac_update(&ctx, (uint8_t*)&ssc_n, 8);
|
||||
mrtd_bac_mac_update(
|
||||
&ctx, data, data_length - 10); // 10 = len(DO'8E) = len(header + length + MAC) = 1 + 1 + 8
|
||||
uint8_t mac_calc[8];
|
||||
mrtd_bac_mac_finalize(&ctx, mac_calc);
|
||||
|
||||
if(memcmp(mac_calc, data + data_length - 8, 8)) {
|
||||
printf("SM MAC failed\r\n");
|
||||
for(uint8_t i = 0; i < 8; ++i) {
|
||||
printf("%02X <=> %02X\r\n", mac_calc[i], data[data_length - 8 + i]);
|
||||
}
|
||||
return 0xff02;
|
||||
}
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
bool mrtd_bac_mac_init(mrtd_bac_mac_ctx* ctx, const 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) {
|
||||
//printf("MAC add %d: ", data_length); print_hex(data, data_length); printf("\n");
|
||||
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(
|
||||
"DES buf: %02X %02X %02X %02X %02X %02X %02X %02X\r\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(
|
||||
"DES add: %02X %02X %02X %02X %02X %02X %02X %02X\r\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, const 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);
|
||||
printf(
|
||||
"DES1: %02X %02X %02X %02X %02X %02X %02X %02X\r\n",
|
||||
xormac[0],
|
||||
xormac[1],
|
||||
xormac[2],
|
||||
xormac[3],
|
||||
xormac[4],
|
||||
xormac[5],
|
||||
xormac[6],
|
||||
xormac[7]);
|
||||
}
|
||||
|
||||
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) {
|
||||
//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);
|
||||
memcpy(padded, data, data_length);
|
||||
padded[data_length] = 0x80;
|
||||
|
||||
if(!mrtd_bac_mac(padded, newlength, key, output)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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,
|
||||
const uint8_t* key_enc,
|
||||
const 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);
|
||||
//printf("ssc: %016llx\r\n", ssc);
|
||||
//printf("ssc_n: "); print_hex(ssc_n, 8); printf("\n");
|
||||
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
|
||||
// TODO: if ins is odd, use 0x85
|
||||
if(lc > 0) {
|
||||
size_t newlength = ((lc + 8) / 8) * 8;
|
||||
uint8_t padded[newlength];
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Build DO'97
|
||||
if(le >= 0) {
|
||||
output[idx++] = 0x97; // Header
|
||||
output[idx++] = 0x01; // Length
|
||||
output[idx++] = le;
|
||||
}
|
||||
|
||||
mrtd_bac_mac_update(&mac_ctx, output + idx_lc + 1, idx - idx_lc - 1);
|
||||
|
||||
// Build DO'8E
|
||||
// TODO: conditions?
|
||||
{
|
||||
output[idx++] = 0x8E; // Header
|
||||
output[idx++] = 0x08; // Length
|
||||
|
||||
mrtd_bac_mac_finalize(&mac_ctx, output + idx);
|
||||
idx += 8;
|
||||
|
||||
printf("MAC: ");
|
||||
for(uint8_t i = 0; i < 8; ++i) {
|
||||
printf("%02X ", output[idx - 8 + i]);
|
||||
}
|
||||
printf("\r\n");
|
||||
}
|
||||
|
||||
output[idx_lc] = idx - idx_lc - 1; // Set Lc
|
||||
|
||||
output[idx++] = 0x00;
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
EFFile EFNone = {.name = NULL, .file_id = 0x0000, .short_id = 0x00, .tag = 0x00};
|
||||
|
||||
const struct EFFormat EF = {
|
||||
.ATR = {.name = "ATR", .file_id = 0x2F01, .short_id = 0x01},
|
||||
.DIR = {.name = "DIR", .file_id = 0x2F00, .short_id = 0x1E},
|
||||
.CardAccess = {.name = "CardAccess", .file_id = 0x011C, .short_id = 0x1C},
|
||||
.CardSecurity = {.name = "CardSecurity", .file_id = 0x011D, .short_id = 0x1D},
|
||||
.COM = {.name = "COM", .file_id = 0x011E, .short_id = 0x1E, .tag = 0x60},
|
||||
.SOD = {.name = "SOD", .file_id = 0X011D, .short_id = 0X1D, .tag = 0x77},
|
||||
.DG1 = {.name = "DG1", .file_id = 0X0101, .short_id = 0X01, .tag = 0x61},
|
||||
.DG2 = {.name = "DG2", .file_id = 0X0102, .short_id = 0X02, .tag = 0x75},
|
||||
.DG3 = {.name = "DG3", .file_id = 0X0103, .short_id = 0X03, .tag = 0x63},
|
||||
.DG4 = {.name = "DG4", .file_id = 0X0104, .short_id = 0X04, .tag = 0x76},
|
||||
.DG5 = {.name = "DG5", .file_id = 0X0105, .short_id = 0X05, .tag = 0x65},
|
||||
.DG6 = {.name = "DG6", .file_id = 0X0106, .short_id = 0X06, .tag = 0x66},
|
||||
.DG7 = {.name = "DG7", .file_id = 0X0107, .short_id = 0X07, .tag = 0x67},
|
||||
.DG8 = {.name = "DG8", .file_id = 0X0108, .short_id = 0X08, .tag = 0x68},
|
||||
.DG9 = {.name = "DG9", .file_id = 0X0109, .short_id = 0X09, .tag = 0x69},
|
||||
.DG10 = {.name = "DG10", .file_id = 0X010A, .short_id = 0X0A, .tag = 0x6a},
|
||||
.DG11 = {.name = "DG11", .file_id = 0X010B, .short_id = 0X0B, .tag = 0x6b},
|
||||
.DG12 = {.name = "DG12", .file_id = 0X010C, .short_id = 0X0C, .tag = 0x6c},
|
||||
.DG13 = {.name = "DG13", .file_id = 0X010D, .short_id = 0X0D, .tag = 0x6d},
|
||||
.DG14 = {.name = "DG14", .file_id = 0X010E, .short_id = 0X0E, .tag = 0x6e},
|
||||
.DG15 = {.name = "DG15", .file_id = 0X010F, .short_id = 0X0F, .tag = 0x6f},
|
||||
.DG16 = {.name = "DG16", .file_id = 0X0110, .short_id = 0X10, .tag = 0x70},
|
||||
};
|
||||
|
||||
struct AIDSet AID = {
|
||||
.eMRTDApplication = {0xA0, 0x00, 0x00, 0x02, 0x47, 0x10, 0x01},
|
||||
.TravelRecords = {0xA0, 0x00, 0x00, 0x02, 0x47, 0x20, 0x01},
|
||||
.VisaRecords = {0xA0, 0x00, 0x00, 0x02, 0x47, 0x20, 0x02},
|
||||
.AdditionalBiometrics = {0xA0, 0x00, 0x00, 0x02, 0x47, 0x20, 0x03},
|
||||
};
|
||||
|
||||
const EFFile* mrtd_tag_to_file(uint8_t tag) {
|
||||
//TODO: generate this code with macros?
|
||||
switch(tag) {
|
||||
case 0x60:
|
||||
return &EF.COM;
|
||||
case 0x77:
|
||||
return &EF.SOD;
|
||||
case 0x61:
|
||||
return &EF.DG1;
|
||||
case 0x75:
|
||||
return &EF.DG2;
|
||||
case 0x63:
|
||||
return &EF.DG3;
|
||||
case 0x76:
|
||||
return &EF.DG4;
|
||||
case 0x65:
|
||||
return &EF.DG5;
|
||||
case 0x66:
|
||||
return &EF.DG6;
|
||||
case 0x67:
|
||||
return &EF.DG7;
|
||||
case 0x68:
|
||||
return &EF.DG8;
|
||||
case 0x69:
|
||||
return &EF.DG9;
|
||||
case 0x6a:
|
||||
return &EF.DG10;
|
||||
case 0x6b:
|
||||
return &EF.DG11;
|
||||
case 0x6c:
|
||||
return &EF.DG12;
|
||||
case 0x6d:
|
||||
return &EF.DG13;
|
||||
case 0x6e:
|
||||
return &EF.DG14;
|
||||
case 0x6f:
|
||||
return &EF.DG15;
|
||||
case 0x70:
|
||||
return &EF.DG16;
|
||||
default:
|
||||
return &EFNone;
|
||||
}
|
||||
};
|
||||
|
||||
int tlv_number(TlvInfo tlv) {
|
||||
//TODO: negative numbers?
|
||||
const uint8_t* str = tlv.value;
|
||||
size_t length = tlv.length;
|
||||
|
||||
int value = 0;
|
||||
while(length--) {
|
||||
char c = *(str++);
|
||||
|
||||
if(c >= '0' && c <= '9') {
|
||||
value = value * 10 + (c - '0');
|
||||
} else {
|
||||
//TODO: warning? return? crash?
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
@@ -1,212 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <mbedtls/des.h>
|
||||
|
||||
#include "../helpers/iso7816.h"
|
||||
|
||||
typedef struct {
|
||||
uint8_t year;
|
||||
uint8_t month;
|
||||
uint8_t day;
|
||||
} MrtdDate;
|
||||
|
||||
// NULL terminated document ID
|
||||
#define MRTD_DOCNR_MAX_LENGTH 21
|
||||
|
||||
typedef enum {
|
||||
MrtdAuthMethodNone,
|
||||
MrtdAuthMethodAny,
|
||||
MrtdAuthMethodBac,
|
||||
MrtdAuthMethodPace,
|
||||
} MrtdAuthMethod;
|
||||
|
||||
typedef enum {
|
||||
MrtdTypeUnknown,
|
||||
MrtdTypeTD1,
|
||||
MrtdTypeTD2,
|
||||
MrtdTypeTD3,
|
||||
} MrtdType;
|
||||
|
||||
typedef struct {
|
||||
MrtdAuthMethod method;
|
||||
|
||||
// BAC input fields
|
||||
MrtdDate birth_date;
|
||||
MrtdDate expiry_date;
|
||||
char doc_number[MRTD_DOCNR_MAX_LENGTH];
|
||||
|
||||
//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;
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
const uint8_t short_id;
|
||||
const uint16_t file_id;
|
||||
const uint8_t tag;
|
||||
} EFFile;
|
||||
|
||||
struct EFFormat {
|
||||
// Under Master File (MF)
|
||||
const EFFile ATR;
|
||||
const EFFile DIR;
|
||||
const EFFile CardAccess;
|
||||
const EFFile CardSecurity;
|
||||
|
||||
// Under LDS1 eMRTD Application
|
||||
const EFFile COM;
|
||||
const EFFile SOD;
|
||||
const EFFile DG1;
|
||||
const EFFile DG2;
|
||||
const EFFile DG3;
|
||||
const EFFile DG4;
|
||||
const EFFile DG5;
|
||||
const EFFile DG6;
|
||||
const EFFile DG7;
|
||||
const EFFile DG8;
|
||||
const EFFile DG9;
|
||||
const EFFile DG10;
|
||||
const EFFile DG11;
|
||||
const EFFile DG12;
|
||||
const EFFile DG13;
|
||||
const EFFile DG14;
|
||||
const EFFile DG15;
|
||||
const EFFile DG16;
|
||||
};
|
||||
|
||||
extern const struct EFFormat EF;
|
||||
|
||||
typedef uint8_t AIDValue[7];
|
||||
|
||||
struct AIDSet {
|
||||
AIDValue eMRTDApplication;
|
||||
AIDValue TravelRecords;
|
||||
AIDValue VisaRecords;
|
||||
AIDValue AdditionalBiometrics;
|
||||
};
|
||||
|
||||
extern struct AIDSet AID;
|
||||
|
||||
#define MAX_EFDIR_APPS 4
|
||||
|
||||
typedef struct {
|
||||
AIDValue applications[MAX_EFDIR_APPS];
|
||||
uint8_t applications_count;
|
||||
} EF_DIR_contents;
|
||||
|
||||
#define MAX_EFCOM_TAGS 18
|
||||
|
||||
typedef struct {
|
||||
uint16_t lds_version; // xxyy => xx.yy (major.minor)
|
||||
uint32_t unicode_version; // aabbcc => aa.bb.cc (major.minor.release)
|
||||
uint8_t tag_list[MAX_EFCOM_TAGS];
|
||||
} EF_COM_contents;
|
||||
|
||||
typedef struct {
|
||||
MrtdType type;
|
||||
// ICAO9303 max sizes + 1 for 0-byte
|
||||
uint8_t doctype[3];
|
||||
uint8_t issuing_state[4];
|
||||
uint8_t name[40];
|
||||
MrtdDate birth_date;
|
||||
uint8_t docnr[10];
|
||||
uint8_t nationality[4];
|
||||
uint8_t sex[2];
|
||||
MrtdDate expiry_date;
|
||||
} EF_DG1_contents;
|
||||
|
||||
uint8_t mrtd_bac_check_digit(const char* input, const uint8_t length);
|
||||
|
||||
//TODO: swap order, all other functions have output last
|
||||
void mrtd_print_date(char* output, MrtdDate* date);
|
||||
|
||||
void mrtd_parse_date(MrtdDate* date, const unsigned char* input);
|
||||
|
||||
bool mrtd_bac_get_kmrz(MrtdAuthData* auth, char* output, uint8_t output_size);
|
||||
|
||||
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, const uint8_t* key, uint8_t* output);
|
||||
|
||||
bool mrtd_bac_mac(const uint8_t* data, size_t data_length, const uint8_t* key, uint8_t* output);
|
||||
|
||||
bool mrtd_bac_mac_init(mrtd_bac_mac_ctx* ctx, const 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_mac_pad(mrtd_bac_mac_ctx* ctx); // TODO: internal only, remove from .h?
|
||||
|
||||
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);
|
||||
|
||||
//TODO: add some consts
|
||||
uint16_t mrtd_bac_decrypt_verify_sm(
|
||||
const uint8_t* data,
|
||||
size_t data_length,
|
||||
uint8_t* key_enc,
|
||||
uint8_t* key_mac,
|
||||
uint64_t ssc,
|
||||
uint8_t* output,
|
||||
size_t* output_written);
|
||||
|
||||
#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 (((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
|
||||
}
|
||||
|
||||
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,
|
||||
const uint8_t* key_enc,
|
||||
const uint8_t* key_mac,
|
||||
uint64_t ssc,
|
||||
uint8_t* output);
|
||||
|
||||
int tlv_number(TlvInfo tlv);
|
||||
|
||||
const EFFile* mrtd_tag_to_file(uint8_t tag);
|
||||
Reference in New Issue
Block a user