Merge pull request #121 from frederikmoellers/master

Add PACE replay functionality
This commit is contained in:
Martin Holst Swende
2015-06-23 22:23:08 +02:00
6 changed files with 234 additions and 42 deletions

View File

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

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