]> git.zerfleddert.de Git - proxmark3-svn/blobdiff - armsrc/epa.c
Add PACE replay functionality
[proxmark3-svn] / armsrc / epa.c
index 0006d59d01c03fd4944caefef829771b1b8068fc..6bd8692ecaa52d53c7c18ea3364acac26fdb670a 100644 (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;
-}
\ No newline at end of file
+}
Impressum, Datenschutz