mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-14 10:37:23 -07:00
Improve Tesla NFC card reader:
- Compatibility with more javacard variants - Read the full certificate - Optionally parse the certificate (ASN.1) - Don't bail at each error and try to read what it can - Better form factor parsing - Read all four public keys Read all four public keys
This commit is contained in:
parent
f448ed5c51
commit
14a58a7427
1 changed files with 96 additions and 38 deletions
|
@ -24,6 +24,7 @@
|
|||
#include "cmdtrace.h"
|
||||
#include "cliparser.h"
|
||||
#include "cmdhf14a.h"
|
||||
#include "crypto/asn1utils.h" // ASN1 decode / print
|
||||
#include "protocols.h" // definitions of ISO14A/7816 protocol
|
||||
#include "iso7816/apduinfo.h" // GetAPDUCodeDescription
|
||||
#include "commonutil.h" // get_sw
|
||||
|
@ -32,6 +33,7 @@
|
|||
#include "cmdhf14a.h" // apdu chaining
|
||||
|
||||
#define TIMEOUT 2000
|
||||
#define MAX_CERT_SIZE 768
|
||||
|
||||
static int CmdHelp(const char *Cmd);
|
||||
|
||||
|
@ -51,17 +53,22 @@ static int CmdHelp(const char *Cmd);
|
|||
*/
|
||||
|
||||
// TESLA
|
||||
static int info_hf_tesla(void) {
|
||||
static int info_hf_tesla(bool parse_certs) {
|
||||
|
||||
bool activate_field = true;
|
||||
bool keep_field_on = true;
|
||||
uint8_t response[PM3_CMD_DATA_SIZE];
|
||||
uint8_t response[MAX_CERT_SIZE]; // Some cards have pretty large certificates
|
||||
int resplen = 0;
|
||||
|
||||
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------");
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
|
||||
// --------------- Select TESLA application ----------------
|
||||
uint8_t aSELECT_AID[80];
|
||||
int aSELECT_AID_n = 0;
|
||||
param_gethex_to_eol("00a404000a7465736c614c6f676963", 0, aSELECT_AID, sizeof(aSELECT_AID), &aSELECT_AID_n);
|
||||
param_gethex_to_eol("00a404000a7465736c614c6f67696300", 0, aSELECT_AID, sizeof(aSELECT_AID), &aSELECT_AID_n);
|
||||
int res = ExchangeAPDU14a(aSELECT_AID, aSELECT_AID_n, activate_field, keep_field_on, response, sizeof(response), &resplen);
|
||||
if (res != PM3_SUCCESS) {
|
||||
DropField();
|
||||
|
@ -73,7 +80,7 @@ static int info_hf_tesla(void) {
|
|||
|
||||
if ((resplen < 2) || (sw != ISO7816_OK)) {
|
||||
|
||||
param_gethex_to_eol("00a404000af465736c614c6f676963", 0, aSELECT_AID, sizeof(aSELECT_AID), &aSELECT_AID_n);
|
||||
param_gethex_to_eol("00a404000af465736c614c6f67696300", 0, aSELECT_AID, sizeof(aSELECT_AID), &aSELECT_AID_n);
|
||||
res = ExchangeAPDU14a(aSELECT_AID, aSELECT_AID_n, activate_field, keep_field_on, response, sizeof(response), &resplen);
|
||||
if (res != PM3_SUCCESS) {
|
||||
DropField();
|
||||
|
@ -92,9 +99,9 @@ static int info_hf_tesla(void) {
|
|||
|
||||
|
||||
// --------------- ECDH public key file reading ----------------
|
||||
uint8_t pk[3][65] = {{0}};
|
||||
uint8_t pk[4][65] = {{0}};
|
||||
|
||||
for (uint8_t i = 0; i < 3; i++) {
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
|
||||
uint8_t aSELECT_PK[5] = {0x80, 0x04, i, 0x00, 0x00};
|
||||
res = ExchangeAPDU14a(aSELECT_PK, sizeof(aSELECT_PK), activate_field, keep_field_on, response, sizeof(response), &resplen);
|
||||
|
@ -110,7 +117,7 @@ static int info_hf_tesla(void) {
|
|||
|
||||
uint8_t aREAD_FORM_FACTOR[30];
|
||||
int aREAD_FORM_FACTOR_n = 0;
|
||||
param_gethex_to_eol("80140000", 0, aREAD_FORM_FACTOR, sizeof(aREAD_FORM_FACTOR), &aREAD_FORM_FACTOR_n);
|
||||
param_gethex_to_eol("8014000000", 0, aREAD_FORM_FACTOR, sizeof(aREAD_FORM_FACTOR), &aREAD_FORM_FACTOR_n);
|
||||
res = ExchangeAPDU14a(aREAD_FORM_FACTOR, aREAD_FORM_FACTOR_n, activate_field, keep_field_on, response, sizeof(response), &resplen);
|
||||
if (res != PM3_SUCCESS) {
|
||||
DropField();
|
||||
|
@ -149,23 +156,58 @@ static int info_hf_tesla(void) {
|
|||
Set_apdu_in_framing(true);
|
||||
for (uint8_t i = 0; i < 5; i++) {
|
||||
|
||||
uint8_t aSELECT_CERT[PM3_CMD_DATA_SIZE] = {0x80, 0x06, i, 0x00, 0x00, 0x00, 0xFF};
|
||||
int aSELECT_CERT_n = 7;
|
||||
// First, read the certificate length
|
||||
uint8_t aSELECT_CERT[PM3_CMD_DATA_SIZE] = {0x80, 0x06, i, 0x00, 0x04};
|
||||
int aSELECT_CERT_n = 5;
|
||||
|
||||
res = ExchangeAPDU14a(aSELECT_CERT, aSELECT_CERT_n, activate_field, keep_field_on, response, PM3_CMD_DATA_SIZE, &resplen);
|
||||
res = ExchangeAPDU14a(aSELECT_CERT, aSELECT_CERT_n, activate_field, keep_field_on, response, sizeof(response), &resplen);
|
||||
if (res != PM3_SUCCESS) {
|
||||
PrintAndLogEx(ERR, "Could not read certificate %i length", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
sw = get_sw(response, resplen);
|
||||
bool cert_len_present = false;
|
||||
|
||||
if (sw == ISO7816_OK) {
|
||||
// save CERT for later
|
||||
uint8_t cert[515] = {0};
|
||||
memcpy(cert, response, resplen - 2);
|
||||
|
||||
if (sw == ISO7816_OK && resplen > 3) {
|
||||
uint16_t cert_len = response[0] << 8 | response[1];
|
||||
PrintAndLogEx(INFO, "CERT # %i", i);
|
||||
PrintAndLogEx(INFO, "%s", sprint_hex_inrow(cert, resplen - 2));
|
||||
if (cert_len == 0x3082) {
|
||||
cert_len = (response[2] << 8 | response[3]) + 4;
|
||||
PrintAndLogEx(INFO, "Length (calculated from ASN.1): %i", cert_len);
|
||||
} else {
|
||||
PrintAndLogEx(INFO, "Length (included at start of cert slot): %i", cert_len);
|
||||
cert_len_present = true;
|
||||
}
|
||||
cert_len += 2; // Add 2 bytes for the 9000 at the end
|
||||
// Read the entire cert (extended length APDU)
|
||||
aSELECT_CERT[4] = 0x00;
|
||||
aSELECT_CERT[5] = (cert_len >> 8) & 0xff;
|
||||
aSELECT_CERT[6] = cert_len & 0xff;
|
||||
aSELECT_CERT_n = 7;
|
||||
|
||||
res = ExchangeAPDU14a(aSELECT_CERT, aSELECT_CERT_n, activate_field, keep_field_on, response, sizeof(response), &resplen);
|
||||
if (res != PM3_SUCCESS) {
|
||||
PrintAndLogEx(ERR, "Could not read certificate %i (return code %i)", i, res);
|
||||
continue;
|
||||
}
|
||||
|
||||
sw = get_sw(response, resplen);
|
||||
if (sw == ISO7816_OK ) {
|
||||
// save CERT for later
|
||||
uint8_t cert[MAX_CERT_SIZE] = {0};
|
||||
memcpy(cert, response, resplen - 2);
|
||||
|
||||
PrintAndLogEx(INFO, "%s", sprint_hex_inrow(cert+ (cert_len_present ? 2 : 0), resplen - 2));
|
||||
if (parse_certs) {
|
||||
asn1_print(cert+ (cert_len_present ? 2 : 0), cert_len-2, " ");
|
||||
}
|
||||
}
|
||||
} else if ( sw == 0x6f17 ){
|
||||
PrintAndLogEx(INFO, "CERT # %i", i);
|
||||
PrintAndLogEx(INFO, "No certificate in slot %i", i);
|
||||
} else {
|
||||
PrintAndLogEx(ERR, "Could not read certificate %i", i);
|
||||
}
|
||||
}
|
||||
Set_apdu_in_framing(false);
|
||||
|
@ -175,30 +217,28 @@ static int info_hf_tesla(void) {
|
|||
// vehicle public key , 16 byte CHALLENGE
|
||||
// 00112233445566778899AABBCCDDEEFF
|
||||
// 0x51 = 81 dec
|
||||
// param_gethex_to_eol("8011000051 046F08AE62526ABB5690643458152AC963CF5D7C113949F3C2453D1DDC6E4385B430523524045A22F5747BF236F1B5F60F0EA32DC2B8276D75ACDE9813EF77C330 00112233445566778899AABBCCDDEEFF", 0, aAUTH, sizeof(aAUTH), &aAUTH_n);
|
||||
param_gethex_to_eol("8011000051046F08AE62526ABB5690643458152AC963CF5D7C113949F3C2453D1DDC6E4385B430523524045A22F5747BF236F1B5F60F0EA32DC2B8276D75ACDE9813EF77C33000112233445566778899AABBCCDDEEFF", 0, aAUTH, sizeof(aAUTH), &aAUTH_n);
|
||||
// param_gethex_to_eol("8011000051 046F08AE62526ABB5690643458152AC963CF5D7C113949F3C2453D1DDC6E4385B430523524045A22F5747BF236F1B5F60F0EA32DC2B8276D75ACDE9813EF77C330 00112233445566778899AABBCCDDEEFF", 0, aAUTH, sizeof(aAUTH), &aAUTH_n);
|
||||
param_gethex_to_eol("8011000051046F08AE62526ABB5690643458152AC963CF5D7C113949F3C2453D1DDC6E4385B430523524045A22F5747BF236F1B5F60F0EA32DC2B8276D75ACDE9813EF77C33000112233445566778899AABBCCDDEEFF00", 0, aAUTH, sizeof(aAUTH), &aAUTH_n);
|
||||
res = ExchangeAPDU14a(aAUTH, aAUTH_n, activate_field, keep_field_on, response, sizeof(response), &resplen);
|
||||
if (res != PM3_SUCCESS) {
|
||||
DropField();
|
||||
return res;
|
||||
}
|
||||
PrintAndLogEx(ERR, "Could not exchange authentication challenge");
|
||||
} else {
|
||||
|
||||
uint8_t auth[resplen - 2];
|
||||
uint8_t auth[resplen - 2];
|
||||
|
||||
sw = get_sw(response, resplen);
|
||||
if (sw == ISO7816_OK) {
|
||||
// store CHALLENGE for later
|
||||
memcpy(auth, response, sizeof(auth));
|
||||
sw = get_sw(response, resplen);
|
||||
if (sw == ISO7816_OK) {
|
||||
// store CHALLENGE for later
|
||||
memcpy(auth, response, sizeof(auth));
|
||||
}
|
||||
PrintAndLogEx(INFO, "CHALL......... %s", sprint_hex_inrow(auth, sizeof(auth)));
|
||||
}
|
||||
|
||||
keep_field_on = false;
|
||||
DropField();
|
||||
DropField(); // No further interaction with the card is needed
|
||||
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------");
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(INFO, "PUBLIC KEY");
|
||||
for (int i = 0; i < 3; i++) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
PrintAndLogEx(INFO, "%d - %s", i, sprint_hex_inrow(pk[i], 65));
|
||||
}
|
||||
PrintAndLogEx(INFO, "Form factor... %s " NOLF, sprint_hex_inrow(form_factor, sizeof(form_factor)));
|
||||
|
@ -207,16 +247,33 @@ static int info_hf_tesla(void) {
|
|||
|
||||
switch (form_factor_value) {
|
||||
case 0x0001:
|
||||
PrintAndLogEx(NORMAL, "( card )");
|
||||
PrintAndLogEx(NORMAL, "(NXP P60 card)");
|
||||
break;
|
||||
case 0x0002:
|
||||
PrintAndLogEx(NORMAL, "(NXP P71 card)");
|
||||
break;
|
||||
case 0x0021:
|
||||
PrintAndLogEx(NORMAL, "(Model 3 fob without passive entry)");
|
||||
break;
|
||||
case 0x0022:
|
||||
PrintAndLogEx(NORMAL, "( fob )");
|
||||
PrintAndLogEx(NORMAL, "(Model 3 fob with passive entry)");
|
||||
break;
|
||||
case 0x0023:
|
||||
case 0x0025:
|
||||
case 0x0026:
|
||||
PrintAndLogEx(NORMAL, "(Model S fob)");
|
||||
break;
|
||||
case 0x0024:
|
||||
PrintAndLogEx(NORMAL, "(Model X fob)");
|
||||
break;
|
||||
case 0x0031:
|
||||
PrintAndLogEx(NORMAL, "( phone app )");
|
||||
PrintAndLogEx(NORMAL, "(Android phone app with NFC)");
|
||||
break;
|
||||
case 0x0032:
|
||||
PrintAndLogEx(NORMAL, "(iOS phone app with NFC)");
|
||||
break;
|
||||
default:
|
||||
PrintAndLogEx(NORMAL, "( unknown )");
|
||||
PrintAndLogEx(NORMAL, "(Unknown)");
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -224,8 +281,6 @@ static int info_hf_tesla(void) {
|
|||
PrintAndLogEx(INFO, "Version....... %s", sprint_hex_inrow(version, sizeof(version)));
|
||||
}
|
||||
|
||||
PrintAndLogEx(INFO, "CHALL......... %s", sprint_hex_inrow(auth, sizeof(auth)));
|
||||
|
||||
PrintAndLogEx(INFO, "Fingerprint");
|
||||
if ((memcmp(pk[0], pk[1], 65) == 0)) {
|
||||
PrintAndLogEx(INFO, " GaussKey detected");
|
||||
|
@ -244,11 +299,14 @@ static int CmdHFTeslaInfo(const char *Cmd) {
|
|||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_lit0("p", "parse", "Parse the certificates as ASN.1"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
|
||||
bool parse_certs = arg_get_lit(ctx, 1);
|
||||
CLIParserFree(ctx);
|
||||
return info_hf_tesla();
|
||||
return info_hf_tesla(parse_certs);
|
||||
}
|
||||
|
||||
static int CmdHFTeslaList(const char *Cmd) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue