Skip to content

Commit 89fef68

Browse files
authored
Merge branch 'dev' into felica-handle-lites-ndef-mode
2 parents 7b4e60d + ad94694 commit 89fef68

File tree

24 files changed

+457
-53
lines changed

24 files changed

+457
-53
lines changed

applications/debug/unit_tests/resources/unit_tests/js/basic.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ let flipper = require("flipper");
44
tests.assert_eq(1337, 1337);
55
tests.assert_eq("hello", "hello");
66

7-
tests.assert_eq("compatible", sdkCompatibilityStatus(0, 1));
7+
tests.assert_eq("compatible", sdkCompatibilityStatus(1, 0));
88
tests.assert_eq("firmwareTooOld", sdkCompatibilityStatus(100500, 0));
99
tests.assert_eq("firmwareTooNew", sdkCompatibilityStatus(-100500, 0));
1010
tests.assert_eq(true, doesSdkSupport(["baseline"]));
1111
tests.assert_eq(false, doesSdkSupport(["abobus", "other-nonexistent-feature"]));
1212

1313
tests.assert_eq("flipperdevices", flipper.firmwareVendor);
14-
tests.assert_eq(0, flipper.jsSdkVersion[0]);
15-
tests.assert_eq(3, flipper.jsSdkVersion[1]);
14+
tests.assert_eq(1, flipper.jsSdkVersion[0]);
15+
tests.assert_eq(0, flipper.jsSdkVersion[1]);

applications/main/infrared/resources/infrared/assets/audio.ir

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4689,3 +4689,35 @@ protocol: NECext
46894689
address: 87 7C 00 00
46904690
command: 1D E2 00 00
46914691
#
4692+
# Model: JVC RM-SRVNB1A for JVC RV-NB1 boombox (2004)
4693+
#
4694+
name: Power
4695+
type: raw
4696+
frequency: 38000
4697+
duty_cycle: 0.333333
4698+
data: 8400 4200 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 526 526 526 526 526 526 526 526 526 526 526 526 526 526 526 526 25248 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 526 526 526 526 526 526 526 526 526 526 526 526 526 526 526 526 33664
4699+
#
4700+
name: Vol_up
4701+
type: raw
4702+
frequency: 38000
4703+
duty_cycle: 0.333333
4704+
data: 8400 4200 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 1578 526 526 526 526 526 526 526 526 526 526 526 526 526 526 526 25248 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 1578 526 526 526 526 526 526 526 526 526 526 526 526 526 526 526 33664
4705+
#
4706+
name: Vol_dn
4707+
type: raw
4708+
frequency: 38000
4709+
duty_cycle: 0.333333
4710+
data: 8400 4200 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 526 526 1578 526 526 526 526 526 526 526 526 526 526 526 526 526 25248 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 526 526 1578 526 526 526 526 526 526 526 526 526 526 526 526 526 33664
4711+
#
4712+
name: Next
4713+
type: raw
4714+
frequency: 38000
4715+
duty_cycle: 0.333333
4716+
data: 8400 4200 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 1578 526 526 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 25248 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 1578 526 526 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 33664
4717+
#
4718+
name: Prev
4719+
type: raw
4720+
frequency: 38000
4721+
duty_cycle: 0.333333
4722+
data: 8400 4200 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 526 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 25248 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 526 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 33664
4723+
#

applications/main/nfc/application.fam

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,3 +265,12 @@ App(
265265
requires=["cli"],
266266
sources=["nfc_cli.c"],
267267
)
268+
269+
App(
270+
appid="banapass_parser",
271+
apptype=FlipperAppType.PLUGIN,
272+
entry_point="banapass_plugin_ep",
273+
targets=["f7"],
274+
requires=["nfc"],
275+
sources=["plugins/supported_cards/banapass.c"],
276+
)
Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
#include "nfc_supported_card_plugin.h"
2+
3+
#include <flipper_application/flipper_application.h>
4+
5+
#include <nfc/nfc_device.h>
6+
#include <bit_lib/bit_lib.h>
7+
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
8+
9+
#define TAG "Banapass"
10+
11+
static const uint64_t banapass_key_b_value_block = 0x019761AA8082;
12+
static const uint64_t banapass_key_b_access_code = 0x574343467632;
13+
typedef struct {
14+
uint64_t a;
15+
uint64_t b;
16+
} MfClassicKeyPair;
17+
18+
static MfClassicKeyPair banapass_keys_if_value_block[] = {
19+
{.a = 0x6090D00632F5, .b = 0x019761AA8082},
20+
{.a = 0xA99164400748, .b = 0x62742819AD7C},
21+
{.a = 0xCC5075E42BA1, .b = 0xB9DF35A0814C},
22+
{.a = 0x8AF9C718F23D, .b = 0x58CD5C3673CB},
23+
{.a = 0xFC80E88EB88C, .b = 0x7A3CDAD7C023},
24+
{.a = 0x30424C029001, .b = 0x024E4E44001F},
25+
{.a = 0xECBBFA57C6AD, .b = 0x4757698143BD},
26+
{.a = 0x1D30972E6485, .b = 0xF8526D1A8D6D},
27+
{.a = 0x1300EC8C7E80, .b = 0xF80A65A87FFA},
28+
{.a = 0xDEB06ED4AF8E, .b = 0x4AD96BF28190},
29+
{.a = 0x000390014D41, .b = 0x0800F9917CB0},
30+
{.a = 0x730050555253, .b = 0x4146D4A956C4},
31+
{.a = 0x131157FBB126, .b = 0xE69DD9015A43},
32+
{.a = 0x337237F254D5, .b = 0x9A8389F32FBF},
33+
{.a = 0x7B8FB4A7100B, .b = 0xC8382A233993},
34+
{.a = 0x7B304F2A12A6, .b = 0xFC9418BF788B},
35+
};
36+
37+
static MfClassicKeyPair banapass_keys_if_access_code[] = {
38+
{.a = 0x6090D00632F5, .b = 0x574343467632},
39+
{.a = 0xA99164400748, .b = 0x62742819AD7C},
40+
{.a = 0xCC5075E42BA1, .b = 0xB9DF35A0814C},
41+
{.a = 0x8AF9C718F23D, .b = 0x58CD5C3673CB},
42+
{.a = 0xFC80E88EB88C, .b = 0x7A3CDAD7C023},
43+
{.a = 0x30424C029001, .b = 0x024E4E44001F},
44+
{.a = 0xECBBFA57C6AD, .b = 0x4757698143BD},
45+
{.a = 0x1D30972E6485, .b = 0xF8526D1A8D6D},
46+
{.a = 0x1300EC8C7E80, .b = 0xF80A65A87FFA},
47+
{.a = 0xDEB06ED4AF8E, .b = 0x4AD96BF28190},
48+
{.a = 0x000390014D41, .b = 0x0800F9917CB0},
49+
{.a = 0x730050555253, .b = 0x4146D4A956C4},
50+
{.a = 0x131157FBB126, .b = 0xE69DD9015A43},
51+
{.a = 0x337237F254D5, .b = 0x9A8389F32FBF},
52+
{.a = 0x7B8FB4A7100B, .b = 0xC8382A233993},
53+
{.a = 0x7B304F2A12A6, .b = 0xFC9418BF788B},
54+
};
55+
56+
static bool banapass_verify(Nfc* nfc) {
57+
bool verified = true;
58+
59+
const uint8_t verify_sector = 0;
60+
uint8_t block_num = mf_classic_get_first_block_num_of_sector(verify_sector);
61+
FURI_LOG_D(TAG, "Verifying sector %u", verify_sector);
62+
63+
MfClassicKey key_a_0 = {};
64+
bit_lib_num_to_bytes_be(
65+
banapass_keys_if_value_block[0].a, COUNT_OF(key_a_0.data), key_a_0.data);
66+
67+
MfClassicAuthContext auth_ctx = {};
68+
MfClassicError error =
69+
mf_classic_poller_sync_auth(nfc, block_num, &key_a_0, MfClassicKeyTypeA, &auth_ctx);
70+
71+
if(error != MfClassicErrorNone) {
72+
FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error);
73+
verified = false;
74+
}
75+
76+
return verified;
77+
}
78+
79+
static bool banapass_read(Nfc* nfc, NfcDevice* device) {
80+
furi_assert(nfc);
81+
furi_assert(device);
82+
83+
bool is_read = false;
84+
85+
MfClassicData* data = mf_classic_alloc();
86+
nfc_device_copy_data(device, NfcProtocolMfClassic, data);
87+
88+
do {
89+
MfClassicType type = MfClassicType1k;
90+
MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);
91+
if(error != MfClassicErrorNone) break;
92+
if(type != MfClassicType1k) {
93+
FURI_LOG_E(TAG, "Card not MIFARE Classic 1k");
94+
break;
95+
}
96+
97+
data->type = type;
98+
MfClassicDeviceKeys keys = {};
99+
100+
// Access Code Read Attempt
101+
for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {
102+
bit_lib_num_to_bytes_be(
103+
banapass_keys_if_access_code[i].a, sizeof(MfClassicKey), keys.key_a[i].data);
104+
FURI_BIT_SET(keys.key_a_mask, i);
105+
bit_lib_num_to_bytes_be(
106+
banapass_keys_if_access_code[i].b, sizeof(MfClassicKey), keys.key_b[i].data);
107+
FURI_BIT_SET(keys.key_b_mask, i);
108+
}
109+
110+
MfClassicError error_access_code = mf_classic_poller_sync_read(nfc, &keys, data);
111+
112+
if(error_access_code == MfClassicErrorNone) {
113+
nfc_device_set_data(device, NfcProtocolMfClassic, data);
114+
is_read = true;
115+
break;
116+
}
117+
118+
// Value Block Read Attempt
119+
for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {
120+
bit_lib_num_to_bytes_be(
121+
banapass_keys_if_value_block[i].a, sizeof(MfClassicKey), keys.key_a[i].data);
122+
FURI_BIT_SET(keys.key_a_mask, i);
123+
bit_lib_num_to_bytes_be(
124+
banapass_keys_if_value_block[i].b, sizeof(MfClassicKey), keys.key_b[i].data);
125+
FURI_BIT_SET(keys.key_b_mask, i);
126+
}
127+
128+
MfClassicError error_value_block = mf_classic_poller_sync_read(nfc, &keys, data);
129+
130+
if(error_value_block == MfClassicErrorNone) {
131+
nfc_device_set_data(device, NfcProtocolMfClassic, data);
132+
is_read = true;
133+
break;
134+
}
135+
FURI_LOG_E(TAG, "Failed to read data. Bad keys?");
136+
} while(false);
137+
138+
mf_classic_free(data);
139+
140+
return is_read;
141+
}
142+
143+
static bool banapass_parse(const NfcDevice* device, FuriString* parsed_data) {
144+
furi_assert(device);
145+
146+
const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);
147+
148+
bool parsed = false;
149+
150+
do {
151+
// verify key
152+
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 0);
153+
uint64_t key_a = bit_lib_bytes_to_num_be(sec_tr->key_a.data, 6);
154+
uint64_t key_b = bit_lib_bytes_to_num_be(sec_tr->key_b.data, 6);
155+
if(key_a != banapass_keys_if_value_block[0].a) break;
156+
157+
furi_string_set_str(parsed_data, "\e#Banapass\n");
158+
furi_string_cat_str(
159+
parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
160+
furi_string_cat_str(parsed_data, "\nBandai Namco Passport\n");
161+
162+
// banapass Magic is stored at block 1, byte 2-7
163+
uint8_t magic_bytes[6];
164+
for(int i = 0; i < 6; i++) {
165+
magic_bytes[i] = data->block[1].data[2 + i];
166+
}
167+
168+
// verify banapass magic
169+
if(magic_bytes[0] != 'N' || magic_bytes[1] != 'B' || magic_bytes[2] != 'G' ||
170+
magic_bytes[3] != 'I' || magic_bytes[4] != 'C')
171+
break;
172+
173+
// banapass checksum is stored at block 1, starts from byte 8-15
174+
uint8_t check_sum[8];
175+
for(int i = 0; i < 8; i++) {
176+
check_sum[i] = data->block[1].data[8 + i];
177+
}
178+
179+
furi_string_cat_str(
180+
parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
181+
182+
bool is_block_2_null = true;
183+
for(int i = 0; i < 16; i++) {
184+
if(data->block[2].data[i] != 0) {
185+
is_block_2_null = false;
186+
break;
187+
}
188+
}
189+
if(is_block_2_null) {
190+
furi_string_cat_str(
191+
parsed_data,
192+
"\nPlease scan the clone at the\nnearest CHUNITHM or\nmaimai Cabinet for the\nAccess Code.\n");
193+
furi_string_cat_str(
194+
parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
195+
} else {
196+
switch(key_b) {
197+
case banapass_key_b_value_block:
198+
int32_t value = 0;
199+
uint8_t addr = 0;
200+
bool value_found = mf_classic_block_to_value(
201+
&data->block[2], &value, &addr); // block 2 is value block
202+
if(value_found) {
203+
furi_string_cat_printf(parsed_data, "\nValue: %08lX", value);
204+
} else {
205+
furi_string_cat_str(parsed_data, "\nPotential clone:\nInvalid value block.");
206+
}
207+
furi_string_cat_str(
208+
parsed_data,
209+
"\nPlease check the back of\nyour Bandai Namco Passport\nfor the Access Code.\n");
210+
furi_string_cat_str(
211+
parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
212+
break;
213+
214+
case banapass_key_b_access_code:
215+
// banapass access code is stored as decimal hex representation in block 2, starts from byte 6, len 10 bytes
216+
uint8_t access_code[10];
217+
218+
furi_string_cat_printf(parsed_data, "\nAccess Code:\n");
219+
bool access_code_is_bcd = true;
220+
221+
for(int i = 0; i < 10; i++) {
222+
access_code[i] = data->block[2].data[6 + i];
223+
furi_string_cat_printf(parsed_data, "%02X", access_code[i]);
224+
if(i % 2 == 1) {
225+
furi_string_cat_str(parsed_data, " ");
226+
}
227+
if((access_code[i] >> 4) > 9) access_code_is_bcd = false;
228+
if((access_code[i] & 0x0F) > 9) access_code_is_bcd = false;
229+
}
230+
furi_string_cat_printf(
231+
parsed_data, "\nBCD valid: %s\n", access_code_is_bcd ? "Yes" : "No");
232+
if((access_code[0] >> 4) != 3) {
233+
furi_string_cat_printf(
234+
parsed_data,
235+
"Potential clone:\nAccess Code preamble\nexpected 3, got %d\n",
236+
(access_code[0] >> 4));
237+
}
238+
furi_string_cat_str(
239+
parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
240+
241+
break;
242+
default:
243+
break;
244+
}
245+
}
246+
furi_string_cat_str(parsed_data, "\nMagic:\n");
247+
for(int i = 0; i < 6; i++) {
248+
furi_string_cat_printf(parsed_data, "%c", magic_bytes[i]);
249+
}
250+
furi_string_cat_str(parsed_data, "\nChecksum:\n");
251+
for(int i = 0; i < 8; i++) {
252+
furi_string_cat_printf(parsed_data, "%02X ", check_sum[i]);
253+
}
254+
255+
furi_string_cat_str(parsed_data, "\n");
256+
furi_string_cat_str(
257+
parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
258+
259+
parsed = true;
260+
261+
} while(false);
262+
263+
return parsed;
264+
}
265+
266+
/* Actual implementation of app<>plugin interface */
267+
static const NfcSupportedCardsPlugin banapass_plugin = {
268+
.protocol = NfcProtocolMfClassic,
269+
.verify = banapass_verify,
270+
.read = banapass_read,
271+
.parse = banapass_parse,
272+
};
273+
274+
/* Plugin descriptor to comply with basic plugin specification */
275+
static const FlipperAppPluginDescriptor banapass_plugin_descriptor = {
276+
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
277+
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
278+
.entry_point = &banapass_plugin,
279+
};
280+
281+
/* Plugin entry point - must return a pointer to const descriptor */
282+
const FlipperAppPluginDescriptor* banapass_plugin_ep(void) {
283+
return &banapass_plugin_descriptor;
284+
}

applications/main/subghz/subghz_cli.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1092,7 +1092,7 @@ static void subghz_cli_command_chat(PipeSide* pipe, FuriString* args) {
10921092
break;
10931093
}
10941094
}
1095-
if(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {
1095+
if(cli_is_pipe_broken_or_is_etx_next_char(pipe)) {
10961096
printf("\r\n");
10971097
chat_event.event = SubGhzChatEventUserExit;
10981098
subghz_chat_worker_put_event_chat(subghz_chat, &chat_event);

applications/services/cli/cli_vcp.c

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,11 @@ static void cli_vcp_internal_event_happened(FuriEventLoopObject* object, void* c
218218
pipe_detach_from_event_loop(cli_vcp->own_pipe);
219219
pipe_free(cli_vcp->own_pipe);
220220
cli_vcp->own_pipe = NULL;
221+
222+
// wait for shell to stop
223+
cli_shell_join(cli_vcp->shell);
224+
cli_shell_free(cli_vcp->shell);
225+
pipe_free(cli_vcp->shell_pipe);
221226
break;
222227
}
223228

@@ -226,14 +231,6 @@ static void cli_vcp_internal_event_happened(FuriEventLoopObject* object, void* c
226231
FURI_LOG_D(TAG, "Connected");
227232
cli_vcp->is_connected = true;
228233

229-
// wait for previous shell to stop
230-
furi_check(!cli_vcp->own_pipe);
231-
if(cli_vcp->shell) {
232-
cli_shell_join(cli_vcp->shell);
233-
cli_shell_free(cli_vcp->shell);
234-
pipe_free(cli_vcp->shell_pipe);
235-
}
236-
237234
// start shell thread
238235
PipeSideBundle bundle = pipe_alloc(VCP_BUF_SIZE, 1);
239236
cli_vcp->own_pipe = bundle.alices_side;

0 commit comments

Comments
 (0)