diff --git a/armsrc/epa.c b/armsrc/epa.c index 5382b02cd..777549728 100644 --- a/armsrc/epa.c +++ b/armsrc/epa.c @@ -117,13 +117,10 @@ static int EPA_APDU(uint8_t *apdu, size_t length, uint8_t *response, uint16_t re switch (iso_type) { case 'a': return iso14_apdu(apdu, (uint16_t) length, false, response, NULL); - break; case 'b': return iso14443b_apdu(apdu, length, response, respmaxlen); - break; default: 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 proprietary PACE domain parameters //----------------------------------------------------------------------------- -size_t EPA_Parse_CardAccess(uint8_t *data, - size_t length, - pace_version_info_t *pace_info) { +size_t EPA_Parse_CardAccess(uint8_t *data, size_t length, pace_version_info_t *pace_info) { + size_t index = 0; while (index <= length - 2) { @@ -165,19 +161,22 @@ size_t EPA_Parse_CardAccess(uint8_t *data, index += (data[index - 1] & 0x7F); } } + // OID else if (data[index] == 0x06) { // is this a PACE OID? if (data[index + 1] == 0x0A // length matches - && memcmp(data + index + 2, - oid_pace_start, - sizeof(oid_pace_start)) == 0 // content matches + && memcmp(data + index + 2, oid_pace_start, sizeof(oid_pace_start)) == 0 // content matches && pace_info != NULL) { + // first, clear the pace_info struct memset(pace_info, 0, sizeof(pace_version_info_t)); + memcpy(pace_info->oid, data + index + 2, sizeof(pace_info->oid)); + // a PACE OID is followed by the version index += data[index + 1] + 2; + if (data[index] == 02 && data[index + 1] == 01) { pace_info->version = data[index + 2]; index += 3; @@ -189,6 +188,7 @@ size_t EPA_Parse_CardAccess(uint8_t *data, pace_info->parameter_id = data[index + 2]; index += 3; } + } else { // skip this OID index += 2 + data[index + 1]; @@ -230,6 +230,7 @@ int EPA_Read_CardAccess(uint8_t *buffer, size_t max_length) { response_apdu, sizeof(response_apdu) ); + if (rapdu_length < 6 || response_apdu[rapdu_length - 4] != 0x90 || response_apdu[rapdu_length - 3] != 0x00) { @@ -243,6 +244,7 @@ int EPA_Read_CardAccess(uint8_t *buffer, size_t max_length) { response_apdu, sizeof(response_apdu) ); + if (rapdu_length <= 6 || response_apdu[rapdu_length - 4] != 0x90 || 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 // length of data available: apdu_length - 4 (ISO frame) - 2 (SW) - size_t to_copy = rapdu_length - 6; - to_copy = to_copy < max_length ? to_copy : max_length; - memcpy(buffer, response_apdu + 2, to_copy); - return to_copy; + size_t len = rapdu_length - 6; + len = len < max_length ? len : max_length; + memcpy(buffer, response_apdu + 2, len); + return len; } //----------------------------------------------------------------------------- // Abort helper function for EPA_PACE_Collect_Nonce // 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 EPA_Finish(); // 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 int func_return = EPA_Setup(); 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; } // read the CardAccess file // this array will hold the CardAccess file 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 - if (card_access_length < 18) { - EPA_PACE_Collect_Nonce_Abort(2, card_access_length); + if (cardlen < 18) { + EPA_PACE_Collect_Nonce_Abort(CMD_HF_EPA_COLLECT_NONCE, 2, cardlen); return; } // this will hold the PACE info of the card pace_version_info_t pace_version_info; + // search for the PACE OID - func_return = EPA_Parse_CardAccess(card_access, - card_access_length, - &pace_version_info); + func_return = EPA_Parse_CardAccess(card_access, cardlen, &pace_version_info); + 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; } @@ -317,25 +319,29 @@ void EPA_PACE_Collect_Nonce(PacketCommandNG *c) { func_return = EPA_PACE_MSE_Set_AT(pace_version_info, 2); // check if the command succeeded 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; } // now get the nonce 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 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; } - // all done, return EPA_Finish(); // 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. //----------------------------------------------------------------------------- int EPA_PACE_Get_Nonce(uint8_t requested_length, uint8_t *nonce) { + // build the APDU uint8_t apdu[sizeof(apdu_general_authenticate_pace_get_nonce) + 1]; + // copy the constant part - memcpy(apdu, - apdu_general_authenticate_pace_get_nonce, - sizeof(apdu_general_authenticate_pace_get_nonce)); + memcpy(apdu, 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 apdu[sizeof(apdu_general_authenticate_pace_get_nonce)] = requested_length + 4; - // send it uint8_t response_apdu[262]; - int send_return = EPA_APDU(apdu, - sizeof(apdu), - response_apdu, - sizeof(response_apdu) - ); - // check if the command succeeded + int send_return = EPA_APDU(apdu, sizeof(apdu), response_apdu, sizeof(response_apdu)); if (send_return < 6 || response_apdu[send_return - 4] != 0x90 || 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) { // create the MSE: Set AT APDU uint8_t apdu[23]; + // the minimum length (will be increased as more data is added) size_t apdu_length = 20; + // copy the constant part - memcpy(apdu, - apdu_mse_set_at_start, - sizeof(apdu_mse_set_at_start)); + memcpy(apdu, apdu_mse_set_at_start, sizeof(apdu_mse_set_at_start)); + // type: OID apdu[5] = 0x80; + // length of the OID apdu[6] = sizeof(pace_version_info.oid); + // copy the OID - memcpy(apdu + 7, - pace_version_info.oid, - sizeof(pace_version_info.oid)); + memcpy(apdu + 7, pace_version_info.oid, sizeof(pace_version_info.oid)); + // type: password apdu[17] = 0x83; + // length: 1 apdu[18] = 1; + // password apdu[19] = password; + // if standardized domain parameters are used, copy the ID if (pace_version_info.parameter_id != 0) { 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 apdu[22] = pace_version_info.parameter_id; } + // now set Lc to the actual length apdu[4] = apdu_length - 5; + // send it uint8_t response_apdu[6]; - int send_return = EPA_APDU(apdu, - apdu_length, - response_apdu, - sizeof(response_apdu) - ); + int send_return = EPA_APDU(apdu, apdu_length, response_apdu, sizeof(response_apdu)); + + Dbprintf("send ret %d bytes", send_return); + +// Dbhexdump(send_return, response_apdu, false); + // check if the command succeeded - if (send_return != 6 - || response_apdu[send_return - 4] != 0x90 - || response_apdu[send_return - 3] != 0x00) { + if (send_return != 6) +// && response_apdu[send_return - 4] != 0x90 +// || response_apdu[send_return - 3] != 0x00) + { return 1; } return 0; @@ -546,6 +556,6 @@ int EPA_Setup(void) { iso_type = 'b'; return 0; } - Dbprintf("No card found."); + Dbprintf("No card found"); return 1; } diff --git a/client/src/cmdhfepa.c b/client/src/cmdhfepa.c index b7c49bd83..eb4efc9f1 100644 --- a/client/src/cmdhfepa.c +++ b/client/src/cmdhfepa.c @@ -14,6 +14,7 @@ #include #include #include +#include // tolower #include "cmdparser.h" // command_t #include "commonutil.h" // ARRAYLEN @@ -23,8 +24,27 @@ 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 \n" + "Options:\n" + "\t nonce size\n" + "\t number of nonces to collect\n" + "\t delay between\n" + "\n" + "Example:\n" + _YELLOW_("\thf epa cnonces 4 4 1") + ); + return PM3_SUCCESS; +} + // Perform (part of) the PACE protocol static int CmdHFEPACollectPACENonces(const char *Cmd) { + + char cmdp = tolower(param_getchar(Cmd, 0)); + if (cmdp == 'h') return usage_epa_collect(); + // requested nonce size uint32_t m = 0; // 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, "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++) { // execute PACE - clearCommandBuffer(); - SendCommandMIX(CMD_HF_EPA_COLLECT_NONCE, (int)m, 0, 0, NULL, 0); + 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 if (resp.oldarg[0] != 0) { @@ -65,6 +92,7 @@ static int CmdHFEPACollectPACENonces(const char *Cmd) { sleep(d); } } + PrintAndLogEx(SUCCESS, "End: %" PRIu64, msclock() / 1000); return PM3_SUCCESS; } @@ -155,6 +183,7 @@ static int CmdHFEPAPACEReplay(const char *Cmd) { clearCommandBuffer(); SendCommandMIX(CMD_HF_EPA_REPLAY, 0, 0, 0, NULL, 0); WaitForResponse(CMD_ACK, &resp); + if (resp.oldarg[0] != 0) { PrintAndLogEx(SUCCESS, "\nPACE replay failed in step %u!", (uint32_t)resp.oldarg[0]); PrintAndLogEx(SUCCESS, "Measured times:");