mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-04-24 03:29:57 -07:00
MRTD multiple changes
This commit is contained in:
@@ -17,9 +17,15 @@ void nfc_scene_passport_read_auth_on_enter(void* context) {
|
||||
// Setup Custom Widget view
|
||||
string_t temp_str;
|
||||
string_init_printf(temp_str, "\e#Passport\n");
|
||||
string_cat_printf(temp_str, "Authenticated: %d", mrtd_data->auth_success);
|
||||
string_cat_printf(temp_str, "Authenticated: %d\n", mrtd_data->auth_success);
|
||||
// TODO: indicate BAC / PACE used
|
||||
|
||||
uint16_t lds_version = mrtd_data->files.EF_COM.lds_version;
|
||||
string_cat_printf(temp_str, "LDS version: %d.%d\n", lds_version/100, lds_version%100);
|
||||
|
||||
uint32_t unicode_version = mrtd_data->files.EF_COM.unicode_version;
|
||||
string_cat_printf(temp_str, "Unicode version: %d.%d.%d\n", unicode_version/10000, unicode_version/100%100, unicode_version%100);
|
||||
|
||||
/*
|
||||
char iso_type = FURI_BIT(data->sak, 5) ? '4' : '3';
|
||||
//TODO: NFC-B?
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
|
||||
//TODO: idea - generalize ISO7816 reading. List available apps
|
||||
|
||||
#define num_elements(A) (sizeof(A)/sizeof(A[0]))
|
||||
|
||||
static void hexdump(FuriLogLevel level, char* prefix, void* data, size_t length) {
|
||||
if(furi_log_get_level() >= level) {
|
||||
printf("%s ", prefix);
|
||||
@@ -47,69 +49,9 @@ uint16_t mrtd_decode_response(uint8_t* buffer, size_t len) {
|
||||
return (buffer[len-2] << 8) | buffer[len-1];
|
||||
}
|
||||
|
||||
EFFile EFNone = {.name = NULL, .file_id = 0x0000, .short_id = 0x00, .tag = 0x00 };
|
||||
|
||||
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},
|
||||
};
|
||||
|
||||
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:
|
||||
furi_assert(false);
|
||||
return &EFNone;
|
||||
}
|
||||
};
|
||||
|
||||
//TODO: rename to transceive?
|
||||
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, uint8_t* output) {
|
||||
//TODO: PRIO output and output written writing seems to crash flipper, sometimes
|
||||
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, uint8_t* output, size_t* output_written) {
|
||||
FuriHalNfcTxRxContext* tx_rx = app->tx_rx;
|
||||
size_t idx = 0;
|
||||
|
||||
@@ -121,6 +63,8 @@ bool mrtd_send_apdu(MrtdApplication* app, uint8_t cla, uint8_t ins, uint8_t p1,
|
||||
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);
|
||||
|
||||
FURI_LOG_D(TAG, "Protect APDU - done");
|
||||
|
||||
} else {
|
||||
tx_rx->tx_data[idx++] = cla;
|
||||
tx_rx->tx_data[idx++] = ins;
|
||||
@@ -139,22 +83,25 @@ bool mrtd_send_apdu(MrtdApplication* app, uint8_t cla, uint8_t ins, uint8_t p1,
|
||||
tx_rx->tx_bits = idx * 8;
|
||||
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
|
||||
|
||||
FURI_LOG_D(TAG, "Sending...");
|
||||
//TODO: timeout as param?
|
||||
if(furi_hal_nfc_tx_rx(tx_rx, 300)) {
|
||||
mrtd_trace(app);
|
||||
FURI_LOG_D(TAG, "Sending - done");
|
||||
uint16_t ret_code = mrtd_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8);
|
||||
|
||||
if(app->secure_messaging && ret_code == 0x9000) {
|
||||
app->ssc_long++;
|
||||
mrtd_bac_decrypt_verify_sm(tx_rx->rx_data, tx_rx->rx_bits / 8 - 2,
|
||||
app->ksenc, app->ksmac, app->ssc_long, output, &ret_code);
|
||||
ret_code = mrtd_bac_decrypt_verify_sm(tx_rx->rx_data, tx_rx->rx_bits / 8 - 2,
|
||||
app->ksenc, app->ksmac, app->ssc_long, output, output_written);
|
||||
//ret_code = 0x1337; //TODO: remove PRIO
|
||||
}
|
||||
|
||||
//TODO: handle other return codes?
|
||||
if(ret_code == 0x9000) {
|
||||
if(!app->secure_messaging && le > 0) {
|
||||
// Secure Messaging sets output while decrypting
|
||||
memcpy(output, tx_rx->rx_data, le);
|
||||
output_written = memcpy(output, tx_rx->rx_data, le);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
@@ -173,6 +120,8 @@ bool mrtd_send_apdu(MrtdApplication* app, uint8_t cla, uint8_t ins, uint8_t p1,
|
||||
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_D(TAG, "Sending - failed");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -181,7 +130,7 @@ bool mrtd_send_apdu(MrtdApplication* app, uint8_t cla, uint8_t ins, uint8_t p1,
|
||||
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, NULL)) {
|
||||
if(!mrtd_send_apdu(app, 0x00, 0xA4, 0x04, 0x0C, 0x07, aid, -1, NULL, NULL)) {
|
||||
FURI_LOG_W(TAG, "Failed select App");
|
||||
return false;
|
||||
}
|
||||
@@ -190,7 +139,8 @@ bool mrtd_select_app(MrtdApplication* app, AIDValue aid) {
|
||||
|
||||
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, challenge)) {
|
||||
size_t chal_size;
|
||||
if(!mrtd_send_apdu(app, 0x00, 0x84, 0x00, 0x00, 0x00, NULL, 0x08, challenge, &chal_size)) {
|
||||
FURI_LOG_W(TAG, "Failed get challenge");
|
||||
return false;
|
||||
}
|
||||
@@ -203,7 +153,7 @@ bool mrtd_external_authenticate(MrtdApplication* app, uint8_t* cmd_data, size_t
|
||||
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, out_data)) {
|
||||
if(!mrtd_send_apdu(app, 0x00, 0x82, 0x00, 0x00, cmd_size, cmd_data, 0x28, out_data, &out_size)) {
|
||||
FURI_LOG_W(TAG, "Failed External Authenticate");
|
||||
return false;
|
||||
}
|
||||
@@ -213,8 +163,12 @@ bool mrtd_external_authenticate(MrtdApplication* app, uint8_t* cmd_data, size_t
|
||||
|
||||
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);
|
||||
if(!mrtd_send_apdu(app, 0x00, 0xA4, 0x02, 0x0C, 0x02, data, -1, NULL)) {
|
||||
FURI_LOG_D(TAG, "Send select EF: %s (0x%04X)", file.name, file.file_id);
|
||||
uint8_t buffer[100];
|
||||
size_t buffer_written = 0;
|
||||
if(!mrtd_send_apdu(app, 0x00, 0xA4, 0x02, 0x0C, 0x02, data, -1, buffer, &buffer_written)) {
|
||||
FURI_LOG_D(TAG, "Buffer_written: %d", buffer_written);
|
||||
hexdump(FuriLogLevelDebug, "Buffer:", buffer, buffer_written);
|
||||
FURI_LOG_E(TAG, "Failed select EF 0x%04X", file.file_id);
|
||||
return false;
|
||||
}
|
||||
@@ -222,7 +176,6 @@ 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);
|
||||
@@ -231,15 +184,15 @@ size_t mrtd_read_binary(MrtdApplication* app, uint8_t* buffer, size_t bufsize, s
|
||||
//TODO: read first 4 bytes, determine length, iterate through file
|
||||
//TODO: limit reading/buffer fill to max bufsize
|
||||
|
||||
int16_t max_read = 0; // 0 = 'everything', -1 = 'nothing'
|
||||
if(!mrtd_send_apdu(app, 0x00, 0xB0, offset>>8, offset&0xff, 0x00, NULL, max_read, buffer)) {
|
||||
//TODO: test with max_read = bufsize (value !0, > file size)
|
||||
int16_t max_read = 0; // 0 = 'everything', -1 = 'nothing', >0 = amount of bytes
|
||||
size_t buf_written;
|
||||
if(!mrtd_send_apdu(app, 0x00, 0xB0, offset>>8, offset&0xff, 0x00, NULL, max_read, buffer, &buf_written)) {
|
||||
FURI_LOG_E(TAG, "Failed to read");
|
||||
return 0;
|
||||
}
|
||||
|
||||
//TODO: return read amount
|
||||
|
||||
return 0;
|
||||
return buf_written;
|
||||
}
|
||||
|
||||
//TODO: use short id to read, because it's mandatory for eMRTD
|
||||
@@ -258,7 +211,7 @@ void mrtd_read_dump(MrtdApplication* app, EFFile file, const char* descr) {
|
||||
} while(read > 0);
|
||||
}
|
||||
|
||||
void parse_ef_dir(EF_DIR_contents* EF_DIR, const uint8_t* data, size_t length) {
|
||||
bool parse_ef_dir(EF_DIR_contents* EF_DIR, const uint8_t* data, size_t length) {
|
||||
size_t offset = 0;
|
||||
uint8_t app_idx = 0;
|
||||
|
||||
@@ -270,13 +223,13 @@ void parse_ef_dir(EF_DIR_contents* EF_DIR, const uint8_t* data, size_t length) {
|
||||
|
||||
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;
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(EF_DIR->applications[app_idx], tlv.value, tlv.length);
|
||||
@@ -296,26 +249,89 @@ void parse_ef_dir(EF_DIR_contents* EF_DIR, const uint8_t* data, size_t length) {
|
||||
printf("\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
bool parse_ef_com(EF_COM_contents* EF_COM, const uint8_t* data, size_t length) {
|
||||
uint16_t lds_tag_path[] = {0x60, 0x5f01};
|
||||
uint16_t unicode_tag_path[] = {0x60, 0x5f36};
|
||||
uint16_t tags_tag_path[] = {0x60, 0x5c};
|
||||
|
||||
TlvInfo tlv = iso7816_tlv_parse(data + offset);
|
||||
UNUSED(tlv); //TODO
|
||||
TlvInfo tlv_lds_version = iso7816_tlv_select(data, length, lds_tag_path, num_elements(lds_tag_path));
|
||||
if(tlv_lds_version.tag) {
|
||||
EF_COM->lds_version = tlv_number(tlv_lds_version);
|
||||
} else {
|
||||
FURI_LOG_W(TAG, "EF.COM LDS version not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
TlvInfo tlv_unicode_version = iso7816_tlv_select(data, length, unicode_tag_path, num_elements(unicode_tag_path));
|
||||
if(tlv_unicode_version.tag) {
|
||||
EF_COM->unicode_version = tlv_number(tlv_unicode_version);
|
||||
} else {
|
||||
FURI_LOG_W(TAG, "EF.COM Unicode info not found!");
|
||||
return false;
|
||||
}
|
||||
|
||||
TlvInfo tlv_tag_list = iso7816_tlv_select(data, length, tags_tag_path, num_elements(tags_tag_path));
|
||||
if(tlv_tag_list.tag) {
|
||||
for(size_t i=0; i<MAX_EFCOM_TAGS; ++i) {
|
||||
EF_COM->tag_list[i] = (i < tlv_tag_list.length) ? tlv_tag_list.value[i] : 0x00;
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_W(TAG, "EF.CO Tag List not found!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mrtd_read_parse_file(MrtdApplication* app, MrtdData* mrtd_data, EFFile file) {
|
||||
uint8_t buffer[100];
|
||||
size_t buf_len;
|
||||
|
||||
FURI_LOG_D(TAG, "Read and parse %s (%04X)", file.name, file.file_id);
|
||||
|
||||
if(!mrtd_select_file(app, file)) {
|
||||
FURI_LOG_E(TAG, "Could not select %s", file.name);
|
||||
return false;
|
||||
}
|
||||
|
||||
FURI_LOG_D(TAG, "Selected %s", file.name);
|
||||
|
||||
buf_len = mrtd_read_binary(app, buffer, num_elements(buffer), 0);
|
||||
|
||||
if(!buf_len) {
|
||||
FURI_LOG_E(TAG, "Could not read %s", file.name);
|
||||
return false;
|
||||
}
|
||||
|
||||
FURI_LOG_D(TAG, "Read %s", file.name);
|
||||
|
||||
bool result = false;
|
||||
|
||||
if(file.file_id == EF.COM.file_id) {
|
||||
result = parse_ef_com(&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);
|
||||
FURI_LOG_D(TAG, "Parsed EF.DIR");
|
||||
} else {
|
||||
FURI_LOG_W(TAG, "Don't know how to parse file with id 0x%04X", file.file_id);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//TODO: remove testing function
|
||||
void mrtd_test(MrtdApplication* app, MrtdData* mrtd_data) {
|
||||
FuriHalNfcTxRxContext* tx_rx = app->tx_rx;
|
||||
//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");
|
||||
mrtd_read_dump(app, EF.CardSecurity, "EF.CardSecurity");
|
||||
|
||||
@@ -349,7 +365,8 @@ void mrtd_test(MrtdApplication* app, MrtdData* mrtd_data) {
|
||||
return;
|
||||
}
|
||||
|
||||
mrtd_read_dump(app, EF.COM, "EF.COM");
|
||||
mrtd_read_parse_file(app, mrtd_data, EF.COM);
|
||||
mrtd_read_parse_file(app, mrtd_data, EF.DIR);
|
||||
}
|
||||
|
||||
MrtdApplication* mrtd_alloc_init(FuriHalNfcTxRxContext* tx_rx) {
|
||||
|
||||
@@ -4,30 +4,6 @@
|
||||
|
||||
#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 {
|
||||
uint16_t lds_version;
|
||||
uint16_t unicode_version;
|
||||
//TODO: taglist
|
||||
} EF_COM_contents;
|
||||
|
||||
typedef struct {
|
||||
FuriHalNfcTxRxContext* tx_rx;
|
||||
uint16_t file_offset;
|
||||
@@ -36,61 +12,18 @@ typedef struct {
|
||||
uint64_t ssc_long; // TODO: rename without _long
|
||||
|
||||
bool secure_messaging;
|
||||
|
||||
struct {
|
||||
EF_DIR_contents EF_DIR;
|
||||
} files;
|
||||
} MrtdApplication;
|
||||
|
||||
typedef struct {
|
||||
MrtdAuthData auth;
|
||||
bool auth_success;
|
||||
|
||||
struct {
|
||||
EF_DIR_contents EF_DIR;
|
||||
EF_COM_contents EF_COM;
|
||||
} files;
|
||||
} MrtdData;
|
||||
|
||||
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)
|
||||
EFFile ATR;
|
||||
EFFile DIR;
|
||||
EFFile CardAccess;
|
||||
EFFile CardSecurity;
|
||||
|
||||
// Under LDS1 eMRTD Application
|
||||
EFFile COM;
|
||||
EFFile SOD;
|
||||
EFFile DG1;
|
||||
EFFile DG2;
|
||||
EFFile DG3;
|
||||
EFFile DG4;
|
||||
EFFile DG5;
|
||||
EFFile DG6;
|
||||
EFFile DG7;
|
||||
EFFile DG8;
|
||||
EFFile DG9;
|
||||
EFFile DG10;
|
||||
EFFile DG11;
|
||||
EFFile DG12;
|
||||
EFFile DG13;
|
||||
EFFile DG14;
|
||||
EFFile DG15;
|
||||
EFFile DG16;
|
||||
};
|
||||
|
||||
extern struct EFFormat EF;
|
||||
|
||||
#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];
|
||||
} EFComFormat;
|
||||
|
||||
//TODO: description
|
||||
MrtdApplication* mrtd_alloc_init(FuriHalNfcTxRxContext* tx_rx);
|
||||
bool mrtd_select_app(MrtdApplication* app, AIDValue aid);
|
||||
|
||||
@@ -129,7 +129,7 @@ bool mrtd_bac_keys(MrtdAuthData* auth, uint8_t ksenc[16], uint8_t ksmac[16]) {
|
||||
}
|
||||
|
||||
//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) {
|
||||
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;
|
||||
@@ -173,38 +173,49 @@ bool mrtd_bac_decrypt_verify(const uint8_t* data, size_t data_length, uint8_t* k
|
||||
return true;
|
||||
}
|
||||
|
||||
bool 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, uint16_t* ret_code) {
|
||||
// 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
|
||||
|
||||
*ret_code = data[data_length - 10 - 2] <<8 | data[data_length - 10 - 1];
|
||||
//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);
|
||||
|
||||
if(data[0] == 0x87) {
|
||||
uint8_t do87_length = data[1] - 1;
|
||||
mrtd_bac_decrypt(data + 3, do87_length, key_enc, output);
|
||||
printf("Decrypted: "); for(uint8_t i=0; i<do87_length; ++i) printf("%02X ", output[i]); printf("\r\n");
|
||||
if(output_written != NULL && output != NULL) {
|
||||
uint8_t do87_length = data[1] - 1;
|
||||
mrtd_bac_decrypt(data + 3, do87_length, key_enc, output);
|
||||
printf("Decrypted: "); for(uint8_t i=0; i<do87_length; ++i) printf("%02X ", output[i]); printf("\r\n");
|
||||
|
||||
//TODO: mrtd_bac_unpad
|
||||
int padidx;
|
||||
for(padidx=do87_length-1; padidx>=0; --padidx) {
|
||||
if(output[padidx] == 0x00) {
|
||||
continue;
|
||||
} else if(output[padidx] == 0x80) {
|
||||
break;
|
||||
} else {
|
||||
printf("Invalid padding\r\n");
|
||||
return false;
|
||||
//TODO: function mrtd_bac_unpad?
|
||||
int padidx;
|
||||
for(padidx=do87_length-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);
|
||||
printf(" ");
|
||||
for(int i=0; i<padidx; ++i) {
|
||||
printf(" ");
|
||||
}
|
||||
printf("^^\r\n");
|
||||
printf("Pad starts at: %d\r\n", padidx);
|
||||
|
||||
//TODO: return padidx-1 as output length
|
||||
*output_written = padidx-1;
|
||||
}
|
||||
} else {
|
||||
if(output_written != NULL) {
|
||||
*output_written = 0;
|
||||
}
|
||||
}
|
||||
|
||||
mrtd_bac_mac_ctx ctx;
|
||||
@@ -216,16 +227,16 @@ bool mrtd_bac_decrypt_verify_sm(const uint8_t* data, size_t data_length, uint8_t
|
||||
mrtd_bac_mac_finalize(&ctx, mac_calc);
|
||||
|
||||
if(memcmp(mac_calc, data + data_length - 8, 8)) {
|
||||
printf( "SM MAC failed\r\n");
|
||||
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 false;
|
||||
return 0xff02;
|
||||
}
|
||||
return true;
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
bool mrtd_bac_mac_init(mrtd_bac_mac_ctx* ctx, uint8_t key[16]) {
|
||||
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);
|
||||
@@ -316,7 +327,7 @@ bool mrtd_bac_mac_finalize(mrtd_bac_mac_ctx* ctx, uint8_t output[8]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mrtd_bac_mac(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, const uint8_t* key, uint8_t* output) {
|
||||
// MAC
|
||||
uint8_t mac[8];
|
||||
uint8_t xormac[8];
|
||||
@@ -366,7 +377,7 @@ 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) {
|
||||
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;
|
||||
|
||||
@@ -374,7 +385,7 @@ size_t mrtd_protect_apdu(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8
|
||||
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: %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);
|
||||
|
||||
@@ -439,9 +450,83 @@ size_t mrtd_protect_apdu(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8
|
||||
|
||||
output[idx++] = 0x00;
|
||||
|
||||
if(le) {
|
||||
//TODO: le?
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#include <mbedtls/des.h>
|
||||
|
||||
#include "../helpers/iso7816.h"
|
||||
|
||||
typedef struct {
|
||||
uint8_t year;
|
||||
uint8_t month;
|
||||
@@ -44,6 +46,69 @@ typedef struct {
|
||||
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;
|
||||
|
||||
uint8_t mrtd_bac_check_digit(const char* input, const uint8_t length);
|
||||
|
||||
//TODO: swap order, all other functions have output last
|
||||
@@ -55,23 +120,26 @@ bool mrtd_bac_keys_from_seed(const uint8_t* kseed, uint8_t* ksenc, uint8_t* ksma
|
||||
|
||||
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_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, 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, uint8_t key[16]);
|
||||
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);
|
||||
|
||||
bool 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, uint16_t* ret_code);
|
||||
//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))
|
||||
@@ -94,4 +162,6 @@ static __inline uint64_t mrtd_ssc_from_data(const uint8_t* rnd_ic, const uint8_t
|
||||
#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);
|
||||
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);
|
||||
|
||||
@@ -27,24 +27,6 @@ void print_tlv(char* fmt, TlvInfo tlv) {
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int tlv_number(TlvInfo tlv) {
|
||||
//TODO: negative numbers?
|
||||
const char* 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;
|
||||
}
|
||||
|
||||
void test_iso7816_tlv_parse(const uint8_t* input, size_t input_size, uint16_t exp_tag, size_t exp_length) {
|
||||
TlvInfo tlv = iso7816_tlv_parse(input);
|
||||
|
||||
|
||||
@@ -281,18 +281,14 @@ void test_mrtd_bac_decrypt_verify(const uint8_t* data, size_t data_length, uint8
|
||||
printf(COLOR_RESET "\n");
|
||||
}
|
||||
|
||||
void test_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* exp_output, size_t exp_output_length, bool should_verify) {
|
||||
void test_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* exp_output, size_t exp_output_length, uint16_t exp_code) {
|
||||
uint8_t buffer[256];
|
||||
uint16_t ret_code;
|
||||
size_t buffer_len;
|
||||
|
||||
bool result = mrtd_bac_decrypt_verify_sm(data, data_length, key_enc, key_mac, ssc, buffer, &ret_code);
|
||||
if(result != should_verify) {
|
||||
printf(COLOR_RED "FAILED - mrtd_bac_decrypt_verify_sm, expected verify: %d, but is: %d\n" COLOR_RESET, should_verify, result);
|
||||
return;
|
||||
}
|
||||
|
||||
if(ret_code != 0x9000) {
|
||||
printf(COLOR_RED "FAILED - mrtd_bac_decrypt_verify_sm, expected ret_code: %04X, but is: %04X\n" COLOR_RESET, 0x9000, ret_code);
|
||||
ret_code = mrtd_bac_decrypt_verify_sm(data, data_length, key_enc, key_mac, ssc, buffer, &buffer_len);
|
||||
if(ret_code != exp_code) {
|
||||
printf(COLOR_RED "FAILED - mrtd_bac_decrypt_verify_sm, expected ret_code: %04X, but is: %04X\n" COLOR_RESET, exp_code, ret_code);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -438,7 +434,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
ssc++; // Increment for decrypt, verify
|
||||
|
||||
test_mrtd_bac_decrypt_verify_sm((uint8_t*)"\x99\x02\x90\x00\x8E\x08\xFA\x85\x5A\x5D\x4C\x50\xA8\xED", 14, ks_enc, ks_mac, ssc, NULL, 0, 1);
|
||||
test_mrtd_bac_decrypt_verify_sm((uint8_t*)"\x99\x02\x90\x00\x8E\x08\xFA\x85\x5A\x5D\x4C\x50\xA8\xED", 14, ks_enc, ks_mac, ssc, NULL, 0, 0x9000);
|
||||
|
||||
ssc++; // Increment for encrypt, sign
|
||||
|
||||
@@ -446,7 +442,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
ssc++; // Increment for decrypt, verify
|
||||
|
||||
test_mrtd_bac_decrypt_verify_sm((uint8_t*)"\x87\x09\x01\x9F\xF0\xEC\x34\xF9\x92\x26\x51\x99\x02\x90\x00\x8E\x08\xAD\x55\xCC\x17\x14\x0B\x2D\xED", 25, ks_enc, ks_mac, ssc, (uint8_t*)"\x60\x14\x5F\x01", 4, 1);
|
||||
test_mrtd_bac_decrypt_verify_sm((uint8_t*)"\x87\x09\x01\x9F\xF0\xEC\x34\xF9\x92\x26\x51\x99\x02\x90\x00\x8E\x08\xAD\x55\xCC\x17\x14\x0B\x2D\xED", 25, ks_enc, ks_mac, ssc, (uint8_t*)"\x60\x14\x5F\x01", 4, 0x9000);
|
||||
|
||||
ssc++; // Increment for encrypt, sign
|
||||
|
||||
|
||||
Reference in New Issue
Block a user