mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-03-20 12:07:05 +08:00
Merge pull request #121 from frederikmoellers/master
Add PACE replay functionality
This commit is contained in:
commit
1e1de234ac
6 changed files with 234 additions and 42 deletions
|
@ -764,6 +764,9 @@ void UsbPacketReceived(uint8_t *packet, int len)
|
||||||
case CMD_EPA_PACE_COLLECT_NONCE:
|
case CMD_EPA_PACE_COLLECT_NONCE:
|
||||||
EPA_PACE_Collect_Nonce(c);
|
EPA_PACE_Collect_Nonce(c);
|
||||||
break;
|
break;
|
||||||
|
case CMD_EPA_PACE_REPLAY:
|
||||||
|
EPA_PACE_Replay(c);
|
||||||
|
break;
|
||||||
|
|
||||||
case CMD_READER_MIFARE:
|
case CMD_READER_MIFARE:
|
||||||
ReaderMifare(c->arg[0]);
|
ReaderMifare(c->arg[0]);
|
||||||
|
|
|
@ -160,6 +160,7 @@ void RAMFUNC SniffMifare(uint8_t param);
|
||||||
|
|
||||||
/// epa.h
|
/// epa.h
|
||||||
void EPA_PACE_Collect_Nonce(UsbCommand * c);
|
void EPA_PACE_Collect_Nonce(UsbCommand * c);
|
||||||
|
void EPA_PACE_Replay(UsbCommand *c);
|
||||||
|
|
||||||
// mifarecmd.h
|
// mifarecmd.h
|
||||||
void ReaderMifare(bool first_try);
|
void ReaderMifare(bool first_try);
|
||||||
|
|
117
armsrc/epa.c
117
armsrc/epa.c
|
@ -5,7 +5,7 @@
|
||||||
// at your option, any later version. See the LICENSE.txt file for the text of
|
// at your option, any later version. See the LICENSE.txt file for the text of
|
||||||
// the license.
|
// 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
|
// 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
|
// the card (with iso14443a_select_card etc.). If You want to use these
|
||||||
// functions, You need to do the setup before calling them!
|
// functions, You need to do the setup before calling them!
|
||||||
|
@ -74,6 +74,32 @@ static const uint8_t oid_pace_start[] = {
|
||||||
0x04 // id-PACE
|
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
|
// Closes the communication channel and turns off the field
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
@ -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)
|
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
|
// power down the field
|
||||||
EPA_Finish();
|
EPA_Finish();
|
||||||
|
|
||||||
// send the USB packet
|
// 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
|
// return value of a function
|
||||||
int func_return = 0;
|
int func_return = 0;
|
||||||
|
|
||||||
// // initialize ack with 0s
|
|
||||||
// memset(ack->arg, 0, 12);
|
|
||||||
// memset(ack->d.asBytes, 0, 48);
|
|
||||||
|
|
||||||
// set up communication
|
// set up communication
|
||||||
func_return = EPA_Setup();
|
func_return = EPA_Setup();
|
||||||
if (func_return != 0) {
|
if (func_return != 0) {
|
||||||
|
@ -293,13 +310,11 @@ void EPA_PACE_Collect_Nonce(UsbCommand *c)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// all done, return
|
// all done, return
|
||||||
EPA_Finish();
|
EPA_Finish();
|
||||||
|
|
||||||
// save received information
|
// save received information
|
||||||
// ack->arg[1] = func_return;
|
cmd_send(CMD_ACK,0,func_return,0,nonce,func_return);
|
||||||
// memcpy(ack->d.asBytes, nonce, func_return);
|
|
||||||
cmd_send(CMD_ACK,0,func_return,0,nonce,func_return);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
@ -407,13 +422,79 @@ int EPA_PACE_MSE_Set_AT(pace_version_info_t pace_version_info, uint8_t password)
|
||||||
return 0;
|
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)
|
// Set up a communication channel (Card Select, PPS)
|
||||||
// Returns 0 on success or a non-zero error code on failure
|
// Returns 0 on success or a non-zero error code on failure
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
int EPA_Setup()
|
int EPA_Setup()
|
||||||
{
|
{
|
||||||
|
|
||||||
int return_code = 0;
|
int return_code = 0;
|
||||||
uint8_t uid[10];
|
uint8_t uid[10];
|
||||||
uint8_t pps_response[3];
|
uint8_t pps_response[3];
|
||||||
|
@ -422,20 +503,16 @@ int EPA_Setup()
|
||||||
|
|
||||||
// power up the field
|
// power up the field
|
||||||
iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD);
|
iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD);
|
||||||
|
|
||||||
// select the card
|
// select the card
|
||||||
return_code = iso14443a_select_card(uid, &card_select_info, NULL);
|
return_code = iso14443a_select_card(uid, &card_select_info, NULL);
|
||||||
if (return_code != 1) {
|
if (return_code != 1) {
|
||||||
Dbprintf("Epa: Can't select card");
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// send the PPS request
|
// send the PPS request
|
||||||
ReaderTransmit((uint8_t *)pps, sizeof(pps), NULL);
|
ReaderTransmit((uint8_t *)pps, sizeof(pps), NULL);
|
||||||
return_code = ReaderReceive(pps_response, pps_response_par);
|
return_code = ReaderReceive(pps_response, pps_response_par);
|
||||||
if (return_code != 3 || pps_response[0] != 0xD0) {
|
if (return_code != 3 || pps_response[0] != 0xD0) {
|
||||||
return return_code == 0 ? 2 : return_code;
|
return return_code == 0 ? 2 : return_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
|
@ -19,7 +19,7 @@ typedef struct {
|
||||||
uint8_t parameter_id;
|
uint8_t parameter_id;
|
||||||
} pace_version_info_t;
|
} 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
|
// general functions
|
||||||
void EPA_Finish();
|
void EPA_Finish();
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
//#include "proxusb.h"
|
|
||||||
#include "proxmark3.h"
|
#include "proxmark3.h"
|
||||||
#include "ui.h"
|
#include "ui.h"
|
||||||
#include "cmdparser.h"
|
#include "cmdparser.h"
|
||||||
|
@ -68,6 +68,114 @@ int CmdHFEPACollectPACENonces(const char *Cmd)
|
||||||
return 1;
|
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
|
// UI-related stuff
|
||||||
|
|
||||||
static const command_t CommandTable[] =
|
static const command_t CommandTable[] =
|
||||||
|
@ -75,6 +183,8 @@ static const command_t CommandTable[] =
|
||||||
{"help", CmdHelp, 1, "This help"},
|
{"help", CmdHelp, 1, "This help"},
|
||||||
{"cnonces", CmdHFEPACollectPACENonces, 0,
|
{"cnonces", CmdHFEPACollectPACENonces, 0,
|
||||||
"<m> <n> <d> Acquire n>0 encrypted PACE nonces of size m>0 with d sec pauses"},
|
"<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}
|
{NULL, NULL, 0, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -126,6 +126,7 @@ typedef struct{
|
||||||
#define CMD_READER_LEGIC_RF 0x0388
|
#define CMD_READER_LEGIC_RF 0x0388
|
||||||
#define CMD_WRITER_LEGIC_RF 0x0389
|
#define CMD_WRITER_LEGIC_RF 0x0389
|
||||||
#define CMD_EPA_PACE_COLLECT_NONCE 0x038A
|
#define CMD_EPA_PACE_COLLECT_NONCE 0x038A
|
||||||
|
#define CMD_EPA_PACE_REPLAY 0x038B
|
||||||
|
|
||||||
#define CMD_SNOOP_ICLASS 0x0392
|
#define CMD_SNOOP_ICLASS 0x0392
|
||||||
#define CMD_SIMULATE_TAG_ICLASS 0x0393
|
#define CMD_SIMULATE_TAG_ICLASS 0x0393
|
||||||
|
|
Loading…
Add table
Reference in a new issue