]> git.zerfleddert.de Git - proxmark3-svn/blobdiff - armsrc/epa.c
Merge pull request #281 from brianredbeard/lf-script
[proxmark3-svn] / armsrc / epa.c
index 8ab4311cf3976c0a13801252e7a5fdcf62fee2a3..f434aa3422421c0bdc872c5fc98068b180a548f7 100644 (file)
@@ -5,17 +5,22 @@
 // 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!
 //-----------------------------------------------------------------------------
 
+#include "apps.h"
 #include "iso14443a.h"
-
+#include "iso14443b.h"
 #include "epa.h"
+#include "cmd.h"
+#include "fpgaloader.h"
+#include "string.h"
+#include "util.h"
 
-// Protocol and Parameter Selection Request
+// Protocol and Parameter Selection Request for ISO 14443 type A cards
 // use regular (1x) speed in both directions
 // CRC is already included
 static const uint8_t pps[] = {0xD0, 0x11, 0x00, 0x52, 0xA6};
@@ -74,6 +79,54 @@ 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];
+
+// type of card (ISO 14443 A or B)
+static char iso_type = 0;
+
+//-----------------------------------------------------------------------------
+// Wrapper for sending APDUs to type A and B cards
+//-----------------------------------------------------------------------------
+int EPA_APDU(uint8_t *apdu, size_t length, uint8_t *response)
+{
+       switch(iso_type)
+       {
+               case 'a':
+                       return iso14_apdu(apdu, (uint16_t) length, response);
+                       break;
+               case 'b':
+                       return iso14443b_apdu(apdu, length, response);
+                       break;
+               default:
+                       return 0;
+                       break;
+       }
+}
+
 //-----------------------------------------------------------------------------
 // Closes the communication channel and turns off the field
 //-----------------------------------------------------------------------------
@@ -81,6 +134,7 @@ void EPA_Finish()
 {
        FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
        LEDsoff();
+       iso_type = 0;
 }
 
 //-----------------------------------------------------------------------------
@@ -101,16 +155,16 @@ 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
                if (data[index] == 0x31 || data[index] == 0x30) {
                        // enter the set (skip tag + length)
                        index += 2;
-                       // extended length
+                       // check for extended length
                        if ((data[index - 1] & 0x80) != 0) {
-                               index += (data[index] & 0x7F);
+                               index += (data[index-1] & 0x7F);
                        }
                }
                // OID
@@ -158,7 +212,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,29 +230,31 @@ 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,
+       rapdu_length = EPA_APDU((uint8_t *)apdu_select_binary_cardaccess,
                                  sizeof(apdu_select_binary_cardaccess),
                                  response_apdu);
-       if (rapdu_length != 6
+       if (rapdu_length < 6
            || response_apdu[rapdu_length - 4] != 0x90
            || response_apdu[rapdu_length - 3] != 0x00)
        {
+               DbpString("Failed to select EF.CardAccess!");
                return -1;
        }
-       
+
        // read the file
-       rapdu_length = iso14_apdu((uint8_t *)apdu_read_binary,
+       rapdu_length = EPA_APDU((uint8_t *)apdu_read_binary,
                                  sizeof(apdu_read_binary),
                                  response_apdu);
        if (rapdu_length <= 6
            || response_apdu[rapdu_length - 4] != 0x90
            || response_apdu[rapdu_length - 3] != 0x00)
        {
+               Dbprintf("Failed to read EF.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;
@@ -211,24 +267,19 @@ int EPA_Read_CardAccess(uint8_t *buffer, size_t max_length)
 // Abort helper function for EPA_PACE_Collect_Nonce
 // sets relevant data in ack, sends the response
 //-----------------------------------------------------------------------------
-static void EPA_PACE_Collect_Nonce_Abort(UsbCommand *ack, uint8_t step, int func_return)
+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;
-       
-       // send the USB packet
-       UsbSendPacket((void *)ack, sizeof(UsbCommand));
-
        // power down the field
        EPA_Finish();
+
+       // send the USB packet
+       cmd_send(CMD_ACK,step,func_return,0,0,0);
 }
 
 //-----------------------------------------------------------------------------
 // Acquire one encrypted PACE nonce
 //-----------------------------------------------------------------------------
-void EPA_PACE_Collect_Nonce(UsbCommand *c, UsbCommand *ack)
+void EPA_PACE_Collect_Nonce(UsbCommand *c)
 {
        /*
         * ack layout:
@@ -242,29 +293,22 @@ void EPA_PACE_Collect_Nonce(UsbCommand *c, UsbCommand *ack)
         */
 
        // return value of a function
-       int func_return;
+       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(ack, 1, func_return);
+               EPA_PACE_Collect_Nonce_Abort(1, func_return);
                return;
        }
 
-       // increase the timeout (at least some cards really do need this!)
-       iso14a_set_timeout(0x0002FFFF);
-       
        // read the CardAccess file
        // this array will hold the CardAccess file
        uint8_t card_access[256] = {0};
        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) {
-               EPA_PACE_Collect_Nonce_Abort(ack, 2, card_access_length);
+               EPA_PACE_Collect_Nonce_Abort(2, card_access_length);
                return;
        }
 
@@ -275,14 +319,14 @@ void EPA_PACE_Collect_Nonce(UsbCommand *c, UsbCommand *ack)
                                           card_access_length,
                                           &pace_version_info);
        if (func_return != 0 || pace_version_info.version == 0) {
-               EPA_PACE_Collect_Nonce_Abort(ack, 3, func_return);
+               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];
@@ -290,17 +334,15 @@ void EPA_PACE_Collect_Nonce(UsbCommand *c, UsbCommand *ack)
        // check if the command succeeded
        if (func_return < 0)
        {
-               EPA_PACE_Collect_Nonce_Abort(ack, 4, func_return);
+               EPA_PACE_Collect_Nonce_Abort(4, func_return);
                return;
        }
-       
-       // save received information
-       ack->arg[1] = func_return;
-       memcpy(ack->d.asBytes, nonce, func_return);
 
        // all done, return
        EPA_Finish();
-       UsbSendPacket((void *)ack, sizeof(UsbCommand));
+
+       // save received information
+       cmd_send(CMD_ACK,0,func_return,0,nonce,func_return);
 }
 
 //-----------------------------------------------------------------------------
@@ -321,10 +363,10 @@ 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,
+       int send_return = EPA_APDU(apdu,
                                     sizeof(apdu),
                                     response_apdu);
        // check if the command succeeded
@@ -334,7 +376,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)
        {
@@ -349,7 +391,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;
 }
 
@@ -395,7 +437,7 @@ int EPA_PACE_MSE_Set_AT(pace_version_info_t pace_version_info, uint8_t password)
        apdu[4] = apdu_length - 5;
        // send it
        uint8_t response_apdu[6];
-       int send_return = iso14_apdu(apdu,
+       int send_return = EPA_APDU(apdu,
                                     apdu_length,
                                     response_apdu);
        // check if the command succeeded
@@ -408,34 +450,109 @@ 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;
+       }
+
+       // 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 = EPA_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()
 {
-       // return code
        int return_code = 0;
-       // card UID
-       uint8_t uid[8];
-       // card select information
+       uint8_t uid[10];
+       uint8_t pps_response[3];
+       uint8_t pps_response_par[1];
        iso14a_card_select_t card_select_info;
-       // power up the field
-       iso14443a_setup();
 
+       // first, look for type A cards
+       // 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) {
-               return 1;
+       return_code = iso14443a_select_card(uid, &card_select_info, NULL, true, 0);
+       if (return_code == 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;
+               }
+               Dbprintf("ISO 14443 Type A");
+               iso_type = 'a';
+               return 0;
        }
 
-       // send the PPS request
-       ReaderTransmit((uint8_t *)pps, sizeof(pps));
-       uint8_t pps_response[3];
-       return_code = ReaderReceive(pps_response);
-       if (return_code != 3 || pps_response[0] != 0xD0) {
-               return return_code == 0 ? 2 : return_code;
+       // if we're here, there is no type A card, so we look for type B
+       // power up the field
+       iso14443b_setup();
+       // select the card
+       return_code = iso14443b_select_card();
+       if (return_code == 1) {
+               Dbprintf("ISO 14443 Type B");
+               iso_type = 'b';
+               return 0;
        }
-       
-       return 0;
-}
\ No newline at end of file
+       Dbprintf("No card found.");
+       return 1;
+}
Impressum, Datenschutz