Skip to content

Commit c610548

Browse files
committed
Add picopass emulation
1 parent 0685939 commit c610548

29 files changed

+1145
-89
lines changed

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
dist/*
2+
.vscode
3+
.clang-format
4+
.editorconfig

lib/loclass/optimized_cipher.c

+16-1
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,22 @@ void loclass_opt_doTagMAC_2(
280280
loclass_opt_output(div_key_p, &_init, mac);
281281
}
282282

283-
void loclass_iclass_calc_div_key(uint8_t* csn, uint8_t* key, uint8_t* div_key, bool elite) {
283+
void loclass_opt_doBothMAC_2(
284+
LoclassState_t _init,
285+
uint8_t* nr,
286+
uint8_t rmac[4],
287+
uint8_t tmac[4],
288+
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);
296+
}
297+
298+
void loclass_iclass_calc_div_key(uint8_t* csn, const uint8_t* key, uint8_t* div_key, bool elite) {
284299
if(elite) {
285300
uint8_t keytable[128] = {0};
286301
uint8_t key_index[8] = {0};

lib/loclass/optimized_cipher.h

+16-1
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,21 @@ void loclass_opt_doTagMAC_2(
9393
uint8_t mac[4],
9494
const uint8_t* div_key_p);
9595

96+
/**
97+
* The same as loclass_opt_doTagMAC_2, but calculates both the reader and tag MACs at the same time
98+
* @param _init - precalculated cipher state
99+
* @param nr - the reader challenge
100+
* @param rmac - where to store the reader MAC
101+
* @param tmac - where to store the tag MAC
102+
* @param div_key_p - the key to use
103+
*/
104+
void loclass_opt_doBothMAC_2(
105+
LoclassState_t _init,
106+
uint8_t* nr,
107+
uint8_t rmac[4],
108+
uint8_t tmac[4],
109+
const uint8_t* div_key_p);
110+
96111
void loclass_doMAC_N(uint8_t* in_p, uint8_t in_size, uint8_t* div_key_p, uint8_t mac[4]);
97-
void loclass_iclass_calc_div_key(uint8_t* csn, uint8_t* key, uint8_t* div_key, bool elite);
112+
void loclass_iclass_calc_div_key(uint8_t* csn, const uint8_t* key, uint8_t* div_key, bool elite);
98113
#endif // OPTIMIZED_CIPHER_H

lib/loclass/optimized_elite.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ Definition 14. Define the rotate key function loclass_rk : (F 82 ) 8 × N → (F
153153
loclass_rk(x [0] . . . x [7] , 0) = x [0] . . . x [7]
154154
loclass_rk(x [0] . . . x [7] , n + 1) = loclass_rk(loclass_rl(x [0] ) . . . loclass_rl(x [7] ), n)
155155
**/
156-
static void loclass_rk(uint8_t* key, uint8_t n, uint8_t* outp_key) {
156+
static void loclass_rk(const uint8_t* key, uint8_t n, uint8_t* outp_key) {
157157
memcpy(outp_key, key, 8);
158158
uint8_t j;
159159
while(n-- > 0) {
@@ -172,7 +172,7 @@ static void loclass_desdecrypt_iclass(uint8_t* iclass_key, uint8_t* input, uint8
172172
mbedtls_des_crypt_ecb(&loclass_ctx_dec, input, output);
173173
}
174174

175-
static void loclass_desencrypt_iclass(uint8_t* iclass_key, uint8_t* input, uint8_t* output) {
175+
static void loclass_desencrypt_iclass(const uint8_t* iclass_key, uint8_t* input, uint8_t* output) {
176176
uint8_t key_std_format[8] = {0};
177177
loclass_permutekey_rev(iclass_key, key_std_format);
178178
mbedtls_des_setkey_enc(&loclass_ctx_enc, key_std_format);
@@ -185,7 +185,7 @@ static void loclass_desencrypt_iclass(uint8_t* iclass_key, uint8_t* input, uint8
185185
* @param loclass_hash1 loclass_hash1
186186
* @param key_sel output key_sel=h[loclass_hash1[i]]
187187
*/
188-
void loclass_hash2(uint8_t* key64, uint8_t* outp_keytable) {
188+
void loclass_hash2(const uint8_t* key64, uint8_t* outp_keytable) {
189189
/**
190190
*Expected:
191191
* High Security Key Table

lib/loclass/optimized_elite.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,6 @@ void loclass_permutekey_rev(const uint8_t key[8], uint8_t dest[8]);
5353
* @param k output
5454
*/
5555
void loclass_hash1(const uint8_t* csn, uint8_t* k);
56-
void loclass_hash2(uint8_t* key64, uint8_t* outp_keytable);
56+
void loclass_hash2(const uint8_t* key64, uint8_t* outp_keytable);
5757

5858
#endif

loclass_writer.c

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
#include "loclass_writer.h"
2+
3+
#include <furi/furi.h>
4+
#include <furi_hal.h>
5+
#include <storage/storage.h>
6+
#include <stream/stream.h>
7+
#include <stream/buffered_file_stream.h>
8+
9+
struct LoclassWriter {
10+
Stream* file_stream;
11+
};
12+
13+
#define LOCLASS_LOGS_PATH EXT_PATH("apps_data/picopass/.loclass.log")
14+
15+
LoclassWriter* loclass_writer_alloc() {
16+
LoclassWriter* instance = malloc(sizeof(LoclassWriter));
17+
Storage* storage = furi_record_open(RECORD_STORAGE);
18+
instance->file_stream = buffered_file_stream_alloc(storage);
19+
if(!buffered_file_stream_open(
20+
instance->file_stream, LOCLASS_LOGS_PATH, FSAM_WRITE, FSOM_OPEN_APPEND)) {
21+
buffered_file_stream_close(instance->file_stream);
22+
stream_free(instance->file_stream);
23+
free(instance);
24+
instance = NULL;
25+
}
26+
27+
furi_record_close(RECORD_STORAGE);
28+
29+
return instance;
30+
}
31+
32+
void loclass_writer_free(LoclassWriter* instance) {
33+
furi_assert(instance != NULL);
34+
35+
buffered_file_stream_close(instance->file_stream);
36+
stream_free(instance->file_stream);
37+
free(instance);
38+
}
39+
40+
bool loclass_writer_write_start_stop(LoclassWriter* instance, bool start) {
41+
FuriHalRtcDateTime curr_dt;
42+
furi_hal_rtc_get_datetime(&curr_dt);
43+
uint32_t curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt);
44+
45+
FuriString* str = furi_string_alloc_printf(
46+
"loclass-v1-info ts %lu %s\n", curr_ts, start ? "started" : "finished");
47+
bool write_success = stream_write_string(instance->file_stream, str);
48+
furi_string_free(str);
49+
return write_success;
50+
}
51+
52+
bool loclass_writer_write_params(
53+
LoclassWriter* instance,
54+
uint8_t log_no,
55+
const uint8_t csn[8],
56+
const uint8_t epurse[8],
57+
const uint8_t nr[4],
58+
const uint8_t mac[4]) {
59+
furi_assert(instance != NULL);
60+
61+
FuriHalRtcDateTime curr_dt;
62+
furi_hal_rtc_get_datetime(&curr_dt);
63+
uint32_t curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt);
64+
65+
FuriString* str = furi_string_alloc_printf(
66+
"loclass-v1-mac ts %lu no %u "
67+
"csn %02x%02x%02x%02x%02x%02x%02x%02x "
68+
"cc %02x%02x%02x%02x%02x%02x%02x%02x "
69+
"nr %02x%02x%02x%02x "
70+
"mac %02x%02x%02x%02x\n",
71+
curr_ts,
72+
log_no,
73+
csn[0],
74+
csn[1],
75+
csn[2],
76+
csn[3],
77+
csn[4],
78+
csn[5],
79+
csn[6],
80+
csn[7],
81+
epurse[0],
82+
epurse[1],
83+
epurse[2],
84+
epurse[3],
85+
epurse[4],
86+
epurse[5],
87+
epurse[6],
88+
epurse[7],
89+
nr[0],
90+
nr[1],
91+
nr[2],
92+
nr[3],
93+
mac[0],
94+
mac[1],
95+
mac[2],
96+
mac[3]);
97+
bool write_success = stream_write_string(instance->file_stream, str);
98+
furi_string_free(str);
99+
return write_success;
100+
}

loclass_writer.h

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#pragma once
2+
3+
#include <stdint.h>
4+
#include <stdbool.h>
5+
6+
typedef struct LoclassWriter LoclassWriter;
7+
8+
LoclassWriter* loclass_writer_alloc();
9+
10+
void loclass_writer_free(LoclassWriter* instance);
11+
12+
bool loclass_writer_write_start_stop(LoclassWriter* instance, bool start);
13+
14+
bool loclass_writer_write_params(
15+
LoclassWriter* instance,
16+
uint8_t log_no,
17+
const uint8_t csn[8],
18+
const uint8_t epurse[8],
19+
const uint8_t nr[4],
20+
const uint8_t mac[4]);

picopass.c

+18
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ Picopass* picopass_alloc() {
7979
PicopassViewDictAttack,
8080
dict_attack_get_view(picopass->dict_attack));
8181

82+
picopass->loclass = loclass_alloc();
83+
view_dispatcher_add_view(
84+
picopass->view_dispatcher, PicopassViewLoclass, loclass_get_view(picopass->loclass));
85+
8286
return picopass;
8387
}
8488

@@ -112,6 +116,9 @@ void picopass_free(Picopass* picopass) {
112116
view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewDictAttack);
113117
dict_attack_free(picopass->dict_attack);
114118

119+
view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewLoclass);
120+
loclass_free(picopass->loclass);
121+
115122
// Worker
116123
picopass_worker_stop(picopass->worker);
117124
picopass_worker_free(picopass->worker);
@@ -153,6 +160,13 @@ static const NotificationSequence picopass_sequence_blink_start_cyan = {
153160
NULL,
154161
};
155162

163+
static const NotificationSequence picopass_sequence_blink_start_magenta = {
164+
&message_blink_start_10,
165+
&message_blink_set_color_magenta,
166+
&message_do_not_reset,
167+
NULL,
168+
};
169+
156170
static const NotificationSequence picopass_sequence_blink_stop = {
157171
&message_blink_stop,
158172
NULL,
@@ -162,6 +176,10 @@ void picopass_blink_start(Picopass* picopass) {
162176
notification_message(picopass->notifications, &picopass_sequence_blink_start_cyan);
163177
}
164178

179+
void picopass_blink_emulate_start(Picopass* picopass) {
180+
notification_message(picopass->notifications, &picopass_sequence_blink_start_magenta);
181+
}
182+
165183
void picopass_blink_stop(Picopass* picopass) {
166184
notification_message(picopass->notifications, &picopass_sequence_blink_stop);
167185
}

picopass_device.c

+13-9
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,14 @@ static bool picopass_device_save_file(
6868
if(!flipper_format_write_uint32(file, "Facility Code", &fc, 1)) break;
6969
if(!flipper_format_write_uint32(file, "Card Number", &cn, 1)) break;
7070
if(!flipper_format_write_hex(
71-
file, "Credential", pacs->credential, PICOPASS_BLOCK_LEN))
71+
file, "Credential", pacs->credential, RFAL_PICOPASS_BLOCK_LEN))
7272
break;
7373
if(pacs->pin_length > 0) {
74-
if(!flipper_format_write_hex(file, "PIN\t\t", pacs->pin0, PICOPASS_BLOCK_LEN))
74+
if(!flipper_format_write_hex(
75+
file, "PIN\t\t", pacs->pin0, RFAL_PICOPASS_BLOCK_LEN))
7576
break;
7677
if(!flipper_format_write_hex(
77-
file, "PIN(cont.)\t", pacs->pin1, PICOPASS_BLOCK_LEN))
78+
file, "PIN(cont.)\t", pacs->pin1, RFAL_PICOPASS_BLOCK_LEN))
7879
break;
7980
}
8081
}
@@ -88,7 +89,10 @@ static bool picopass_device_save_file(
8889
for(size_t i = 0; i < app_limit; i++) {
8990
furi_string_printf(temp_str, "Block %d", i);
9091
if(!flipper_format_write_hex(
91-
file, furi_string_get_cstr(temp_str), AA1[i].data, PICOPASS_BLOCK_LEN)) {
92+
file,
93+
furi_string_get_cstr(temp_str),
94+
AA1[i].data,
95+
RFAL_PICOPASS_BLOCK_LEN)) {
9296
block_saved = false;
9397
break;
9498
}
@@ -162,7 +166,7 @@ static bool picopass_device_load_data(PicopassDevice* dev, FuriString* path, boo
162166
for(size_t i = 0; i < 6; i++) {
163167
furi_string_printf(temp_str, "Block %d", i);
164168
if(!flipper_format_read_hex(
165-
file, furi_string_get_cstr(temp_str), AA1[i].data, PICOPASS_BLOCK_LEN)) {
169+
file, furi_string_get_cstr(temp_str), AA1[i].data, RFAL_PICOPASS_BLOCK_LEN)) {
166170
block_read = false;
167171
break;
168172
}
@@ -174,7 +178,7 @@ static bool picopass_device_load_data(PicopassDevice* dev, FuriString* path, boo
174178
for(size_t i = 6; i < app_limit; i++) {
175179
furi_string_printf(temp_str, "Block %d", i);
176180
if(!flipper_format_read_hex(
177-
file, furi_string_get_cstr(temp_str), AA1[i].data, PICOPASS_BLOCK_LEN)) {
181+
file, furi_string_get_cstr(temp_str), AA1[i].data, RFAL_PICOPASS_BLOCK_LEN)) {
178182
block_read = false;
179183
break;
180184
}
@@ -338,9 +342,9 @@ ReturnCode picopass_device_parse_credential(PicopassBlock* AA1, PicopassPacs* pa
338342
}
339343
} else if(pacs->encryption == PicopassDeviceEncryptionNone) {
340344
FURI_LOG_D(TAG, "No Encryption");
341-
memcpy(pacs->credential, AA1[7].data, PICOPASS_BLOCK_LEN);
342-
memcpy(pacs->pin0, AA1[8].data, PICOPASS_BLOCK_LEN);
343-
memcpy(pacs->pin1, AA1[9].data, PICOPASS_BLOCK_LEN);
345+
memcpy(pacs->credential, AA1[7].data, RFAL_PICOPASS_BLOCK_LEN);
346+
memcpy(pacs->pin0, AA1[8].data, RFAL_PICOPASS_BLOCK_LEN);
347+
memcpy(pacs->pin1, AA1[9].data, RFAL_PICOPASS_BLOCK_LEN);
344348
} else if(pacs->encryption == PicopassDeviceEncryptionDES) {
345349
FURI_LOG_D(TAG, "DES Encrypted");
346350
} else {

0 commit comments

Comments
 (0)