mirror of
https://github.com/Proxmark/proxmark3.git
synced 2025-08-14 10:36:58 -07: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
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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue