mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-04-24 03:29:57 -07:00
fmt
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
|
||||
//TODO: idea - generalize ISO7816 reading. List available apps
|
||||
|
||||
#define num_elements(A) (sizeof(A)/sizeof(A[0]))
|
||||
#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) {
|
||||
@@ -46,12 +46,22 @@ static void mrtd_trace(MrtdApplication* app) {
|
||||
|
||||
uint16_t mrtd_decode_response(uint8_t* buffer, size_t len) {
|
||||
// Last two bytes are return code
|
||||
return (buffer[len-2] << 8) | buffer[len-1];
|
||||
return (buffer[len - 2] << 8) | buffer[len - 1];
|
||||
}
|
||||
|
||||
//TODO: rename to transceive?
|
||||
//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) {
|
||||
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;
|
||||
|
||||
@@ -59,7 +69,8 @@ bool mrtd_send_apdu(MrtdApplication* app, uint8_t cla, uint8_t ins, uint8_t p1,
|
||||
|
||||
if(app->secure_messaging) {
|
||||
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);
|
||||
idx = mrtd_protect_apdu(
|
||||
cla, ins, p1, p2, lc, data, le, app->ksenc, app->ksmac, app->ssc_long, tx_rx->tx_data);
|
||||
} else {
|
||||
tx_rx->tx_data[idx++] = cla;
|
||||
tx_rx->tx_data[idx++] = ins;
|
||||
@@ -71,7 +82,7 @@ bool mrtd_send_apdu(MrtdApplication* app, uint8_t cla, uint8_t ins, uint8_t p1,
|
||||
idx += lc;
|
||||
}
|
||||
if(le >= 0) {
|
||||
tx_rx->tx_data[idx++] = le&0xff;
|
||||
tx_rx->tx_data[idx++] = le & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,8 +96,14 @@ bool mrtd_send_apdu(MrtdApplication* app, uint8_t cla, uint8_t ins, uint8_t p1,
|
||||
|
||||
if(app->secure_messaging && ret_code == 0x9000) {
|
||||
app->ssc_long++;
|
||||
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 = 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
|
||||
}
|
||||
|
||||
@@ -101,22 +118,22 @@ bool mrtd_send_apdu(MrtdApplication* app, uint8_t cla, uint8_t ins, uint8_t p1,
|
||||
FURI_LOG_I(TAG, "APDU answer is not 0x9000, but 0x%04X", ret_code);
|
||||
|
||||
switch(ret_code) {
|
||||
case 0x6987:
|
||||
FURI_LOG_I(TAG, "'expected secure messaging data objects are missing'");
|
||||
app->secure_messaging = false;
|
||||
break;
|
||||
case 0x6988:
|
||||
FURI_LOG_I(TAG, "'secure messaging data objects are incorrect'");
|
||||
app->secure_messaging = false;
|
||||
break;
|
||||
case 0xff01:
|
||||
//CUSTOM ERROR CODE from mrtd_helpers.c
|
||||
FURI_LOG_I(TAG, "'invalid padding'");
|
||||
break;
|
||||
case 0xff02:
|
||||
//CUSTOM ERROR CODE from mrtd_helpers.c
|
||||
FURI_LOG_I(TAG, "'verify failed'");
|
||||
break;
|
||||
case 0x6987:
|
||||
FURI_LOG_I(TAG, "'expected secure messaging data objects are missing'");
|
||||
app->secure_messaging = false;
|
||||
break;
|
||||
case 0x6988:
|
||||
FURI_LOG_I(TAG, "'secure messaging data objects are incorrect'");
|
||||
app->secure_messaging = false;
|
||||
break;
|
||||
case 0xff01:
|
||||
//CUSTOM ERROR CODE from mrtd_helpers.c
|
||||
FURI_LOG_I(TAG, "'invalid padding'");
|
||||
break;
|
||||
case 0xff02:
|
||||
//CUSTOM ERROR CODE from mrtd_helpers.c
|
||||
FURI_LOG_I(TAG, "'verify failed'");
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -129,8 +146,16 @@ bool mrtd_send_apdu(MrtdApplication* app, uint8_t cla, uint8_t ins, uint8_t p1,
|
||||
|
||||
//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]);
|
||||
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, NULL)) {
|
||||
FURI_LOG_W(TAG, "Failed select App");
|
||||
return false;
|
||||
@@ -149,12 +174,18 @@ bool mrtd_get_challenge(MrtdApplication* app, uint8_t challenge[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) {
|
||||
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, out_data, &out_size)) {
|
||||
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;
|
||||
}
|
||||
@@ -184,7 +215,8 @@ size_t mrtd_read_binary(MrtdApplication* app, uint8_t* buffer, size_t bufsize, s
|
||||
//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 = 0;
|
||||
if(!mrtd_send_apdu(app, 0x00, 0xB0, offset>>8, offset&0xff, 0x00, NULL, max_read, buffer, &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;
|
||||
}
|
||||
@@ -225,13 +257,19 @@ bool parse_ef_dir(EF_DIR_contents* EF_DIR, const uint8_t* data, size_t 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);
|
||||
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 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);
|
||||
FURI_LOG_E(
|
||||
TAG, "Invalid EF.DIR, subtag at offset %d must be '4F' and length 7", offset);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -244,9 +282,9 @@ bool parse_ef_dir(EF_DIR_contents* EF_DIR, const uint8_t* data, size_t length) {
|
||||
//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) {
|
||||
for(uint8_t i = 0; i < EF_DIR->applications_count; ++i) {
|
||||
printf("- ");
|
||||
for(uint8_t n=0; n<sizeof(AIDValue); ++n) {
|
||||
for(uint8_t n = 0; n < sizeof(AIDValue); ++n) {
|
||||
printf("%02X ", EF_DIR->applications[i][n]);
|
||||
}
|
||||
printf("\r\n");
|
||||
@@ -261,7 +299,8 @@ bool parse_ef_com(EF_COM_contents* EF_COM, const uint8_t* data, size_t length) {
|
||||
uint16_t unicode_tag_path[] = {0x60, 0x5f36};
|
||||
uint16_t tags_tag_path[] = {0x60, 0x5c};
|
||||
|
||||
TlvInfo tlv_lds_version = iso7816_tlv_select(data, length, lds_tag_path, num_elements(lds_tag_path));
|
||||
TlvInfo tlv_lds_version =
|
||||
iso7816_tlv_select(data, length, lds_tag_path, num_elements(lds_tag_path));
|
||||
if(!tlv_lds_version.tag) {
|
||||
FURI_LOG_W(TAG, "EF.COM LDS version not found");
|
||||
return false;
|
||||
@@ -269,7 +308,8 @@ bool parse_ef_com(EF_COM_contents* EF_COM, const uint8_t* data, size_t length) {
|
||||
|
||||
EF_COM->lds_version = tlv_number(tlv_lds_version);
|
||||
|
||||
TlvInfo tlv_unicode_version = iso7816_tlv_select(data, length, unicode_tag_path, num_elements(unicode_tag_path));
|
||||
TlvInfo tlv_unicode_version =
|
||||
iso7816_tlv_select(data, length, unicode_tag_path, num_elements(unicode_tag_path));
|
||||
if(!tlv_unicode_version.tag) {
|
||||
FURI_LOG_W(TAG, "EF.COM Unicode info not found!");
|
||||
return false;
|
||||
@@ -277,13 +317,14 @@ bool parse_ef_com(EF_COM_contents* EF_COM, const uint8_t* data, size_t length) {
|
||||
|
||||
EF_COM->unicode_version = tlv_number(tlv_unicode_version);
|
||||
|
||||
TlvInfo tlv_tag_list = iso7816_tlv_select(data, length, tags_tag_path, num_elements(tags_tag_path));
|
||||
TlvInfo tlv_tag_list =
|
||||
iso7816_tlv_select(data, length, tags_tag_path, num_elements(tags_tag_path));
|
||||
if(!tlv_tag_list.tag) {
|
||||
FURI_LOG_W(TAG, "EF.CO Tag List not found!");
|
||||
return false;
|
||||
}
|
||||
|
||||
for(size_t i=0; i<MAX_EFCOM_TAGS; ++i) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -293,7 +334,7 @@ bool parse_ef_com(EF_COM_contents* EF_COM, const uint8_t* data, size_t length) {
|
||||
void mrzcpy(uint8_t* dest, const uint8_t* src, size_t* idx, size_t n) {
|
||||
//FURI_LOG_D(TAG, "mrzcpy %d: %.*s", n, n, src + *idx);
|
||||
//memcpy(dest, src + *idx, n);
|
||||
for(size_t i=0; i<n; ++i) {
|
||||
for(size_t i = 0; i < n; ++i) {
|
||||
uint8_t c = src[i + *idx];
|
||||
if(c == '<') {
|
||||
c = ' ';
|
||||
@@ -316,68 +357,69 @@ bool parse_ef_dg1(EF_DG1_contents* DG1, const uint8_t* data, size_t length) {
|
||||
size_t idx = 0;
|
||||
|
||||
switch(tlv_mrz.length) {
|
||||
case 90:
|
||||
DG1->type = MrtdTypeTD1;
|
||||
mrzcpy(DG1->doctype, mrz, &idx, 2);
|
||||
mrzcpy(DG1->issuing_state, mrz, &idx, 3);
|
||||
mrzcpy(DG1->docnr, mrz, &idx, 9);
|
||||
idx += 1; // docnr check digit
|
||||
idx += 15; // optional data
|
||||
mrtd_parse_date(&DG1->birth_date, mrz + idx);
|
||||
idx += 6; // birth_date
|
||||
idx += 1; // birth date check digit
|
||||
mrzcpy(DG1->sex, mrz, &idx, 1);
|
||||
mrtd_parse_date(&DG1->expiry_date, mrz + idx);
|
||||
idx += 6; // expiry_date
|
||||
idx += 1; // expiry date check digit
|
||||
mrzcpy(DG1->nationality, mrz, &idx, 3);
|
||||
idx += 11; // optional data
|
||||
idx += 1; // check digit
|
||||
mrzcpy(DG1->name, mrz, &idx, 30);
|
||||
// 30 + 30 + 30
|
||||
break;
|
||||
case 72:
|
||||
DG1->type = MrtdTypeTD2;
|
||||
mrzcpy(DG1->doctype, mrz, &idx, 2);
|
||||
mrzcpy(DG1->issuing_state, mrz, &idx, 3);
|
||||
mrzcpy(DG1->name, mrz, &idx, 31);
|
||||
mrzcpy(DG1->docnr, mrz, &idx, 9);
|
||||
idx += 1; // docnr check digit
|
||||
mrzcpy(DG1->nationality, mrz, &idx, 3);
|
||||
mrtd_parse_date(&DG1->birth_date, mrz + idx);
|
||||
idx += 6; // birth_date
|
||||
idx += 1; // birth date check digit
|
||||
mrzcpy(DG1->sex, mrz, &idx, 1);
|
||||
mrtd_parse_date(&DG1->expiry_date, mrz + idx);
|
||||
idx += 6; // expiry_date
|
||||
idx += 1; // expiry date check digit
|
||||
idx += 7; // optional data
|
||||
idx += 1; // check digit
|
||||
// 36 + 36
|
||||
break;
|
||||
case 88:
|
||||
DG1->type = MrtdTypeTD3;
|
||||
mrzcpy(DG1->doctype, mrz, &idx, 2);
|
||||
mrzcpy(DG1->issuing_state, mrz, &idx, 3);
|
||||
mrzcpy(DG1->name, mrz, &idx, 39);
|
||||
mrzcpy(DG1->docnr, mrz, &idx, 9);
|
||||
idx += 1; // docnr check digit
|
||||
mrzcpy(DG1->nationality, mrz, &idx, 3);
|
||||
mrtd_parse_date(&DG1->birth_date, mrz + idx);
|
||||
idx += 1; // birth date check digit
|
||||
idx += 6; // birth_date
|
||||
mrzcpy(DG1->sex, mrz, &idx, 1);
|
||||
mrtd_parse_date(&DG1->expiry_date, mrz + idx);
|
||||
idx += 6; // expiry_date
|
||||
idx += 1; // expiry date check digit
|
||||
idx += 14; // optional data
|
||||
idx += 1; // check digit
|
||||
idx += 1; // check digit
|
||||
// 44 + 44
|
||||
break;
|
||||
default:
|
||||
FURI_LOG_W(TAG, "Unexpected MRZ length in DG1: %d. TD1=90, TD2=72, TD3=88.", tlv_mrz.length);
|
||||
return false;
|
||||
case 90:
|
||||
DG1->type = MrtdTypeTD1;
|
||||
mrzcpy(DG1->doctype, mrz, &idx, 2);
|
||||
mrzcpy(DG1->issuing_state, mrz, &idx, 3);
|
||||
mrzcpy(DG1->docnr, mrz, &idx, 9);
|
||||
idx += 1; // docnr check digit
|
||||
idx += 15; // optional data
|
||||
mrtd_parse_date(&DG1->birth_date, mrz + idx);
|
||||
idx += 6; // birth_date
|
||||
idx += 1; // birth date check digit
|
||||
mrzcpy(DG1->sex, mrz, &idx, 1);
|
||||
mrtd_parse_date(&DG1->expiry_date, mrz + idx);
|
||||
idx += 6; // expiry_date
|
||||
idx += 1; // expiry date check digit
|
||||
mrzcpy(DG1->nationality, mrz, &idx, 3);
|
||||
idx += 11; // optional data
|
||||
idx += 1; // check digit
|
||||
mrzcpy(DG1->name, mrz, &idx, 30);
|
||||
// 30 + 30 + 30
|
||||
break;
|
||||
case 72:
|
||||
DG1->type = MrtdTypeTD2;
|
||||
mrzcpy(DG1->doctype, mrz, &idx, 2);
|
||||
mrzcpy(DG1->issuing_state, mrz, &idx, 3);
|
||||
mrzcpy(DG1->name, mrz, &idx, 31);
|
||||
mrzcpy(DG1->docnr, mrz, &idx, 9);
|
||||
idx += 1; // docnr check digit
|
||||
mrzcpy(DG1->nationality, mrz, &idx, 3);
|
||||
mrtd_parse_date(&DG1->birth_date, mrz + idx);
|
||||
idx += 6; // birth_date
|
||||
idx += 1; // birth date check digit
|
||||
mrzcpy(DG1->sex, mrz, &idx, 1);
|
||||
mrtd_parse_date(&DG1->expiry_date, mrz + idx);
|
||||
idx += 6; // expiry_date
|
||||
idx += 1; // expiry date check digit
|
||||
idx += 7; // optional data
|
||||
idx += 1; // check digit
|
||||
// 36 + 36
|
||||
break;
|
||||
case 88:
|
||||
DG1->type = MrtdTypeTD3;
|
||||
mrzcpy(DG1->doctype, mrz, &idx, 2);
|
||||
mrzcpy(DG1->issuing_state, mrz, &idx, 3);
|
||||
mrzcpy(DG1->name, mrz, &idx, 39);
|
||||
mrzcpy(DG1->docnr, mrz, &idx, 9);
|
||||
idx += 1; // docnr check digit
|
||||
mrzcpy(DG1->nationality, mrz, &idx, 3);
|
||||
mrtd_parse_date(&DG1->birth_date, mrz + idx);
|
||||
idx += 1; // birth date check digit
|
||||
idx += 6; // birth_date
|
||||
mrzcpy(DG1->sex, mrz, &idx, 1);
|
||||
mrtd_parse_date(&DG1->expiry_date, mrz + idx);
|
||||
idx += 6; // expiry_date
|
||||
idx += 1; // expiry date check digit
|
||||
idx += 14; // optional data
|
||||
idx += 1; // check digit
|
||||
idx += 1; // check digit
|
||||
// 44 + 44
|
||||
break;
|
||||
default:
|
||||
FURI_LOG_W(
|
||||
TAG, "Unexpected MRZ length in DG1: %d. TD1=90, TD2=72, TD3=88.", tlv_mrz.length);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -437,17 +479,17 @@ void mrtd_test(MrtdApplication* app, MrtdData* mrtd_data) {
|
||||
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;
|
||||
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) {
|
||||
@@ -514,14 +556,14 @@ bool mrtd_bac(MrtdApplication* app, MrtdAuthData* auth) {
|
||||
|
||||
uint8_t S[32];
|
||||
memcpy(S, rnd_ifd, 8);
|
||||
memcpy(S+8, rnd_ic, 8);
|
||||
memcpy(S+16, k_ifd, 16);
|
||||
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 *mifd = cmd_data+32;
|
||||
uint8_t* eifd = cmd_data;
|
||||
uint8_t* mifd = cmd_data + 32;
|
||||
mrtd_bac_encrypt(S, 32, kenc, eifd);
|
||||
mrtd_bac_padded_mac(eifd, 32, kmac, mifd);
|
||||
|
||||
@@ -536,8 +578,8 @@ bool mrtd_bac(MrtdApplication* app, MrtdAuthData* auth) {
|
||||
FURI_LOG_W(TAG, "BAC DecryptVerify failed");
|
||||
}
|
||||
|
||||
uint8_t *rnd_ifd_recv = buffer + 8;
|
||||
uint8_t *kic = buffer + 16;
|
||||
uint8_t* rnd_ifd_recv = buffer + 8;
|
||||
uint8_t* kic = buffer + 16;
|
||||
|
||||
hexdump(FuriLogLevelDebug, "kic:", kic, 16);
|
||||
|
||||
@@ -546,7 +588,7 @@ bool mrtd_bac(MrtdApplication* app, MrtdAuthData* auth) {
|
||||
}
|
||||
|
||||
uint8_t kseed[16];
|
||||
for(uint8_t i=0; i<16; ++i) {
|
||||
for(uint8_t i = 0; i < 16; ++i) {
|
||||
kseed[i] = k_ifd[i] ^ kic[i];
|
||||
//printf("seed %2d = %02X ^ %02X = %02X\r\n", i, k_ifd[i], kic[i], kseed[i]);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
#include <mbedtls/sha1.h>
|
||||
#include <mbedtls/des.h>
|
||||
|
||||
static inline unsigned char *ucstr(const char *str) { return (unsigned char *)str; }
|
||||
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;
|
||||
@@ -15,7 +17,7 @@ uint8_t mrtd_bac_check_digit(const char* input, const uint8_t length) {
|
||||
uint8_t check_digit = 0;
|
||||
uint8_t idx;
|
||||
|
||||
for(uint8_t i=0; i<length; ++i) {
|
||||
for(uint8_t i = 0; i < length; ++i) {
|
||||
char c = input[i];
|
||||
if(c >= 'A' && c <= 'Z') {
|
||||
idx = c - 'A' + 10;
|
||||
@@ -26,7 +28,7 @@ uint8_t mrtd_bac_check_digit(const char* input, const uint8_t length) {
|
||||
} else {
|
||||
idx = 0;
|
||||
}
|
||||
check_digit = (check_digit + idx * weights[i%num_weights]) % 10;
|
||||
check_digit = (check_digit + idx * weights[i % num_weights]) % 10;
|
||||
}
|
||||
return check_digit;
|
||||
}
|
||||
@@ -62,7 +64,7 @@ bool mrtd_bac_get_kmrz(MrtdAuthData* auth, char* output, uint8_t output_size) {
|
||||
}
|
||||
|
||||
cd_idx = idx;
|
||||
for(uint8_t i=0; i<docnr_length; ++i) {
|
||||
for(uint8_t i = 0; i < docnr_length; ++i) {
|
||||
char c = auth->doc_number[i];
|
||||
if(c >= 'a' && c <= 'z') {
|
||||
c = c - 'a' + 'A';
|
||||
@@ -71,21 +73,21 @@ bool mrtd_bac_get_kmrz(MrtdAuthData* auth, char* output, uint8_t output_size) {
|
||||
}
|
||||
|
||||
if(docnr_length < 9) {
|
||||
memset(output+idx, '<', 9-docnr_length);
|
||||
idx += 9-docnr_length;
|
||||
memset(output + idx, '<', 9 - docnr_length);
|
||||
idx += 9 - docnr_length;
|
||||
}
|
||||
|
||||
output[idx++] = mrtd_bac_check_digit(output+cd_idx, docnr_length) + '0';
|
||||
output[idx++] = mrtd_bac_check_digit(output + cd_idx, docnr_length) + '0';
|
||||
|
||||
cd_idx = idx;
|
||||
mrtd_print_date(output+idx, &auth->birth_date);
|
||||
mrtd_print_date(output + idx, &auth->birth_date);
|
||||
idx += 6;
|
||||
output[idx++] = mrtd_bac_check_digit(output+cd_idx, 6) + '0';
|
||||
output[idx++] = mrtd_bac_check_digit(output + cd_idx, 6) + '0';
|
||||
|
||||
cd_idx = idx;
|
||||
mrtd_print_date(output+idx, &auth->expiry_date);
|
||||
mrtd_print_date(output + idx, &auth->expiry_date);
|
||||
idx += 6;
|
||||
output[idx++] = mrtd_bac_check_digit(output+cd_idx, 6) + '0';
|
||||
output[idx++] = mrtd_bac_check_digit(output + cd_idx, 6) + '0';
|
||||
|
||||
output[idx++] = '\x00';
|
||||
return true;
|
||||
@@ -97,7 +99,7 @@ bool mrtd_bac_keys_from_seed(const uint8_t kseed[16], uint8_t ksenc[16], uint8_t
|
||||
mbedtls_sha1_init(&ctx);
|
||||
|
||||
do {
|
||||
for(uint8_t i=1; i<=2; ++i) {
|
||||
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;
|
||||
@@ -105,16 +107,16 @@ bool mrtd_bac_keys_from_seed(const uint8_t kseed[16], uint8_t ksenc[16], uint8_t
|
||||
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;
|
||||
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);
|
||||
@@ -171,15 +173,20 @@ bool mrtd_bac_decrypt(const uint8_t* data, size_t data_length, uint8_t* key, uin
|
||||
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) {
|
||||
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("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;
|
||||
@@ -191,12 +198,19 @@ bool mrtd_bac_decrypt_verify(const uint8_t* data, size_t data_length, uint8_t* k
|
||||
// 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) {
|
||||
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];
|
||||
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);
|
||||
@@ -213,11 +227,13 @@ uint16_t mrtd_bac_decrypt_verify_sm(const uint8_t* data, size_t data_length, uin
|
||||
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");
|
||||
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) {
|
||||
for(padidx = enclength - 1; padidx >= 0; --padidx) {
|
||||
if(output[padidx] == 0x00) {
|
||||
continue;
|
||||
} else if(output[padidx] == 0x80) {
|
||||
@@ -228,13 +244,13 @@ uint16_t mrtd_bac_decrypt_verify_sm(const uint8_t* data, size_t data_length, uin
|
||||
}
|
||||
}
|
||||
printf(" ");
|
||||
for(int i=0; i<padidx; ++i) {
|
||||
for(int i = 0; i < padidx; ++i) {
|
||||
printf(" ");
|
||||
}
|
||||
printf("^^\r\n");
|
||||
printf("Pad starts at: %d\r\n", padidx);
|
||||
|
||||
*output_written = padidx-1;
|
||||
*output_written = padidx - 1;
|
||||
}
|
||||
} else {
|
||||
if(output_written != NULL) {
|
||||
@@ -246,13 +262,14 @@ uint16_t mrtd_bac_decrypt_verify_sm(const uint8_t* data, size_t data_length, uin
|
||||
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
|
||||
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) {
|
||||
for(uint8_t i = 0; i < 8; ++i) {
|
||||
printf("%02X <=> %02X\r\n", mac_calc[i], data[data_length - 8 + i]);
|
||||
}
|
||||
return 0xff02;
|
||||
@@ -284,18 +301,25 @@ bool mrtd_bac_mac_update(mrtd_bac_mac_ctx* ctx, const uint8_t* data, size_t data
|
||||
data_idx += buff_add;
|
||||
|
||||
if(ctx->idx_in == 0) { // buffer_in filled
|
||||
for(uint8_t j=0; j<8; ++j) {
|
||||
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(
|
||||
"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]);
|
||||
//xormac[0], xormac[1], xormac[2], xormac[3],
|
||||
//xormac[4], xormac[5], xormac[6], xormac[7]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,18 +328,25 @@ bool mrtd_bac_mac_update(mrtd_bac_mac_ctx* ctx, const uint8_t* data, size_t data
|
||||
// Not a full block
|
||||
break;
|
||||
}
|
||||
for(uint8_t j=0; j<8; ++j) {
|
||||
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(
|
||||
"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]);
|
||||
//xormac[0], xormac[1], xormac[2], xormac[3],
|
||||
//xormac[4], xormac[5], xormac[6], xormac[7]);
|
||||
}
|
||||
|
||||
if(data_idx < data_length) {
|
||||
@@ -340,7 +371,7 @@ bool mrtd_bac_mac_finalize(mrtd_bac_mac_ctx* ctx, uint8_t output[8]) {
|
||||
|
||||
uint8_t tmp[8];
|
||||
mbedtls_des_init(&ctx->des);
|
||||
mbedtls_des_setkey_dec(&ctx->des, ctx->key+8);
|
||||
mbedtls_des_setkey_dec(&ctx->des, ctx->key + 8);
|
||||
mbedtls_des_crypt_ecb(&ctx->des, ctx->mac, tmp);
|
||||
|
||||
mbedtls_des_init(&ctx->des);
|
||||
@@ -362,19 +393,26 @@ bool mrtd_bac_mac(const uint8_t* data, size_t data_length, const uint8_t* key, u
|
||||
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) {
|
||||
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]);
|
||||
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_setkey_dec(&ctx, key + 8);
|
||||
mbedtls_des_crypt_ecb(&ctx, mac, tmp);
|
||||
|
||||
mbedtls_des_init(&ctx);
|
||||
@@ -388,7 +426,7 @@ bool mrtd_bac_mac(const uint8_t* data, size_t data_length, const uint8_t* key, u
|
||||
|
||||
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?
|
||||
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);
|
||||
@@ -401,7 +439,18 @@ 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, const uint8_t* key_enc, const 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;
|
||||
|
||||
@@ -430,11 +479,11 @@ size_t mrtd_protect_apdu(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8
|
||||
// TODO: condition on data presence
|
||||
// TODO: if ins is odd, use 0x85
|
||||
if(lc > 0) {
|
||||
size_t newlength = ((lc+8)/8)*8;
|
||||
size_t newlength = ((lc + 8) / 8) * 8;
|
||||
uint8_t padded[newlength];
|
||||
|
||||
output[idx++] = 0x87; // Header
|
||||
output[idx++] = newlength + 1; // Length
|
||||
output[idx++] = newlength + 1; // Length
|
||||
output[idx++] = 0x01; //TODO: check this value
|
||||
|
||||
memset(padded, 0, newlength);
|
||||
@@ -464,7 +513,7 @@ size_t mrtd_protect_apdu(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8
|
||||
idx += 8;
|
||||
|
||||
printf("MAC: ");
|
||||
for(uint8_t i=0; i<8; ++i) {
|
||||
for(uint8_t i = 0; i < 8; ++i) {
|
||||
printf("%02X ", output[idx - 8 + i]);
|
||||
}
|
||||
printf("\r\n");
|
||||
@@ -477,63 +526,81 @@ size_t mrtd_protect_apdu(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8
|
||||
return idx;
|
||||
}
|
||||
|
||||
EFFile EFNone = {.name = NULL, .file_id = 0x0000, .short_id = 0x00, .tag = 0x00 };
|
||||
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 },
|
||||
.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},
|
||||
.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;
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -158,33 +158,54 @@ bool mrtd_bac_padded_mac(const uint8_t* data, size_t data_length, uint8_t* key,
|
||||
|
||||
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(
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user