mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-03-17 18:50:32 +08:00
Add PACE replay functionality
This function allows the user to specify APDUs which are sent to a card supporting the PACE protocol. The response times are measured and printed. The code was pulled from the old Google Code repository (branch "epa") and modified to fit into the new code base.
This commit is contained in:
parent
9f9b6b7469
commit
3bb07d96c8
6 changed files with 234 additions and 42 deletions
|
@ -816,6 +816,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]);
|
||||
|
|
|
@ -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);
|
||||
|
|
145
armsrc/epa.c
145
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
#endif /* __EPA_H */
|
||||
|
|
|
@ -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,
|
||||
"<m> <n> <d> Acquire n>0 encrypted PACE nonces of size m>0 with d sec pauses"},
|
||||
{"preplay", CmdHFEPAPACEReplay, 0,
|
||||
"<mse> <get> <map> <pka> <ma> 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -128,6 +128,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
|
||||
|
|
Loading…
Reference in a new issue