Skip to content

Commit 7b3170a

Browse files
authored
Optimise picopass crypto to fix timing issues with newer readers. (#34)
1 parent 2bf5b80 commit 7b3170a

8 files changed

+61
-30
lines changed

application.fam

+3-2
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,16 @@ App(
1010
],
1111
stack_size=4 * 1024,
1212
fap_description="App to communicate with NFC tags using the PicoPass(iClass) format",
13-
fap_version="1.3",
13+
fap_version="1.4",
1414
fap_icon="125_10px.png",
1515
fap_category="NFC",
1616
fap_libs=["mbedtls"],
1717
fap_private_libs=[
1818
Lib(
1919
name="loclass",
20+
cflags=["-O3"],
2021
),
2122
],
2223
fap_icon_assets="icons",
23-
fap_file_assets="files"
24+
fap_file_assets="files",
2425
)

lib/loclass/optimized_cipher.c

+22-21
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,7 @@ static void init_opt_select_LUT(void) {
110110
}
111111
***********************************************************************************/
112112

113-
#define loclass_opt__select(x, y, r) \
114-
(4 & ((((r) & ((r) << 2)) >> 5) ^ (((r) & ~((r) << 2)) >> 4) ^ (((r) | (r) << 2) >> 3))) | \
115-
(2 & ((((r) | (r) << 2) >> 6) ^ (((r) | (r) << 2) >> 1) ^ ((r) >> 5) ^ (r) ^ (((x) ^ (y)) << 1))) | \
116-
(1 & ((((r) & ~((r) << 2)) >> 4) ^ (((r) & ((r) << 2)) >> 3) ^ (r) ^ (x)))
117-
118-
static void loclass_opt_successor(const uint8_t* k, LoclassState_t* s, uint8_t y) {
113+
static inline void loclass_opt_successor(const uint8_t* k, LoclassState_t* s, uint8_t y) {
119114
uint16_t Tt = s->t & 0xc533;
120115
Tt = Tt ^ (Tt >> 1);
121116
Tt = Tt ^ (Tt >> 4);
@@ -133,38 +128,38 @@ static void loclass_opt_successor(const uint8_t* k, LoclassState_t* s, uint8_t y
133128
s->b = s->b >> 1;
134129
s->b |= (opt_B ^ s->r) << 7;
135130

136-
uint8_t opt_select = loclass_opt_select_LUT[s->r] & 0x04;
137-
opt_select |= (loclass_opt_select_LUT[s->r] ^ ((Tt ^ y) << 1)) & 0x02;
138-
opt_select |= (loclass_opt_select_LUT[s->r] ^ Tt) & 0x01;
131+
uint8_t Tt1 = Tt & 0x01;
132+
uint8_t opt_select = loclass_opt_select_LUT[s->r] ^ Tt1 ^ ((Tt1 ^ (y & 0x01)) << 1);
139133

140134
uint8_t r = s->r;
141135
s->r = (k[opt_select] ^ s->b) + s->l;
142136
s->l = s->r + r;
143137
}
144138

145-
static void loclass_opt_suc(
139+
static inline void loclass_opt_suc(
146140
const uint8_t* k,
147141
LoclassState_t* s,
148142
const uint8_t* in,
149143
uint8_t length,
150144
bool add32Zeroes) {
151145
for(int i = 0; i < length; i++) {
152146
uint8_t head = in[i];
147+
#pragma GCC unroll 8
153148
for(int j = 0; j < 8; j++) {
154149
loclass_opt_successor(k, s, head);
155150
head >>= 1;
156151
}
157152
}
158-
//For tag MAC, an additional 32 zeroes
153+
// For tag MAC, an additional 32 zeroes
159154
if(add32Zeroes) {
160-
for(int i = 0; i < 16; i++) {
161-
loclass_opt_successor(k, s, 0);
155+
for(int i = 0; i < 32; i++) {
162156
loclass_opt_successor(k, s, 0);
163157
}
164158
}
165159
}
166160

167-
static void loclass_opt_output(const uint8_t* k, LoclassState_t* s, uint8_t* buffer) {
161+
static inline void loclass_opt_output(const uint8_t* k, LoclassState_t* s, uint8_t* buffer) {
162+
#pragma GCC unroll 4
168163
for(uint8_t times = 0; times < 4; times++) {
169164
uint8_t bout = 0;
170165
bout |= (s->r & 0x4) >> 2;
@@ -280,19 +275,25 @@ void loclass_opt_doTagMAC_2(
280275
loclass_opt_output(div_key_p, &_init, mac);
281276
}
282277

278+
/**
279+
* The second part of the tag MAC calculation, since the CC is already calculated into the state,
280+
* this function is fed only the NR, and generates both the reader and tag MACs.
281+
* @param _init - precalculated cipher state
282+
* @param nr - the reader challenge
283+
* @param rmac - where to store the reader MAC
284+
* @param tmac - where to store the tag MAC
285+
* @param div_key_p - the key to use
286+
*/
283287
void loclass_opt_doBothMAC_2(
284288
LoclassState_t _init,
285289
uint8_t* nr,
286290
uint8_t rmac[4],
287291
uint8_t tmac[4],
288292
const uint8_t* div_key_p) {
289-
loclass_opt_suc(div_key_p, &_init, nr, 4, false);
290-
// Save internal state for reuse before outputting
291-
LoclassState_t nr_state = _init;
292-
loclass_opt_output(div_key_p, &_init, rmac);
293-
// Feed the 32 0 bits for the tag mac
294-
loclass_opt_suc(div_key_p, &nr_state, NULL, 0, true);
295-
loclass_opt_output(div_key_p, &nr_state, tmac);
293+
LoclassState_t* s = &_init;
294+
loclass_opt_suc(div_key_p, s, nr, 4, false);
295+
loclass_opt_output(div_key_p, s, rmac);
296+
loclass_opt_output(div_key_p, s, tmac);
296297
}
297298

298299
void loclass_iclass_calc_div_key(uint8_t* csn, const uint8_t* key, uint8_t* div_key, bool elite) {

loclass_writer.c

+2
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ void loclass_writer_free(LoclassWriter* instance) {
3838
}
3939

4040
bool loclass_writer_write_start_stop(LoclassWriter* instance, bool start) {
41+
furi_assert(instance != NULL);
42+
4143
FuriHalRtcDateTime curr_dt;
4244
furi_hal_rtc_get_datetime(&curr_dt);
4345
uint32_t curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt);

picopass_worker.c

+20-3
Original file line numberDiff line numberDiff line change
@@ -1054,13 +1054,15 @@ static void picopass_emu_handle_packet(
10541054
uint8_t rmac[4];
10551055
loclass_opt_doBothMAC_2(ctx->cipher_state, nfcv_data->frame + 1, rmac, response, key);
10561056

1057+
#ifndef PICOPASS_DEBUG_IGNORE_BAD_RMAC
10571058
if(memcmp(nfcv_data->frame + 5, rmac, 4)) {
10581059
// Bad MAC from reader, do not send a response.
10591060
FURI_LOG_I(TAG, "Got bad MAC from reader");
1060-
#ifndef PICOPASS_DEBUG_IGNORE_BAD_RMAC
1061+
// Reset the cipher state since we don't do it in READCHECK
1062+
picopass_init_cipher_state(nfcv_data, ctx);
10611063
return;
1062-
#endif
10631064
}
1065+
#endif
10641066

10651067
// CHIPRESPONSE(4)
10661068
response_length = 4;
@@ -1220,6 +1222,21 @@ void picopass_worker_emulate(PicopassWorker* picopass_worker, bool loclass_mode)
12201222
PicopassBlock* blocks = dev_data->AA1;
12211223

12221224
if(loclass_mode) {
1225+
emu_ctx.loclass_writer = loclass_writer_alloc();
1226+
if(emu_ctx.loclass_writer == NULL) {
1227+
picopass_worker->callback(
1228+
PicopassWorkerEventLoclassFileError, picopass_worker->context);
1229+
1230+
while(picopass_worker->state == PicopassWorkerStateEmulate ||
1231+
picopass_worker->state == PicopassWorkerStateLoclass) {
1232+
furi_delay_ms(1);
1233+
}
1234+
1235+
free(nfcv_data);
1236+
1237+
return;
1238+
}
1239+
12231240
// Setup blocks for loclass attack
12241241
emu_ctx.key_block_num = 0;
12251242
loclass_update_csn(&nfc_data, nfcv_data, &emu_ctx);
@@ -1233,7 +1250,6 @@ void picopass_worker_emulate(PicopassWorker* picopass_worker, bool loclass_mode)
12331250
uint8_t aia[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
12341251
picopass_emu_write_blocks(nfcv_data, aia, PICOPASS_SECURE_AIA_BLOCK_INDEX, 1);
12351252

1236-
emu_ctx.loclass_writer = loclass_writer_alloc();
12371253
loclass_writer_write_start_stop(emu_ctx.loclass_writer, true);
12381254
} else {
12391255
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)
12631279
}
12641280
}
12651281
}
1282+
furi_delay_us(1);
12661283
}
12671284

12681285
if(emu_ctx.loclass_writer) {

picopass_worker.h

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ typedef enum {
3636
PicopassWorkerEventNoDictFound,
3737
PicopassWorkerEventLoclassGotMac,
3838
PicopassWorkerEventLoclassGotStandardKey,
39+
PicopassWorkerEventLoclassFileError,
3940
} PicopassWorkerEvent;
4041

4142
typedef void (*PicopassWorkerCallback)(PicopassWorkerEvent event, void* context);

scenes/picopass_scene_loclass.c

+8-3
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,10 @@ bool picopass_scene_loclass_on_event(void* context, SceneManagerEvent event) {
4040
Picopass* picopass = context;
4141
bool consumed = false;
4242

43-
uint32_t loclass_macs_collected =
44-
scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneLoclass);
45-
4643
if(event.type == SceneManagerEventTypeCustom) {
4744
if(event.event == PicopassWorkerEventLoclassGotMac) {
45+
uint32_t loclass_macs_collected =
46+
scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneLoclass);
4847
loclass_macs_collected++;
4948
scene_manager_set_scene_state(
5049
picopass->scene_manager, PicopassSceneLoclass, loclass_macs_collected);
@@ -56,6 +55,12 @@ bool picopass_scene_loclass_on_event(void* context, SceneManagerEvent event) {
5655
} else if(event.event == PicopassWorkerEventLoclassGotStandardKey) {
5756
loclass_set_header(picopass->loclass, "Loclass (Got Std Key)");
5857
consumed = true;
58+
} else if(event.event == PicopassWorkerEventLoclassFileError) {
59+
scene_manager_set_scene_state(picopass->scene_manager, PicopassSceneLoclass, 255);
60+
loclass_set_num_macs(picopass->loclass, 255);
61+
loclass_set_header(picopass->loclass, "Error Opening Log File");
62+
picopass_blink_stop(picopass);
63+
consumed = true;
5964
} else if(event.event == PicopassCustomEventViewExit) {
6065
consumed = scene_manager_previous_scene(picopass->scene_manager);
6166
}

scenes/picopass_scene_saved_menu.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
#include "../picopass_i.h"
22

33
enum SubmenuIndex {
4-
SubmenuIndexDelete,
54
SubmenuIndexInfo,
65
SubmenuIndexWrite,
76
SubmenuIndexEmulate,
87
SubmenuIndexRename,
8+
SubmenuIndexDelete,
99
};
1010

1111
void picopass_scene_saved_menu_submenu_callback(void* context, uint32_t index) {

views/loclass.c

+4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ static void loclass_draw_callback(Canvas* canvas, void* model) {
2121
canvas_set_font(canvas, FontSecondary);
2222
canvas_draw_str_aligned(canvas, 64, 0, AlignCenter, AlignTop, furi_string_get_cstr(m->header));
2323

24+
if(m->num_macs == 255) {
25+
return;
26+
}
27+
2428
float progress = m->num_macs == 0 ? 0 :
2529
(float)(m->num_macs) / (float)(LOCLASS_MACS_TO_COLLECT);
2630

0 commit comments

Comments
 (0)