mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2024-09-21 15:56:21 +08:00
emrtd: Improve hashing logic, support SHA1
This commit is contained in:
parent
2c5c58d128
commit
040520d865
|
@ -19,8 +19,7 @@
|
|||
#include "cmdhf14a.h" // ExchangeAPDU14a
|
||||
#include "protocols.h" // definitions of ISO14A/7816 protocol
|
||||
#include "emv/apduinfo.h" // GetAPDUCodeDescription
|
||||
#include "sha1.h" // KSeed calculation etc
|
||||
#include "crypto/libpcrypto.h" // Hash calculation (sha256, sha512)
|
||||
#include "crypto/libpcrypto.h" // Hash calculation (sha1, sha256, sha512)
|
||||
#include "mifare/desfire_crypto.h" // des_encrypt/des_decrypt
|
||||
#include "des.h" // mbedtls_des_key_set_parity
|
||||
#include "cmdhf14b.h" // exchange_14b_apdu
|
||||
|
@ -108,6 +107,16 @@ static emrtd_dg_t dg_table[] = {
|
|||
{0x00, 0, NULL, NULL, NULL, false, false, false, false, NULL, NULL}
|
||||
};
|
||||
|
||||
// https://security.stackexchange.com/questions/131241/where-do-magic-constants-for-signature-algorithms-come-from
|
||||
// https://tools.ietf.org/html/rfc3447#page-43
|
||||
static emrtd_hashalg_t hashalg_table[] = {
|
||||
// name hash func len len descriptor
|
||||
{"SHA-1", sha1hash, 20, 9, {0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00}},
|
||||
{"SHA-256", sha256hash, 32, 11, {0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01}},
|
||||
{"SHA-512", sha512hash, 64, 11, {0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03}},
|
||||
{NULL, NULL, 0, 0, {}}
|
||||
};
|
||||
|
||||
static emrtd_dg_t *emrtd_tag_to_dg(uint8_t tag) {
|
||||
for (int dgi = 0; dg_table[dgi].filename != NULL; dgi++) {
|
||||
if (dg_table[dgi].tag == tag) {
|
||||
|
@ -339,7 +348,7 @@ static void emrtd_deskey(uint8_t *seed, const uint8_t *type, int length, uint8_t
|
|||
|
||||
// SHA1 the key
|
||||
unsigned char key[64];
|
||||
mbedtls_sha1(data, length + 4, key);
|
||||
sha1hash(data, length + 4, key);
|
||||
PrintAndLogEx(DEBUG, "key............... %s", sprint_hex_inrow(key, length + 4));
|
||||
|
||||
// Set parity bits
|
||||
|
@ -822,7 +831,7 @@ static bool emrtd_do_bac(char *documentnumber, char *dob, char *expiry, uint8_t
|
|||
PrintAndLogEx(DEBUG, "kmrz.............. " _GREEN_("%s"), kmrz);
|
||||
|
||||
uint8_t kseed[20] = { 0x00 };
|
||||
mbedtls_sha1((unsigned char *)kmrz, strlen(kmrz), kseed);
|
||||
sha1hash((unsigned char *)kmrz, strlen(kmrz), kseed);
|
||||
PrintAndLogEx(DEBUG, "kseed (sha1)...... %s ", sprint_hex_inrow(kseed, 16));
|
||||
|
||||
emrtd_deskey(kseed, KENC_type, 16, kenc);
|
||||
|
@ -1531,10 +1540,6 @@ static int emrtd_ef_sod_extract_signatures(uint8_t *data, size_t datalen, uint8_
|
|||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
// https://security.stackexchange.com/questions/131241/where-do-magic-constants-for-signature-algorithms-come-from
|
||||
static const uint8_t emrtd_hashalgo_sha256[] = {0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01};
|
||||
static const uint8_t emrtd_hashalgo_sha512[] = {0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03};
|
||||
|
||||
static int emrtd_parse_ef_sod_hash_algo(uint8_t *data, size_t datalen, int *hashalgo) {
|
||||
uint8_t hashalgoset[64] = { 0x00 };
|
||||
size_t hashalgosetlen = 0;
|
||||
|
@ -1546,17 +1551,24 @@ static int emrtd_parse_ef_sod_hash_algo(uint8_t *data, size_t datalen, int *hash
|
|||
|
||||
PrintAndLogEx(DEBUG, "hash algo set: %s", sprint_hex_inrow(hashalgoset, hashalgosetlen));
|
||||
|
||||
if (memcmp(emrtd_hashalgo_sha256, hashalgoset, 11) == 0) {
|
||||
*hashalgo = 1;
|
||||
} else if (memcmp(emrtd_hashalgo_sha512, hashalgoset, 11) == 0) {
|
||||
*hashalgo = 3;
|
||||
} else {
|
||||
*hashalgo = 0;
|
||||
return PM3_ESOFT;
|
||||
for (int hashi = 0; hashalg_table[hashi].name != NULL; hashi++) {
|
||||
PrintAndLogEx(DEBUG, "trying: %s", hashalg_table[hashi].name);
|
||||
// We're only interested in checking if the length matches to avoid memory shenanigans
|
||||
if (hashalg_table[hashi].descriptorlen != hashalgosetlen) {
|
||||
PrintAndLogEx(DEBUG, "len mismatch: %i", hashalgosetlen);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (memcmp(hashalg_table[hashi].descriptor, hashalgoset, hashalgosetlen) == 0) {
|
||||
*hashalgo = hashi;
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
// Return hash algo 0 if we can't find anything
|
||||
*hashalgo = -1;
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
static int emrtd_parse_ef_sod_hashes(uint8_t *data, size_t datalen, uint8_t *hashes, int *hashalgo) {
|
||||
uint8_t emrtdsig[EMRTD_MAX_FILE_SIZE] = { 0x00 };
|
||||
|
@ -1580,7 +1592,7 @@ static int emrtd_parse_ef_sod_hashes(uint8_t *data, size_t datalen, uint8_t *has
|
|||
PrintAndLogEx(DEBUG, "hash data: %s", sprint_hex_inrow(emrtdsig, emrtdsiglen));
|
||||
|
||||
if (emrtd_parse_ef_sod_hash_algo(emrtdsig, emrtdsiglen, hashalgo) != PM3_SUCCESS) {
|
||||
PrintAndLogEx(ERR, "Failed to parse hash list. Unknown algo?");
|
||||
PrintAndLogEx(ERR, "Failed to parse hash list (Unknown algo?). Hash verification won't be available.");
|
||||
}
|
||||
|
||||
if (!emrtd_lds_get_data_by_tag(emrtdsig, emrtdsiglen, hashlist, &hashlistlen, 0x30, 0x00, false, true, 1)) {
|
||||
|
@ -1615,16 +1627,6 @@ static int emrtd_parse_ef_sod_hashes(uint8_t *data, size_t datalen, uint8_t *has
|
|||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static void emrtd_calc_dg_hash(uint8_t *data, size_t datalen, uint8_t *hash_out, int hash_algo) {
|
||||
memset(hash_out, 0, 64);
|
||||
|
||||
if (hash_algo == 1) {
|
||||
sha256hash(data, datalen, hash_out);
|
||||
} else if (hash_algo == 3) {
|
||||
sha512hash(data, datalen, hash_out);
|
||||
}
|
||||
}
|
||||
|
||||
int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available) {
|
||||
uint8_t response[EMRTD_MAX_FILE_SIZE] = { 0x00 };
|
||||
int resplen = 0;
|
||||
|
@ -1703,10 +1705,14 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab
|
|||
if (dg->parser != NULL)
|
||||
dg->parser(response, resplen);
|
||||
|
||||
PrintAndLogEx(DEBUG, "EF_DG%i hash algo: %i", dg->dgnum, hash_algo);
|
||||
// Check file hash
|
||||
emrtd_calc_dg_hash(response, resplen, hash_out, hash_algo);
|
||||
if (hash_algo != -1) {
|
||||
PrintAndLogEx(DEBUG, "EF_DG%i hash on EF_SOD: %s", dg->dgnum, sprint_hex_inrow(dg_hashes[dg->dgnum], hashalg_table[hash_algo].hashlen));
|
||||
hashalg_table[hash_algo].hasher(response, resplen, hash_out);
|
||||
PrintAndLogEx(DEBUG, "EF_DG%i hash calc: %s", dg->dgnum, sprint_hex_inrow(hash_out, hashalg_table[hash_algo].hashlen));
|
||||
|
||||
if (memcmp(dg_hashes[dg->dgnum], hash_out, 64) == 0) {
|
||||
if (memcmp(dg_hashes[dg->dgnum], hash_out, hashalg_table[hash_algo].hashlen) == 0) {
|
||||
PrintAndLogEx(SUCCESS, _GREEN_("Hash verification passed for EF_DG%i."), dg->dgnum);
|
||||
} else {
|
||||
PrintAndLogEx(ERR, _RED_("Hash verification failed for EF_DG%i."), dg->dgnum);
|
||||
|
@ -1714,6 +1720,7 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
DropField();
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
@ -1789,14 +1796,19 @@ int infoHF_EMRTD_offline(const char *path) {
|
|||
if (dg->parser != NULL)
|
||||
dg->parser(data, datalen);
|
||||
|
||||
PrintAndLogEx(DEBUG, "EF_DG%i hash algo: %i", dg->dgnum, hash_algo);
|
||||
// Check file hash
|
||||
emrtd_calc_dg_hash(data, datalen, hash_out, hash_algo);
|
||||
if (hash_algo != -1) {
|
||||
PrintAndLogEx(DEBUG, "EF_DG%i hash on EF_SOD: %s", dg->dgnum, sprint_hex_inrow(dg_hashes[dg->dgnum], hashalg_table[hash_algo].hashlen));
|
||||
hashalg_table[hash_algo].hasher(data, datalen, hash_out);
|
||||
PrintAndLogEx(DEBUG, "EF_DG%i hash calc: %s", dg->dgnum, sprint_hex_inrow(hash_out, hashalg_table[hash_algo].hashlen));
|
||||
|
||||
if (memcmp(dg_hashes[dg->dgnum], hash_out, 64) == 0) {
|
||||
if (memcmp(dg_hashes[dg->dgnum], hash_out, hashalg_table[hash_algo].hashlen) == 0) {
|
||||
PrintAndLogEx(SUCCESS, _GREEN_("Hash verification passed for EF_DG%i."), dg->dgnum);
|
||||
} else {
|
||||
PrintAndLogEx(ERR, _RED_("Hash verification failed for EF_DG%i."), dg->dgnum);
|
||||
}
|
||||
}
|
||||
free(data);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,14 @@ typedef struct emrtd_dg_s {
|
|||
int (*dumper)(uint8_t *data, size_t datalen);
|
||||
} emrtd_dg_t;
|
||||
|
||||
typedef struct emrtd_hashalg_s {
|
||||
const char *name;
|
||||
int (*hasher)(uint8_t *datain, int datainlen, uint8_t *dataout);
|
||||
size_t hashlen;
|
||||
size_t descriptorlen;
|
||||
const uint8_t descriptor[15];
|
||||
} emrtd_hashalg_t;
|
||||
|
||||
int CmdHFeMRTD(const char *Cmd);
|
||||
|
||||
int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <mbedtls/cmac.h>
|
||||
#include <mbedtls/pk.h>
|
||||
#include <mbedtls/ecdsa.h>
|
||||
#include <mbedtls/sha1.h>
|
||||
#include <mbedtls/sha256.h>
|
||||
#include <mbedtls/sha512.h>
|
||||
#include <mbedtls/ctr_drbg.h>
|
||||
|
@ -93,6 +94,15 @@ static int fixed_rand(void *rng_state, unsigned char *output, size_t len) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int sha1hash(uint8_t *input, int length, uint8_t *hash) {
|
||||
if (!hash || !input)
|
||||
return 1;
|
||||
|
||||
mbedtls_sha1(input, length, hash);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sha256hash(uint8_t *input, int length, uint8_t *hash) {
|
||||
if (!hash || !input)
|
||||
return 1;
|
||||
|
|
|
@ -21,6 +21,7 @@ int aes_decode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int l
|
|||
int aes_cmac(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *mac, int length);
|
||||
int aes_cmac8(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *mac, int length);
|
||||
|
||||
int sha1hash(uint8_t *input, int length, uint8_t *hash);
|
||||
int sha256hash(uint8_t *input, int length, uint8_t *hash);
|
||||
int sha512hash(uint8_t *input, int length, uint8_t *hash);
|
||||
|
||||
|
|
Loading…
Reference in a new issue