adapting epa cnonces

This commit is contained in:
iceman1001 2020-08-17 22:06:54 +02:00
commit 6b8c5e0d24
2 changed files with 97 additions and 58 deletions

View file

@ -117,13 +117,10 @@ static int EPA_APDU(uint8_t *apdu, size_t length, uint8_t *response, uint16_t re
switch (iso_type) { switch (iso_type) {
case 'a': case 'a':
return iso14_apdu(apdu, (uint16_t) length, false, response, NULL); return iso14_apdu(apdu, (uint16_t) length, false, response, NULL);
break;
case 'b': case 'b':
return iso14443b_apdu(apdu, length, response, respmaxlen); return iso14443b_apdu(apdu, length, response, respmaxlen);
break;
default: default:
return 0; return 0;
break;
} }
} }
@ -149,9 +146,8 @@ void EPA_Finish(void) {
// TODO: Support elements with long tags (tag is longer than 1 byte) // TODO: Support elements with long tags (tag is longer than 1 byte)
// TODO: Support proprietary PACE domain parameters // TODO: Support proprietary PACE domain parameters
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
size_t EPA_Parse_CardAccess(uint8_t *data, size_t EPA_Parse_CardAccess(uint8_t *data, size_t length, pace_version_info_t *pace_info) {
size_t length,
pace_version_info_t *pace_info) {
size_t index = 0; size_t index = 0;
while (index <= length - 2) { while (index <= length - 2) {
@ -165,19 +161,22 @@ size_t EPA_Parse_CardAccess(uint8_t *data,
index += (data[index - 1] & 0x7F); index += (data[index - 1] & 0x7F);
} }
} }
// OID // OID
else if (data[index] == 0x06) { else if (data[index] == 0x06) {
// is this a PACE OID? // is this a PACE OID?
if (data[index + 1] == 0x0A // length matches if (data[index + 1] == 0x0A // length matches
&& memcmp(data + index + 2, && memcmp(data + index + 2, oid_pace_start, sizeof(oid_pace_start)) == 0 // content matches
oid_pace_start,
sizeof(oid_pace_start)) == 0 // content matches
&& pace_info != NULL) { && pace_info != NULL) {
// first, clear the pace_info struct // first, clear the pace_info struct
memset(pace_info, 0, sizeof(pace_version_info_t)); memset(pace_info, 0, sizeof(pace_version_info_t));
memcpy(pace_info->oid, data + index + 2, sizeof(pace_info->oid)); memcpy(pace_info->oid, data + index + 2, sizeof(pace_info->oid));
// a PACE OID is followed by the version // a PACE OID is followed by the version
index += data[index + 1] + 2; index += data[index + 1] + 2;
if (data[index] == 02 && data[index + 1] == 01) { if (data[index] == 02 && data[index + 1] == 01) {
pace_info->version = data[index + 2]; pace_info->version = data[index + 2];
index += 3; index += 3;
@ -189,6 +188,7 @@ size_t EPA_Parse_CardAccess(uint8_t *data,
pace_info->parameter_id = data[index + 2]; pace_info->parameter_id = data[index + 2];
index += 3; index += 3;
} }
} else { } else {
// skip this OID // skip this OID
index += 2 + data[index + 1]; index += 2 + data[index + 1];
@ -230,6 +230,7 @@ int EPA_Read_CardAccess(uint8_t *buffer, size_t max_length) {
response_apdu, response_apdu,
sizeof(response_apdu) sizeof(response_apdu)
); );
if (rapdu_length < 6 if (rapdu_length < 6
|| response_apdu[rapdu_length - 4] != 0x90 || response_apdu[rapdu_length - 4] != 0x90
|| response_apdu[rapdu_length - 3] != 0x00) { || response_apdu[rapdu_length - 3] != 0x00) {
@ -243,6 +244,7 @@ int EPA_Read_CardAccess(uint8_t *buffer, size_t max_length) {
response_apdu, response_apdu,
sizeof(response_apdu) sizeof(response_apdu)
); );
if (rapdu_length <= 6 if (rapdu_length <= 6
|| response_apdu[rapdu_length - 4] != 0x90 || response_apdu[rapdu_length - 4] != 0x90
|| response_apdu[rapdu_length - 3] != 0x00) { || response_apdu[rapdu_length - 3] != 0x00) {
@ -252,22 +254,22 @@ int EPA_Read_CardAccess(uint8_t *buffer, size_t max_length) {
// copy the content into the buffer // copy the content into the buffer
// length of data available: apdu_length - 4 (ISO frame) - 2 (SW) // length of data available: apdu_length - 4 (ISO frame) - 2 (SW)
size_t to_copy = rapdu_length - 6; size_t len = rapdu_length - 6;
to_copy = to_copy < max_length ? to_copy : max_length; len = len < max_length ? len : max_length;
memcpy(buffer, response_apdu + 2, to_copy); memcpy(buffer, response_apdu + 2, len);
return to_copy; return len;
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Abort helper function for EPA_PACE_Collect_Nonce // Abort helper function for EPA_PACE_Collect_Nonce
// sets relevant data in ack, sends the response // sets relevant data in ack, sends the response
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
static void EPA_PACE_Collect_Nonce_Abort(uint8_t step, int func_return) { static void EPA_PACE_Collect_Nonce_Abort(uint32_t cmd, uint8_t step, int func_return) {
// power down the field // power down the field
EPA_Finish(); EPA_Finish();
// send the USB packet // send the USB packet
reply_mix(CMD_ACK, step, func_return, 0, 0, 0); reply_mix(cmd, step, func_return, 0, 0, 0);
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -287,28 +289,28 @@ void EPA_PACE_Collect_Nonce(PacketCommandNG *c) {
// set up communication // set up communication
int func_return = EPA_Setup(); int func_return = EPA_Setup();
if (func_return != 0) { if (func_return != 0) {
EPA_PACE_Collect_Nonce_Abort(1, func_return); EPA_PACE_Collect_Nonce_Abort(CMD_HF_EPA_COLLECT_NONCE, 1, func_return);
return; return;
} }
// read the CardAccess file // read the CardAccess file
// this array will hold the CardAccess file // this array will hold the CardAccess file
uint8_t card_access[256] = {0}; uint8_t card_access[256] = {0};
int card_access_length = EPA_Read_CardAccess(card_access, 256); int cardlen = EPA_Read_CardAccess(card_access, 256);
// the response has to be at least this big to hold the OID // the response has to be at least this big to hold the OID
if (card_access_length < 18) { if (cardlen < 18) {
EPA_PACE_Collect_Nonce_Abort(2, card_access_length); EPA_PACE_Collect_Nonce_Abort(CMD_HF_EPA_COLLECT_NONCE, 2, cardlen);
return; return;
} }
// this will hold the PACE info of the card // this will hold the PACE info of the card
pace_version_info_t pace_version_info; pace_version_info_t pace_version_info;
// search for the PACE OID // search for the PACE OID
func_return = EPA_Parse_CardAccess(card_access, func_return = EPA_Parse_CardAccess(card_access, cardlen, &pace_version_info);
card_access_length,
&pace_version_info);
if (func_return != 0 || pace_version_info.version == 0) { if (func_return != 0 || pace_version_info.version == 0) {
EPA_PACE_Collect_Nonce_Abort(3, func_return); EPA_PACE_Collect_Nonce_Abort(CMD_HF_EPA_COLLECT_NONCE, 3, func_return);
return; return;
} }
@ -317,25 +319,29 @@ void EPA_PACE_Collect_Nonce(PacketCommandNG *c) {
func_return = EPA_PACE_MSE_Set_AT(pace_version_info, 2); func_return = EPA_PACE_MSE_Set_AT(pace_version_info, 2);
// check if the command succeeded // check if the command succeeded
if (func_return != 0) { if (func_return != 0) {
EPA_PACE_Collect_Nonce_Abort(4, func_return); EPA_PACE_Collect_Nonce_Abort(CMD_HF_EPA_COLLECT_NONCE, 4, func_return);
return; return;
} }
// now get the nonce // now get the nonce
uint8_t nonce[256] = {0}; uint8_t nonce[256] = {0};
uint8_t requested_size = (uint8_t)c->oldarg[0];
func_return = EPA_PACE_Get_Nonce(requested_size, nonce); struct p {
uint32_t m;
} PACKED;
struct p *packet = (struct p*)c->data.asBytes;
func_return = EPA_PACE_Get_Nonce(packet->m, nonce);
// check if the command succeeded // check if the command succeeded
if (func_return < 0) { if (func_return < 0) {
EPA_PACE_Collect_Nonce_Abort(5, func_return); EPA_PACE_Collect_Nonce_Abort(CMD_HF_EPA_COLLECT_NONCE, 5, func_return);
return; return;
} }
// all done, return
EPA_Finish(); EPA_Finish();
// save received information // save received information
reply_mix(CMD_ACK, 0, func_return, 0, nonce, func_return); reply_mix(CMD_HF_EPA_COLLECT_NONCE, 0, func_return, 0, nonce, func_return);
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -347,23 +353,18 @@ void EPA_PACE_Collect_Nonce(PacketCommandNG *c) {
// code on failure. // code on failure.
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
int EPA_PACE_Get_Nonce(uint8_t requested_length, uint8_t *nonce) { int EPA_PACE_Get_Nonce(uint8_t requested_length, uint8_t *nonce) {
// build the APDU // build the APDU
uint8_t apdu[sizeof(apdu_general_authenticate_pace_get_nonce) + 1]; uint8_t apdu[sizeof(apdu_general_authenticate_pace_get_nonce) + 1];
// copy the constant part // copy the constant part
memcpy(apdu, memcpy(apdu, apdu_general_authenticate_pace_get_nonce, sizeof(apdu_general_authenticate_pace_get_nonce));
apdu_general_authenticate_pace_get_nonce,
sizeof(apdu_general_authenticate_pace_get_nonce));
// append Le (requested length + 2 due to tag/length taking 2 bytes) in RAPDU // append Le (requested length + 2 due to tag/length taking 2 bytes) in RAPDU
apdu[sizeof(apdu_general_authenticate_pace_get_nonce)] = requested_length + 4; apdu[sizeof(apdu_general_authenticate_pace_get_nonce)] = requested_length + 4;
// send it
uint8_t response_apdu[262]; uint8_t response_apdu[262];
int send_return = EPA_APDU(apdu, int send_return = EPA_APDU(apdu, sizeof(apdu), response_apdu, sizeof(response_apdu));
sizeof(apdu),
response_apdu,
sizeof(response_apdu)
);
// check if the command succeeded
if (send_return < 6 if (send_return < 6
|| response_apdu[send_return - 4] != 0x90 || response_apdu[send_return - 4] != 0x90
|| response_apdu[send_return - 3] != 0x00) { || response_apdu[send_return - 3] != 0x00) {
@ -393,26 +394,31 @@ int EPA_PACE_Get_Nonce(uint8_t requested_length, uint8_t *nonce) {
int EPA_PACE_MSE_Set_AT(pace_version_info_t pace_version_info, uint8_t password) { int EPA_PACE_MSE_Set_AT(pace_version_info_t pace_version_info, uint8_t password) {
// create the MSE: Set AT APDU // create the MSE: Set AT APDU
uint8_t apdu[23]; uint8_t apdu[23];
// the minimum length (will be increased as more data is added) // the minimum length (will be increased as more data is added)
size_t apdu_length = 20; size_t apdu_length = 20;
// copy the constant part // copy the constant part
memcpy(apdu, memcpy(apdu, apdu_mse_set_at_start, sizeof(apdu_mse_set_at_start));
apdu_mse_set_at_start,
sizeof(apdu_mse_set_at_start));
// type: OID // type: OID
apdu[5] = 0x80; apdu[5] = 0x80;
// length of the OID // length of the OID
apdu[6] = sizeof(pace_version_info.oid); apdu[6] = sizeof(pace_version_info.oid);
// copy the OID // copy the OID
memcpy(apdu + 7, memcpy(apdu + 7, pace_version_info.oid, sizeof(pace_version_info.oid));
pace_version_info.oid,
sizeof(pace_version_info.oid));
// type: password // type: password
apdu[17] = 0x83; apdu[17] = 0x83;
// length: 1 // length: 1
apdu[18] = 1; apdu[18] = 1;
// password // password
apdu[19] = password; apdu[19] = password;
// if standardized domain parameters are used, copy the ID // if standardized domain parameters are used, copy the ID
if (pace_version_info.parameter_id != 0) { if (pace_version_info.parameter_id != 0) {
apdu_length += 3; apdu_length += 3;
@ -423,19 +429,23 @@ int EPA_PACE_MSE_Set_AT(pace_version_info_t pace_version_info, uint8_t password)
// copy the parameter ID // copy the parameter ID
apdu[22] = pace_version_info.parameter_id; apdu[22] = pace_version_info.parameter_id;
} }
// now set Lc to the actual length // now set Lc to the actual length
apdu[4] = apdu_length - 5; apdu[4] = apdu_length - 5;
// send it // send it
uint8_t response_apdu[6]; uint8_t response_apdu[6];
int send_return = EPA_APDU(apdu, int send_return = EPA_APDU(apdu, apdu_length, response_apdu, sizeof(response_apdu));
apdu_length,
response_apdu, Dbprintf("send ret %d bytes", send_return);
sizeof(response_apdu)
); // Dbhexdump(send_return, response_apdu, false);
// check if the command succeeded // check if the command succeeded
if (send_return != 6 if (send_return != 6)
|| response_apdu[send_return - 4] != 0x90 // && response_apdu[send_return - 4] != 0x90
|| response_apdu[send_return - 3] != 0x00) { // || response_apdu[send_return - 3] != 0x00)
{
return 1; return 1;
} }
return 0; return 0;
@ -546,6 +556,6 @@ int EPA_Setup(void) {
iso_type = 'b'; iso_type = 'b';
return 0; return 0;
} }
Dbprintf("No card found."); Dbprintf("No card found");
return 1; return 1;
} }

View file

@ -14,6 +14,7 @@
#include <stdio.h> #include <stdio.h>
#include <inttypes.h> #include <inttypes.h>
#include <stdlib.h> #include <stdlib.h>
#include <ctype.h> // tolower
#include "cmdparser.h" // command_t #include "cmdparser.h" // command_t
#include "commonutil.h" // ARRAYLEN #include "commonutil.h" // ARRAYLEN
@ -23,8 +24,27 @@
static int CmdHelp(const char *Cmd); static int CmdHelp(const char *Cmd);
static int usage_epa_collect(void) {
PrintAndLogEx(NORMAL, "Tries to collect nonces when doing part of PACE protocol.\n"
"\n"
"Usage: hf epa cnonces <m> <n> <d>\n"
"Options:\n"
"\t<m> nonce size\n"
"\t<n> number of nonces to collect\n"
"\t<d> delay between\n"
"\n"
"Example:\n"
_YELLOW_("\thf epa cnonces 4 4 1")
);
return PM3_SUCCESS;
}
// Perform (part of) the PACE protocol // Perform (part of) the PACE protocol
static int CmdHFEPACollectPACENonces(const char *Cmd) { static int CmdHFEPACollectPACENonces(const char *Cmd) {
char cmdp = tolower(param_getchar(Cmd, 0));
if (cmdp == 'h') return usage_epa_collect();
// requested nonce size // requested nonce size
uint32_t m = 0; uint32_t m = 0;
// requested number of Nonces // requested number of Nonces
@ -40,13 +60,20 @@ static int CmdHFEPACollectPACENonces(const char *Cmd) {
PrintAndLogEx(SUCCESS, "Collecting %u %u byte nonces", n, m); PrintAndLogEx(SUCCESS, "Collecting %u %u byte nonces", n, m);
PrintAndLogEx(SUCCESS, "Start: %" PRIu64, msclock() / 1000); PrintAndLogEx(SUCCESS, "Start: %" PRIu64, msclock() / 1000);
// repeat n times
struct p {
uint32_t m;
} PACKED payload;
payload.m = m;
for (uint32_t i = 0; i < n; i++) { for (uint32_t i = 0; i < n; i++) {
// execute PACE // execute PACE
clearCommandBuffer();
SendCommandMIX(CMD_HF_EPA_COLLECT_NONCE, (int)m, 0, 0, NULL, 0);
PacketResponseNG resp; PacketResponseNG resp;
WaitForResponse(CMD_ACK, &resp); clearCommandBuffer();
SendCommandNG(CMD_HF_EPA_COLLECT_NONCE, (uint8_t*)&payload, sizeof(payload));
WaitForResponse(CMD_HF_EPA_COLLECT_NONCE, &resp);
// check if command failed // check if command failed
if (resp.oldarg[0] != 0) { if (resp.oldarg[0] != 0) {
@ -65,6 +92,7 @@ static int CmdHFEPACollectPACENonces(const char *Cmd) {
sleep(d); sleep(d);
} }
} }
PrintAndLogEx(SUCCESS, "End: %" PRIu64, msclock() / 1000); PrintAndLogEx(SUCCESS, "End: %" PRIu64, msclock() / 1000);
return PM3_SUCCESS; return PM3_SUCCESS;
} }
@ -155,6 +183,7 @@ static int CmdHFEPAPACEReplay(const char *Cmd) {
clearCommandBuffer(); clearCommandBuffer();
SendCommandMIX(CMD_HF_EPA_REPLAY, 0, 0, 0, NULL, 0); SendCommandMIX(CMD_HF_EPA_REPLAY, 0, 0, 0, NULL, 0);
WaitForResponse(CMD_ACK, &resp); WaitForResponse(CMD_ACK, &resp);
if (resp.oldarg[0] != 0) { if (resp.oldarg[0] != 0) {
PrintAndLogEx(SUCCESS, "\nPACE replay failed in step %u!", (uint32_t)resp.oldarg[0]); PrintAndLogEx(SUCCESS, "\nPACE replay failed in step %u!", (uint32_t)resp.oldarg[0]);
PrintAndLogEx(SUCCESS, "Measured times:"); PrintAndLogEx(SUCCESS, "Measured times:");