Skip to content

Commit f327e1a

Browse files
committed
Merge branch 'elite_keygen' into 'main'
Elite keygen (VB6 RNG) See merge request bettse/picopass!2
2 parents 9d49cdf + 5373476 commit f327e1a

File tree

6 files changed

+234
-0
lines changed

6 files changed

+234
-0
lines changed

.catalog/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,9 @@ Due to the nature of how secure picopass works, it is possible to emulate some p
6464
3. Card will authenticate and read
6565
4. Suggested to both "Save" the card and "Save as Seader"
6666

67+
68+
# Elite Keygen Attack
69+
70+
Background: https://youtu.be/MKSXSKQHz6o?si=DEKkW60x858pUI0a&t=600
71+
72+
The keys used for early Elite systems used the VB6 (yes, as in Visual Basic) RNG to generate the keys. This attack uses the known VB6 RNG to generate the keys. This attack is only useful for early Elite systems, as later systems are keyed in some other manor. Since this can generate an insanely large number of values (and eventually loop), by default it is limited to the first 2000 keys. Please provide feedback if you would like this increased. Also, the leaked iCopyX dictionary included 700ish of these, so the first 700 are redundant to the System Elite Dictionary attack run during "Read". This attack is not useful for iClass SE systems.

picopass_elite_keygen.c

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#include "picopass_elite_keygen.h"
2+
3+
/* Based on https://youtu.be/MKSXSKQHz6o?si=DEKkW60x858pUI0a&t=600 */
4+
5+
#define INITIAL_SEED 0x429080
6+
7+
uint32_t seed = INITIAL_SEED;
8+
uint8_t key_state[8];
9+
bool prepared = false;
10+
11+
void picopass_elite_reset() {
12+
memset(key_state, 0, sizeof(key_state));
13+
seed = INITIAL_SEED;
14+
prepared = false;
15+
}
16+
17+
uint32_t picopass_elite_lcg() {
18+
uint32_t mod = 0x1000000; // 2^24,
19+
uint32_t a = 0xFD43FD;
20+
uint32_t c = 0xC39EC3;
21+
22+
return (a * seed + c) % mod;
23+
}
24+
25+
uint32_t picopass_elite_rng() {
26+
seed = picopass_elite_lcg();
27+
return seed;
28+
}
29+
30+
uint8_t picopass_elite_nextByte() {
31+
return (picopass_elite_rng() >> 16) & 0xFF;
32+
}
33+
34+
void picopass_elite_nextKey(uint8_t* key) {
35+
if(prepared) {
36+
for(size_t i = 0; i < 7; i++) {
37+
key_state[i] = key_state[i + 1];
38+
}
39+
key_state[7] = picopass_elite_nextByte();
40+
} else {
41+
for(size_t i = 0; i < 8; i++) {
42+
key_state[i] = picopass_elite_nextByte();
43+
}
44+
prepared = true;
45+
}
46+
memcpy(key, key_state, 8);
47+
}
48+
49+
/*
50+
int main() {
51+
size_t limit = 700;
52+
53+
for (size_t i = 0; i < limit; i++) {
54+
nextKey();
55+
printKey(key);
56+
// printf("%04lx: %08x\n", i, nextByte());
57+
}
58+
return 0;
59+
}
60+
*/

picopass_elite_keygen.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#pragma once
2+
3+
#include <stdbool.h>
4+
#include <stddef.h>
5+
#include <stdint.h>
6+
#include <stdio.h>
7+
#include <string.h>
8+
9+
void picopass_elite_nextKey(uint8_t* key);
10+
void picopass_elite_reset();

scenes/picopass_scene_config.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@ ADD_SCENE(picopass, nr_mac_saved, NrMacSaved)
2323
ADD_SCENE(picopass, more_info, MoreInfo)
2424
ADD_SCENE(picopass, formats, Formats)
2525
ADD_SCENE(picopass, acknowledgements, Acknowledgements)
26+
ADD_SCENE(picopass, elite_keygen_attack, EliteKeygenAttack)
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
#include "../picopass_i.h"
2+
#include <dolphin/dolphin.h>
3+
#include "../picopass_elite_keygen.h"
4+
5+
#define PICOPASS_SCENE_DICT_ATTACK_KEYS_BATCH_UPDATE (10)
6+
#define PICOPASS_SCENE_ELITE_KEYGEN_ATTACK_LIMIT (2000)
7+
8+
NfcCommand picopass_elite_keygen_attack_worker_callback(PicopassPollerEvent event, void* context) {
9+
furi_assert(context);
10+
NfcCommand command = NfcCommandContinue;
11+
12+
Picopass* picopass = context;
13+
14+
if(event.type == PicopassPollerEventTypeRequestMode) {
15+
event.data->req_mode.mode = PicopassPollerModeRead;
16+
} else if(event.type == PicopassPollerEventTypeRequestKey) {
17+
uint8_t key[PICOPASS_KEY_LEN] = {};
18+
bool is_key_provided = false;
19+
if(picopass->dict_attack_ctx.current_key < PICOPASS_SCENE_ELITE_KEYGEN_ATTACK_LIMIT) {
20+
picopass_elite_nextKey(key);
21+
is_key_provided = true;
22+
}
23+
24+
memcpy(event.data->req_key.key, key, PICOPASS_KEY_LEN);
25+
event.data->req_key.is_elite_key = true;
26+
event.data->req_key.is_key_provided = is_key_provided;
27+
if(is_key_provided) {
28+
picopass->dict_attack_ctx.current_key++;
29+
if(picopass->dict_attack_ctx.current_key %
30+
PICOPASS_SCENE_DICT_ATTACK_KEYS_BATCH_UPDATE ==
31+
0) {
32+
view_dispatcher_send_custom_event(
33+
picopass->view_dispatcher, PicopassCustomEventDictAttackUpdateView);
34+
}
35+
}
36+
} else if(
37+
event.type == PicopassPollerEventTypeSuccess ||
38+
event.type == PicopassPollerEventTypeFail ||
39+
event.type == PicopassPollerEventTypeAuthFail) {
40+
const PicopassDeviceData* data = picopass_poller_get_data(picopass->poller);
41+
memcpy(&picopass->dev->dev_data, data, sizeof(PicopassDeviceData));
42+
view_dispatcher_send_custom_event(
43+
picopass->view_dispatcher, PicopassCustomEventPollerSuccess);
44+
} else if(event.type == PicopassPollerEventTypeCardLost) {
45+
picopass->dict_attack_ctx.card_detected = false;
46+
view_dispatcher_send_custom_event(
47+
picopass->view_dispatcher, PicopassCustomEventDictAttackUpdateView);
48+
} else if(event.type == PicopassPollerEventTypeCardDetected) {
49+
picopass->dict_attack_ctx.card_detected = true;
50+
view_dispatcher_send_custom_event(
51+
picopass->view_dispatcher, PicopassCustomEventDictAttackUpdateView);
52+
}
53+
54+
return command;
55+
}
56+
57+
static void picopass_scene_elite_keygen_attack_update_view(Picopass* instance) {
58+
if(instance->dict_attack_ctx.card_detected) {
59+
dict_attack_set_card_detected(instance->dict_attack);
60+
dict_attack_set_header(instance->dict_attack, instance->dict_attack_ctx.name);
61+
dict_attack_set_total_dict_keys(
62+
instance->dict_attack, PICOPASS_SCENE_ELITE_KEYGEN_ATTACK_LIMIT);
63+
dict_attack_set_current_dict_key(
64+
instance->dict_attack, instance->dict_attack_ctx.current_key);
65+
} else {
66+
dict_attack_set_card_removed(instance->dict_attack);
67+
}
68+
}
69+
70+
static void picopass_scene_elite_keygen_attack_callback(void* context) {
71+
Picopass* instance = context;
72+
73+
view_dispatcher_send_custom_event(
74+
instance->view_dispatcher, PicopassCustomEventDictAttackSkip);
75+
}
76+
77+
void picopass_scene_elite_keygen_attack_on_enter(void* context) {
78+
Picopass* picopass = context;
79+
dolphin_deed(DolphinDeedNfcRead);
80+
81+
// Setup dict attack context
82+
uint32_t state = PicopassSceneEliteKeygenAttack;
83+
84+
picopass->dict = keys_dict_alloc(
85+
PICOPASS_ICLASS_STANDARD_DICT_FLIPPER_NAME, KeysDictModeOpenExisting, PICOPASS_KEY_LEN);
86+
87+
dict_attack_reset(picopass->dict_attack);
88+
picopass->dict_attack_ctx.card_detected = false;
89+
picopass->dict_attack_ctx.total_keys = PICOPASS_SCENE_ELITE_KEYGEN_ATTACK_LIMIT;
90+
picopass->dict_attack_ctx.current_key = 0;
91+
picopass->dict_attack_ctx.name = "Elite Keygen Attack";
92+
scene_manager_set_scene_state(picopass->scene_manager, PicopassSceneEliteKeygenAttack, state);
93+
94+
// Setup view
95+
picopass_scene_elite_keygen_attack_update_view(picopass);
96+
dict_attack_set_callback(
97+
picopass->dict_attack, picopass_scene_elite_keygen_attack_callback, picopass);
98+
99+
// Start worker
100+
picopass->poller = picopass_poller_alloc(picopass->nfc);
101+
picopass_poller_start(
102+
picopass->poller, picopass_elite_keygen_attack_worker_callback, picopass);
103+
104+
view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewDictAttack);
105+
picopass_blink_start(picopass);
106+
}
107+
108+
bool picopass_scene_elite_keygen_attack_on_event(void* context, SceneManagerEvent event) {
109+
Picopass* picopass = context;
110+
bool consumed = false;
111+
112+
if(event.type == SceneManagerEventTypeCustom) {
113+
if(event.event == PicopassCustomEventPollerSuccess) {
114+
scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCardSuccess);
115+
consumed = true;
116+
} else if(event.event == PicopassCustomEventDictAttackUpdateView) {
117+
picopass_scene_elite_keygen_attack_update_view(picopass);
118+
consumed = true;
119+
} else if(event.event == PicopassCustomEventDictAttackSkip) {
120+
scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCardSuccess);
121+
consumed = true;
122+
}
123+
}
124+
return consumed;
125+
}
126+
127+
void picopass_scene_elite_keygen_attack_on_exit(void* context) {
128+
Picopass* picopass = context;
129+
130+
if(picopass->dict) {
131+
keys_dict_free(picopass->dict);
132+
picopass->dict = NULL;
133+
}
134+
picopass->dict_attack_ctx.current_key = 0;
135+
picopass->dict_attack_ctx.total_keys = 0;
136+
picopass_elite_reset();
137+
138+
picopass_poller_stop(picopass->poller);
139+
picopass_poller_free(picopass->poller);
140+
141+
// Clear view
142+
popup_reset(picopass->popup);
143+
144+
picopass_blink_stop(picopass);
145+
}

scenes/picopass_scene_start.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ enum SubmenuIndex {
44
SubmenuIndexSaved,
55
SubmenuIndexLoclass,
66
SubmenuIndexAcknowledgements,
7+
SubmenuIndexKeygenAttack,
78
};
89

910
void picopass_scene_start_submenu_callback(void* context, uint32_t index) {
@@ -26,6 +27,12 @@ void picopass_scene_start_on_enter(void* context) {
2627
SubmenuIndexAcknowledgements,
2728
picopass_scene_start_submenu_callback,
2829
picopass);
30+
submenu_add_item(
31+
submenu,
32+
"Elite Keygen Attack",
33+
SubmenuIndexKeygenAttack,
34+
picopass_scene_start_submenu_callback,
35+
picopass);
2936

3037
submenu_set_selected_item(
3138
submenu, scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneStart));
@@ -60,6 +67,11 @@ bool picopass_scene_start_on_event(void* context, SceneManagerEvent event) {
6067
picopass->scene_manager, PicopassSceneStart, PicopassSceneAcknowledgements);
6168
scene_manager_next_scene(picopass->scene_manager, PicopassSceneAcknowledgements);
6269
consumed = true;
70+
} else if(event.event == SubmenuIndexKeygenAttack) {
71+
scene_manager_set_scene_state(
72+
picopass->scene_manager, PicopassSceneStart, SubmenuIndexKeygenAttack);
73+
scene_manager_next_scene(picopass->scene_manager, PicopassSceneEliteKeygenAttack);
74+
consumed = true;
6375
}
6476
}
6577

0 commit comments

Comments
 (0)