Просмотр исходного кода

Optimise picopass crypto to fix timing issues with newer readers. (#34)

Tiernan 2 лет назад
Родитель
Сommit
7b3170a1cd

+ 3 - 2
application.fam

@@ -10,15 +10,16 @@ App(
     ],
     stack_size=4 * 1024,
     fap_description="App to communicate with NFC tags using the PicoPass(iClass) format",
-    fap_version="1.3",
+    fap_version="1.4",
     fap_icon="125_10px.png",
     fap_category="NFC",
     fap_libs=["mbedtls"],
     fap_private_libs=[
         Lib(
             name="loclass",
+            cflags=["-O3"],
         ),
     ],
     fap_icon_assets="icons",
-    fap_file_assets="files"
+    fap_file_assets="files",
 )

+ 22 - 21
lib/loclass/optimized_cipher.c

@@ -110,12 +110,7 @@ static void init_opt_select_LUT(void) {
 }
 ***********************************************************************************/
 
-#define loclass_opt__select(x, y, r)                                                        \
-    (4 & ((((r) & ((r) << 2)) >> 5) ^ (((r) & ~((r) << 2)) >> 4) ^ (((r) | (r) << 2) >> 3))) |          \
-        (2 & ((((r) | (r) << 2) >> 6) ^ (((r) | (r) << 2) >> 1) ^ ((r) >> 5) ^ (r) ^ (((x) ^ (y)) << 1))) | \
-        (1 & ((((r) & ~((r) << 2)) >> 4) ^ (((r) & ((r) << 2)) >> 3) ^ (r) ^ (x)))
-
-static void loclass_opt_successor(const uint8_t* k, LoclassState_t* s, uint8_t y) {
+static inline void loclass_opt_successor(const uint8_t* k, LoclassState_t* s, uint8_t y) {
     uint16_t Tt = s->t & 0xc533;
     Tt = Tt ^ (Tt >> 1);
     Tt = Tt ^ (Tt >> 4);
@@ -133,16 +128,15 @@ static void loclass_opt_successor(const uint8_t* k, LoclassState_t* s, uint8_t y
     s->b = s->b >> 1;
     s->b |= (opt_B ^ s->r) << 7;
 
-    uint8_t opt_select = loclass_opt_select_LUT[s->r] & 0x04;
-    opt_select |= (loclass_opt_select_LUT[s->r] ^ ((Tt ^ y) << 1)) & 0x02;
-    opt_select |= (loclass_opt_select_LUT[s->r] ^ Tt) & 0x01;
+    uint8_t Tt1 = Tt & 0x01;
+    uint8_t opt_select = loclass_opt_select_LUT[s->r] ^ Tt1 ^ ((Tt1 ^ (y & 0x01)) << 1);
 
     uint8_t r = s->r;
     s->r = (k[opt_select] ^ s->b) + s->l;
     s->l = s->r + r;
 }
 
-static void loclass_opt_suc(
+static inline void loclass_opt_suc(
     const uint8_t* k,
     LoclassState_t* s,
     const uint8_t* in,
@@ -150,21 +144,22 @@ static void loclass_opt_suc(
     bool add32Zeroes) {
     for(int i = 0; i < length; i++) {
         uint8_t head = in[i];
+#pragma GCC unroll 8
         for(int j = 0; j < 8; j++) {
             loclass_opt_successor(k, s, head);
             head >>= 1;
         }
     }
-    //For tag MAC, an additional 32 zeroes
+    // For tag MAC, an additional 32 zeroes
     if(add32Zeroes) {
-        for(int i = 0; i < 16; i++) {
-            loclass_opt_successor(k, s, 0);
+        for(int i = 0; i < 32; i++) {
             loclass_opt_successor(k, s, 0);
         }
     }
 }
 
-static void loclass_opt_output(const uint8_t* k, LoclassState_t* s, uint8_t* buffer) {
+static inline void loclass_opt_output(const uint8_t* k, LoclassState_t* s, uint8_t* buffer) {
+#pragma GCC unroll 4
     for(uint8_t times = 0; times < 4; times++) {
         uint8_t bout = 0;
         bout |= (s->r & 0x4) >> 2;
@@ -280,19 +275,25 @@ void loclass_opt_doTagMAC_2(
     loclass_opt_output(div_key_p, &_init, mac);
 }
 
+/**
+ * The second part of the tag MAC calculation, since the CC is already calculated into the state,
+ * this function is fed only the NR, and generates both the reader and tag MACs.
+ * @param _init - precalculated cipher state
+ * @param nr - the reader challenge
+ * @param rmac - where to store the reader MAC
+ * @param tmac - where to store the tag MAC
+ * @param div_key_p - the key to use
+ */
 void loclass_opt_doBothMAC_2(
     LoclassState_t _init,
     uint8_t* nr,
     uint8_t rmac[4],
     uint8_t tmac[4],
     const uint8_t* div_key_p) {
-    loclass_opt_suc(div_key_p, &_init, nr, 4, false);
-    // Save internal state for reuse before outputting
-    LoclassState_t nr_state = _init;
-    loclass_opt_output(div_key_p, &_init, rmac);
-    // Feed the 32 0 bits for the tag mac
-    loclass_opt_suc(div_key_p, &nr_state, NULL, 0, true);
-    loclass_opt_output(div_key_p, &nr_state, tmac);
+    LoclassState_t* s = &_init;
+    loclass_opt_suc(div_key_p, s, nr, 4, false);
+    loclass_opt_output(div_key_p, s, rmac);
+    loclass_opt_output(div_key_p, s, tmac);
 }
 
 void loclass_iclass_calc_div_key(uint8_t* csn, const uint8_t* key, uint8_t* div_key, bool elite) {

+ 2 - 0
loclass_writer.c

@@ -38,6 +38,8 @@ void loclass_writer_free(LoclassWriter* instance) {
 }
 
 bool loclass_writer_write_start_stop(LoclassWriter* instance, bool start) {
+    furi_assert(instance != NULL);
+
     FuriHalRtcDateTime curr_dt;
     furi_hal_rtc_get_datetime(&curr_dt);
     uint32_t curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt);

+ 20 - 3
picopass_worker.c

@@ -1054,13 +1054,15 @@ static void picopass_emu_handle_packet(
         uint8_t rmac[4];
         loclass_opt_doBothMAC_2(ctx->cipher_state, nfcv_data->frame + 1, rmac, response, key);
 
+#ifndef PICOPASS_DEBUG_IGNORE_BAD_RMAC
         if(memcmp(nfcv_data->frame + 5, rmac, 4)) {
             // Bad MAC from reader, do not send a response.
             FURI_LOG_I(TAG, "Got bad MAC from reader");
-#ifndef PICOPASS_DEBUG_IGNORE_BAD_RMAC
+            // Reset the cipher state since we don't do it in READCHECK
+            picopass_init_cipher_state(nfcv_data, ctx);
             return;
-#endif
         }
+#endif
 
         // CHIPRESPONSE(4)
         response_length = 4;
@@ -1220,6 +1222,21 @@ void picopass_worker_emulate(PicopassWorker* picopass_worker, bool loclass_mode)
     PicopassBlock* blocks = dev_data->AA1;
 
     if(loclass_mode) {
+        emu_ctx.loclass_writer = loclass_writer_alloc();
+        if(emu_ctx.loclass_writer == NULL) {
+            picopass_worker->callback(
+                PicopassWorkerEventLoclassFileError, picopass_worker->context);
+
+            while(picopass_worker->state == PicopassWorkerStateEmulate ||
+                  picopass_worker->state == PicopassWorkerStateLoclass) {
+                furi_delay_ms(1);
+            }
+
+            free(nfcv_data);
+
+            return;
+        }
+
         // Setup blocks for loclass attack
         emu_ctx.key_block_num = 0;
         loclass_update_csn(&nfc_data, nfcv_data, &emu_ctx);
@@ -1233,7 +1250,6 @@ void picopass_worker_emulate(PicopassWorker* picopass_worker, bool loclass_mode)
         uint8_t aia[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
         picopass_emu_write_blocks(nfcv_data, aia, PICOPASS_SECURE_AIA_BLOCK_INDEX, 1);
 
-        emu_ctx.loclass_writer = loclass_writer_alloc();
         loclass_writer_write_start_stop(emu_ctx.loclass_writer, true);
     } else {
         memcpy(nfc_data.uid, blocks[PICOPASS_CSN_BLOCK_INDEX].data, RFAL_PICOPASS_BLOCK_LEN);
@@ -1263,6 +1279,7 @@ void picopass_worker_emulate(PicopassWorker* picopass_worker, bool loclass_mode)
                 }
             }
         }
+        furi_delay_us(1);
     }
 
     if(emu_ctx.loclass_writer) {

+ 1 - 0
picopass_worker.h

@@ -36,6 +36,7 @@ typedef enum {
     PicopassWorkerEventNoDictFound,
     PicopassWorkerEventLoclassGotMac,
     PicopassWorkerEventLoclassGotStandardKey,
+    PicopassWorkerEventLoclassFileError,
 } PicopassWorkerEvent;
 
 typedef void (*PicopassWorkerCallback)(PicopassWorkerEvent event, void* context);

+ 8 - 3
scenes/picopass_scene_loclass.c

@@ -40,11 +40,10 @@ bool picopass_scene_loclass_on_event(void* context, SceneManagerEvent event) {
     Picopass* picopass = context;
     bool consumed = false;
 
-    uint32_t loclass_macs_collected =
-        scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneLoclass);
-
     if(event.type == SceneManagerEventTypeCustom) {
         if(event.event == PicopassWorkerEventLoclassGotMac) {
+            uint32_t loclass_macs_collected =
+                scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneLoclass);
             loclass_macs_collected++;
             scene_manager_set_scene_state(
                 picopass->scene_manager, PicopassSceneLoclass, loclass_macs_collected);
@@ -56,6 +55,12 @@ bool picopass_scene_loclass_on_event(void* context, SceneManagerEvent event) {
         } else if(event.event == PicopassWorkerEventLoclassGotStandardKey) {
             loclass_set_header(picopass->loclass, "Loclass (Got Std Key)");
             consumed = true;
+        } else if(event.event == PicopassWorkerEventLoclassFileError) {
+            scene_manager_set_scene_state(picopass->scene_manager, PicopassSceneLoclass, 255);
+            loclass_set_num_macs(picopass->loclass, 255);
+            loclass_set_header(picopass->loclass, "Error Opening Log File");
+            picopass_blink_stop(picopass);
+            consumed = true;
         } else if(event.event == PicopassCustomEventViewExit) {
             consumed = scene_manager_previous_scene(picopass->scene_manager);
         }

+ 1 - 1
scenes/picopass_scene_saved_menu.c

@@ -1,11 +1,11 @@
 #include "../picopass_i.h"
 
 enum SubmenuIndex {
-    SubmenuIndexDelete,
     SubmenuIndexInfo,
     SubmenuIndexWrite,
     SubmenuIndexEmulate,
     SubmenuIndexRename,
+    SubmenuIndexDelete,
 };
 
 void picopass_scene_saved_menu_submenu_callback(void* context, uint32_t index) {

+ 4 - 0
views/loclass.c

@@ -21,6 +21,10 @@ static void loclass_draw_callback(Canvas* canvas, void* model) {
     canvas_set_font(canvas, FontSecondary);
     canvas_draw_str_aligned(canvas, 64, 0, AlignCenter, AlignTop, furi_string_get_cstr(m->header));
 
+    if(m->num_macs == 255) {
+        return;
+    }
+
     float progress = m->num_macs == 0 ? 0 :
                                         (float)(m->num_macs) / (float)(LOCLASS_MACS_TO_COLLECT);