From 7b3170a1cdd801936cd21e5d9fbb8cb708de1774 Mon Sep 17 00:00:00 2001 From: Tiernan Date: Fri, 8 Sep 2023 18:32:42 +1000 Subject: [PATCH] Optimise picopass crypto to fix timing issues with newer readers. (#34) --- application.fam | 5 ++-- lib/loclass/optimized_cipher.c | 43 +++++++++++++++--------------- loclass_writer.c | 2 ++ picopass_worker.c | 23 +++++++++++++--- picopass_worker.h | 1 + scenes/picopass_scene_loclass.c | 11 +++++--- scenes/picopass_scene_saved_menu.c | 2 +- views/loclass.c | 4 +++ 8 files changed, 61 insertions(+), 30 deletions(-) diff --git a/application.fam b/application.fam index 3013d4a3f93..58e470001a1 100644 --- a/application.fam +++ b/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", ) diff --git a/lib/loclass/optimized_cipher.c b/lib/loclass/optimized_cipher.c index 01d48817dde..b2ea4538b8d 100644 --- a/lib/loclass/optimized_cipher.c +++ b/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) { diff --git a/loclass_writer.c b/loclass_writer.c index 273fa67eb39..3539a9e3d2f 100644 --- a/loclass_writer.c +++ b/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); diff --git a/picopass_worker.c b/picopass_worker.c index a0aac623115..b50e9035e3f 100644 --- a/picopass_worker.c +++ b/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) { diff --git a/picopass_worker.h b/picopass_worker.h index 642e4c9620e..ad1f5a1d865 100644 --- a/picopass_worker.h +++ b/picopass_worker.h @@ -36,6 +36,7 @@ typedef enum { PicopassWorkerEventNoDictFound, PicopassWorkerEventLoclassGotMac, PicopassWorkerEventLoclassGotStandardKey, + PicopassWorkerEventLoclassFileError, } PicopassWorkerEvent; typedef void (*PicopassWorkerCallback)(PicopassWorkerEvent event, void* context); diff --git a/scenes/picopass_scene_loclass.c b/scenes/picopass_scene_loclass.c index 01e24555733..17d0f3faa43 100644 --- a/scenes/picopass_scene_loclass.c +++ b/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); } diff --git a/scenes/picopass_scene_saved_menu.c b/scenes/picopass_scene_saved_menu.c index 0283906f28b..35e4573adb9 100644 --- a/scenes/picopass_scene_saved_menu.c +++ b/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) { diff --git a/views/loclass.c b/views/loclass.c index 4158019a83a..4257c25164d 100644 --- a/views/loclass.c +++ b/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);