mirror of
https://github.com/Proxmark/proxmark3.git
synced 2025-07-31 12:00:10 -07:00
fix emv roca hash error (#781)
* fix ICC Public Key Hash calculation * add -a option to 'emv roca' * replace print() by PrintAndLogEx() in emv_pki.c
This commit is contained in:
parent
c719d385ef
commit
3783c45af1
3 changed files with 337 additions and 303 deletions
|
@ -1631,6 +1631,8 @@ int CmdEMVRoca(const char *cmd) {
|
|||
size_t len = 0;
|
||||
uint16_t sw = 0;
|
||||
int res;
|
||||
uint8_t ODAiList[4096];
|
||||
size_t ODAiListLen = 0;
|
||||
|
||||
CLIParserInit("emv roca",
|
||||
"Tries to extract public keys and run the ROCA test against them.\n",
|
||||
|
@ -1642,25 +1644,29 @@ int CmdEMVRoca(const char *cmd) {
|
|||
void* argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_lit0("tT", "selftest", "self test"),
|
||||
arg_lit0("aA", "apdu", "show APDU reqests and responses"),
|
||||
arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(cmd, argtable, true);
|
||||
|
||||
EMVCommandChannel channel = ECC_CONTACTLESS;
|
||||
if (arg_get_lit(1))
|
||||
return roca_self_test();
|
||||
bool showAPDU = arg_get_lit(2);
|
||||
|
||||
EMVCommandChannel channel = ECC_CONTACTLESS;
|
||||
#ifdef WITH_SMARTCARD
|
||||
if (arg_get_lit(2))
|
||||
if (arg_get_lit(3))
|
||||
channel = ECC_CONTACT;
|
||||
#endif
|
||||
PrintChannel(channel);
|
||||
CLIParserFree();
|
||||
|
||||
// select card
|
||||
uint8_t psenum = (channel == ECC_CONTACT) ? 1 : 2;
|
||||
char *PSE_or_PPSE = psenum == 1 ? "PSE" : "PPSE";
|
||||
|
||||
SetAPDULogging(false);
|
||||
SetAPDULogging(showAPDU);
|
||||
|
||||
// init applets list tree
|
||||
const char *al = "Applets list";
|
||||
|
@ -1688,7 +1694,6 @@ int CmdEMVRoca(const char *cmd) {
|
|||
}
|
||||
|
||||
// EMV SELECT application
|
||||
SetAPDULogging(false);
|
||||
EMVSelectApplication(tlvSelect, AID, &AIDlen);
|
||||
|
||||
tlvdb_free(tlvSelect);
|
||||
|
@ -1779,12 +1784,40 @@ int CmdEMVRoca(const char *cmd) {
|
|||
PrintAndLogEx(ERR, "SFI[%02x]. APDU error %4x", SFI, sw);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Build Input list for Offline Data Authentication
|
||||
// EMV 4.3 book3 10.3, page 96
|
||||
if (SFIoffline > 0) {
|
||||
if (SFI < 11) {
|
||||
const unsigned char *abuf = buf;
|
||||
size_t elmlen = len;
|
||||
struct tlv e;
|
||||
if (tlv_parse_tl(&abuf, &elmlen, &e)) {
|
||||
memcpy(&ODAiList[ODAiListLen], &buf[len - elmlen], elmlen);
|
||||
ODAiListLen += elmlen;
|
||||
} else {
|
||||
PrintAndLogEx(WARNING, "Error SFI[%02x]. Creating input list for Offline Data Authentication error.", SFI);
|
||||
}
|
||||
} else {
|
||||
memcpy(&ODAiList[ODAiListLen], buf, len);
|
||||
ODAiListLen += len;
|
||||
}
|
||||
|
||||
SFIoffline--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// copy Input list for Offline Data Authentication
|
||||
if (ODAiListLen) {
|
||||
struct tlvdb *oda = tlvdb_fixed(0x21, ODAiListLen, ODAiList); // not a standard tag
|
||||
tlvdb_add(tlvRoot, oda);
|
||||
PrintAndLogEx(NORMAL, "* Input list for Offline Data Authentication added to TLV. len=%d \n", ODAiListLen);
|
||||
}
|
||||
|
||||
// getting certificates
|
||||
if (tlvdb_get(tlvRoot, 0x90, NULL)) {
|
||||
PrintAndLogEx(INFO, "-->Recovering certificates.");
|
||||
|
@ -1805,14 +1838,14 @@ int CmdEMVRoca(const char *cmd) {
|
|||
|
||||
char RID[15] = {0};
|
||||
memcpy(RID, sprint_hex(issuer_pk->rid, 5), 14);
|
||||
PrintAndLogEx(SUCCESS, "Issuer PK recovered. RID %s IDX %02hhx CSN %s",
|
||||
PrintAndLogEx(SUCCESS, "Issuer Public Key recovered. RID %s IDX %02hhx CSN %s",
|
||||
RID,
|
||||
issuer_pk->index,
|
||||
sprint_hex(issuer_pk->serial, 3)
|
||||
);
|
||||
|
||||
|
||||
struct emv_pk *icc_pk = emv_pki_recover_icc_cert(issuer_pk, tlvRoot, NULL);
|
||||
const struct tlv *sda_tlv = tlvdb_get(tlvRoot, 0x21, NULL);
|
||||
struct emv_pk *icc_pk = emv_pki_recover_icc_cert(issuer_pk, tlvRoot, sda_tlv);
|
||||
if (!icc_pk) {
|
||||
emv_pk_free(pk);
|
||||
emv_pk_free(issuer_pk);
|
||||
|
@ -1821,21 +1854,21 @@ int CmdEMVRoca(const char *cmd) {
|
|||
}
|
||||
|
||||
memcpy(RID, sprint_hex(icc_pk->rid, 5), 14);
|
||||
PrintAndLogEx(SUCCESS, "ICC PK recovered. RID %s IDX %02hhx CSN %s\n",
|
||||
PrintAndLogEx(SUCCESS, "ICC Public Key recovered. RID %s IDX %02hhx CSN %s\n",
|
||||
RID,
|
||||
icc_pk->index,
|
||||
sprint_hex(icc_pk->serial, 3)
|
||||
);
|
||||
|
||||
PrintAndLogEx(INFO, "ICC pk modulus: %s", sprint_hex_inrow(icc_pk->modulus, icc_pk->mlen));
|
||||
PrintAndLogEx(INFO, "ICC Public Key modulus: %s\n", sprint_hex_inrow(icc_pk->modulus, icc_pk->mlen));
|
||||
|
||||
// icc_pk->exp, icc_pk->elen
|
||||
// icc_pk->modulus, icc_pk->mlen
|
||||
if (icc_pk->elen > 0 && icc_pk->mlen > 0) {
|
||||
if (emv_rocacheck(icc_pk->modulus, icc_pk->mlen, true)) {
|
||||
PrintAndLogEx(INFO, "ICC pk is a subject to ROCA vulnerability, insecure..");
|
||||
if (emv_rocacheck(icc_pk->modulus, icc_pk->mlen, false)) {
|
||||
PrintAndLogEx(INFO, "ICC Public Key is subject to ROCA vulnerability (it is NOT secure).");
|
||||
} else {
|
||||
PrintAndLogEx(INFO, "ICC pk is OK(");
|
||||
PrintAndLogEx(INFO, "ICC Public Key is not subject to ROCA vulnerability (it is secure)");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -503,7 +503,7 @@ struct emv_pk *emv_pk_get_ca_pk(const unsigned char *rid, unsigned char idx)
|
|||
if (!pk)
|
||||
return NULL;
|
||||
|
||||
printf("Verifying CA PK for %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx %zd bits...",
|
||||
printf("Verifying CA Public Key for %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx %zd bits...",
|
||||
pk->rid[0],
|
||||
pk->rid[1],
|
||||
pk->rid[2],
|
||||
|
|
|
@ -18,14 +18,14 @@
|
|||
#endif
|
||||
|
||||
#include "emv_pki.h"
|
||||
#include "crypto.h"
|
||||
#include "dump.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include "crypto.h"
|
||||
#include "dump.h"
|
||||
#include "util.h"
|
||||
#include "ui.h"
|
||||
|
||||
static bool strictExecution = true;
|
||||
void PKISetStrictExecution(bool se) {
|
||||
|
@ -54,12 +54,12 @@ static unsigned char *emv_pki_decode_message(const struct emv_pk *enc_pk,
|
|||
return NULL;
|
||||
|
||||
if (!cert_tlv) {
|
||||
printf("ERROR: Can't find certificate\n");
|
||||
PrintAndLogEx(ERR, "Can't find certificate\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (cert_tlv->len != enc_pk->mlen) {
|
||||
printf("ERROR: Certificate length (%zd) not equal key length (%zd)\n", cert_tlv->len, enc_pk->mlen);
|
||||
PrintAndLogEx(ERR, "Certificate length (%zd) not equal key length (%zd)\n", cert_tlv->len, enc_pk->mlen);
|
||||
return NULL;
|
||||
}
|
||||
kcp = crypto_pk_open(enc_pk->pk_algo,
|
||||
|
@ -72,19 +72,19 @@ static unsigned char *emv_pki_decode_message(const struct emv_pk *enc_pk,
|
|||
crypto_pk_close(kcp);
|
||||
|
||||
/*if (true){
|
||||
printf("Recovered data:\n");
|
||||
PrintAndLogEx(INFO, "Recovered data:\n");
|
||||
dump_buffer(data, data_len, stdout, 0);
|
||||
}*/
|
||||
|
||||
if (data[data_len-1] != 0xbc || data[0] != 0x6a || data[1] != msgtype) {
|
||||
printf("ERROR: Certificate format\n");
|
||||
PrintAndLogEx(ERR, "Certificate format\n");
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t hash_pos = emv_pki_hash_psn[msgtype];
|
||||
if (hash_pos == 0 || hash_pos > data_len){
|
||||
printf("ERROR: Cant get hash position in the certificate\n");
|
||||
PrintAndLogEx(ERR, "Can't get hash position in the certificate\n");
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ static unsigned char *emv_pki_decode_message(const struct emv_pk *enc_pk,
|
|||
struct crypto_hash *ch;
|
||||
ch = crypto_hash_open(data[hash_pos]);
|
||||
if (!ch) {
|
||||
printf("ERROR: Cant do hash\n");
|
||||
PrintAndLogEx(ERR, "Can't do hash\n");
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -114,9 +114,9 @@ static unsigned char *emv_pki_decode_message(const struct emv_pk *enc_pk,
|
|||
memset(hash, 0, hash_len);
|
||||
memcpy(hash, crypto_hash_read(ch), hash_len);
|
||||
if (memcmp(data + data_len - 1 - hash_len, hash, hash_len)) {
|
||||
printf("ERROR: Calculated wrong hash\n");
|
||||
printf("decoded: %s\n",sprint_hex(data + data_len - 1 - hash_len, hash_len));
|
||||
printf("calculated: %s\n",sprint_hex(hash, hash_len));
|
||||
PrintAndLogEx(ERR, "Calculated wrong hash\n");
|
||||
PrintAndLogEx(INFO, "decoded: %s\n",sprint_hex(data + data_len - 1 - hash_len, hash_len));
|
||||
PrintAndLogEx(INFO, "calculated: %s\n",sprint_hex(hash, hash_len));
|
||||
|
||||
if (strictExecution) {
|
||||
crypto_hash_close(ch);
|
||||
|
@ -189,7 +189,7 @@ static struct emv_pk *emv_pki_decode_key_ex(const struct emv_pk *enc_pk,
|
|||
else if (msgtype == 4)
|
||||
pan_length = 10;
|
||||
else {
|
||||
printf("ERROR: Message type must be 2 or 4\n");
|
||||
PrintAndLogEx(ERR, "Message type must be 2 or 4\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -202,12 +202,12 @@ static struct emv_pk *emv_pki_decode_key_ex(const struct emv_pk *enc_pk,
|
|||
sdatl_tlv,
|
||||
NULL);
|
||||
if (!data || data_len < 11 + pan_length) {
|
||||
printf("ERROR: Can't decode message\n");
|
||||
PrintAndLogEx(ERR, "Can't decode message\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (showData){
|
||||
printf("Recovered data:\n");
|
||||
PrintAndLogEx(INFO, "Recovered data:\n");
|
||||
dump_buffer(data, data_len, stdout, 0);
|
||||
}
|
||||
|
||||
|
@ -223,7 +223,7 @@ static struct emv_pk *emv_pki_decode_key_ex(const struct emv_pk *enc_pk,
|
|||
|
||||
if (((msgtype == 2) && (pan2_len < 4 || pan2_len > pan_len)) ||
|
||||
((msgtype == 4) && (pan2_len != pan_len))) {
|
||||
printf("ERROR: Invalid PAN lengths\n");
|
||||
PrintAndLogEx(ERR, "Invalid PAN lengths\n");
|
||||
free(data);
|
||||
|
||||
return NULL;
|
||||
|
@ -232,9 +232,9 @@ static struct emv_pk *emv_pki_decode_key_ex(const struct emv_pk *enc_pk,
|
|||
unsigned i;
|
||||
for (i = 0; i < pan2_len; i++)
|
||||
if (emv_cn_get(pan_tlv, i) != emv_cn_get(&pan2_tlv, i)) {
|
||||
printf("ERROR: PAN data mismatch\n");
|
||||
printf("tlv pan=%s\n", sprint_hex(pan_tlv->value, pan_tlv->len));
|
||||
printf("cert pan=%s\n", sprint_hex(pan2_tlv.value, pan2_tlv.len));
|
||||
PrintAndLogEx(ERR, "PAN data mismatch\n");
|
||||
PrintAndLogEx(INFO, "tlv pan=%s\n", sprint_hex(pan_tlv->value, pan_tlv->len));
|
||||
PrintAndLogEx(INFO, "cert pan=%s\n", sprint_hex(pan2_tlv.value, pan2_tlv.len));
|
||||
free(data);
|
||||
|
||||
return NULL;
|
||||
|
@ -242,7 +242,7 @@ static struct emv_pk *emv_pki_decode_key_ex(const struct emv_pk *enc_pk,
|
|||
|
||||
pk_len = data[9 + pan_length];
|
||||
if (pk_len > data_len - 11 - pan_length + rem_tlv->len) {
|
||||
printf("ERROR: Invalid pk length\n");
|
||||
PrintAndLogEx(ERR, "Invalid pk length\n");
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -388,7 +388,7 @@ struct tlvdb *emv_pki_recover_dac_ex(const struct emv_pk *enc_pk, const struct t
|
|||
return NULL;
|
||||
|
||||
if (showData){
|
||||
printf("Recovered data:\n");
|
||||
PrintAndLogEx(INFO, "Recovered data:\n");
|
||||
dump_buffer(data, data_len, stdout, 0);
|
||||
}
|
||||
|
||||
|
@ -398,6 +398,7 @@ struct tlvdb *emv_pki_recover_dac_ex(const struct emv_pk *enc_pk, const struct t
|
|||
|
||||
return dac_db;
|
||||
}
|
||||
|
||||
struct tlvdb *emv_pki_recover_dac(const struct emv_pk *enc_pk, const struct tlvdb *db, const struct tlv *sda_tlv) {
|
||||
return emv_pki_recover_dac_ex(enc_pk, db, sda_tlv, false);
|
||||
}
|
||||
|
@ -424,7 +425,7 @@ struct tlvdb *emv_pki_recover_idn_ex(const struct emv_pk *enc_pk, const struct t
|
|||
}
|
||||
|
||||
if (showData){
|
||||
printf("Recovered data:\n");
|
||||
PrintAndLogEx(INFO, "Recovered data:\n");
|
||||
dump_buffer(data, data_len, stdout, 0);
|
||||
}
|
||||
|
||||
|
@ -463,7 +464,7 @@ struct tlvdb *emv_pki_recover_atc_ex(const struct emv_pk *enc_pk, const struct t
|
|||
}
|
||||
|
||||
if (showData){
|
||||
printf("Recovered data:\n");
|
||||
PrintAndLogEx(INFO, "Recovered data:\n");
|
||||
dump_buffer(data, data_len, stdout, 0);
|
||||
}
|
||||
|
||||
|
@ -528,23 +529,23 @@ struct tlvdb *emv_pki_perform_cda_ex(const struct emv_pk *enc_pk, const struct t
|
|||
un_tlv,
|
||||
NULL);
|
||||
if (!data || data_len < 3) {
|
||||
printf("ERROR: can't decode message. len %zd\n", data_len);
|
||||
PrintAndLogEx(ERR, "can't decode message. len %zd\n", data_len);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (showData){
|
||||
printf("Recovered data:\n");
|
||||
PrintAndLogEx(INFO, "Recovered data:\n");
|
||||
dump_buffer(data, data_len, stdout, 0);
|
||||
}
|
||||
|
||||
if (data[3] < 30 || data[3] > data_len - 4) {
|
||||
printf("ERROR: Invalid data length\n");
|
||||
PrintAndLogEx(ERR, "Invalid data length\n");
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!cid_tlv || cid_tlv->len != 1 || cid_tlv->value[0] != data[5 + data[4]]) {
|
||||
printf("ERROR: CID mismatch\n");
|
||||
PrintAndLogEx(ERR, "CID mismatch\n");
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -552,7 +553,7 @@ struct tlvdb *emv_pki_perform_cda_ex(const struct emv_pk *enc_pk, const struct t
|
|||
struct crypto_hash *ch;
|
||||
ch = crypto_hash_open(enc_pk->hash_algo);
|
||||
if (!ch) {
|
||||
printf("ERROR: can't create hash\n");
|
||||
PrintAndLogEx(ERR, "Can't create hash\n");
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -567,7 +568,7 @@ struct tlvdb *emv_pki_perform_cda_ex(const struct emv_pk *enc_pk, const struct t
|
|||
tlvdb_visit(this_db, tlv_hash, ch, 0);
|
||||
|
||||
if (memcmp(data + 5 + data[4] + 1 + 8, crypto_hash_read(ch), 20)) {
|
||||
printf("ERROR: calculated hash error\n");
|
||||
PrintAndLogEx(ERR, "Calculated hash error\n");
|
||||
crypto_hash_close(ch);
|
||||
free(data);
|
||||
return NULL;
|
||||
|
@ -576,7 +577,7 @@ struct tlvdb *emv_pki_perform_cda_ex(const struct emv_pk *enc_pk, const struct t
|
|||
|
||||
size_t idn_len = data[4];
|
||||
if (idn_len > data[3] - 1) {
|
||||
printf("ERROR: Invalid IDN length\n");
|
||||
PrintAndLogEx(ERR, "Invalid IDN length\n");
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue