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:
Frederik Möllers 2015-06-22 14:20:13 +02:00
parent 9f9b6b7469
commit 3bb07d96c8
6 changed files with 234 additions and 42 deletions

View file

@ -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]);

View file

@ -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);

View file

@ -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;
}
}

View file

@ -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 */

View file

@ -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;
}
}

View file

@ -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