FIX:  some missing usb_commands for EPA.
This commit is contained in:
iceman1001 2015-06-23 22:30:18 +02:00
commit d0f3338e0c
8 changed files with 218 additions and 32 deletions

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!
@ -13,8 +13,7 @@
#include "iso14443a.h"
#include "epa.h"
#include "../common/cmd.h"
#include "cmd.h"
// Protocol and Parameter Selection Request
// use regular (1x) speed in both directions
@ -75,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
//-----------------------------------------------------------------------------
@ -216,11 +241,6 @@ 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();
@ -247,15 +267,10 @@ 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) {
EPA_PACE_Collect_Nonce_Abort(1, func_return);
Dbprintf("epa: setup fucked up! %d", func_return);
return;
}
@ -265,13 +280,10 @@ void EPA_PACE_Collect_Nonce(UsbCommand *c)
int card_access_length = EPA_Read_CardAccess(card_access, 256);
// the response has to be at least this big to hold the OID
if (card_access_length < 18) {
Dbprintf("epa: Too small!");
EPA_PACE_Collect_Nonce_Abort(2, card_access_length);
return;
}
Dbprintf("epa: foo!");
// this will hold the PACE info of the card
pace_version_info_t pace_version_info;
// search for the PACE OID
@ -283,8 +295,6 @@ void EPA_PACE_Collect_Nonce(UsbCommand *c)
return;
}
Dbprintf("epa: bar!");
// 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);
@ -304,8 +314,6 @@ void EPA_PACE_Collect_Nonce(UsbCommand *c)
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);
}
@ -414,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];
@ -429,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;
}
}