proxmark3/client/src/cmdhfemrtd.c

785 lines
25 KiB
C
Raw Normal View History

//-----------------------------------------------------------------------------
// Copyright (C) 2020 A. Ozkal
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// High frequency Electronic Machine Readable Travel Document commands
//-----------------------------------------------------------------------------
2020-12-09 09:48:17 +08:00
// This code is heavily based on mrpkey.py of RFIDIOt
#include "cmdhfemrtd.h"
#include <ctype.h>
#include "fileutils.h"
2020-12-09 06:15:09 +08:00
#include "cmdparser.h" // command_t
#include "comms.h" // clearCommandBuffer
#include "cmdtrace.h"
#include "cliparser.h"
#include "crc16.h"
#include "cmdhf14a.h"
2020-12-09 06:15:09 +08:00
#include "protocols.h" // definitions of ISO14A/7816 protocol
#include "emv/apduinfo.h" // GetAPDUCodeDescription
#include "sha1.h" // KSeed calculation etc
#include "mifare/desfire_crypto.h" // des_encrypt/des_decrypt
#include "des.h" // mbedtls_des_key_set_parity
#define TIMEOUT 2000
// ISO7816 commands
#define SELECT "A4"
2020-12-09 09:48:17 +08:00
#define EXTERNAL_AUTHENTICATE "82"
#define GET_CHALLENGE "84"
#define READ_BINARY "B0"
#define P1_SELECT_BY_EF "02"
#define P1_SELECT_BY_NAME "04"
#define P2_PROPRIETARY "0C"
// File IDs
#define EF_CARDACCESS "011C"
#define EF_COM "011E"
#define EF_DG1 "0101"
// App IDs
#define AID_MRTD "A0000002471001"
static int CmdHelp(const char *Cmd);
static uint16_t get_sw(uint8_t *d, uint8_t n) {
if (n < 2)
return 0;
n -= 2;
return d[n] * 0x0100 + d[n + 1];
}
2020-12-07 06:38:39 +08:00
static int exchange_commands(const char *cmd, uint8_t *dataout, int *dataoutlen, bool activate_field, bool keep_field_on) {
uint8_t response[PM3_CMD_DATA_SIZE];
int resplen = 0;
2020-12-11 10:38:14 +08:00
PrintAndLogEx(DEBUG, "Sending: %s", cmd);
2020-12-13 01:26:54 +08:00
uint8_t aCMD[500];
2020-12-07 06:38:39 +08:00
int aCMD_n = 0;
param_gethex_to_eol(cmd, 0, aCMD, sizeof(aCMD), &aCMD_n);
int res = ExchangeAPDU14a(aCMD, aCMD_n, activate_field, keep_field_on, response, sizeof(response), &resplen);
if (res) {
DropField();
return false;
}
if (resplen < 2) {
return false;
}
2020-12-11 10:38:14 +08:00
PrintAndLogEx(DEBUG, "Response: %s", sprint_hex(response, resplen));
2020-12-07 06:38:39 +08:00
// drop sw
memcpy(dataout, &response, resplen - 2);
*dataoutlen = (resplen - 2);
uint16_t sw = get_sw(response, resplen);
if (sw != 0x9000) {
2020-12-11 10:38:14 +08:00
PrintAndLogEx(DEBUG, "Command %s failed (%04x - %s).", cmd, sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
return false;
}
return true;
}
2020-12-07 04:52:08 +08:00
static char calculate_check_digit(char *data) {
int mrz_weight[] = {7, 3, 1};
int cd = 0;
int value = 0;
char d;
for (int i = 0; i < strlen(data); i++) {
d = data[i];
if ('A' <= d && d <= 'Z') {
value = d - 55;
} else if ('a' <= d && d <= 'z') {
value = d - 87;
} else if (d == '<') {
value = 0;
} else { // Numbers
value = d - 48;
}
cd += value * mrz_weight[i % 3];
}
return cd % 10;
}
static int asn1datalength(uint8_t *datain, int datainlen) {
2020-12-12 02:17:15 +08:00
char *dataintext = sprint_hex_inrow(datain, datainlen);
// lazy - https://stackoverflow.com/a/4214350/3286892
char subbuff[8];
2020-12-13 01:26:54 +08:00
int thing = *(datain + 1);
if (thing <= 0x7f) {
return thing;
} else if (thing == 0x81) {
memcpy(subbuff, &dataintext[2], 3);
subbuff[3] = '\0';
return (int)strtol(subbuff, NULL, 16);
} else if (thing == 0x82) {
memcpy(subbuff, &dataintext[2], 5);
subbuff[5] = '\0';
return (int)strtol(subbuff, NULL, 16);
} else if (thing == 0x83) {
memcpy(subbuff, &dataintext[2], 7);
subbuff[7] = '\0';
return (int)strtol(subbuff, NULL, 16);
}
return false;
}
static int asn1fieldlength(uint8_t *datain, int datainlen) {
2020-12-13 01:26:54 +08:00
int thing = *(datain + 1);
if (thing <= 0x7f) {
return 2;
} else if (thing == 0x81) {
return 4;
} else if (thing == 0x82) {
return 6;
} else if (thing == 0x83) {
return 8;
}
return false;
}
static void des_encrypt_ecb(uint8_t *key, uint8_t *input, uint8_t *output) {
mbedtls_des_context ctx_enc;
mbedtls_des_setkey_enc(&ctx_enc, key);
mbedtls_des_crypt_ecb(&ctx_enc, input, output);
mbedtls_des_free(&ctx_enc);
}
static void des_decrypt_ecb(uint8_t *key, uint8_t *input, uint8_t *output) {
mbedtls_des_context ctx_dec;
mbedtls_des_setkey_dec(&ctx_dec, key);
mbedtls_des_crypt_ecb(&ctx_dec, input, output);
mbedtls_des_free(&ctx_dec);
}
static void des3_encrypt_cbc(uint8_t *iv, uint8_t *key, uint8_t *input, int inputlen, uint8_t *output) {
mbedtls_des3_context ctx;
mbedtls_des3_set2key_enc(&ctx, key);
mbedtls_des3_crypt_cbc(&ctx // des3_context
, MBEDTLS_DES_ENCRYPT // int mode
, inputlen // length
, iv // iv[8]
, input // input
, output // output
);
mbedtls_des3_free(&ctx);
}
2020-12-11 11:27:47 +08:00
static void des3_decrypt_cbc(uint8_t *iv, uint8_t *key, uint8_t *input, int inputlen, uint8_t *output) {
mbedtls_des3_context ctx;
mbedtls_des3_set2key_dec(&ctx, key);
mbedtls_des3_crypt_cbc(&ctx // des3_context
, MBEDTLS_DES_DECRYPT // int mode
, inputlen // length
, iv // iv[8]
, input // input
, output // output
);
mbedtls_des3_free(&ctx);
}
2020-12-12 02:07:52 +08:00
static int pad_block(uint8_t *input, int inputlen, uint8_t *output) {
uint8_t padding[8] = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
memcpy(output, input, inputlen);
int to_pad = (8 - (inputlen % 8));
for (int i = 0; i < to_pad; i++) {
output[inputlen + i] = padding[i];
}
return inputlen + to_pad;
}
static void retail_mac(uint8_t *key, uint8_t *input, int inputlen, uint8_t *output) {
2020-12-13 01:26:54 +08:00
// This code assumes blocklength (n) = 8, and input len of up to 240 or so chars
// This code takes inspirations from https://github.com/devinvenable/iso9797algorithm3
uint8_t k0[8];
uint8_t k1[8];
uint8_t intermediate[8] = {0x00};
2020-12-13 01:26:54 +08:00
uint8_t intermediate_des[256];
uint8_t block[8];
2020-12-13 01:26:54 +08:00
uint8_t message[256];
// Populate keys
memcpy(k0, key, 8);
memcpy(k1, key + 8, 8);
// Prepare message
2020-12-12 02:07:52 +08:00
int blocksize = pad_block(input, inputlen, message);
// Do chaining and encryption
2020-12-12 02:07:52 +08:00
for (int i = 0; i < (blocksize / 8); i++) {
memcpy(block, message + (i * 8), 8);
// XOR
for (int x = 0; x < 8; x++) {
intermediate[x] = intermediate[x] ^ block[x];
}
des_encrypt_ecb(k0, intermediate, intermediate_des);
memcpy(intermediate, intermediate_des, 8);
}
des_decrypt_ecb(k1, intermediate, intermediate_des);
memcpy(intermediate, intermediate_des, 8);
des_encrypt_ecb(k0, intermediate, intermediate_des);
memcpy(output, intermediate_des, 8);
}
2020-12-09 06:15:09 +08:00
2020-12-09 06:15:09 +08:00
static void deskey(uint8_t *seed, uint8_t *type, int length, uint8_t *dataout) {
2020-12-11 10:38:14 +08:00
PrintAndLogEx(DEBUG, "seed: %s", sprint_hex_inrow(seed, 16));
2020-12-09 06:15:09 +08:00
// combine seed and type
uint8_t data[50];
2020-12-12 06:50:54 +08:00
memcpy(data, seed, length);
memcpy(data + length, type, 4);
PrintAndLogEx(DEBUG, "data: %s", sprint_hex_inrow(data, length + 4));
2020-12-09 06:15:09 +08:00
// SHA1 the key
2020-12-12 06:50:54 +08:00
unsigned char key[64];
mbedtls_sha1(data, length + 4, key);
PrintAndLogEx(DEBUG, "key: %s", sprint_hex_inrow(key, length + 4));
2020-12-09 06:15:09 +08:00
// Set parity bits
2020-12-12 06:50:54 +08:00
for (int i = 0; i < ((length + 4) / 8); i++) {
mbedtls_des_key_set_parity(key + (i * 8));
}
2020-12-11 10:38:14 +08:00
PrintAndLogEx(DEBUG, "post-parity key: %s", sprint_hex_inrow(key, 20));
2020-12-09 06:15:09 +08:00
memcpy(dataout, &key, length);
}
2020-12-07 06:38:39 +08:00
static int select_file(const char *select_by, const char *file_id, bool activate_field, bool keep_field_on) {
size_t file_id_len = strlen(file_id) / 2;
// Get data even tho we'll not use it
uint8_t response[PM3_CMD_DATA_SIZE];
int resplen = 0;
char cmd[50];
2020-12-07 06:38:39 +08:00
sprintf(cmd, "00%s%s0C%02lu%s", SELECT, select_by, file_id_len, file_id);
2020-12-07 06:38:39 +08:00
return exchange_commands(cmd, response, &resplen, activate_field, keep_field_on);
}
2020-12-07 06:38:39 +08:00
static int get_challenge(int length, uint8_t *dataout, int *dataoutlen) {
char cmd[50];
sprintf(cmd, "00%s0000%02X", GET_CHALLENGE, length);
2020-12-07 06:38:39 +08:00
return exchange_commands(cmd, dataout, dataoutlen, false, true);
}
static int external_authenticate(uint8_t *data, int length, uint8_t *dataout, int *dataoutlen) {
char cmd[100];
sprintf(cmd, "00%s0000%02X%s%02X", EXTERNAL_AUTHENTICATE, length, sprint_hex_inrow(data, length), length);
return exchange_commands(cmd, dataout, dataoutlen, false, true);
}
static int _read_binary(int offset, int bytes_to_read, uint8_t *dataout, int *dataoutlen) {
char cmd[50];
sprintf(cmd, "00%s%04i%02i", READ_BINARY, offset, bytes_to_read);
2020-12-07 06:38:39 +08:00
return exchange_commands(cmd, dataout, dataoutlen, false, true);
}
static int read_file(uint8_t *dataout, int *dataoutlen) {
uint8_t response[PM3_CMD_DATA_SIZE];
int resplen = 0;
uint8_t tempresponse[PM3_CMD_DATA_SIZE];
int tempresplen = 0;
if (!_read_binary(0, 4, response, &resplen)) {
return false;
}
int datalen = asn1datalength(response, resplen);
int readlen = datalen - (3 - asn1fieldlength(response, resplen) / 2);
int offset = 4;
int toread;
while (readlen > 0) {
toread = readlen;
if (readlen > 118) {
toread = 118;
}
if (!_read_binary(offset, toread, tempresponse, &tempresplen)) {
return false;
}
memcpy(&response[resplen], &tempresponse, tempresplen);
offset += toread;
readlen -= toread;
resplen += tempresplen;
}
memcpy(dataout, &response, resplen);
*dataoutlen = resplen;
return true;
}
2020-12-12 09:34:16 +08:00
static bool check_cc(uint8_t *ssc, uint8_t *key, uint8_t *rapdu, int rapdulength) {
// https://elixi.re/i/clarkson.png
uint8_t k[100];
uint8_t cc[100];
(*(ssc + 7)) += 1;
memcpy(k, ssc, 8);
int length = 0;
int length2 = 0;
if (*(rapdu) == 0x87) {
length += 2 + (*(rapdu + 1));
memcpy(k + 8, rapdu, length);
PrintAndLogEx(DEBUG, "len1: %i", length);
}
if ((*(rapdu + length)) == 0x99) {
length2 += 2 + (*(rapdu + (length + 1)));
memcpy(k + length + 8, rapdu + length, length2);
PrintAndLogEx(DEBUG, "len2: %i", length2);
}
int klength = length + length2 + 8;
retail_mac(key, k, klength, cc);
PrintAndLogEx(DEBUG, "cc: %s", sprint_hex_inrow(cc, 8));
PrintAndLogEx(DEBUG, "rapdu: %s", sprint_hex_inrow(rapdu, rapdulength));
PrintAndLogEx(DEBUG, "rapdu cut: %s", sprint_hex_inrow(rapdu + (rapdulength - 8), 8));
PrintAndLogEx(DEBUG, "k: %s", sprint_hex_inrow(k, klength));
return memcmp(cc, rapdu + (rapdulength - 8), 8) == 0;
}
static bool secure_select_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, uint8_t *file) {
2020-12-12 02:07:52 +08:00
// Get data even tho we'll not use it
uint8_t response[PM3_CMD_DATA_SIZE];
int resplen = 0;
uint8_t iv[8] = { 0x00 };
2020-12-12 02:14:47 +08:00
char command[54];
uint8_t cmd[8];
uint8_t data[21];
uint8_t temp[8] = {0x0c, 0xa4, 0x02, 0x0c};
2020-12-12 02:07:52 +08:00
int cmdlen = pad_block(temp, 4, cmd);
int datalen = pad_block(file, 2, data);
PrintAndLogEx(DEBUG, "cmd: %s", sprint_hex_inrow(cmd, cmdlen));
PrintAndLogEx(DEBUG, "data: %s", sprint_hex_inrow(data, datalen));
2020-12-12 02:14:47 +08:00
des3_encrypt_cbc(iv, kenc, data, datalen, temp);
PrintAndLogEx(DEBUG, "temp: %s", sprint_hex_inrow(temp, datalen));
uint8_t do87[11] = {0x87, 0x09, 0x01};
memcpy(do87 + 3, temp, datalen);
2020-12-12 02:07:52 +08:00
PrintAndLogEx(DEBUG, "do87: %s", sprint_hex_inrow(do87, datalen + 3));
2020-12-12 02:14:47 +08:00
uint8_t m[19];
2020-12-12 02:07:52 +08:00
memcpy(m, cmd, cmdlen);
memcpy(m + cmdlen, do87, (datalen + 3));
PrintAndLogEx(DEBUG, "m: %s", sprint_hex_inrow(m, datalen + cmdlen + 3));
2020-12-12 02:14:47 +08:00
// TODO: this is hacky
2020-12-12 02:07:52 +08:00
PrintAndLogEx(DEBUG, "ssc-b: %s", sprint_hex_inrow(ssc, 8));
(*(ssc + 7)) += 1;
PrintAndLogEx(DEBUG, "ssc-a: %s", sprint_hex_inrow(ssc, 8));
2020-12-12 02:14:47 +08:00
uint8_t n[27];
2020-12-12 02:07:52 +08:00
memcpy(n, ssc, 8);
memcpy(n + 8, m, (cmdlen + datalen + 3));
PrintAndLogEx(DEBUG, "n: %s", sprint_hex_inrow(n, (cmdlen + datalen + 11)));
uint8_t cc[8];
retail_mac(kmac, n, (cmdlen + datalen + 11), cc);
PrintAndLogEx(DEBUG, "cc: %s", sprint_hex_inrow(cc, 8));
uint8_t do8e[10] = {0x8E, 0x08};
memcpy(do8e + 2, cc, 8);
PrintAndLogEx(DEBUG, "do8e: %s", sprint_hex_inrow(do8e, 10));
int lc = datalen + 3 + 10;
PrintAndLogEx(DEBUG, "lc: %i", lc);
memcpy(data, do87, datalen + 3);
memcpy(data + (datalen + 3), do8e, 10);
PrintAndLogEx(DEBUG, "data: %s", sprint_hex_inrow(data, lc));
sprintf(command, "0C%s020C%02X%s00", SELECT, lc, sprint_hex_inrow(data, lc));
PrintAndLogEx(DEBUG, "command: %s", command);
2020-12-12 09:34:16 +08:00
exchange_commands(command, response, &resplen, false, true);
2020-12-12 06:50:54 +08:00
2020-12-12 09:34:16 +08:00
return check_cc(ssc, kmac, response, resplen);
2020-12-12 02:07:52 +08:00
}
2020-12-13 01:26:54 +08:00
static bool _secure_read_binary(uint8_t *kmac, uint8_t *ssc, int offset, int bytes_to_read, uint8_t *dataout, int *dataoutlen) {
2020-12-12 06:50:54 +08:00
char command[54];
uint8_t cmd[8];
uint8_t data[21];
uint8_t temp[8] = {0x0c, 0xb0};
PrintAndLogEx(DEBUG, "kmac: %s", sprint_hex_inrow(kmac, 20));
// TODO: hacky
char offsethex[5];
sprintf(offsethex, "%04X", offset);
char offsetbuffer[8];
memcpy(offsetbuffer, offsethex, 2);
int p1 = (int)strtol(offsetbuffer, NULL, 16);
memcpy(offsetbuffer, offsethex + 2, 2);
int p2 = (int)strtol(offsetbuffer, NULL, 16);
temp[2] = p1;
temp[3] = p2;
int cmdlen = pad_block(temp, 4, cmd);
PrintAndLogEx(DEBUG, "cmd: %s", sprint_hex_inrow(cmd, cmdlen));
uint8_t do97[3] = {0x97, 0x01, bytes_to_read};
uint8_t m[11];
memcpy(m, cmd, 8);
memcpy(m + 8, do97, 3);
// TODO: this is hacky
PrintAndLogEx(DEBUG, "ssc-b: %s", sprint_hex_inrow(ssc, 8));
(*(ssc + 7)) += 1;
PrintAndLogEx(DEBUG, "ssc-a: %s", sprint_hex_inrow(ssc, 8));
uint8_t n[19];
memcpy(n, ssc, 8);
memcpy(n + 8, m, 11);
PrintAndLogEx(DEBUG, "n: %s", sprint_hex_inrow(n, 19));
uint8_t cc[8];
retail_mac(kmac, n, 19, cc);
PrintAndLogEx(DEBUG, "cc: %s", sprint_hex_inrow(cc, 8));
uint8_t do8e[10] = {0x8E, 0x08};
memcpy(do8e + 2, cc, 8);
PrintAndLogEx(DEBUG, "do8e: %s", sprint_hex_inrow(do8e, 10));
int lc = 13;
PrintAndLogEx(DEBUG, "lc: %i", lc);
memcpy(data, do97, 3);
memcpy(data + 3, do8e, 10);
PrintAndLogEx(DEBUG, "data: %s", sprint_hex_inrow(data, lc));
sprintf(command, "0C%s%02X%02X%02X%s00", READ_BINARY, p1, p2, lc, sprint_hex_inrow(data, lc));
PrintAndLogEx(DEBUG, "command: %s", command);
2020-12-12 09:34:16 +08:00
exchange_commands(command, dataout, dataoutlen, false, true);
return check_cc(ssc, kmac, dataout, *dataoutlen);
2020-12-12 06:50:54 +08:00
}
2020-12-13 01:26:54 +08:00
static bool _secure_read_binary_decrypt(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, int offset, int bytes_to_read, uint8_t *dataout, int *dataoutlen) {
uint8_t response[500];
uint8_t temp[500];
int resplen, cutat = 0;
uint8_t iv[8] = { 0x00 };
if (_secure_read_binary(kmac, ssc, offset, bytes_to_read, response, &resplen) == false) {
return false;
}
PrintAndLogEx(DEBUG, "0offset %i on %i: ? (crypt: %s)", offset, bytes_to_read, sprint_hex_inrow(response, resplen));
cutat = ((int) response[1]) - 1;
PrintAndLogEx(DEBUG, "1offset %i on %i: ? (crypt: %s)", offset, bytes_to_read, sprint_hex_inrow(response, resplen));
des3_decrypt_cbc(iv, kenc, response + 3, cutat, temp);
PrintAndLogEx(DEBUG, "2eoffset %i on %i: ? (crypt: %s)", offset, bytes_to_read, sprint_hex_inrow(response, resplen));
PrintAndLogEx(DEBUG, "2aoffset %i on %i: %s", offset, bytes_to_read, sprint_hex_inrow(temp, cutat));
PrintAndLogEx(DEBUG, "2boffset %i on %i: c %s", offset, bytes_to_read, sprint_hex_inrow(response, resplen));
memcpy(dataout, temp, bytes_to_read);
PrintAndLogEx(DEBUG, "3offset %i on %i: %s (crypt: %s)", offset, bytes_to_read, sprint_hex_inrow(temp, cutat), sprint_hex_inrow(response, resplen));
*dataoutlen = bytes_to_read;
return true;
}
static int secure_read_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, uint8_t *dataout, int *dataoutlen) {
uint8_t response[500];
int resplen = 0;
uint8_t tempresponse[500];
int tempresplen = 0;
if (!_secure_read_binary_decrypt(kenc, kmac, ssc, 0, 4, response, &resplen)) {
return false;
}
int datalen = asn1datalength(response, resplen);
int readlen = datalen - (3 - asn1fieldlength(response, resplen) / 2);
int offset = 4;
int toread;
while (readlen > 0) {
toread = readlen;
if (readlen > 118) {
toread = 118;
}
if (!_secure_read_binary_decrypt(kenc, kmac, ssc, offset, toread, tempresponse, &tempresplen)) {
return false;
}
memcpy(response + resplen, tempresponse, tempresplen);
offset += toread;
readlen -= toread;
resplen += tempresplen;
}
memcpy(dataout, &response, resplen);
*dataoutlen = resplen;
return true;
}
2020-12-07 04:52:08 +08:00
int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) {
2020-12-13 01:26:54 +08:00
uint8_t response[500];
2020-12-09 09:48:17 +08:00
uint8_t rnd_ic[8];
2020-12-09 06:15:09 +08:00
uint8_t kenc[50];
uint8_t kmac[50];
2020-12-06 09:48:38 +08:00
int resplen = 0;
// bool BAC = true;
2020-12-09 09:48:17 +08:00
uint8_t S[32];
// TODO: Code sponsored jointly by duracell and sony
uint8_t rnd_ifd[8] = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA};
uint8_t k_ifd[16] = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA};
// TODO: get these _types into a better spot
uint8_t KENC_type[4] = {0x00, 0x00, 0x00, 0x01};
uint8_t KMAC_type[4] = {0x00, 0x00, 0x00, 0x02};
2020-12-06 09:48:38 +08:00
// Select and read EF_CardAccess
if (select_file(P1_SELECT_BY_EF, EF_CARDACCESS, true, true)) {
read_file(response, &resplen);
PrintAndLogEx(INFO, "EF_CardAccess: %s", sprint_hex(response, resplen));
} else {
PrintAndLogEx(INFO, "PACE unsupported. Will not read EF_CardAccess.");
}
2020-12-06 09:48:38 +08:00
// Select MRTD applet
if (select_file(P1_SELECT_BY_NAME, AID_MRTD, false, true) == false) {
PrintAndLogEx(ERR, "Couldn't select the MRTD application.");
DropField();
2020-12-06 09:48:38 +08:00
return PM3_ESOFT;
}
// Select EF_COM
if (select_file(P1_SELECT_BY_EF, EF_COM, false, true) == false) {
// BAC = true;
2020-12-11 10:38:14 +08:00
PrintAndLogEx(INFO, "Basic Access Control is enforced. Will attempt external authentication.");
2020-12-06 09:48:38 +08:00
} else {
// BAC = false;
// Select EF_DG1
select_file(P1_SELECT_BY_EF, EF_DG1, false, true);
if (read_file(response, &resplen) == false) {
2020-12-06 09:48:38 +08:00
// BAC = true;
2020-12-11 10:38:14 +08:00
PrintAndLogEx(INFO, "Basic Access Control is enforced. Will attempt external authentication.");
2020-12-06 09:48:38 +08:00
} else {
// BAC = false;
PrintAndLogEx(INFO, "EF_DG1: %s", sprint_hex(response, resplen));
}
}
2020-12-11 10:38:14 +08:00
PrintAndLogEx(DEBUG, "doc: %s", documentnumber);
PrintAndLogEx(DEBUG, "dob: %s", dob);
PrintAndLogEx(DEBUG, "exp: %s", expiry);
2020-12-06 09:48:38 +08:00
2020-12-07 04:52:08 +08:00
char documentnumbercd = calculate_check_digit(documentnumber);
char dobcd = calculate_check_digit(dob);
char expirycd = calculate_check_digit(expiry);
char kmrz[25];
sprintf(kmrz, "%s%i%s%i%s%i", documentnumber, documentnumbercd, dob, dobcd, expiry, expirycd);
2020-12-11 10:38:14 +08:00
PrintAndLogEx(DEBUG, "kmrz: %s", kmrz);
2020-12-07 04:52:08 +08:00
2020-12-11 11:52:26 +08:00
uint8_t kseed[16] = {0x00};
2020-12-07 05:40:01 +08:00
mbedtls_sha1((unsigned char *)kmrz, strlen(kmrz), kseed);
2020-12-11 10:38:14 +08:00
PrintAndLogEx(DEBUG, "kseed: %s", sprint_hex_inrow(kseed, 16));
2020-12-07 05:40:01 +08:00
2020-12-09 06:15:09 +08:00
deskey(kseed, KENC_type, 16, kenc);
deskey(kseed, KMAC_type, 16, kmac);
2020-12-11 10:38:14 +08:00
PrintAndLogEx(DEBUG, "kenc: %s", sprint_hex_inrow(kenc, 16));
PrintAndLogEx(DEBUG, "kmac: %s", sprint_hex_inrow(kmac, 16));
2020-12-09 06:15:09 +08:00
// Get Challenge
2020-12-09 09:48:17 +08:00
if (get_challenge(8, rnd_ic, &resplen) == false) {
PrintAndLogEx(ERR, "Couldn't get challenge.");
DropField();
return PM3_ESOFT;
}
2020-12-11 10:38:14 +08:00
PrintAndLogEx(DEBUG, "rnd_ic: %s", sprint_hex_inrow(rnd_ic, 8));
2020-12-09 09:48:17 +08:00
memcpy(S, rnd_ifd, 8);
memcpy(S + 8, rnd_ic, 8);
memcpy(S + 16, k_ifd, 16);
2020-12-11 10:38:14 +08:00
PrintAndLogEx(DEBUG, "S: %s", sprint_hex_inrow(S, 32));
2020-12-09 09:48:17 +08:00
uint8_t iv[8] = { 0x00 };
uint8_t e_ifd[32] = { 0x00 };
des3_encrypt_cbc(iv, kenc, S, sizeof(S), e_ifd);
2020-12-11 10:38:14 +08:00
PrintAndLogEx(DEBUG, "e_ifd: %s", sprint_hex_inrow(e_ifd, 32));
2020-12-09 09:48:17 +08:00
uint8_t m_ifd[8] = { 0x00 };
2020-12-09 09:48:17 +08:00
2020-12-12 02:07:52 +08:00
retail_mac(kmac, e_ifd, 32, m_ifd);
2020-12-11 10:38:14 +08:00
PrintAndLogEx(DEBUG, "m_ifd: %s", sprint_hex_inrow(m_ifd, 8));
uint8_t cmd_data[40];
memcpy(cmd_data, e_ifd, 32);
memcpy(cmd_data + 32, m_ifd, 8);
// Do external authentication
if (external_authenticate(cmd_data, sizeof(cmd_data), response, &resplen) == false) {
PrintAndLogEx(ERR, "Couldn't do external authentication. Did you supply the correct MRZ info?");
DropField();
return PM3_ESOFT;
}
2020-12-11 10:38:14 +08:00
PrintAndLogEx(INFO, "External authentication successful.");
2020-12-09 09:48:17 +08:00
2020-12-11 11:27:47 +08:00
uint8_t dec_output[32] = { 0x00 };
des3_decrypt_cbc(iv, kenc, response, 32, dec_output);
2020-12-11 11:52:26 +08:00
PrintAndLogEx(DEBUG, "dec_output: %s", sprint_hex_inrow(dec_output, 32));
2020-12-11 11:27:47 +08:00
if (memcmp(rnd_ifd, dec_output + 8, 8) != 0) {
PrintAndLogEx(ERR, "Challenge failed, rnd_ifd does not match.");
DropField();
return PM3_ESOFT;
}
2020-12-11 11:52:26 +08:00
uint8_t ssc[8] = { 0x00 };
uint8_t ks_enc[16] = { 0x00 };
uint8_t ks_mac[16] = { 0x00 };
uint8_t k_icc[16] = { 0x00 };
memcpy(k_icc, dec_output + 16, 16);
// Calculate session keys
for (int x = 0; x < 16; x++) {
kseed[x] = k_ifd[x] ^ k_icc[x];
}
PrintAndLogEx(DEBUG, "kseed: %s", sprint_hex_inrow(kseed, 16));
deskey(kseed, KENC_type, 16, ks_enc);
deskey(kseed, KMAC_type, 16, ks_mac);
PrintAndLogEx(DEBUG, "ks_enc: %s", sprint_hex_inrow(ks_enc, 16));
PrintAndLogEx(DEBUG, "ks_mac: %s", sprint_hex_inrow(ks_mac, 16));
memcpy(ssc, rnd_ic + 4, 4);
memcpy(ssc + 4, rnd_ifd + 4, 4);
PrintAndLogEx(DEBUG, "ssc: %s", sprint_hex_inrow(ssc, 8));
2020-12-12 02:07:52 +08:00
// Select EF_COM
uint8_t file_id[2] = {0x01, 0x1E};
2020-12-12 09:34:16 +08:00
if (secure_select_file(ks_enc, ks_mac, ssc, file_id) == false) {
PrintAndLogEx(ERR, "Failed to secure select EF_COM, crypto checksum check failed.");
DropField();
return PM3_ESOFT;
}
2020-12-12 02:07:52 +08:00
2020-12-13 01:26:54 +08:00
if (secure_read_file(ks_enc, ks_mac, ssc, response, &resplen) == false) {
PrintAndLogEx(ERR, "Failed to read EF_COM.");
DropField();
return PM3_ESOFT;
}
PrintAndLogEx(INFO, "EF_COM: %s", sprint_hex_inrow(response, resplen));
// Select EF_DG1
file_id[1] = 0x01;
if (secure_select_file(ks_enc, ks_mac, ssc, file_id) == false) {
PrintAndLogEx(ERR, "Failed to secure select EF_DG1, crypto checksum check failed.");
DropField();
return PM3_ESOFT;
}
if (secure_read_file(ks_enc, ks_mac, ssc, response, &resplen) == false) {
PrintAndLogEx(ERR, "Failed to read EF_DG1.");
2020-12-12 09:34:16 +08:00
DropField();
return PM3_ESOFT;
}
2020-12-13 01:26:54 +08:00
PrintAndLogEx(INFO, "EF_DG1: %s", sprint_hex_inrow(response, resplen));
2020-12-11 11:52:26 +08:00
DropField();
return PM3_SUCCESS;
}
static int cmd_hf_emrtd_info(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf emrtd info",
"Get info about an eMRTD",
"hf emrtd info"
);
void *argtable[] = {
arg_param_begin,
2020-12-07 04:52:08 +08:00
arg_str1("n", "documentnumber", "<number>", "9 character document number"),
arg_str1("d", "dateofbirth", "<number>", "date of birth in YYMMDD format"),
arg_str1("e", "expiry", "<number>", "expiry in YYMMDD format"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
2020-12-07 04:52:08 +08:00
2020-12-07 05:40:01 +08:00
uint8_t docnum[10];
uint8_t dob[7];
uint8_t expiry[7];
int docnumlen = 9;
int doblen = 6;
int expirylen = 6;
2020-12-07 04:52:08 +08:00
CLIGetStrWithReturn(ctx, 1, docnum, &docnumlen);
CLIGetStrWithReturn(ctx, 2, dob, &doblen);
CLIGetStrWithReturn(ctx, 3, expiry, &expirylen);
CLIParserFree(ctx);
2020-12-07 04:52:08 +08:00
return infoHF_EMRTD((char *)docnum, (char *)dob, (char *)expiry);
}
static int cmd_hf_emrtd_list(const char *Cmd) {
char args[128] = {0};
if (strlen(Cmd) == 0) {
snprintf(args, sizeof(args), "-t 7816");
} else {
strncpy(args, Cmd, sizeof(args) - 1);
}
return CmdTraceList(args);
}
static command_t CommandTable[] = {
{"help", CmdHelp, AlwaysAvailable, "This help"},
{"info", cmd_hf_emrtd_info, IfPm3Iso14443a, "Tag information"},
{"list", cmd_hf_emrtd_list, AlwaysAvailable, "List ISO 14443A/7816 history"},
{NULL, NULL, NULL, NULL}
};
static int CmdHelp(const char *Cmd) {
(void)Cmd; // Cmd is not used so far
CmdsHelp(CommandTable);
return PM3_SUCCESS;
}
int CmdHFeMRTD(const char *Cmd) {
clearCommandBuffer();
return CmdsParse(CommandTable, Cmd);
}