From d0f3338e0c168f44512cec3c225ebc5648e68363 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 23 Jun 2015 22:30:18 +0200 Subject: [PATCH 1/1] ADD: PACE functionality ref: https://github.com/Proxmark/proxmark3/pull/121 FIX: some missing usb_commands for EPA. --- armsrc/appmain.c | 3 + armsrc/apps.h | 1 + armsrc/epa.c | 122 +++++++++++++++++++++++++++-------- armsrc/epa.h | 4 +- client/cmdhfepa.c | 116 ++++++++++++++++++++++++++++++++- client/hid-flasher/usb_cmd.h | 1 + client/lualibs/commands.lua | 2 +- include/usb_cmd.h | 1 + 8 files changed, 218 insertions(+), 32 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 9db69f0c..035b11c0 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -764,6 +764,9 @@ void UsbPacketReceived(uint8_t *packet, int len) case CMD_EPA_PACE_COLLECT_NONCE: EPA_PACE_Collect_Nonce(c); break; + case CMD_EPA_PACE_REPLAY: + EPA_PACE_Replay(c); + break; case CMD_READER_MIFARE: ReaderMifare(c->arg[0]); diff --git a/armsrc/apps.h b/armsrc/apps.h index 83eb4141..404c2681 100644 --- a/armsrc/apps.h +++ b/armsrc/apps.h @@ -169,6 +169,7 @@ void RAMFUNC SniffMifare(uint8_t param); /// epa.h void EPA_PACE_Collect_Nonce(UsbCommand * c); +void EPA_PACE_Replay(UsbCommand *c); // mifarecmd.h void ReaderMifare(bool first_try); diff --git a/armsrc/epa.c b/armsrc/epa.c index 9012bf11..68061512 100644 --- a/armsrc/epa.c +++ b/armsrc/epa.c @@ -5,7 +5,7 @@ // at your option, any later version. See the LICENSE.txt file for the text of // the license. //----------------------------------------------------------------------------- -// Routines to support the German eletronic "Personalausweis" (ID card) +// Routines to support the German electronic "Personalausweis" (ID card) // Note that the functions which do not implement USB commands do NOT initialize // the card (with iso14443a_select_card etc.). If You want to use these // functions, You need to do the setup before calling them! @@ -13,8 +13,7 @@ #include "iso14443a.h" #include "epa.h" -#include "../common/cmd.h" - +#include "cmd.h" // Protocol and Parameter Selection Request // use regular (1x) speed in both directions @@ -75,6 +74,32 @@ static const uint8_t oid_pace_start[] = { 0x04 // id-PACE }; +// APDUs for replaying: +// MSE: Set AT (initiate PACE) +static uint8_t apdu_replay_mse_set_at_pace[41]; +// General Authenticate (Get Nonce) +static uint8_t apdu_replay_general_authenticate_pace_get_nonce[8]; +// General Authenticate (Map Nonce) +static uint8_t apdu_replay_general_authenticate_pace_map_nonce[75]; +// General Authenticate (Mutual Authenticate) +static uint8_t apdu_replay_general_authenticate_pace_mutual_authenticate[75]; +// General Authenticate (Perform Key Agreement) +static uint8_t apdu_replay_general_authenticate_pace_perform_key_agreement[18]; +// pointers to the APDUs (for iterations) +static struct { + uint8_t len; + uint8_t *data; +} const apdus_replay[] = { + {sizeof(apdu_replay_mse_set_at_pace), apdu_replay_mse_set_at_pace}, + {sizeof(apdu_replay_general_authenticate_pace_get_nonce), apdu_replay_general_authenticate_pace_get_nonce}, + {sizeof(apdu_replay_general_authenticate_pace_map_nonce), apdu_replay_general_authenticate_pace_map_nonce}, + {sizeof(apdu_replay_general_authenticate_pace_mutual_authenticate), apdu_replay_general_authenticate_pace_mutual_authenticate}, + {sizeof(apdu_replay_general_authenticate_pace_perform_key_agreement), apdu_replay_general_authenticate_pace_perform_key_agreement} +}; + +// lengths of the replay APDUs +static uint8_t apdu_lengths_replay[5]; + //----------------------------------------------------------------------------- // Closes the communication channel and turns off the field //----------------------------------------------------------------------------- @@ -216,11 +241,6 @@ int EPA_Read_CardAccess(uint8_t *buffer, size_t max_length) //----------------------------------------------------------------------------- static void EPA_PACE_Collect_Nonce_Abort(uint8_t step, int func_return) { -// // step in which the failure occured -// ack->arg[0] = step; -// // last return code -// ack->arg[1] = func_return; - // power down the field EPA_Finish(); @@ -247,15 +267,10 @@ void EPA_PACE_Collect_Nonce(UsbCommand *c) // return value of a function int func_return = 0; -// // initialize ack with 0s -// memset(ack->arg, 0, 12); -// memset(ack->d.asBytes, 0, 48); - // set up communication func_return = EPA_Setup(); if (func_return != 0) { EPA_PACE_Collect_Nonce_Abort(1, func_return); - Dbprintf("epa: setup fucked up! %d", func_return); return; } @@ -265,13 +280,10 @@ void EPA_PACE_Collect_Nonce(UsbCommand *c) int card_access_length = EPA_Read_CardAccess(card_access, 256); // the response has to be at least this big to hold the OID if (card_access_length < 18) { - Dbprintf("epa: Too small!"); EPA_PACE_Collect_Nonce_Abort(2, card_access_length); return; } - Dbprintf("epa: foo!"); - // this will hold the PACE info of the card pace_version_info_t pace_version_info; // search for the PACE OID @@ -283,8 +295,6 @@ void EPA_PACE_Collect_Nonce(UsbCommand *c) return; } - Dbprintf("epa: bar!"); - // initiate the PACE protocol // use the CAN for the password since that doesn't change func_return = EPA_PACE_MSE_Set_AT(pace_version_info, 2); @@ -304,8 +314,6 @@ void EPA_PACE_Collect_Nonce(UsbCommand *c) EPA_Finish(); // save received information -// ack->arg[1] = func_return; -// memcpy(ack->d.asBytes, nonce, func_return); cmd_send(CMD_ACK,0,func_return,0,nonce,func_return); } @@ -414,13 +422,79 @@ int EPA_PACE_MSE_Set_AT(pace_version_info_t pace_version_info, uint8_t password) return 0; } +//----------------------------------------------------------------------------- +// Perform the PACE protocol by replaying given APDUs +//----------------------------------------------------------------------------- +void EPA_PACE_Replay(UsbCommand *c) +{ + uint32_t timings[sizeof(apdu_lengths_replay) / sizeof(apdu_lengths_replay[0])] = {0}; + + // if an APDU has been passed, save it + if (c->arg[0] != 0) { + // make sure it's not too big + if(c->arg[2] > apdus_replay[c->arg[0] - 1].len) + { + cmd_send(CMD_ACK, 1, 0, 0, NULL, 0); + } + memcpy(apdus_replay[c->arg[0] - 1].data + c->arg[1], + c->d.asBytes, + c->arg[2]); + // save/update APDU length + if (c->arg[1] == 0) { + apdu_lengths_replay[c->arg[0] - 1] = c->arg[2]; + } else { + apdu_lengths_replay[c->arg[0] - 1] += c->arg[2]; + } + cmd_send(CMD_ACK, 0, 0, 0, NULL, 0); + return; + } + + // return value of a function + int func_return; + + // set up communication + func_return = EPA_Setup(); + if (func_return != 0) { + EPA_Finish(); + cmd_send(CMD_ACK, 2, func_return, 0, NULL, 0); + return; + } + + // increase the timeout (at least some cards really do need this!)///////////// + // iso14a_set_timeout(0x0003FFFF); + + // response APDU + uint8_t response_apdu[300] = {0}; + + // now replay the data and measure the timings + for (int i = 0; i < sizeof(apdu_lengths_replay); i++) { + StartCountUS(); + func_return = iso14_apdu(apdus_replay[i].data, + apdu_lengths_replay[i], + response_apdu); + timings[i] = GetCountUS(); + // every step but the last one should succeed + if (i < sizeof(apdu_lengths_replay) - 1 + && (func_return < 6 + || response_apdu[func_return - 4] != 0x90 + || response_apdu[func_return - 3] != 0x00)) + { + EPA_Finish(); + cmd_send(CMD_ACK, 3 + i, func_return, 0, timings, 20); + return; + } + } + EPA_Finish(); + cmd_send(CMD_ACK,0,0,0,timings,20); + return; +} + //----------------------------------------------------------------------------- // Set up a communication channel (Card Select, PPS) // Returns 0 on success or a non-zero error code on failure //----------------------------------------------------------------------------- int EPA_Setup() { - int return_code = 0; uint8_t uid[10]; uint8_t pps_response[3]; @@ -429,20 +503,16 @@ int EPA_Setup() // power up the field iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD); - // select the card return_code = iso14443a_select_card(uid, &card_select_info, NULL); if (return_code != 1) { - Dbprintf("Epa: Can't select card"); return 1; } - // send the PPS request ReaderTransmit((uint8_t *)pps, sizeof(pps), NULL); return_code = ReaderReceive(pps_response, pps_response_par); if (return_code != 3 || pps_response[0] != 0xD0) { return return_code == 0 ? 2 : return_code; } - return 0; -} \ No newline at end of file +} diff --git a/armsrc/epa.h b/armsrc/epa.h index 730652b7..0c580205 100644 --- a/armsrc/epa.h +++ b/armsrc/epa.h @@ -19,7 +19,7 @@ typedef struct { uint8_t parameter_id; } pace_version_info_t; -// note: EPA_PACE_GetNonce is declared in apps.h +// note: EPA_PACE_Collect_Nonce is declared in apps.h // general functions void EPA_Finish(); @@ -33,4 +33,4 @@ int EPA_Setup(); int EPA_PACE_MSE_Set_AT(pace_version_info_t pace_version_info, uint8_t password); int EPA_PACE_Get_Nonce(uint8_t requested_length, uint8_t *nonce); -#endif /* __EPA_H */ \ No newline at end of file +#endif /* __EPA_H */ diff --git a/client/cmdhfepa.c b/client/cmdhfepa.c index afcdb0fa..44a539c8 100644 --- a/client/cmdhfepa.c +++ b/client/cmdhfepa.c @@ -9,11 +9,11 @@ //----------------------------------------------------------------------------- #include "util.h" -//#include "proxusb.h" + #include "proxmark3.h" #include "ui.h" #include "cmdparser.h" -#include "../include/common.h" +#include "common.h" #include "cmdmain.h" #include "sleep.h" #include "cmdhfepa.h" @@ -68,6 +68,114 @@ int CmdHFEPACollectPACENonces(const char *Cmd) return 1; } +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////The commands lie below here///////////////////////////////////////////////////////////////////////////////////////// + +// perform the PACE protocol by replaying APDUs +int CmdHFEPAPACEReplay(const char *Cmd) +{ + // the 4 APDUs which are replayed + their lengths + uint8_t msesa_apdu[41], gn_apdu[8], map_apdu[75]; + uint8_t pka_apdu[75], ma_apdu[18], apdu_lengths[5] = {0}; + // pointers to the arrays to be able to iterate + uint8_t *apdus[] = {msesa_apdu, gn_apdu, map_apdu, pka_apdu, ma_apdu}; + + // usage message + static const char const *usage_msg = + "Please specify 5 APDUs separated by spaces. " + "Example:\n preplay 0022C1A4 1068000000 1086000002 1234ABCDEF 1A2B3C4D"; + + // Proxmark response + UsbCommand resp; + + int skip = 0, skip_add = 0, scan_return = 0; + // for each APDU + for (int i = 0; i < sizeof(apdu_lengths); i++) { + // scan to next space or end of string + while (Cmd[skip] != ' ' && Cmd[skip] != '\0') { + // convert + scan_return = sscanf(Cmd + skip, "%2X%n", + (unsigned int *) (apdus[i] + apdu_lengths[i]), + &skip_add); + if (scan_return < 1) { + PrintAndLog((char *)usage_msg); + PrintAndLog("Not enough APDUs! Try again!"); + return 0; + } + skip += skip_add; + apdu_lengths[i]++; + } + + // break on EOF + if (Cmd[skip] == '\0') { + if (i < sizeof(apdu_lengths) - 1) { + + PrintAndLog((char *)usage_msg); + return 0; + } + break; + } + // skip the space + skip++; + } + + // transfer the APDUs to the Proxmark + UsbCommand usb_cmd; + usb_cmd.cmd = CMD_EPA_PACE_REPLAY; + for (int i = 0; i < sizeof(apdu_lengths); i++) { + // APDU number + usb_cmd.arg[0] = i + 1; + // transfer the APDU in several parts if necessary + for (int j = 0; j * sizeof(usb_cmd.d.asBytes) < apdu_lengths[i]; j++) { + // offset into the APDU + usb_cmd.arg[1] = j * sizeof(usb_cmd.d.asBytes); + // amount of data in this packet + int packet_length = apdu_lengths[i] - (j * sizeof(usb_cmd.d.asBytes)); + if (packet_length > sizeof(usb_cmd.d.asBytes)) { + packet_length = sizeof(usb_cmd.d.asBytes); + } + usb_cmd.arg[2] = packet_length; + + memcpy(usb_cmd.d.asBytes, // + (j * sizeof(usb_cmd.d.asBytes)), + apdus[i] + (j * sizeof(usb_cmd.d.asBytes)), + packet_length); + SendCommand(&usb_cmd); + WaitForResponse(CMD_ACK, &resp); + if (resp.arg[0] != 0) { + PrintAndLog("Transfer of APDU #%d Part %d failed!", i, j); + return 0; + } + } + } + + // now perform the replay + usb_cmd.arg[0] = 0; + SendCommand(&usb_cmd); + WaitForResponse(CMD_ACK, &resp); + if (resp.arg[0] != 0) { + PrintAndLog("\nPACE replay failed in step %u!", (uint32_t)resp.arg[0]); + PrintAndLog("Measured times:"); + PrintAndLog("MSE Set AT: %u us", resp.d.asDwords[0]); + PrintAndLog("GA Get Nonce: %u us", resp.d.asDwords[1]); + PrintAndLog("GA Map Nonce: %u us", resp.d.asDwords[2]); + PrintAndLog("GA Perform Key Agreement: %u us", resp.d.asDwords[3]); + PrintAndLog("GA Mutual Authenticate: %u us", resp.d.asDwords[4]); + } else { + PrintAndLog("PACE replay successfull!"); + PrintAndLog("MSE Set AT: %u us", resp.d.asDwords[0]); + PrintAndLog("GA Get Nonce: %u us", resp.d.asDwords[1]); + PrintAndLog("GA Map Nonce: %u us", resp.d.asDwords[2]); + PrintAndLog("GA Perform Key Agreement: %u us", resp.d.asDwords[3]); + PrintAndLog("GA Mutual Authenticate: %u us", resp.d.asDwords[4]); + } + + + return 1; +} + +////////////////////////////////The new commands lie above here///////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // UI-related stuff static const command_t CommandTable[] = @@ -75,6 +183,8 @@ static const command_t CommandTable[] = {"help", CmdHelp, 1, "This help"}, {"cnonces", CmdHFEPACollectPACENonces, 0, " Acquire n>0 encrypted PACE nonces of size m>0 with d sec pauses"}, + {"preplay", CmdHFEPAPACEReplay, 0, + " Perform PACE protocol by replaying given APDUs"}, {NULL, NULL, 0, NULL} }; @@ -92,4 +202,4 @@ int CmdHFEPA(const char *Cmd) // parse CmdsParse(CommandTable, Cmd); return 0; -} \ No newline at end of file +} diff --git a/client/hid-flasher/usb_cmd.h b/client/hid-flasher/usb_cmd.h index c5b91f99..1881b561 100644 --- a/client/hid-flasher/usb_cmd.h +++ b/client/hid-flasher/usb_cmd.h @@ -115,6 +115,7 @@ typedef struct { #define CMD_READER_LEGIC_RF 0x0388 #define CMD_WRITER_LEGIC_RF 0x0389 #define CMD_EPA_PACE_COLLECT_NONCE 0x038A +#define CMD_EPA_PACE_REPLAY 0x038B #define CMD_SNOOP_ICLASS 0x0392 #define CMD_SIMULATE_TAG_ICLASS 0x0393 diff --git a/client/lualibs/commands.lua b/client/lualibs/commands.lua index fd186fb7..239884ab 100644 --- a/client/lualibs/commands.lua +++ b/client/lualibs/commands.lua @@ -84,7 +84,7 @@ local _commands = { CMD_READER_LEGIC_RF = 0x0388, CMD_WRITER_LEGIC_RF = 0x0389, CMD_EPA_PACE_COLLECT_NONCE = 0x038A, - --//CMD_EPA_ = 0x038B, + CMD_EPA_PACE_REPLAY = 0x038B, CMD_SNOOP_ICLASS = 0x0392, CMD_SIMULATE_TAG_ICLASS = 0x0393, diff --git a/include/usb_cmd.h b/include/usb_cmd.h index 2080f2de..9b525752 100644 --- a/include/usb_cmd.h +++ b/include/usb_cmd.h @@ -126,6 +126,7 @@ typedef struct{ #define CMD_READER_LEGIC_RF 0x0388 #define CMD_WRITER_LEGIC_RF 0x0389 #define CMD_EPA_PACE_COLLECT_NONCE 0x038A +#define CMD_EPA_PACE_REPLAY 0x038B #define CMD_SNOOP_ICLASS 0x0392 #define CMD_SIMULATE_TAG_ICLASS 0x0393 -- 2.39.5