diff --git a/armsrc/appmain.c b/armsrc/appmain.c index fb3c0f18..152f8a3e 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 542f3a65..a4144a91 100644 --- a/armsrc/apps.h +++ b/armsrc/apps.h @@ -160,6 +160,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 0006d59d..6bd8692e 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! @@ -74,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 //----------------------------------------------------------------------------- @@ -101,7 +127,7 @@ size_t EPA_Parse_CardAccess(uint8_t *data, pace_version_info_t *pace_info) { size_t index = 0; - + while (index <= length - 2) { // determine type of element // SET or SEQUENCE @@ -158,7 +184,7 @@ size_t EPA_Parse_CardAccess(uint8_t *data, index += 2 + data[index + 1]; } } - + // TODO: We should check whether we reached the end in error, but for that // we need a better parser (e.g. with states like IN_SET or IN_PACE_INFO) return 0; @@ -176,7 +202,7 @@ int EPA_Read_CardAccess(uint8_t *buffer, size_t max_length) // we reserve 262 bytes here just to be safe (256-byte APDU + SW + ISO frame) uint8_t response_apdu[262]; int rapdu_length = 0; - + // select the file EF.CardAccess rapdu_length = iso14_apdu((uint8_t *)apdu_select_binary_cardaccess, sizeof(apdu_select_binary_cardaccess), @@ -188,7 +214,7 @@ int EPA_Read_CardAccess(uint8_t *buffer, size_t max_length) Dbprintf("epa - no select cardaccess"); return -1; } - + // read the file rapdu_length = iso14_apdu((uint8_t *)apdu_read_binary, sizeof(apdu_read_binary), @@ -200,7 +226,7 @@ int EPA_Read_CardAccess(uint8_t *buffer, size_t max_length) Dbprintf("epa - no read cardaccess"); return -1; } - + // copy the content into the buffer // length of data available: apdu_length - 4 (ISO frame) - 2 (SW) size_t to_copy = rapdu_length - 6; @@ -215,16 +241,11 @@ 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(); - + // send the USB packet - cmd_send(CMD_ACK,step,func_return,0,0,0); + cmd_send(CMD_ACK,step,func_return,0,0,0); } //----------------------------------------------------------------------------- @@ -246,10 +267,6 @@ 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) { @@ -277,11 +294,11 @@ void EPA_PACE_Collect_Nonce(UsbCommand *c) EPA_PACE_Collect_Nonce_Abort(3, func_return); return; } - + // 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); - + // now get the nonce uint8_t nonce[256] = {0}; uint8_t requested_size = (uint8_t)c->arg[0]; @@ -292,14 +309,12 @@ void EPA_PACE_Collect_Nonce(UsbCommand *c) EPA_PACE_Collect_Nonce_Abort(4, func_return); return; } - - // all done, return + + // all done, return 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); + cmd_send(CMD_ACK,0,func_return,0,nonce,func_return); } //----------------------------------------------------------------------------- @@ -320,7 +335,7 @@ int EPA_PACE_Get_Nonce(uint8_t requested_length, uint8_t *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 = iso14_apdu(apdu, @@ -333,7 +348,7 @@ int EPA_PACE_Get_Nonce(uint8_t requested_length, uint8_t *nonce) { return -1; } - + // if there is no nonce in the RAPDU, return here if (send_return < 10) { @@ -348,7 +363,7 @@ int EPA_PACE_Get_Nonce(uint8_t requested_length, uint8_t *nonce) } // copy the nonce memcpy(nonce, response_apdu + 6, nonce_length); - + return nonce_length; } @@ -407,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]; @@ -422,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 3286ceb9..e9c63f20 100644 --- a/client/cmdhfepa.c +++ b/client/cmdhfepa.c @@ -9,7 +9,7 @@ //----------------------------------------------------------------------------- #include "util.h" -//#include "proxusb.h" + #include "proxmark3.h" #include "ui.h" #include "cmdparser.h" @@ -29,9 +29,9 @@ int CmdHFEPACollectPACENonces(const char *Cmd) unsigned int n = 0; // delay between requests unsigned int d = 0; - + sscanf(Cmd, "%u %u %u", &m, &n, &d); - + // values are expected to be > 0 m = m > 0 ? m : 1; n = n > 0 ? n : 1; @@ -44,7 +44,7 @@ int CmdHFEPACollectPACENonces(const char *Cmd) UsbCommand c = {CMD_EPA_PACE_COLLECT_NONCE, {(int)m, 0, 0}}; SendCommand(&c); UsbCommand resp; - + WaitForResponse(CMD_ACK,&resp); // check if command failed @@ -68,13 +68,123 @@ 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[] = +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/include/usb_cmd.h b/include/usb_cmd.h index 132e1805..524554e9 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