Skip to content

Commit cb15cbd

Browse files
noprotoskotopeshedger
authored
Replace Mfkey32 with MFKey (#189)
Co-authored-by: あく <alleteam@gmail.com> Co-authored-by: hedger <hedger@nanode.su> Co-authored-by: hedger <hedger@users.noreply.github.com>
1 parent 1fa97ee commit cb15cbd

23 files changed

+1652
-1380
lines changed

.github/workflows/build.yml

+13-5
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@ jobs:
2121
runs-on: ubuntu-latest
2222
steps:
2323
- name: Checkout
24-
uses: actions/checkout@v3
24+
uses: actions/checkout@v4
2525
with:
2626
fetch-depth: 2
2727
submodules: recursive
2828

2929

3030
- name: Set up ufbt
31-
uses: flipperdevices/flipperzero-ufbt-action@v0.1.2
31+
uses: flipperdevices/flipperzero-ufbt-action@v0.1
3232
with:
3333
sdk-channel: ${{ github.event.inputs.sdk-channel || 'rc'}}
3434
task: setup
@@ -48,8 +48,12 @@ jobs:
4848
run: |
4949
BUILD_FAILED=0
5050
for appdir in ${{ steps.changed-files.outputs.all_changed_and_modified_files }} ; do
51+
if [ ! -f "$appdir/application.fam" ] ; then
52+
echo "Skipping $appdir, no application.fam. File may have been deleted."
53+
continue
54+
fi
5155
echo "Building in $appdir"
52-
pushd $appdir
56+
pushd "$appdir"
5357
if ! ufbt build faps ; then
5458
echo "::error::Failed to build $appdir"
5559
BUILD_FAILED=1
@@ -66,8 +70,12 @@ jobs:
6670
run: |
6771
BUILD_FAILED=0
6872
for appdir in $( dirname $( find . -name application.fam ) ) ; do
73+
if [ ! -f "$appdir/application.fam" ] ; then
74+
echo "Skipping $appdir, no application.fam. File may have been deleted."
75+
continue
76+
fi
6977
echo "Building in $appdir"
70-
pushd $appdir
78+
pushd "$appdir"
7179
if ! ufbt build faps ; then
7280
BUILD_FAILED=1
7381
echo "::error::Failed to build $appdir"
@@ -85,7 +93,7 @@ jobs:
8593
cp -v $( find . -name '*.fap' ) dist/
8694
8795
- name: Upload all .fap files
88-
uses: actions/upload-artifact@v3
96+
uses: actions/upload-artifact@v4
8997
with:
9098
name: faps
9199
path: dist/*.fap

mfkey/.catalog/README.md

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Flipper Zero MFKey
2+
3+
This application allows you to calculate the keys of MIFARE Classic cards using the Mfkey32 and Nested algorithms directly on your Flipper Zero. After collecting the nonces using the Detect Reader feature of the NFC app, they can be used to calculate the keys to the card in the MFKey app.
4+
5+
## Usage
6+
7+
After collecting nonces using the Detect Reader option, press the Start button in the MFKey app and wait for it to finish. The calculation can take more than 10 minutes, so you'll have to be patient. After the calculation is complete, the keys will be saved to the user key dictionary.
8+
9+
## Credits
10+
11+
Developers: noproto, AG
12+
Thanks: bettse

mfkey/.catalog/changelog.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
## 2.0
2+
- Added Nested key recovery, use new KeysDict API, fix crashes, more efficient RAM utilization, faster
3+
## 1.1
4+
- Rework application with new NFC API
5+
## 1.0
6+
- Initial release

mfkey/.catalog/screenshots/1.png

4.18 KB
Loading

mfkey/.catalog/screenshots/2.png

6.82 KB
Loading

mfkey/.catalog/screenshots/3.png

5.8 KB
Loading
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,27 @@
11
App(
2-
appid="mfkey32",
3-
name="Mfkey32",
2+
appid="mfkey",
3+
name="MFKey",
44
apptype=FlipperAppType.EXTERNAL,
55
targets=["f7"],
6-
entry_point="mfkey32_main",
6+
entry_point="mfkey_main",
77
requires=[
88
"gui",
99
"storage",
1010
],
1111
stack_size=1 * 1024,
12-
fap_description="Mf Classic key finder",
13-
fap_version="1.1",
1412
fap_icon="mfkey.png",
1513
fap_category="NFC",
1614
fap_author="@noproto",
1715
fap_icon_assets="images",
1816
fap_weburl="https://github.com/noproto/FlipperMfkey",
17+
fap_description="MIFARE Classic key recovery tool",
18+
fap_version="2.2",
19+
)
20+
21+
App(
22+
appid="mfkey_init_plugin",
23+
apptype=FlipperAppType.PLUGIN,
24+
entry_point="init_plugin_ep",
25+
requires=["mfkey"],
26+
sources=["init_plugin.c"],
1927
)

mfkey/crypto1.c

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#pragma GCC optimize("O3")
2+
#pragma GCC optimize("-funroll-all-loops")
3+
4+
#include <inttypes.h>
5+
#include "crypto1.h"
6+
#include "mfkey.h"
7+
8+
#define BIT(x, n) ((x) >> (n) & 1)
9+
10+
void crypto1_get_lfsr(struct Crypto1State* state, MfClassicKey* lfsr) {
11+
int i;
12+
uint64_t lfsr_value = 0;
13+
for(i = 23; i >= 0; --i) {
14+
lfsr_value = lfsr_value << 1 | BIT(state->odd, i ^ 3);
15+
lfsr_value = lfsr_value << 1 | BIT(state->even, i ^ 3);
16+
}
17+
18+
// Assign the key value to the MfClassicKey struct
19+
for(i = 0; i < 6; ++i) {
20+
lfsr->data[i] = (lfsr_value >> ((5 - i) * 8)) & 0xFF;
21+
}
22+
}

mfkey/crypto1.h

+156
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
#ifndef CRYPTO1_H
2+
#define CRYPTO1_H
3+
4+
#include <inttypes.h>
5+
#include "mfkey.h"
6+
#include <nfc/protocols/mf_classic/mf_classic.h>
7+
8+
#define LF_POLY_ODD (0x29CE5C)
9+
#define LF_POLY_EVEN (0x870804)
10+
#define BIT(x, n) ((x) >> (n) & 1)
11+
#define BEBIT(x, n) BIT(x, (n) ^ 24)
12+
#define SWAPENDIAN(x) \
13+
((x) = ((x) >> 8 & 0xff00ff) | ((x) & 0xff00ff) << 8, (x) = (x) >> 16 | (x) << 16)
14+
15+
static inline uint32_t prng_successor(uint32_t x, uint32_t n);
16+
static inline int filter(uint32_t const x);
17+
static inline uint8_t evenparity32(uint32_t x);
18+
static inline void update_contribution(unsigned int data[], int item, int mask1, int mask2);
19+
void crypto1_get_lfsr(struct Crypto1State* state, MfClassicKey* lfsr);
20+
static inline uint32_t crypt_word(struct Crypto1State* s);
21+
static inline void crypt_word_noret(struct Crypto1State* s, uint32_t in, int x);
22+
static inline uint32_t crypt_word_ret(struct Crypto1State* s, uint32_t in, int x);
23+
static inline void rollback_word_noret(struct Crypto1State* s, uint32_t in, int x);
24+
25+
static const uint8_t lookup1[256] = {
26+
0, 0, 16, 16, 0, 16, 0, 0, 0, 16, 0, 0, 16, 16, 16, 16, 0, 0, 16, 16, 0, 16, 0, 0,
27+
0, 16, 0, 0, 16, 16, 16, 16, 0, 0, 16, 16, 0, 16, 0, 0, 0, 16, 0, 0, 16, 16, 16, 16,
28+
8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24, 8, 8, 24, 24, 8, 24, 8, 8,
29+
8, 24, 8, 8, 24, 24, 24, 24, 8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24,
30+
0, 0, 16, 16, 0, 16, 0, 0, 0, 16, 0, 0, 16, 16, 16, 16, 0, 0, 16, 16, 0, 16, 0, 0,
31+
0, 16, 0, 0, 16, 16, 16, 16, 8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24,
32+
0, 0, 16, 16, 0, 16, 0, 0, 0, 16, 0, 0, 16, 16, 16, 16, 0, 0, 16, 16, 0, 16, 0, 0,
33+
0, 16, 0, 0, 16, 16, 16, 16, 8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24,
34+
8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24, 0, 0, 16, 16, 0, 16, 0, 0,
35+
0, 16, 0, 0, 16, 16, 16, 16, 8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24,
36+
8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24};
37+
static const uint8_t lookup2[256] = {
38+
0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4,
39+
4, 4, 4, 2, 2, 6, 6, 2, 6, 2, 2, 2, 6, 2, 2, 6, 6, 6, 6, 2, 2, 6, 6, 2, 6, 2, 2, 2, 6,
40+
2, 2, 6, 6, 6, 6, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 2, 2, 6, 6, 2, 6, 2,
41+
2, 2, 6, 2, 2, 6, 6, 6, 6, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 0, 0, 4, 4,
42+
0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 2,
43+
2, 6, 6, 2, 6, 2, 2, 2, 6, 2, 2, 6, 6, 6, 6, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4,
44+
4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 2, 2, 6, 6, 2, 6, 2, 2, 2, 6, 2,
45+
2, 6, 6, 6, 6, 2, 2, 6, 6, 2, 6, 2, 2, 2, 6, 2, 2, 6, 6, 6, 6, 2, 2, 6, 6, 2, 6, 2, 2,
46+
2, 6, 2, 2, 6, 6, 6, 6, 2, 2, 6, 6, 2, 6, 2, 2, 2, 6, 2, 2, 6, 6, 6, 6};
47+
48+
static inline int filter(uint32_t const x) {
49+
uint32_t f;
50+
f = lookup1[x & 0xff] | lookup2[(x >> 8) & 0xff];
51+
f |= 0x0d938 >> (x >> 16 & 0xf) & 1;
52+
return BIT(0xEC57E80A, f);
53+
}
54+
55+
#ifndef __ARM_ARCH_7EM__
56+
static inline uint8_t evenparity32(uint32_t x) {
57+
return __builtin_parity(x);
58+
}
59+
#endif
60+
61+
#ifdef __ARM_ARCH_7EM__
62+
static inline uint8_t evenparity32(uint32_t x) {
63+
uint32_t result;
64+
__asm__ volatile("eor r1, %[x], %[x], lsr #16 \n\t" // r1 = x ^ (x >> 16)
65+
"eor r1, r1, r1, lsr #8 \n\t" // r1 = r1 ^ (r1 >> 8)
66+
"eor r1, r1, r1, lsr #4 \n\t" // r1 = r1 ^ (r1 >> 4)
67+
"eor r1, r1, r1, lsr #2 \n\t" // r1 = r1 ^ (r1 >> 2)
68+
"eor r1, r1, r1, lsr #1 \n\t" // r1 = r1 ^ (r1 >> 1)
69+
"and %[result], r1, #1 \n\t" // result = r1 & 1
70+
: [result] "=r"(result)
71+
: [x] "r"(x)
72+
: "r1");
73+
return result;
74+
}
75+
#endif
76+
77+
static inline void update_contribution(unsigned int data[], int item, int mask1, int mask2) {
78+
int p = data[item] >> 25;
79+
p = p << 1 | evenparity32(data[item] & mask1);
80+
p = p << 1 | evenparity32(data[item] & mask2);
81+
data[item] = p << 24 | (data[item] & 0xffffff);
82+
}
83+
84+
static inline uint32_t crypt_word(struct Crypto1State* s) {
85+
// "in" and "x" are always 0 (last iteration)
86+
uint32_t res_ret = 0;
87+
uint32_t feedin, t;
88+
for(int i = 0; i <= 31; i++) {
89+
res_ret |= (filter(s->odd) << (24 ^ i)); //-V629
90+
feedin = LF_POLY_EVEN & s->even;
91+
feedin ^= LF_POLY_ODD & s->odd;
92+
s->even = s->even << 1 | (evenparity32(feedin));
93+
t = s->odd, s->odd = s->even, s->even = t;
94+
}
95+
return res_ret;
96+
}
97+
98+
static inline void crypt_word_noret(struct Crypto1State* s, uint32_t in, int x) {
99+
uint8_t ret;
100+
uint32_t feedin, t, next_in;
101+
for(int i = 0; i <= 31; i++) {
102+
next_in = BEBIT(in, i);
103+
ret = filter(s->odd);
104+
feedin = ret & (!!x);
105+
feedin ^= LF_POLY_EVEN & s->even;
106+
feedin ^= LF_POLY_ODD & s->odd;
107+
feedin ^= !!next_in;
108+
s->even = s->even << 1 | (evenparity32(feedin));
109+
t = s->odd, s->odd = s->even, s->even = t;
110+
}
111+
return;
112+
}
113+
114+
static inline uint32_t crypt_word_ret(struct Crypto1State* s, uint32_t in, int x) {
115+
uint32_t ret = 0;
116+
uint32_t feedin, t, next_in;
117+
uint8_t next_ret;
118+
for(int i = 0; i <= 31; i++) {
119+
next_in = BEBIT(in, i);
120+
next_ret = filter(s->odd);
121+
feedin = next_ret & (!!x);
122+
feedin ^= LF_POLY_EVEN & s->even;
123+
feedin ^= LF_POLY_ODD & s->odd;
124+
feedin ^= !!next_in;
125+
s->even = s->even << 1 | (evenparity32(feedin));
126+
t = s->odd, s->odd = s->even, s->even = t;
127+
ret |= next_ret << (24 ^ i);
128+
}
129+
return ret;
130+
}
131+
132+
static inline void rollback_word_noret(struct Crypto1State* s, uint32_t in, int x) {
133+
uint8_t ret;
134+
uint32_t feedin, t, next_in;
135+
for(int i = 31; i >= 0; i--) {
136+
next_in = BEBIT(in, i);
137+
s->odd &= 0xffffff;
138+
t = s->odd, s->odd = s->even, s->even = t;
139+
ret = filter(s->odd);
140+
feedin = ret & (!!x);
141+
feedin ^= s->even & 1;
142+
feedin ^= LF_POLY_EVEN & (s->even >>= 1);
143+
feedin ^= LF_POLY_ODD & s->odd;
144+
feedin ^= !!next_in;
145+
s->even |= (evenparity32(feedin)) << 23;
146+
}
147+
return;
148+
}
149+
150+
static inline uint32_t prng_successor(uint32_t x, uint32_t n) {
151+
SWAPENDIAN(x);
152+
while(n--) x = x >> 1 | (x >> 16 ^ x >> 18 ^ x >> 19 ^ x >> 21) << 31;
153+
return SWAPENDIAN(x);
154+
}
155+
156+
#endif // CRYPTO1_H

mfkey/images/mfkey.png

107 Bytes
Loading

0 commit comments

Comments
 (0)