mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-03-19 19:38:52 +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:
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue