subghz upgrades [ci skip]

docs update
pr template update
changelog update
This commit is contained in:
MX
2026-04-21 03:31:11 +03:00
parent 9cc61f16bf
commit 63d49b6e48
22 changed files with 673 additions and 147 deletions
+1 -3
View File
@@ -290,9 +290,7 @@ bool mf_desfire_file_settings_parse(MfDesfireFileSettings* data, const BitBuffer
printf("\r\n");
break;
}
if(additional_access_rights_len >
MF_DESFIRE_MAX_KEYS * sizeof(MfDesfireFileAccessRights))
break;
if(additional_access_rights_len > MF_DESFIRE_MAX_KEYS - 1) break;
memcpy(
&file_settings_temp.access_rights[1],
@@ -169,6 +169,10 @@ static MfUltralightCommand
MfUltralightPage pages[64] = {};
uint8_t page_cnt = (end_page - start_page) + 1;
if(page_cnt > COUNT_OF(pages)) {
command = MfUltralightCommandNotProcessedNAK;
break;
}
mf_ultralight_listener_perform_read(pages, instance, start_page, page_cnt, do_i2c_check);
bit_buffer_copy_bytes(instance->tx_buffer, (uint8_t*)pages, page_cnt * 4);
+54 -2
View File
@@ -258,6 +258,10 @@ static void subghz_protocol_encoder_came_atomo_get_upload(
btn = 0x4;
} else if(btn == 0x4) {
btn = 0x6;
} else if(btn == 0x5) {
btn = 0x0C;
} else if(btn == 0x6) {
btn = 0x0E;
}
// override button if we change it with signal settings button editor
@@ -482,9 +486,11 @@ void subghz_protocol_decoder_came_atomo_feed(void* context, bool level, uint32_t
ManchesterEvent event = ManchesterEventReset;
switch(instance->decoder.parser_step) {
case CameAtomoDecoderStepReset:
// There are two known options for the header: 72K us (TOP42R, TOP44R) or 12k us (found on TOP44RBN)
// There are two known options for the header: 72K us (TOP42R, TOP44R) or 12k us (found on TOP44RBN) / 19k us (TOPD4REN)
if((!level) && ((DURATION_DIFF(duration, subghz_protocol_came_atomo_const.te_long * 10) <
subghz_protocol_came_atomo_const.te_delta * 20) ||
(DURATION_DIFF(duration, subghz_protocol_came_atomo_const.te_long * 16) <
subghz_protocol_came_atomo_const.te_delta * 10) ||
(DURATION_DIFF(duration, subghz_protocol_came_atomo_const.te_long * 60) <
subghz_protocol_came_atomo_const.te_delta * 40))) {
//Found header CAME
@@ -661,6 +667,10 @@ static void subghz_protocol_came_atomo_remote_controller(SubGhzBlockGeneric* ins
instance->btn = 0x3;
} else if(btn_decode == 0x6) {
instance->btn = 0x4;
} else if(btn_decode == 0x0C) {
instance->btn = 0x5;
} else if(btn_decode == 0x0E) {
instance->btn = 0x6;
}
uint32_t hi = pack[0] << 24 | pack[1] << 16 | pack[2] << 8 | pack[3];
@@ -671,7 +681,7 @@ static void subghz_protocol_came_atomo_remote_controller(SubGhzBlockGeneric* ins
if(subghz_custom_btn_get_original() == 0) {
subghz_custom_btn_set_original(instance->btn);
}
subghz_custom_btn_set_max(3);
subghz_custom_btn_set_max(4);
}
void atomo_encrypt(uint8_t* buff) {
@@ -738,6 +748,12 @@ static uint8_t subghz_protocol_came_atomo_get_btn_code(void) {
case 0x4:
btn = 0x1;
break;
case 0x5:
btn = 0x1;
break;
case 0x6:
btn = 0x1;
break;
default:
break;
@@ -756,6 +772,12 @@ static uint8_t subghz_protocol_came_atomo_get_btn_code(void) {
case 0x4:
btn = 0x2;
break;
case 0x5:
btn = 0x2;
break;
case 0x6:
btn = 0x2;
break;
default:
break;
@@ -774,6 +796,36 @@ static uint8_t subghz_protocol_came_atomo_get_btn_code(void) {
case 0x4:
btn = 0x3;
break;
case 0x5:
btn = 0x4;
break;
case 0x6:
btn = 0x4;
break;
default:
break;
}
} else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_RIGHT) {
switch(original_btn_code) {
case 0x1:
btn = 0x5;
break;
case 0x2:
btn = 0x5;
break;
case 0x3:
btn = 0x5;
break;
case 0x4:
btn = 0x5;
break;
case 0x5:
btn = 0x6;
break;
case 0x6:
btn = 0x5;
break;
default:
break;
+93 -7
View File
@@ -367,7 +367,7 @@ static bool subghz_protocol_keeloq_gen_data(
} else if(
(strcmp(instance->manufacture_name, "DTM_Neo") == 0) ||
(strcmp(instance->manufacture_name, "FAAC_RC,XT") == 0) ||
(strcmp(instance->manufacture_name, "Mutanco_Mutancode") == 0) ||
(strcmp(instance->manufacture_name, "Clemsa_Mutancode") == 0) ||
(strcmp(instance->manufacture_name, "Came_Space") == 0) ||
(strcmp(instance->manufacture_name, "Genius_Bravo") == 0) ||
(strcmp(instance->manufacture_name, "GSN") == 0) ||
@@ -376,20 +376,33 @@ static bool subghz_protocol_keeloq_gen_data(
(strcmp(instance->manufacture_name, "Pecinin") == 0) ||
(strcmp(instance->manufacture_name, "Steelmate") == 0) ||
(strcmp(instance->manufacture_name, "Cardin_S449") == 0) ||
(strcmp(instance->manufacture_name, "Stilmatic") == 0)) {
(strcmp(instance->manufacture_name, "Stilmatic") == 0) ||
(strcmp(instance->manufacture_name, "Wisniowski") == 0) ||
(strcmp(instance->manufacture_name, "ATA_PTX4") == 0) ||
(strcmp(instance->manufacture_name, "Fadini") == 0) ||
(strcmp(instance->manufacture_name, "Seav") == 0)) {
// DTM Neo, Came_Space uses 12bit serial -> simple learning
// FAAC_RC,XT , Mutanco_Mutancode, Genius_Bravo, GSN 12bit serial -> normal learning
// FAAC_RC,XT , Clemsa_Mutancode, Genius_Bravo, GSN 12bit serial -> normal learning
// Rosh, Rossi, Pecinin -> 12bit serial - simple learning
// Steelmate -> 12bit serial - normal learning
// Cardin_S449 -> 12bit serial - normal learning
// Stilmatic (r-tech) -> 12bit serial - normal learning
// Wisniowski -> 12bit serial - normal learning
// ATA_PTX4 -> 12bit serial - normal learning
// Fadini -> 12bit serial - simple learning
// Seav -> 12bit serial - normal learning
decrypt = btn << 28 | (instance->generic.serial & 0xFFF) << 16 |
instance->generic.cnt;
} else if(
(strcmp(instance->manufacture_name, "NICE_Smilo") == 0) ||
(strcmp(instance->manufacture_name, "NICE_MHOUSE") == 0) ||
(strcmp(instance->manufacture_name, "JCM_Tech") == 0)) {
// Nice Smilo, MHouse, JCM -> 8bit serial - simple learning
(strcmp(instance->manufacture_name, "JCM_Tech") == 0) ||
(strcmp(instance->manufacture_name, "Pujol_Vario") == 0) ||
(strcmp(instance->manufacture_name, "Pujol") == 0) ||
(strcmp(instance->manufacture_name, "Erreka") == 0)) {
// Nice Smilo, MHouse, JCM, Pujol_Vario -> 8bit serial - simple learning
// Pujol -> 8bit serial - special learning
// Erreka -> 8bit serial - secure learning with seed
decrypt = btn << 28 | (instance->generic.serial & 0xFF) << 16 |
instance->generic.cnt;
} else if(
@@ -453,6 +466,28 @@ static bool subghz_protocol_keeloq_gen_data(
fix, manufacture_code->key);
hop = subghz_protocol_keeloq_common_encrypt(decrypt, man);
break;
case KEELOQ_LEARNING_AERF:
man = subghz_protocol_keeloq_common_learning_aerf(
fix, manufacture_code->key);
hop = subghz_protocol_keeloq_common_encrypt(decrypt, man);
break;
case KEELOQ_LEARNING_ERREKA:
man = subghz_protocol_keeloq_common_learning_erreka(
fix, instance->generic.seed, manufacture_code->key);
hop = subghz_protocol_keeloq_common_encrypt(decrypt, man);
break;
case KEELOQ_LEARNING_PUJOL:
man = subghz_protocol_keeloq_common_learning_pujol(
fix, manufacture_code->key);
hop = subghz_protocol_keeloq_common_encrypt(decrypt, man);
break;
case KEELOQ_LEARNING_SIMPLE_JCM:
//Simple Learning 8 bit serial
decrypt = btn << 28 | (instance->generic.serial & 0xFF) << 16 |
instance->generic.cnt;
hop = subghz_protocol_keeloq_common_encrypt(
decrypt, manufacture_code->key);
break;
case KEELOQ_LEARNING_UNKNOWN:
if(kl_type_en == 1) {
hop = subghz_protocol_keeloq_common_encrypt(
@@ -511,7 +546,7 @@ bool subghz_protocol_keeloq_create_data(
return false;
}
bool subghz_protocol_keeloq_bft_create_data(
bool subghz_protocol_keeloq_seed_create_data(
void* context,
FlipperFormat* flipper_format,
uint32_t serial,
@@ -1101,6 +1136,55 @@ static uint32_t subghz_protocol_keeloq_check_remote_controller_selector(
return decrypt;
}
break;
case KEELOQ_LEARNING_AERF:
man = subghz_protocol_keeloq_common_learning_aerf(fix, manufacture_code->key);
decrypt = subghz_protocol_keeloq_common_decrypt_derived(hop, man, 0x240u);
if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) {
*manufacture_name = furi_string_get_cstr(manufacture_code->name);
keystore->mfname = *manufacture_name;
return decrypt;
}
decrypt = subghz_protocol_keeloq_common_decrypt_derived(hop, man, 0x210u);
if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) {
*manufacture_name = furi_string_get_cstr(manufacture_code->name);
keystore->mfname = *manufacture_name;
return decrypt;
}
decrypt = subghz_protocol_keeloq_common_decrypt(hop, man);
if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) {
*manufacture_name = furi_string_get_cstr(manufacture_code->name);
keystore->mfname = *manufacture_name;
return decrypt;
}
break;
case KEELOQ_LEARNING_ERREKA:
man = subghz_protocol_keeloq_common_learning_erreka(
fix, instance->seed, manufacture_code->key);
decrypt = subghz_protocol_keeloq_common_decrypt(hop, man);
if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) {
*manufacture_name = furi_string_get_cstr(manufacture_code->name);
keystore->mfname = *manufacture_name;
return decrypt;
}
break;
case KEELOQ_LEARNING_PUJOL:
man = subghz_protocol_keeloq_common_learning_pujol(fix, manufacture_code->key);
decrypt = subghz_protocol_keeloq_common_decrypt(hop, man);
if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) {
*manufacture_name = furi_string_get_cstr(manufacture_code->name);
keystore->mfname = *manufacture_name;
return decrypt;
}
break;
case KEELOQ_LEARNING_SIMPLE_JCM:
// Simple Learning 8 bit serial
decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key);
if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) {
*manufacture_name = furi_string_get_cstr(manufacture_code->name);
keystore->mfname = *manufacture_name;
return decrypt;
}
break;
case KEELOQ_LEARNING_UNKNOWN:
// Simple Learning
decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key);
@@ -1625,7 +1709,9 @@ void subghz_protocol_decoder_keeloq_get_string(void* context, FuriString* output
code_found_reverse_lo,
instance->generic.btn,
instance->manufacture_name);
} else if(strcmp(instance->manufacture_name, "BFT") == 0) {
} else if(
(strcmp(instance->manufacture_name, "BFT") == 0) ||
(strcmp(instance->manufacture_name, "Erreka") == 0)) {
// Allow counter edit
subghz_block_generic_global.cnt_is_available = true;
subghz_block_generic_global.cnt_length_bit = 16;
+94
View File
@@ -140,3 +140,97 @@ inline uint64_t
subghz_protocol_keeloq_common_magic_serial_type3_learning(uint32_t data, uint64_t man) {
return (man & 0xFFFFFFFFFF000000) | (data & 0xFFFFFF);
}
// Key utils
static inline uint32_t subghz_protocol_keeloq_common_manufacturer_nl_extend(
uint32_t x,
uint32_t k_lo,
uint32_t k_hi,
uint32_t outer_limit) {
uint32_t r4 = outer_limit;
uint32_t r5 = 0u;
const uint32_t r6 = KEELOQ_NLF;
while(r5 != r4) {
if(r5 < 0x210u) {
uint32_t r1 = (x >> 15) & 1u;
uint32_t r7 = r1 ^ ((x >> 1) | (x << 31));
r1 = (15u - r5) & 0x3Fu;
uint32_t lr = 32u - r1;
uint32_t ip = r1 - 32u;
lr = k_hi << lr;
r1 = k_lo >> r1;
ip = (r1 < 32u) ? (k_hi >> ip) : 0u;
r1 = (r1 | lr | ip) & 1u;
ip = (x >> 30) & 1u;
r1 ^= r7;
r7 = (x >> 25) & 1u;
r7 += ip << 1;
ip = (x >> 19) & 1u;
ip += r7 << 1;
r7 = (x >> 8) & 1u;
r7 += ip << 1;
x &= 1u;
x += r7 << 1;
x = (int32_t)r6 >> (x & 31u);
x &= 1u;
x ^= r1;
}
r5 += 1u;
}
return x;
}
static inline uint32_t subghz_protocol_keeloq_common_word_rotate16(uint32_t v) {
return (v >> 16) | (v << 16);
}
inline uint32_t subghz_protocol_keeloq_common_decrypt_derived(
uint32_t hop_encrypted,
uint64_t derived_manufacturing_key,
uint32_t outer_limit) {
return subghz_protocol_keeloq_common_manufacturer_nl_extend(
hop_encrypted,
(uint32_t)derived_manufacturing_key,
(uint32_t)(derived_manufacturing_key >> 32u),
outer_limit);
}
// Protocol (Manufacturer) specific learning
// TODO: Better documentation for these functions
inline uint64_t subghz_protocol_keeloq_common_learning_aerf(uint32_t data, const uint64_t key) {
uint32_t k_lo = (uint32_t)key;
uint32_t k_hi = (uint32_t)(key >> 32);
uint32_t d = data & 0x0FFFFFFFu;
uint32_t x = d | 0x20000000u;
x = subghz_protocol_keeloq_common_manufacturer_nl_extend(x, k_lo, k_hi, 0x40u);
uint32_t k1 = x;
x = d | 0x60000000u;
x = subghz_protocol_keeloq_common_manufacturer_nl_extend(x, k_lo, k_hi, 0x40u);
return ((uint64_t)x << 32) | k1;
}
inline uint64_t
subghz_protocol_keeloq_common_learning_erreka(uint32_t data, uint32_t mix, const uint64_t key) {
uint32_t d = data & 0x0FFFFFFFu;
uint32_t k1 = subghz_protocol_keeloq_common_decrypt(d | 0x20000000u, key);
uint32_t r4 = mix >> 4;
uint32_t r1 = (mix << 4) & 0xF000F000u;
r4 = (r4 & 0x0F000F00u) | r1;
uint32_t r5 = mix & 0x00FF00FFu;
uint32_t x = r4 | r5;
x |= 0x60000000u;
uint32_t k2 = subghz_protocol_keeloq_common_decrypt(x, key);
return ((uint64_t)k2 << 32) | k1;
}
inline uint64_t subghz_protocol_keeloq_common_learning_pujol(uint32_t data, const uint64_t key) {
uint32_t d = data & 0x0FFFFFFFu;
uint32_t w1 = subghz_protocol_keeloq_common_decrypt(d | 0x20000000u, key);
uint32_t w2 = subghz_protocol_keeloq_common_decrypt(d | 0x60000000u, key);
uint32_t k1 = subghz_protocol_keeloq_common_word_rotate16(w1);
uint32_t k2 = subghz_protocol_keeloq_common_word_rotate16(w2);
return ((uint64_t)k2 << 32) | k1;
}
+20
View File
@@ -28,6 +28,10 @@
// #define BENINCA_ARC_KEY_TYPE 9u -- RESERVED
#define KEELOQ_LEARNING_SIMPLE_KINGGATES 10u
#define KEELOQ_LEARNING_NORMAL_JAROLIFT 11u
#define KEELOQ_LEARNING_ERREKA 12u
#define KEELOQ_LEARNING_PUJOL 13u
#define KEELOQ_LEARNING_AERF 14u
#define KEELOQ_LEARNING_SIMPLE_JCM 15u
/**
* Simple Learning Encrypt
@@ -101,3 +105,19 @@ uint64_t subghz_protocol_keeloq_common_magic_serial_type2_learning(uint32_t data
*/
uint64_t subghz_protocol_keeloq_common_magic_serial_type3_learning(uint32_t data, uint64_t man);
// Protocol (Manufacturer) specific learning
// TODO: Better documentation for these functions
uint64_t subghz_protocol_keeloq_common_learning_aerf(uint32_t data, const uint64_t key);
uint64_t
subghz_protocol_keeloq_common_learning_erreka(uint32_t data, uint32_t mix, const uint64_t key);
uint64_t subghz_protocol_keeloq_common_learning_pujol(uint32_t data, const uint64_t key);
// Utils
uint32_t subghz_protocol_keeloq_common_decrypt_derived(
uint32_t hop_encrypted,
uint64_t derived_manufacturing_key,
uint32_t outer_limit);
+1 -1
View File
@@ -57,7 +57,7 @@ bool subghz_protocol_keeloq_create_data(
* @param preset Modulation, SubGhzRadioPreset
* @return true On success
*/
bool subghz_protocol_keeloq_bft_create_data(
bool subghz_protocol_keeloq_seed_create_data(
void* context,
FlipperFormat* flipper_format,
uint32_t serial,