mirror of
https://github.com/D4C1-Labs/Flipper-ARF.git
synced 2026-03-30 12:05:49 +00:00
Compare commits
9 Commits
dev-fabb1c
...
dev-8bf12d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8bf12df45d | ||
|
|
f3d08573a1 | ||
|
|
9e52a6eb6b | ||
|
|
faf669b457 | ||
|
|
e445b28d73 | ||
|
|
19e2eaa554 | ||
|
|
2571ad7f22 | ||
|
|
22a0870559 | ||
|
|
1c9d1f404a |
@@ -49,7 +49,7 @@ This project may incorporate, adapt, or build upon **other open-source projects*
|
||||
| PSA (Peugeot/Citroën/DS) | PSA GROUP | 433 MHz | AM/FM | Yes | Yes | Yes |
|
||||
| Ford | Ford V0 | 315/433 MHz | AM | Yes | Yes | Yes |
|
||||
| Fiat | Fiat SpA | 433 MHz | AM | Yes | Yes | Yes |
|
||||
| Fiat | Fiat Marelli | 433 MHz | AM | No | Yes | No |
|
||||
| Fiat | Fiat Marelli/Delphi | 433 MHz | AM | No | Yes | No |
|
||||
| Subaru | Subaru | 433 MHz | AM | Yes | Yes | No |
|
||||
| Mazda | Siemens (5WK49365D) | 315/433 MHz | FM | Yes | Yes | Yes |
|
||||
| Kia/Hyundai | Kia V0 | 433 MHz | FM | Yes | Yes | Yes |
|
||||
|
||||
@@ -10,6 +10,12 @@
|
||||
|
||||
static bool otg_was_enabled = false;
|
||||
|
||||
static bool use_flux_capacitor = false;
|
||||
|
||||
void rolljam_ext_set_flux_capacitor(bool enabled) {
|
||||
use_flux_capacitor = enabled;
|
||||
}
|
||||
|
||||
static void rolljam_ext_power_on(void) {
|
||||
otg_was_enabled = furi_hal_power_is_otg_enabled();
|
||||
if(!otg_was_enabled) {
|
||||
@@ -423,7 +429,7 @@ static int32_t jam_thread_worker(void* context) {
|
||||
0xAA,0x55
|
||||
};
|
||||
|
||||
furi_hal_gpio_write(pin_amp, true);
|
||||
if(use_flux_capacitor) furi_hal_gpio_write(pin_amp, true);
|
||||
jam_start_tx(noise_pattern, 62);
|
||||
|
||||
uint8_t st = cc_state();
|
||||
@@ -432,7 +438,7 @@ static int32_t jam_thread_worker(void* context) {
|
||||
jam_start_tx(noise_pattern, 62);
|
||||
st = cc_state();
|
||||
if(st != MARC_TX) {
|
||||
furi_hal_gpio_write(pin_amp, false);
|
||||
if(use_flux_capacitor) furi_hal_gpio_write(pin_amp, false);
|
||||
FURI_LOG_E(TAG, "JAM: Cannot enter TX!");
|
||||
return -1;
|
||||
}
|
||||
@@ -492,7 +498,7 @@ static int32_t jam_thread_worker(void* context) {
|
||||
}
|
||||
|
||||
cc_idle();
|
||||
furi_hal_gpio_write(pin_amp, false);
|
||||
if(use_flux_capacitor) furi_hal_gpio_write(pin_amp, false);
|
||||
cc_write(CC_IOCFG2, 0x2E);
|
||||
FURI_LOG_I(TAG, "JAM: STOPPED (loops=%lu uf=%lu refills=%lu)", loops, underflows, refills);
|
||||
return 0;
|
||||
@@ -512,13 +518,17 @@ void rolljam_ext_gpio_init(void) {
|
||||
furi_hal_gpio_write(pin_mosi, false);
|
||||
furi_hal_gpio_init(pin_miso, GpioModeInput, GpioPullUp, GpioSpeedVeryHigh);
|
||||
furi_hal_gpio_init(pin_gdo0, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh);
|
||||
furi_hal_gpio_init_simple(pin_amp, GpioModeOutputPushPull);
|
||||
furi_hal_gpio_write(pin_amp, false);
|
||||
if(use_flux_capacitor) {
|
||||
furi_hal_gpio_init_simple(pin_amp, GpioModeOutputPushPull);
|
||||
furi_hal_gpio_write(pin_amp, false);
|
||||
}
|
||||
}
|
||||
|
||||
void rolljam_ext_gpio_deinit(void) {
|
||||
furi_hal_gpio_write(pin_amp, false);
|
||||
furi_hal_gpio_init_simple(pin_amp, GpioModeAnalog);
|
||||
if(use_flux_capacitor) {
|
||||
furi_hal_gpio_write(pin_amp, false);
|
||||
furi_hal_gpio_init_simple(pin_amp, GpioModeAnalog);
|
||||
}
|
||||
furi_hal_gpio_init(pin_cs, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(pin_sck, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(pin_mosi, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
|
||||
void rolljam_ext_gpio_init(void);
|
||||
void rolljam_ext_set_flux_capacitor(bool enabled);
|
||||
void rolljam_ext_gpio_deinit(void);
|
||||
void rolljam_jammer_start(RollJamApp* app);
|
||||
void rolljam_jammer_stop(RollJamApp* app);
|
||||
|
||||
@@ -57,6 +57,11 @@ const char* jam_offset_names[] = {
|
||||
"1000 kHz",
|
||||
};
|
||||
|
||||
const char* hw_names[] = {
|
||||
"CC1101",
|
||||
"Flux Cap",
|
||||
};
|
||||
|
||||
// ============================================================
|
||||
// Scene handlers table (extern declarations in scene header)
|
||||
// ============================================================
|
||||
@@ -119,6 +124,7 @@ static RollJamApp* rolljam_app_alloc(void) {
|
||||
app->mod_index = ModIndex_AM650;
|
||||
app->jam_offset_index = JamOffIndex_700k;
|
||||
app->jam_offset_hz = jam_offset_values[JamOffIndex_700k];
|
||||
app->hw_index = HwIndex_CC1101;
|
||||
|
||||
// Services
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
|
||||
@@ -69,6 +69,17 @@ typedef enum {
|
||||
extern const uint32_t jam_offset_values[];
|
||||
extern const char* jam_offset_names[];
|
||||
|
||||
// ============================================================
|
||||
// Hardware type
|
||||
// ============================================================
|
||||
typedef enum {
|
||||
HwIndex_CC1101 = 0,
|
||||
HwIndex_FluxCapacitor,
|
||||
HwIndex_COUNT,
|
||||
} HwIndex;
|
||||
|
||||
extern const char* hw_names[];
|
||||
|
||||
// ============================================================
|
||||
// Scenes
|
||||
// ============================================================
|
||||
@@ -133,6 +144,7 @@ typedef struct {
|
||||
FreqIndex freq_index;
|
||||
ModIndex mod_index;
|
||||
JamOffIndex jam_offset_index;
|
||||
HwIndex hw_index;
|
||||
uint32_t frequency;
|
||||
uint32_t jam_frequency;
|
||||
uint32_t jam_offset_hz;
|
||||
|
||||
@@ -41,6 +41,9 @@ void rolljam_scene_attack_phase1_on_enter(void* context) {
|
||||
view_dispatcher_switch_to_view(
|
||||
app->view_dispatcher, RollJamViewWidget);
|
||||
|
||||
// Configure hardware type
|
||||
rolljam_ext_set_flux_capacitor(app->hw_index == HwIndex_FluxCapacitor);
|
||||
|
||||
// Start jamming
|
||||
rolljam_jammer_start(app);
|
||||
|
||||
|
||||
@@ -30,10 +30,18 @@ static void menu_jam_offset_changed(VariableItem* item) {
|
||||
variable_item_set_current_value_text(item, jam_offset_names[index]);
|
||||
}
|
||||
|
||||
static void menu_hw_changed(VariableItem* item) {
|
||||
RollJamApp* app = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
|
||||
app->hw_index = index;
|
||||
variable_item_set_current_value_text(item, hw_names[index]);
|
||||
}
|
||||
|
||||
static void menu_enter_callback(void* context, uint32_t index) {
|
||||
RollJamApp* app = context;
|
||||
|
||||
if(index == 3) {
|
||||
if(index == 4) {
|
||||
view_dispatcher_send_custom_event(
|
||||
app->view_dispatcher, RollJamEventStartAttack);
|
||||
}
|
||||
@@ -73,6 +81,16 @@ void rolljam_scene_menu_on_enter(void* context) {
|
||||
variable_item_set_current_value_index(offset_item, app->jam_offset_index);
|
||||
variable_item_set_current_value_text(offset_item, jam_offset_names[app->jam_offset_index]);
|
||||
|
||||
// --- Hardware ---
|
||||
VariableItem* hw_item = variable_item_list_add(
|
||||
app->var_item_list,
|
||||
"Hardware",
|
||||
HwIndex_COUNT,
|
||||
menu_hw_changed,
|
||||
app);
|
||||
variable_item_set_current_value_index(hw_item, app->hw_index);
|
||||
variable_item_set_current_value_text(hw_item, hw_names[app->hw_index]);
|
||||
|
||||
// --- Start button ---
|
||||
variable_item_list_add(
|
||||
app->var_item_list,
|
||||
|
||||
@@ -1,252 +0,0 @@
|
||||
#include "aes_common.h"
|
||||
|
||||
static const uint8_t aes_sbox[256] = {
|
||||
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab,
|
||||
0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4,
|
||||
0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71,
|
||||
0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2,
|
||||
0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6,
|
||||
0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb,
|
||||
0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45,
|
||||
0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
|
||||
0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44,
|
||||
0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a,
|
||||
0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49,
|
||||
0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d,
|
||||
0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25,
|
||||
0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e,
|
||||
0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1,
|
||||
0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
|
||||
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb,
|
||||
0x16};
|
||||
|
||||
static const uint8_t aes_sbox_inv[256] = {
|
||||
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7,
|
||||
0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde,
|
||||
0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42,
|
||||
0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49,
|
||||
0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c,
|
||||
0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15,
|
||||
0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7,
|
||||
0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,
|
||||
0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc,
|
||||
0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad,
|
||||
0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d,
|
||||
0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b,
|
||||
0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8,
|
||||
0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51,
|
||||
0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0,
|
||||
0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
|
||||
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c,
|
||||
0x7d};
|
||||
|
||||
static const uint8_t aes_rcon[10] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36};
|
||||
|
||||
static uint8_t gf_mul2(uint8_t x) {
|
||||
return ((x >> 7) * 0x1b) ^ (x << 1);
|
||||
}
|
||||
|
||||
static void aes_subbytes(uint8_t* state) {
|
||||
for(uint8_t row = 0; row < 4; row++) {
|
||||
for(uint8_t col = 0; col < 4; col++) {
|
||||
state[row + col * 4] = aes_sbox[state[row + col * 4]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void aes_subbytes_inv(uint8_t* state) {
|
||||
for(uint8_t row = 0; row < 4; row++) {
|
||||
for(uint8_t col = 0; col < 4; col++) {
|
||||
state[row + col * 4] = aes_sbox_inv[state[row + col * 4]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void aes_shiftrows(uint8_t* state) {
|
||||
uint8_t temp;
|
||||
|
||||
temp = state[1];
|
||||
state[1] = state[5];
|
||||
state[5] = state[9];
|
||||
state[9] = state[13];
|
||||
state[13] = temp;
|
||||
|
||||
temp = state[2];
|
||||
state[2] = state[10];
|
||||
state[10] = temp;
|
||||
temp = state[6];
|
||||
state[6] = state[14];
|
||||
state[14] = temp;
|
||||
|
||||
temp = state[15];
|
||||
state[15] = state[11];
|
||||
state[11] = state[7];
|
||||
state[7] = state[3];
|
||||
state[3] = temp;
|
||||
}
|
||||
|
||||
static void aes_shiftrows_inv(uint8_t* state) {
|
||||
uint8_t temp;
|
||||
|
||||
temp = state[13];
|
||||
state[13] = state[9];
|
||||
state[9] = state[5];
|
||||
state[5] = state[1];
|
||||
state[1] = temp;
|
||||
|
||||
temp = state[2];
|
||||
state[2] = state[10];
|
||||
state[10] = temp;
|
||||
temp = state[6];
|
||||
state[6] = state[14];
|
||||
state[14] = temp;
|
||||
|
||||
temp = state[3];
|
||||
state[3] = state[7];
|
||||
state[7] = state[11];
|
||||
state[11] = state[15];
|
||||
state[15] = temp;
|
||||
}
|
||||
|
||||
static void aes_mixcolumns(uint8_t* state) {
|
||||
uint8_t a, b, c, d;
|
||||
for(uint8_t i = 0; i < 4; i++) {
|
||||
a = state[i * 4];
|
||||
b = state[i * 4 + 1];
|
||||
c = state[i * 4 + 2];
|
||||
d = state[i * 4 + 3];
|
||||
|
||||
uint8_t a2 = gf_mul2(a);
|
||||
uint8_t b2 = gf_mul2(b);
|
||||
uint8_t c2 = gf_mul2(c);
|
||||
uint8_t d2 = gf_mul2(d);
|
||||
|
||||
state[i * 4] = a2 ^ b2 ^ b ^ c ^ d;
|
||||
state[i * 4 + 1] = a ^ b2 ^ c2 ^ c ^ d;
|
||||
state[i * 4 + 2] = a ^ b ^ c2 ^ d2 ^ d;
|
||||
state[i * 4 + 3] = a2 ^ a ^ b ^ c ^ d2;
|
||||
}
|
||||
}
|
||||
|
||||
static void aes_mixcolumns_inv(uint8_t* state) {
|
||||
uint8_t a, b, c, d;
|
||||
for(uint8_t i = 0; i < 4; i++) {
|
||||
a = state[i * 4];
|
||||
b = state[i * 4 + 1];
|
||||
c = state[i * 4 + 2];
|
||||
d = state[i * 4 + 3];
|
||||
|
||||
uint8_t a2 = gf_mul2(a);
|
||||
uint8_t a4 = gf_mul2(a2);
|
||||
uint8_t a8 = gf_mul2(a4);
|
||||
uint8_t b2 = gf_mul2(b);
|
||||
uint8_t b4 = gf_mul2(b2);
|
||||
uint8_t b8 = gf_mul2(b4);
|
||||
uint8_t c2 = gf_mul2(c);
|
||||
uint8_t c4 = gf_mul2(c2);
|
||||
uint8_t c8 = gf_mul2(c4);
|
||||
uint8_t d2 = gf_mul2(d);
|
||||
uint8_t d4 = gf_mul2(d2);
|
||||
uint8_t d8 = gf_mul2(d4);
|
||||
|
||||
state[i * 4] = (a8 ^ a4 ^ a2) ^ (b8 ^ b2 ^ b) ^ (c8 ^ c4 ^ c) ^ (d8 ^ d);
|
||||
state[i * 4 + 1] = (a8 ^ a) ^ (b8 ^ b4 ^ b2) ^ (c8 ^ c2 ^ c) ^ (d8 ^ d4 ^ d);
|
||||
state[i * 4 + 2] = (a8 ^ a4 ^ a) ^ (b8 ^ b) ^ (c8 ^ c4 ^ c2) ^ (d8 ^ d2 ^ d);
|
||||
state[i * 4 + 3] = (a8 ^ a2 ^ a) ^ (b8 ^ b4 ^ b) ^ (c8 ^ c) ^ (d8 ^ d4 ^ d2);
|
||||
}
|
||||
}
|
||||
|
||||
static void aes_addroundkey(uint8_t* state, const uint8_t* round_key) {
|
||||
for(uint8_t col = 0; col < 4; col++) {
|
||||
state[col * 4] ^= round_key[col * 4];
|
||||
state[col * 4 + 1] ^= round_key[col * 4 + 1];
|
||||
state[col * 4 + 2] ^= round_key[col * 4 + 2];
|
||||
state[col * 4 + 3] ^= round_key[col * 4 + 3];
|
||||
}
|
||||
}
|
||||
|
||||
void aes_key_expansion(const uint8_t* key, uint8_t* round_keys) {
|
||||
for(uint8_t i = 0; i < 16; i++) {
|
||||
round_keys[i] = key[i];
|
||||
}
|
||||
|
||||
for(uint8_t i = 4; i < 44; i++) {
|
||||
uint8_t prev_word_idx = (i - 1) * 4;
|
||||
uint8_t b0 = round_keys[prev_word_idx];
|
||||
uint8_t b1 = round_keys[prev_word_idx + 1];
|
||||
uint8_t b2 = round_keys[prev_word_idx + 2];
|
||||
uint8_t b3 = round_keys[prev_word_idx + 3];
|
||||
|
||||
if((i % 4) == 0) {
|
||||
uint8_t new_b0 = aes_sbox[b1] ^ aes_rcon[(i / 4) - 1];
|
||||
uint8_t new_b1 = aes_sbox[b2];
|
||||
uint8_t new_b2 = aes_sbox[b3];
|
||||
uint8_t new_b3 = aes_sbox[b0];
|
||||
b0 = new_b0;
|
||||
b1 = new_b1;
|
||||
b2 = new_b2;
|
||||
b3 = new_b3;
|
||||
}
|
||||
|
||||
uint8_t back_word_idx = (i - 4) * 4;
|
||||
b0 ^= round_keys[back_word_idx];
|
||||
b1 ^= round_keys[back_word_idx + 1];
|
||||
b2 ^= round_keys[back_word_idx + 2];
|
||||
b3 ^= round_keys[back_word_idx + 3];
|
||||
|
||||
uint8_t curr_word_idx = i * 4;
|
||||
round_keys[curr_word_idx] = b0;
|
||||
round_keys[curr_word_idx + 1] = b1;
|
||||
round_keys[curr_word_idx + 2] = b2;
|
||||
round_keys[curr_word_idx + 3] = b3;
|
||||
}
|
||||
}
|
||||
|
||||
void aes128_encrypt(const uint8_t* expanded_key, uint8_t* data) {
|
||||
uint8_t state[16];
|
||||
memcpy(state, data, 16);
|
||||
|
||||
aes_addroundkey(state, &expanded_key[0]);
|
||||
|
||||
for(uint8_t round = 1; round < 10; round++) {
|
||||
aes_subbytes(state);
|
||||
aes_shiftrows(state);
|
||||
aes_mixcolumns(state);
|
||||
aes_addroundkey(state, &expanded_key[round * 16]);
|
||||
}
|
||||
|
||||
aes_subbytes(state);
|
||||
aes_shiftrows(state);
|
||||
aes_addroundkey(state, &expanded_key[160]);
|
||||
|
||||
memcpy(data, state, 16);
|
||||
}
|
||||
|
||||
void aes128_decrypt(const uint8_t* expanded_key, uint8_t* data) {
|
||||
uint8_t state[16];
|
||||
memcpy(state, data, 16);
|
||||
|
||||
aes_addroundkey(state, &expanded_key[160]);
|
||||
|
||||
for(uint8_t round = 9; round > 0; round--) {
|
||||
aes_shiftrows_inv(state);
|
||||
aes_subbytes_inv(state);
|
||||
aes_addroundkey(state, &expanded_key[round * 16]);
|
||||
aes_mixcolumns_inv(state);
|
||||
}
|
||||
|
||||
aes_shiftrows_inv(state);
|
||||
aes_subbytes_inv(state);
|
||||
aes_addroundkey(state, &expanded_key[0]);
|
||||
|
||||
memcpy(data, state, 16);
|
||||
}
|
||||
|
||||
void reverse_bits_in_bytes(uint8_t* data, uint8_t len) {
|
||||
for(uint8_t i = 0; i < len; i++) {
|
||||
uint8_t byte = data[i];
|
||||
uint8_t step1 = ((byte & 0x55) << 1) | ((byte >> 1) & 0x55);
|
||||
uint8_t step2 = ((step1 & 0x33) << 2) | ((step1 >> 2) & 0x33);
|
||||
data[i] = ((step2 & 0x0F) << 4) | (step2 >> 4);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
void reverse_bits_in_bytes(uint8_t* data, uint8_t len);
|
||||
void aes128_decrypt(const uint8_t* expanded_key, uint8_t* data);
|
||||
void aes128_encrypt(const uint8_t* expanded_key, uint8_t* data);
|
||||
void aes_key_expansion(const uint8_t* key, uint8_t* round_keys);
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "core/log.h"
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "aes_common.h"
|
||||
#include <furi_hal_crypto.h>
|
||||
|
||||
#include "../blocks/custom_btn_i.h"
|
||||
|
||||
@@ -152,6 +152,15 @@ static void get_subghz_protocol_beninca_arc_aes_key(SubGhzKeystore* keystore, ui
|
||||
}
|
||||
}
|
||||
|
||||
static void reverse_bits_in_bytes(uint8_t* data, uint8_t len) {
|
||||
for(uint8_t i = 0; i < len; i++) {
|
||||
uint8_t byte = data[i];
|
||||
uint8_t step1 = ((byte & 0x55) << 1) | ((byte >> 1) & 0x55);
|
||||
uint8_t step2 = ((step1 & 0x33) << 2) | ((step1 >> 2) & 0x33);
|
||||
data[i] = ((step2 & 0x0F) << 4) | (step2 >> 4);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
subghz_protocol_beninca_arc_decrypt(SubGhzBlockGeneric* generic, SubGhzKeystore* keystore) {
|
||||
// Beninca ARC Decoder
|
||||
@@ -170,10 +179,9 @@ static uint64_t
|
||||
uint8_t aes_key[16];
|
||||
get_subghz_protocol_beninca_arc_aes_key(keystore, aes_key);
|
||||
|
||||
uint8_t expanded_key[176];
|
||||
aes_key_expansion(aes_key, expanded_key);
|
||||
|
||||
aes128_decrypt(expanded_key, encrypted_data);
|
||||
uint8_t decrypted[16];
|
||||
furi_hal_crypto_aes128_ecb_decrypt(aes_key, encrypted_data, decrypted);
|
||||
memcpy(encrypted_data, decrypted, 16);
|
||||
|
||||
// Serial number of remote
|
||||
generic->serial = ((uint32_t)encrypted_data[0] << 24) | ((uint32_t)encrypted_data[1] << 16) |
|
||||
@@ -235,10 +243,9 @@ static void subghz_protocol_beninca_arc_encrypt(
|
||||
uint8_t aes_key[16];
|
||||
get_subghz_protocol_beninca_arc_aes_key(keystore, aes_key);
|
||||
|
||||
uint8_t expanded_key[176];
|
||||
aes_key_expansion(aes_key, expanded_key);
|
||||
|
||||
aes128_encrypt(expanded_key, plaintext);
|
||||
uint8_t encrypted[16];
|
||||
furi_hal_crypto_aes128_ecb_encrypt(aes_key, plaintext, encrypted);
|
||||
memcpy(plaintext, encrypted, 16);
|
||||
|
||||
reverse_bits_in_bytes(plaintext, 16);
|
||||
|
||||
|
||||
296
lib/subghz/protocols/bmw.c
Normal file
296
lib/subghz/protocols/bmw.c
Normal file
@@ -0,0 +1,296 @@
|
||||
#include "bmw.h"
|
||||
|
||||
#define TAG "SubGhzProtocolBMW_868"
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_bmw_const = {
|
||||
.te_short = 350, // BMW 868 MHz
|
||||
.te_long = 700, // ~2 × te_short
|
||||
.te_delta = 120,
|
||||
.min_count_bit_for_found = 61,
|
||||
};
|
||||
|
||||
typedef struct SubGhzProtocolDecoderBMW {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
uint16_t header_count;
|
||||
uint8_t crc_type; // 0 = unknown, 8 = CRC8, 16 = CRC16
|
||||
} SubGhzProtocolDecoderBMW;
|
||||
|
||||
typedef struct SubGhzProtocolEncoderBMW {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
} SubGhzProtocolEncoderBMW;
|
||||
|
||||
typedef enum {
|
||||
BMWDecoderStepReset = 0,
|
||||
BMWDecoderStepCheckPreambula,
|
||||
BMWDecoderStepSaveDuration,
|
||||
BMWDecoderStepCheckDuration,
|
||||
} BMWDecoderStep;
|
||||
|
||||
static void subghz_protocol_decoder_bmw_reset_internal(SubGhzProtocolDecoderBMW* instance) {
|
||||
memset(&instance->decoder, 0, sizeof(instance->decoder));
|
||||
memset(&instance->generic, 0, sizeof(instance->generic));
|
||||
instance->decoder.parser_step = BMWDecoderStepReset;
|
||||
instance->header_count = 0;
|
||||
instance->crc_type = 0;
|
||||
}
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_bmw_decoder = {
|
||||
.alloc = subghz_protocol_decoder_bmw_alloc,
|
||||
.free = subghz_protocol_decoder_bmw_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_bmw_feed,
|
||||
.reset = subghz_protocol_decoder_bmw_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_bmw_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_bmw_serialize,
|
||||
.deserialize = subghz_protocol_decoder_bmw_deserialize,
|
||||
.get_string = subghz_protocol_decoder_bmw_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_bmw_encoder = {
|
||||
.alloc = NULL,
|
||||
.free = NULL,
|
||||
|
||||
.deserialize = NULL,
|
||||
.stop = NULL,
|
||||
.yield = NULL,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_bmw = {
|
||||
.name = BMW_PROTOCOL_NAME,
|
||||
.type = SubGhzProtocolTypeDynamic,
|
||||
.flag = SubGhzProtocolFlag_868 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable,
|
||||
|
||||
.decoder = &subghz_protocol_bmw_decoder,
|
||||
.encoder = &subghz_protocol_bmw_encoder,
|
||||
};
|
||||
|
||||
// ----------------- Allocation / Reset / Free -------------------
|
||||
|
||||
void* subghz_protocol_decoder_bmw_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderBMW* instance = calloc(1, sizeof(SubGhzProtocolDecoderBMW));
|
||||
instance->base.protocol = &subghz_protocol_bmw;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
subghz_protocol_decoder_bmw_reset(instance);
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_bmw_free(void* context) {
|
||||
furi_assert(context);
|
||||
free(context);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_bmw_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderBMW* instance = context;
|
||||
subghz_protocol_decoder_bmw_reset_internal(instance);
|
||||
}
|
||||
|
||||
// ----------------- CRC -------------------
|
||||
// BMW utilise CRC-8 (poly 0x31, init 0x00)
|
||||
|
||||
uint8_t subghz_protocol_bmw_crc8(uint8_t* data, size_t len) {
|
||||
uint8_t crc = 0x00;
|
||||
for(size_t i = 0; i < len; i++) {
|
||||
crc ^= data[i];
|
||||
for(uint8_t j = 0; j < 8; j++) {
|
||||
if(crc & 0x80)
|
||||
crc = (uint8_t)((crc << 1) ^ 0x31);
|
||||
else
|
||||
crc <<= 1;
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
// BMW utilise aussi CRC-16 (poly 0x1021, init 0xFFFF)
|
||||
uint16_t subghz_protocol_bmw_crc16(uint8_t* data, size_t len) {
|
||||
uint16_t crc = 0xFFFF;
|
||||
for(size_t i = 0; i < len; i++) {
|
||||
crc ^= ((uint16_t)data[i] << 8);
|
||||
for(uint8_t j = 0; j < 8; j++) {
|
||||
if(crc & 0x8000)
|
||||
crc = (crc << 1) ^ 0x1021;
|
||||
else
|
||||
crc <<= 1;
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
// ----------------- Decoder Feed -------------------
|
||||
|
||||
void subghz_protocol_decoder_bmw_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderBMW* instance = context;
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case BMWDecoderStepReset:
|
||||
if(level && (DURATION_DIFF(duration, subghz_protocol_bmw_const.te_short) <
|
||||
subghz_protocol_bmw_const.te_delta)) {
|
||||
instance->decoder.parser_step = BMWDecoderStepCheckPreambula;
|
||||
instance->decoder.te_last = duration;
|
||||
instance->header_count = 0;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case BMWDecoderStepCheckPreambula:
|
||||
if(level) {
|
||||
if((DURATION_DIFF(duration, subghz_protocol_bmw_const.te_short) <
|
||||
subghz_protocol_bmw_const.te_delta) ||
|
||||
(DURATION_DIFF(duration, subghz_protocol_bmw_const.te_long) <
|
||||
subghz_protocol_bmw_const.te_delta)) {
|
||||
instance->decoder.te_last = duration;
|
||||
} else {
|
||||
instance->decoder.parser_step = BMWDecoderStepReset;
|
||||
}
|
||||
} else if(
|
||||
(DURATION_DIFF(duration, subghz_protocol_bmw_const.te_short) <
|
||||
subghz_protocol_bmw_const.te_delta) &&
|
||||
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_bmw_const.te_short) <
|
||||
subghz_protocol_bmw_const.te_delta)) {
|
||||
instance->header_count++;
|
||||
} else if(
|
||||
(DURATION_DIFF(duration, subghz_protocol_bmw_const.te_long) <
|
||||
subghz_protocol_bmw_const.te_delta) &&
|
||||
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_bmw_const.te_long) <
|
||||
subghz_protocol_bmw_const.te_delta)) {
|
||||
if(instance->header_count > 15) {
|
||||
instance->decoder.parser_step = BMWDecoderStepSaveDuration;
|
||||
instance->decoder.decode_data = 0ULL;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
} else {
|
||||
instance->decoder.parser_step = BMWDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = BMWDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
|
||||
case BMWDecoderStepSaveDuration:
|
||||
if(level) {
|
||||
if(duration >=
|
||||
(subghz_protocol_bmw_const.te_long + subghz_protocol_bmw_const.te_delta * 2UL)) {
|
||||
if(instance->decoder.decode_count_bit >=
|
||||
subghz_protocol_bmw_const.min_count_bit_for_found) {
|
||||
instance->generic.data = instance->decoder.decode_data;
|
||||
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
|
||||
|
||||
// Perform CRC check with both CRC8 and CRC16
|
||||
uint8_t* raw_bytes = (uint8_t*)&instance->generic.data;
|
||||
size_t raw_len = (instance->generic.data_count_bit + 7) / 8;
|
||||
uint8_t crc8 = subghz_protocol_bmw_crc8(raw_bytes, raw_len - 1);
|
||||
if(crc8 == raw_bytes[raw_len - 1]) {
|
||||
instance->crc_type = 8;
|
||||
} else {
|
||||
uint16_t crc16 = subghz_protocol_bmw_crc16(raw_bytes, raw_len - 2);
|
||||
uint16_t rx_crc16 = (raw_bytes[raw_len - 2] << 8) | raw_bytes[raw_len - 1];
|
||||
if(crc16 == rx_crc16) {
|
||||
instance->crc_type = 16;
|
||||
} else {
|
||||
instance->crc_type = 0; // invalid
|
||||
}
|
||||
}
|
||||
|
||||
if(instance->crc_type != 0 && instance->base.callback) {
|
||||
instance->base.callback(&instance->base, instance->base.context);
|
||||
}
|
||||
}
|
||||
subghz_protocol_decoder_bmw_reset_internal(instance);
|
||||
} else {
|
||||
instance->decoder.te_last = duration;
|
||||
instance->decoder.parser_step = BMWDecoderStepCheckDuration;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = BMWDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
|
||||
case BMWDecoderStepCheckDuration:
|
||||
if(!level) {
|
||||
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_bmw_const.te_short) <
|
||||
subghz_protocol_bmw_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_bmw_const.te_short) <
|
||||
subghz_protocol_bmw_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
instance->decoder.parser_step = BMWDecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_bmw_const.te_long) <
|
||||
subghz_protocol_bmw_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_bmw_const.te_long) <
|
||||
subghz_protocol_bmw_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
instance->decoder.parser_step = BMWDecoderStepSaveDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = BMWDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = BMWDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------- Utils -------------------
|
||||
|
||||
static void subghz_protocol_bmw_check_remote_controller(SubGhzBlockGeneric* instance) {
|
||||
instance->serial = (uint32_t)((instance->data >> 12) & 0x0FFFFFFF);
|
||||
instance->btn = (instance->data >> 8) & 0x0F;
|
||||
instance->cnt = (instance->data >> 40) & 0xFFFF;
|
||||
}
|
||||
|
||||
// ----------------- API -------------------
|
||||
|
||||
uint8_t subghz_protocol_decoder_bmw_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderBMW* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_bmw_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderBMW* instance = context;
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_decoder_bmw_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderBMW* instance = context;
|
||||
return subghz_block_generic_deserialize_check_count_bit(
|
||||
&instance->generic, flipper_format, subghz_protocol_bmw_const.min_count_bit_for_found);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_bmw_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderBMW* instance = context;
|
||||
|
||||
subghz_protocol_bmw_check_remote_controller(&instance->generic);
|
||||
uint32_t hi = instance->generic.data >> 32;
|
||||
uint32_t lo = instance->generic.data & 0xFFFFFFFF;
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit (CRC:%d)\r\n"
|
||||
"Key:%08lX%08lX\r\n"
|
||||
"Sn:%07lX Btn:%X Cnt:%04lX\r\n",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
instance->crc_type,
|
||||
hi,
|
||||
lo,
|
||||
instance->generic.serial,
|
||||
instance->generic.btn,
|
||||
instance->generic.cnt);
|
||||
}
|
||||
29
lib/subghz/protocols/bmw.h
Normal file
29
lib/subghz/protocols/bmw.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <lib/subghz/protocols/base.h>
|
||||
#include <lib/subghz/types.h>
|
||||
#include <lib/subghz/blocks/const.h>
|
||||
#include <lib/subghz/blocks/decoder.h>
|
||||
#include <lib/subghz/blocks/encoder.h>
|
||||
#include <lib/subghz/blocks/generic.h>
|
||||
#include <lib/subghz/blocks/math.h>
|
||||
#include <flipper_format/flipper_format.h>
|
||||
#include <lib/toolbox/manchester_decoder.h>
|
||||
|
||||
#define BMW_PROTOCOL_NAME "BMW"
|
||||
|
||||
extern const SubGhzProtocol subghz_protocol_bmw;
|
||||
|
||||
void* subghz_protocol_decoder_bmw_alloc(SubGhzEnvironment* environment);
|
||||
void subghz_protocol_decoder_bmw_free(void* context);
|
||||
void subghz_protocol_decoder_bmw_reset(void* context);
|
||||
void subghz_protocol_decoder_bmw_feed(void* context, bool level, uint32_t duration);
|
||||
uint8_t subghz_protocol_decoder_bmw_get_hash_data(void* context);
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_bmw_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_decoder_bmw_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
void subghz_protocol_decoder_bmw_get_string(void* context, FuriString* output);
|
||||
281
lib/subghz/protocols/citroen.c
Normal file
281
lib/subghz/protocols/citroen.c
Normal file
@@ -0,0 +1,281 @@
|
||||
#include "citroen.h"
|
||||
|
||||
#define TAG "SubGhzProtocolCitroen"
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_citroen_const = {
|
||||
.te_short = 370, // Short pulse duration
|
||||
.te_long = 772, // Long pulse duration
|
||||
.te_delta = 152, // Tolerance
|
||||
.min_count_bit_for_found = 66,
|
||||
};
|
||||
|
||||
typedef struct SubGhzProtocolDecoderCitroen {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
uint16_t header_count;
|
||||
uint8_t packet_count;
|
||||
} SubGhzProtocolDecoderCitroen;
|
||||
|
||||
typedef struct SubGhzProtocolEncoderCitroen {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
} SubGhzProtocolEncoderCitroen;
|
||||
|
||||
typedef enum {
|
||||
CitroenDecoderStepReset = 0,
|
||||
CitroenDecoderStepCheckPreamble,
|
||||
CitroenDecoderStepSaveDuration,
|
||||
CitroenDecoderStepCheckDuration,
|
||||
} CitroenDecoderStep;
|
||||
|
||||
static void subghz_protocol_decoder_citroen_reset_internal(SubGhzProtocolDecoderCitroen* instance) {
|
||||
memset(&instance->decoder, 0, sizeof(instance->decoder));
|
||||
memset(&instance->generic, 0, sizeof(instance->generic));
|
||||
instance->decoder.parser_step = CitroenDecoderStepReset;
|
||||
instance->header_count = 0;
|
||||
instance->packet_count = 0;
|
||||
}
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_citroen_decoder = {
|
||||
.alloc = subghz_protocol_decoder_citroen_alloc,
|
||||
.free = subghz_protocol_decoder_citroen_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_citroen_feed,
|
||||
.reset = subghz_protocol_decoder_citroen_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_citroen_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_citroen_serialize,
|
||||
.deserialize = subghz_protocol_decoder_citroen_deserialize,
|
||||
.get_string = subghz_protocol_decoder_citroen_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_citroen_encoder = {
|
||||
.alloc = NULL,
|
||||
.free = NULL,
|
||||
|
||||
.deserialize = NULL,
|
||||
.stop = NULL,
|
||||
.yield = NULL,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_citroen = {
|
||||
.name = CITROEN_PROTOCOL_NAME,
|
||||
.type = SubGhzProtocolTypeDynamic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable,
|
||||
|
||||
.decoder = &subghz_protocol_citroen_decoder,
|
||||
.encoder = &subghz_protocol_citroen_encoder,
|
||||
};
|
||||
|
||||
// ----------------- Allocation / Reset / Free -------------------
|
||||
|
||||
void* subghz_protocol_decoder_citroen_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderCitroen* instance = calloc(1, sizeof(SubGhzProtocolDecoderCitroen));
|
||||
instance->base.protocol = &subghz_protocol_citroen;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
subghz_protocol_decoder_citroen_reset(instance);
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_citroen_free(void* context) {
|
||||
furi_assert(context);
|
||||
free(context);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_citroen_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderCitroen* instance = context;
|
||||
subghz_protocol_decoder_citroen_reset_internal(instance);
|
||||
}
|
||||
|
||||
// ----------------- Helper Functions -------------------
|
||||
|
||||
static uint8_t reverse8(uint8_t byte) {
|
||||
byte = (byte & 0xF0) >> 4 | (byte & 0x0F) << 4;
|
||||
byte = (byte & 0xCC) >> 2 | (byte & 0x33) << 2;
|
||||
byte = (byte & 0xAA) >> 1 | (byte & 0x55) << 1;
|
||||
return byte;
|
||||
}
|
||||
|
||||
// Parse Citroën/PSA data structure
|
||||
static bool subghz_protocol_citroen_parse_data(SubGhzProtocolDecoderCitroen* instance) {
|
||||
uint8_t* b = (uint8_t*)&instance->generic.data;
|
||||
|
||||
// PSA structure (similar to Peugeot Keeloq)
|
||||
// Check preamble
|
||||
if(b[0] != 0xFF || (b[1] & 0xF0) != 0xF0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extract encrypted part (32 bits) - reversed
|
||||
uint32_t encrypted = ((uint32_t)reverse8(b[3]) << 24) |
|
||||
(reverse8(b[2]) << 16) |
|
||||
(reverse8(b[1] & 0x0F) << 8) |
|
||||
reverse8(b[0]);
|
||||
|
||||
// Extract serial number (28 bits) - reversed
|
||||
uint32_t serial = ((uint32_t)reverse8(b[7] & 0xF0) << 20) |
|
||||
(reverse8(b[6]) << 12) |
|
||||
(reverse8(b[5]) << 4) |
|
||||
(reverse8(b[4]) >> 4);
|
||||
|
||||
// Extract button bits (4 bits)
|
||||
uint8_t button_bits = (encrypted >> 28) & 0x0F;
|
||||
|
||||
// Store parsed data
|
||||
instance->generic.serial = serial;
|
||||
instance->generic.btn = button_bits;
|
||||
instance->generic.cnt = (encrypted >> 16) & 0xFFFF; // Counter
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ----------------- Decoder Feed -------------------
|
||||
|
||||
void subghz_protocol_decoder_citroen_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderCitroen* instance = context;
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case CitroenDecoderStepReset:
|
||||
if(level && (DURATION_DIFF(duration, subghz_protocol_citroen_const.te_short) <
|
||||
subghz_protocol_citroen_const.te_delta)) {
|
||||
instance->decoder.parser_step = CitroenDecoderStepCheckPreamble;
|
||||
instance->decoder.te_last = duration;
|
||||
instance->header_count = 0;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case CitroenDecoderStepCheckPreamble:
|
||||
if(level) {
|
||||
if((DURATION_DIFF(duration, subghz_protocol_citroen_const.te_short) <
|
||||
subghz_protocol_citroen_const.te_delta)) {
|
||||
instance->decoder.te_last = duration;
|
||||
} else {
|
||||
instance->decoder.parser_step = CitroenDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
if((DURATION_DIFF(duration, subghz_protocol_citroen_const.te_short) <
|
||||
subghz_protocol_citroen_const.te_delta) &&
|
||||
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_citroen_const.te_short) <
|
||||
subghz_protocol_citroen_const.te_delta)) {
|
||||
instance->header_count++;
|
||||
} else if((DURATION_DIFF(duration, 4400) < 500) && instance->header_count >= 10) {
|
||||
instance->decoder.parser_step = CitroenDecoderStepSaveDuration;
|
||||
instance->decoder.decode_data = 0ULL;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
} else {
|
||||
instance->decoder.parser_step = CitroenDecoderStepReset;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CitroenDecoderStepSaveDuration:
|
||||
if(level) {
|
||||
if(duration >= (subghz_protocol_citroen_const.te_long * 3)) {
|
||||
if(instance->decoder.decode_count_bit >=
|
||||
subghz_protocol_citroen_const.min_count_bit_for_found) {
|
||||
|
||||
instance->generic.data = instance->decoder.decode_data;
|
||||
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
|
||||
|
||||
if(subghz_protocol_citroen_parse_data(instance)) {
|
||||
instance->packet_count++;
|
||||
|
||||
if(instance->base.callback) {
|
||||
instance->base.callback(&instance->base, instance->base.context);
|
||||
}
|
||||
}
|
||||
}
|
||||
subghz_protocol_decoder_citroen_reset_internal(instance);
|
||||
} else {
|
||||
instance->decoder.te_last = duration;
|
||||
instance->decoder.parser_step = CitroenDecoderStepCheckDuration;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = CitroenDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
|
||||
case CitroenDecoderStepCheckDuration:
|
||||
if(!level) {
|
||||
// PWM decoding
|
||||
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_citroen_const.te_short) <
|
||||
subghz_protocol_citroen_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_citroen_const.te_long) <
|
||||
subghz_protocol_citroen_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
instance->decoder.parser_step = CitroenDecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_citroen_const.te_long) <
|
||||
subghz_protocol_citroen_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_citroen_const.te_short) <
|
||||
subghz_protocol_citroen_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
instance->decoder.parser_step = CitroenDecoderStepSaveDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = CitroenDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = CitroenDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------- API -------------------
|
||||
|
||||
uint8_t subghz_protocol_decoder_citroen_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderCitroen* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_citroen_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderCitroen* instance = context;
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_citroen_deserialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderCitroen* instance = context;
|
||||
return subghz_block_generic_deserialize_check_count_bit(
|
||||
&instance->generic,
|
||||
flipper_format,
|
||||
subghz_protocol_citroen_const.min_count_bit_for_found);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_citroen_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderCitroen* instance = context;
|
||||
|
||||
uint32_t hi = instance->generic.data >> 32;
|
||||
uint32_t lo = instance->generic.data & 0xFFFFFFFF;
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:%08lX%08lX\r\n"
|
||||
"Sn:%07lX Btn:%X Cnt:%04lX\r\n"
|
||||
"Type:PSA/Keeloq\r\n"
|
||||
"Models:2005-2018\r\n",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
hi,
|
||||
lo,
|
||||
instance->generic.serial,
|
||||
instance->generic.btn,
|
||||
instance->generic.cnt);
|
||||
}
|
||||
77
lib/subghz/protocols/citroen.h
Normal file
77
lib/subghz/protocols/citroen.h
Normal file
@@ -0,0 +1,77 @@
|
||||
#pragma once
|
||||
|
||||
#include <lib/subghz/protocols/base.h>
|
||||
#include <lib/subghz/blocks/const.h>
|
||||
#include <lib/subghz/blocks/decoder.h>
|
||||
#include <lib/subghz/blocks/encoder.h>
|
||||
#include <lib/subghz/blocks/generic.h>
|
||||
#include <lib/subghz/blocks/math.h>
|
||||
|
||||
#define CITROEN_PROTOCOL_NAME "Citroen"
|
||||
|
||||
extern const SubGhzProtocol subghz_protocol_citroen;
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_citroen_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_citroen_encoder;
|
||||
|
||||
/**
|
||||
* Allocates memory for the Citroën protocol decoder.
|
||||
* @param environment Pointer to SubGhzEnvironment
|
||||
* @return Pointer to the allocated decoder instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_citroen_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Frees memory used by the Citroën protocol decoder.
|
||||
* @param context Pointer to the decoder instance
|
||||
*/
|
||||
void subghz_protocol_decoder_citroen_free(void* context);
|
||||
|
||||
/**
|
||||
* Resets the Citroën protocol decoder state.
|
||||
* @param context Pointer to the decoder instance
|
||||
*/
|
||||
void subghz_protocol_decoder_citroen_reset(void* context);
|
||||
|
||||
/**
|
||||
* Feeds a pulse/gap into the Citroën protocol decoder.
|
||||
* @param context Pointer to the decoder instance
|
||||
* @param level Signal level (true = high, false = low)
|
||||
* @param duration Duration of the level in microseconds
|
||||
*/
|
||||
void subghz_protocol_decoder_citroen_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Returns a hash of the decoded Citroën data.
|
||||
* @param context Pointer to the decoder instance
|
||||
* @return Hash byte
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_citroen_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serializes the decoded Citroën data into a FlipperFormat file.
|
||||
* @param context Pointer to the decoder instance
|
||||
* @param flipper_format Pointer to the FlipperFormat instance
|
||||
* @param preset Pointer to the radio preset
|
||||
* @return SubGhzProtocolStatus result
|
||||
*/
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_citroen_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserializes Citroën data from a FlipperFormat file.
|
||||
* @param context Pointer to the decoder instance
|
||||
* @param flipper_format Pointer to the FlipperFormat instance
|
||||
* @return SubGhzProtocolStatus result
|
||||
*/
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_citroen_deserialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Formats the decoded Citroën data into a human-readable string.
|
||||
* @param context Pointer to the decoder instance
|
||||
* @param output Pointer to the FuriString output buffer
|
||||
*/
|
||||
void subghz_protocol_decoder_citroen_get_string(void* context, FuriString* output);
|
||||
@@ -4,27 +4,34 @@
|
||||
|
||||
#define TAG "FiatMarelli"
|
||||
|
||||
// Suspected Magneti Marelli BSI keyfob protocol
|
||||
// Found on: Fiat Panda (and possibly other Fiat/Lancia/Alfa ~2003-2012)
|
||||
// Magneti Marelli BSI keyfob protocol (PCF7946)
|
||||
// Found on: Fiat Panda, Grande Punto (and possibly other Fiat/Lancia/Alfa ~2003-2012)
|
||||
//
|
||||
// RF: 433.92 MHz, Manchester encoding
|
||||
// te_short ~260us, te_long ~520us
|
||||
// Preamble: ~191 short-short pairs (alternating 260us HIGH/LOW)
|
||||
// Gap: ~3126us LOW
|
||||
// Sync: ~2065us HIGH
|
||||
// Data: 88 Manchester bits (often decoded as 104 with 16-bit 0xFFFF preamble residue)
|
||||
// Retransmissions: 7-10 per press
|
||||
// Two timing variants with identical frame structure:
|
||||
// Type A (e.g. Panda): te_short ~260us, te_long ~520us
|
||||
// Type B (e.g. Grande Punto): te_short ~100us, te_long ~200us
|
||||
// TE is auto-detected from preamble pulse averaging.
|
||||
//
|
||||
// Frame layout (after stripping 16-bit 0xFFFF preamble):
|
||||
// Bytes 0-3: Fixed ID / Serial (32 bits)
|
||||
// Byte 4: Button (upper nibble) | Type (lower nibble)
|
||||
// Buttons: 0x7=Lock, 0xB=Unlock, 0xD=Trunk
|
||||
// Bytes 5-10: Rolling/encrypted code (48 bits)
|
||||
#define FIAT_MARELLI_PREAMBLE_MIN 200 // Min preamble pulses (100 pairs)
|
||||
#define FIAT_MARELLI_GAP_MIN 2500 // Gap detection threshold (us)
|
||||
#define FIAT_MARELLI_SYNC_MIN 1500 // Sync pulse minimum (us)
|
||||
#define FIAT_MARELLI_SYNC_MAX 2600 // Sync pulse maximum (us)
|
||||
#define FIAT_MARELLI_MAX_DATA_BITS 104 // Max data bits to collect (13 bytes)
|
||||
// Frame layout (103-104 bits = 13 bytes):
|
||||
// Bytes 0-1: 0xFFFF/0xFFFC preamble residue
|
||||
// Bytes 2-5: Serial (32 bits)
|
||||
// Byte 6: [Button:4 | Epoch:4]
|
||||
// Byte 7: [Counter:5 | Scramble:2 | Fixed:1]
|
||||
// Bytes 8-12: Encrypted payload (40 bits)
|
||||
|
||||
#define FIAT_MARELLI_PREAMBLE_PULSE_MIN 50
|
||||
#define FIAT_MARELLI_PREAMBLE_PULSE_MAX 350
|
||||
#define FIAT_MARELLI_PREAMBLE_MIN 80
|
||||
#define FIAT_MARELLI_MAX_DATA_BITS 104
|
||||
#define FIAT_MARELLI_MIN_DATA_BITS 80
|
||||
#define FIAT_MARELLI_GAP_TE_MULT 4
|
||||
#define FIAT_MARELLI_SYNC_TE_MIN_MULT 4
|
||||
#define FIAT_MARELLI_SYNC_TE_MAX_MULT 12
|
||||
#define FIAT_MARELLI_RETX_GAP_MIN 5000
|
||||
#define FIAT_MARELLI_RETX_SYNC_MIN 400
|
||||
#define FIAT_MARELLI_RETX_SYNC_MAX 2800
|
||||
#define FIAT_MARELLI_TE_TYPE_AB_BOUNDARY 180
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_fiat_marelli_const = {
|
||||
.te_short = 260,
|
||||
@@ -40,16 +47,23 @@ struct SubGhzProtocolDecoderFiatMarelli {
|
||||
ManchesterState manchester_state;
|
||||
uint8_t decoder_state;
|
||||
uint16_t preamble_count;
|
||||
uint8_t raw_data[13]; // Up to 104 bits (13 bytes)
|
||||
uint8_t raw_data[13];
|
||||
uint8_t bit_count;
|
||||
uint32_t extra_data; // Bits beyond first 64, right-aligned
|
||||
uint32_t extra_data;
|
||||
uint32_t te_last;
|
||||
uint32_t te_sum;
|
||||
uint16_t te_count;
|
||||
uint32_t te_detected;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderFiatMarelli {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
uint8_t raw_data[13];
|
||||
uint32_t extra_data;
|
||||
uint8_t bit_count;
|
||||
uint32_t te_detected;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
@@ -57,12 +71,9 @@ typedef enum {
|
||||
FiatMarelliDecoderStepPreamble = 1,
|
||||
FiatMarelliDecoderStepSync = 2,
|
||||
FiatMarelliDecoderStepData = 3,
|
||||
FiatMarelliDecoderStepRetxSync = 4,
|
||||
} FiatMarelliDecoderStep;
|
||||
|
||||
// ============================================================================
|
||||
// PROTOCOL INTERFACE DEFINITIONS
|
||||
// ============================================================================
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_fiat_marelli_decoder = {
|
||||
.alloc = subghz_protocol_decoder_fiat_marelli_alloc,
|
||||
.free = subghz_protocol_decoder_fiat_marelli_free,
|
||||
@@ -86,21 +97,29 @@ const SubGhzProtocol subghz_protocol_fiat_marelli = {
|
||||
.name = FIAT_MARELLI_PROTOCOL_NAME,
|
||||
.type = SubGhzProtocolTypeDynamic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable |
|
||||
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save,
|
||||
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
||||
.decoder = &subghz_protocol_fiat_marelli_decoder,
|
||||
.encoder = &subghz_protocol_fiat_marelli_encoder,
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// ENCODER STUBS (decode-only protocol)
|
||||
// Encoder
|
||||
// ============================================================================
|
||||
|
||||
#define FIAT_MARELLI_ENCODER_UPLOAD_MAX 1500
|
||||
#define FIAT_MARELLI_ENCODER_REPEAT 3
|
||||
#define FIAT_MARELLI_PREAMBLE_PAIRS 100
|
||||
|
||||
void* subghz_protocol_encoder_fiat_marelli_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderFiatMarelli* instance = calloc(1, sizeof(SubGhzProtocolEncoderFiatMarelli));
|
||||
furi_check(instance);
|
||||
instance->base.protocol = &subghz_protocol_fiat_marelli;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
instance->encoder.repeat = FIAT_MARELLI_ENCODER_REPEAT;
|
||||
instance->encoder.size_upload = FIAT_MARELLI_ENCODER_UPLOAD_MAX;
|
||||
instance->encoder.upload = malloc(FIAT_MARELLI_ENCODER_UPLOAD_MAX * sizeof(LevelDuration));
|
||||
furi_check(instance->encoder.upload);
|
||||
instance->encoder.is_running = false;
|
||||
return instance;
|
||||
}
|
||||
@@ -108,42 +127,95 @@ void* subghz_protocol_encoder_fiat_marelli_alloc(SubGhzEnvironment* environment)
|
||||
void subghz_protocol_encoder_fiat_marelli_free(void* context) {
|
||||
furi_check(context);
|
||||
SubGhzProtocolEncoderFiatMarelli* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_encoder_fiat_marelli_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
UNUSED(context);
|
||||
UNUSED(flipper_format);
|
||||
return SubGhzProtocolStatusError;
|
||||
// Manchester encoding from decoder FSM:
|
||||
// From Mid1: bit 1 = LOW_TE + HIGH_TE, bit 0 = LOW_2TE
|
||||
// From Mid0: bit 0 = HIGH_TE + LOW_TE, bit 1 = HIGH_2TE
|
||||
static bool fiat_marelli_encoder_get_upload(SubGhzProtocolEncoderFiatMarelli* instance) {
|
||||
uint32_t te = instance->te_detected;
|
||||
if(te == 0) te = subghz_protocol_fiat_marelli_const.te_short;
|
||||
|
||||
uint32_t te_short = te;
|
||||
uint32_t te_long = te * 2;
|
||||
uint32_t gap_duration = te * 12;
|
||||
uint32_t sync_duration = te * 8;
|
||||
|
||||
size_t index = 0;
|
||||
size_t max_upload = FIAT_MARELLI_ENCODER_UPLOAD_MAX;
|
||||
uint8_t data_bits = instance->bit_count;
|
||||
if(data_bits == 0) data_bits = instance->generic.data_count_bit;
|
||||
if(data_bits < FIAT_MARELLI_MIN_DATA_BITS || data_bits > FIAT_MARELLI_MAX_DATA_BITS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(uint8_t i = 0; i < FIAT_MARELLI_PREAMBLE_PAIRS && (index + 1) < max_upload; i++) {
|
||||
instance->encoder.upload[index++] = level_duration_make(true, te_short);
|
||||
if(i < FIAT_MARELLI_PREAMBLE_PAIRS - 1) {
|
||||
instance->encoder.upload[index++] = level_duration_make(false, te_short);
|
||||
}
|
||||
}
|
||||
|
||||
if(index < max_upload) {
|
||||
instance->encoder.upload[index++] = level_duration_make(false, te_short + gap_duration);
|
||||
}
|
||||
|
||||
if(index < max_upload) {
|
||||
instance->encoder.upload[index++] = level_duration_make(true, sync_duration);
|
||||
}
|
||||
|
||||
bool in_mid1 = true;
|
||||
|
||||
for(uint8_t bit_i = 0; bit_i < data_bits && (index + 1) < max_upload; bit_i++) {
|
||||
uint8_t byte_idx = bit_i / 8;
|
||||
uint8_t bit_pos = 7 - (bit_i % 8);
|
||||
bool data_bit = (instance->raw_data[byte_idx] >> bit_pos) & 1;
|
||||
|
||||
if(in_mid1) {
|
||||
if(data_bit) {
|
||||
instance->encoder.upload[index++] = level_duration_make(false, te_short);
|
||||
instance->encoder.upload[index++] = level_duration_make(true, te_short);
|
||||
} else {
|
||||
instance->encoder.upload[index++] = level_duration_make(false, te_long);
|
||||
in_mid1 = false;
|
||||
}
|
||||
} else {
|
||||
if(data_bit) {
|
||||
instance->encoder.upload[index++] = level_duration_make(true, te_long);
|
||||
in_mid1 = true;
|
||||
} else {
|
||||
instance->encoder.upload[index++] = level_duration_make(true, te_short);
|
||||
instance->encoder.upload[index++] = level_duration_make(false, te_short);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(in_mid1) {
|
||||
if(index < max_upload) {
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, te_short + gap_duration * 3);
|
||||
}
|
||||
} else {
|
||||
if(index > 0) {
|
||||
instance->encoder.upload[index - 1] =
|
||||
level_duration_make(false, te_short + gap_duration * 3);
|
||||
}
|
||||
}
|
||||
|
||||
instance->encoder.size_upload = index;
|
||||
return index > 0;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_fiat_marelli_stop(void* context) {
|
||||
furi_check(context);
|
||||
SubGhzProtocolEncoderFiatMarelli* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_fiat_marelli_yield(void* context) {
|
||||
UNUSED(context);
|
||||
return level_duration_reset();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// DECODER IMPLEMENTATION
|
||||
// ============================================================================
|
||||
|
||||
// Helper: rebuild raw_data[] from generic.data + extra_data
|
||||
static void fiat_marelli_rebuild_raw_data(SubGhzProtocolDecoderFiatMarelli* instance) {
|
||||
static void fiat_marelli_encoder_rebuild_raw_data(SubGhzProtocolEncoderFiatMarelli* instance) {
|
||||
memset(instance->raw_data, 0, sizeof(instance->raw_data));
|
||||
|
||||
// First 64 bits from generic.data
|
||||
uint64_t key = instance->generic.data;
|
||||
for(int i = 0; i < 8; i++) {
|
||||
instance->raw_data[i] = (uint8_t)(key >> (56 - i * 8));
|
||||
}
|
||||
|
||||
// Remaining bits from extra_data (right-aligned)
|
||||
uint8_t extra_bits =
|
||||
instance->generic.data_count_bit > 64 ? (instance->generic.data_count_bit - 64) : 0;
|
||||
for(uint8_t i = 0; i < extra_bits && i < 32; i++) {
|
||||
@@ -157,6 +229,117 @@ static void fiat_marelli_rebuild_raw_data(SubGhzProtocolDecoderFiatMarelli* inst
|
||||
instance->bit_count = instance->generic.data_count_bit;
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_encoder_fiat_marelli_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_check(context);
|
||||
SubGhzProtocolEncoderFiatMarelli* instance = context;
|
||||
SubGhzProtocolStatus ret = SubGhzProtocolStatusError;
|
||||
|
||||
do {
|
||||
ret = subghz_block_generic_deserialize(&instance->generic, flipper_format);
|
||||
if(ret != SubGhzProtocolStatusOk) break;
|
||||
|
||||
uint32_t extra = 0;
|
||||
if(flipper_format_read_uint32(flipper_format, "Extra", &extra, 1)) {
|
||||
instance->extra_data = extra;
|
||||
}
|
||||
|
||||
uint32_t te = 0;
|
||||
if(flipper_format_read_uint32(flipper_format, "TE", &te, 1)) {
|
||||
instance->te_detected = te;
|
||||
}
|
||||
|
||||
fiat_marelli_encoder_rebuild_raw_data(instance);
|
||||
|
||||
if(!fiat_marelli_encoder_get_upload(instance)) {
|
||||
ret = SubGhzProtocolStatusErrorEncoderGetUpload;
|
||||
break;
|
||||
}
|
||||
|
||||
instance->encoder.repeat = FIAT_MARELLI_ENCODER_REPEAT;
|
||||
instance->encoder.front = 0;
|
||||
instance->encoder.is_running = true;
|
||||
|
||||
} while(false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_fiat_marelli_stop(void* context) {
|
||||
furi_check(context);
|
||||
SubGhzProtocolEncoderFiatMarelli* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_fiat_marelli_yield(void* context) {
|
||||
furi_check(context);
|
||||
SubGhzProtocolEncoderFiatMarelli* instance = context;
|
||||
|
||||
if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {
|
||||
instance->encoder.is_running = false;
|
||||
return level_duration_reset();
|
||||
}
|
||||
|
||||
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
|
||||
|
||||
if(++instance->encoder.front == instance->encoder.size_upload) {
|
||||
if(!subghz_block_generic_global.endless_tx) {
|
||||
instance->encoder.repeat--;
|
||||
}
|
||||
instance->encoder.front = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Decoder
|
||||
// ============================================================================
|
||||
|
||||
static void fiat_marelli_rebuild_raw_data(SubGhzProtocolDecoderFiatMarelli* instance) {
|
||||
memset(instance->raw_data, 0, sizeof(instance->raw_data));
|
||||
|
||||
uint64_t key = instance->generic.data;
|
||||
for(int i = 0; i < 8; i++) {
|
||||
instance->raw_data[i] = (uint8_t)(key >> (56 - i * 8));
|
||||
}
|
||||
|
||||
uint8_t extra_bits =
|
||||
instance->generic.data_count_bit > 64 ? (instance->generic.data_count_bit - 64) : 0;
|
||||
for(uint8_t i = 0; i < extra_bits && i < 32; i++) {
|
||||
uint8_t byte_idx = 8 + (i / 8);
|
||||
uint8_t bit_pos = 7 - (i % 8);
|
||||
if(instance->extra_data & (1UL << (extra_bits - 1 - i))) {
|
||||
instance->raw_data[byte_idx] |= (1 << bit_pos);
|
||||
}
|
||||
}
|
||||
|
||||
instance->bit_count = instance->generic.data_count_bit;
|
||||
|
||||
if(instance->bit_count >= 56) {
|
||||
instance->generic.serial =
|
||||
((uint32_t)instance->raw_data[2] << 24) |
|
||||
((uint32_t)instance->raw_data[3] << 16) |
|
||||
((uint32_t)instance->raw_data[4] << 8) |
|
||||
((uint32_t)instance->raw_data[5]);
|
||||
instance->generic.btn = (instance->raw_data[6] >> 4) & 0xF;
|
||||
instance->generic.cnt = (instance->raw_data[7] >> 3) & 0x1F;
|
||||
}
|
||||
}
|
||||
|
||||
static void fiat_marelli_prepare_data(SubGhzProtocolDecoderFiatMarelli* instance) {
|
||||
instance->bit_count = 0;
|
||||
instance->extra_data = 0;
|
||||
instance->generic.data = 0;
|
||||
memset(instance->raw_data, 0, sizeof(instance->raw_data));
|
||||
manchester_advance(
|
||||
instance->manchester_state,
|
||||
ManchesterEventReset,
|
||||
&instance->manchester_state,
|
||||
NULL);
|
||||
instance->decoder_state = FiatMarelliDecoderStepData;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_fiat_marelli_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderFiatMarelli* instance =
|
||||
@@ -181,6 +364,9 @@ void subghz_protocol_decoder_fiat_marelli_reset(void* context) {
|
||||
instance->bit_count = 0;
|
||||
instance->extra_data = 0;
|
||||
instance->te_last = 0;
|
||||
instance->te_sum = 0;
|
||||
instance->te_count = 0;
|
||||
instance->te_detected = 0;
|
||||
instance->generic.data = 0;
|
||||
memset(instance->raw_data, 0, sizeof(instance->raw_data));
|
||||
instance->manchester_state = ManchesterStateMid1;
|
||||
@@ -189,35 +375,51 @@ void subghz_protocol_decoder_fiat_marelli_reset(void* context) {
|
||||
void subghz_protocol_decoder_fiat_marelli_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_check(context);
|
||||
SubGhzProtocolDecoderFiatMarelli* instance = context;
|
||||
uint32_t te_short = (uint32_t)subghz_protocol_fiat_marelli_const.te_short;
|
||||
uint32_t te_long = (uint32_t)subghz_protocol_fiat_marelli_const.te_long;
|
||||
uint32_t te_delta = (uint32_t)subghz_protocol_fiat_marelli_const.te_delta;
|
||||
|
||||
uint32_t te_short = instance->te_detected ? instance->te_detected
|
||||
: (uint32_t)subghz_protocol_fiat_marelli_const.te_short;
|
||||
uint32_t te_long = te_short * 2;
|
||||
uint32_t te_delta = te_short / 2;
|
||||
if(te_delta < 30) te_delta = 30;
|
||||
uint32_t diff;
|
||||
|
||||
switch(instance->decoder_state) {
|
||||
case FiatMarelliDecoderStepReset:
|
||||
// Wait for first short HIGH pulse to start preamble
|
||||
if(!level) return;
|
||||
diff = (duration > te_short) ? (duration - te_short) : (te_short - duration);
|
||||
if(diff < te_delta) {
|
||||
instance->decoder_state = FiatMarelliDecoderStepPreamble;
|
||||
instance->preamble_count = 1;
|
||||
instance->te_last = duration;
|
||||
if(level) {
|
||||
if(duration >= FIAT_MARELLI_PREAMBLE_PULSE_MIN &&
|
||||
duration <= FIAT_MARELLI_PREAMBLE_PULSE_MAX) {
|
||||
instance->decoder_state = FiatMarelliDecoderStepPreamble;
|
||||
instance->preamble_count = 1;
|
||||
instance->te_sum = duration;
|
||||
instance->te_count = 1;
|
||||
instance->te_last = duration;
|
||||
}
|
||||
} else {
|
||||
if(duration > FIAT_MARELLI_RETX_GAP_MIN) {
|
||||
instance->decoder_state = FiatMarelliDecoderStepRetxSync;
|
||||
instance->te_last = duration;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case FiatMarelliDecoderStepPreamble:
|
||||
diff = (duration > te_short) ? (duration - te_short) : (te_short - duration);
|
||||
|
||||
if(diff < te_delta) {
|
||||
// Short pulse (HIGH or LOW) preamble continues
|
||||
if(duration >= FIAT_MARELLI_PREAMBLE_PULSE_MIN &&
|
||||
duration <= FIAT_MARELLI_PREAMBLE_PULSE_MAX) {
|
||||
instance->preamble_count++;
|
||||
instance->te_sum += duration;
|
||||
instance->te_count++;
|
||||
instance->te_last = duration;
|
||||
} else if(!level && duration > FIAT_MARELLI_GAP_MIN) {
|
||||
// Long LOW potential gap after preamble
|
||||
if(instance->preamble_count >= FIAT_MARELLI_PREAMBLE_MIN) {
|
||||
instance->decoder_state = FiatMarelliDecoderStepSync;
|
||||
instance->te_last = duration;
|
||||
} else if(!level) {
|
||||
if(instance->preamble_count >= FIAT_MARELLI_PREAMBLE_MIN && instance->te_count > 0) {
|
||||
instance->te_detected = instance->te_sum / instance->te_count;
|
||||
uint32_t gap_threshold = instance->te_detected * FIAT_MARELLI_GAP_TE_MULT;
|
||||
|
||||
if(duration > gap_threshold) {
|
||||
instance->decoder_state = FiatMarelliDecoderStepSync;
|
||||
instance->te_last = duration;
|
||||
} else {
|
||||
instance->decoder_state = FiatMarelliDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder_state = FiatMarelliDecoderStepReset;
|
||||
}
|
||||
@@ -226,20 +428,28 @@ void subghz_protocol_decoder_fiat_marelli_feed(void* context, bool level, uint32
|
||||
}
|
||||
break;
|
||||
|
||||
case FiatMarelliDecoderStepSync:
|
||||
// Expect sync HIGH pulse ~2065us after the gap
|
||||
if(level && duration >= FIAT_MARELLI_SYNC_MIN && duration <= FIAT_MARELLI_SYNC_MAX) {
|
||||
// Sync detected prepare for Manchester data
|
||||
instance->bit_count = 0;
|
||||
instance->extra_data = 0;
|
||||
instance->generic.data = 0;
|
||||
memset(instance->raw_data, 0, sizeof(instance->raw_data));
|
||||
manchester_advance(
|
||||
instance->manchester_state,
|
||||
ManchesterEventReset,
|
||||
&instance->manchester_state,
|
||||
NULL);
|
||||
instance->decoder_state = FiatMarelliDecoderStepData;
|
||||
case FiatMarelliDecoderStepSync: {
|
||||
uint32_t sync_min = instance->te_detected * FIAT_MARELLI_SYNC_TE_MIN_MULT;
|
||||
uint32_t sync_max = instance->te_detected * FIAT_MARELLI_SYNC_TE_MAX_MULT;
|
||||
|
||||
if(level && duration >= sync_min && duration <= sync_max) {
|
||||
fiat_marelli_prepare_data(instance);
|
||||
instance->te_last = duration;
|
||||
} else {
|
||||
instance->decoder_state = FiatMarelliDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case FiatMarelliDecoderStepRetxSync:
|
||||
if(level && duration >= FIAT_MARELLI_RETX_SYNC_MIN &&
|
||||
duration <= FIAT_MARELLI_RETX_SYNC_MAX) {
|
||||
if(!instance->te_detected) {
|
||||
instance->te_detected = duration / 8;
|
||||
if(instance->te_detected < 70) instance->te_detected = 100;
|
||||
if(instance->te_detected > 350) instance->te_detected = 260;
|
||||
}
|
||||
fiat_marelli_prepare_data(instance);
|
||||
instance->te_last = duration;
|
||||
} else {
|
||||
instance->decoder_state = FiatMarelliDecoderStepReset;
|
||||
@@ -250,7 +460,6 @@ void subghz_protocol_decoder_fiat_marelli_feed(void* context, bool level, uint32
|
||||
ManchesterEvent event = ManchesterEventReset;
|
||||
bool frame_complete = false;
|
||||
|
||||
// Classify duration as short or long Manchester edge
|
||||
diff = (duration > te_short) ? (duration - te_short) : (te_short - duration);
|
||||
if(diff < te_delta) {
|
||||
event = level ? ManchesterEventShortLow : ManchesterEventShortHigh;
|
||||
@@ -291,7 +500,7 @@ void subghz_protocol_decoder_fiat_marelli_feed(void* context, bool level, uint32
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(instance->bit_count >= subghz_protocol_fiat_marelli_const.min_count_bit_for_found) {
|
||||
if(instance->bit_count >= FIAT_MARELLI_MIN_DATA_BITS) {
|
||||
frame_complete = true;
|
||||
} else {
|
||||
instance->decoder_state = FiatMarelliDecoderStepReset;
|
||||
@@ -301,36 +510,35 @@ void subghz_protocol_decoder_fiat_marelli_feed(void* context, bool level, uint32
|
||||
if(frame_complete) {
|
||||
instance->generic.data_count_bit = instance->bit_count;
|
||||
|
||||
// Frame layout: bytes 0-1 are 0xFFFF preamble residue
|
||||
// Bytes 2-5: Fixed ID (serial)
|
||||
// Byte 6: Button (upper nibble) | subtype (lower nibble)
|
||||
// Bytes 7-12: Rolling/encrypted code (48 bits)
|
||||
instance->generic.serial =
|
||||
((uint32_t)instance->raw_data[2] << 24) |
|
||||
((uint32_t)instance->raw_data[3] << 16) |
|
||||
((uint32_t)instance->raw_data[4] << 8) |
|
||||
((uint32_t)instance->raw_data[5]);
|
||||
instance->generic.btn = (instance->raw_data[6] >> 4) & 0xF;
|
||||
instance->generic.cnt =
|
||||
((uint32_t)instance->raw_data[7] << 16) |
|
||||
((uint32_t)instance->raw_data[8] << 8) |
|
||||
((uint32_t)instance->raw_data[9]);
|
||||
instance->generic.cnt = (instance->raw_data[7] >> 3) & 0x1F;
|
||||
|
||||
const char* variant = (instance->te_detected &&
|
||||
instance->te_detected < FIAT_MARELLI_TE_TYPE_AB_BOUNDARY)
|
||||
? "B"
|
||||
: "A";
|
||||
|
||||
FURI_LOG_I(
|
||||
TAG,
|
||||
"Decoded %d bits: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",
|
||||
"Type%s TE:%lu %db Sn:%08lX Btn:0x%X Ep:%X Ctr:%lu Roll:%02X%02X%02X%02X%02X%02X",
|
||||
variant,
|
||||
instance->te_detected ? instance->te_detected : te_short,
|
||||
instance->bit_count,
|
||||
instance->raw_data[0],
|
||||
instance->raw_data[1],
|
||||
instance->raw_data[2],
|
||||
instance->raw_data[3],
|
||||
instance->raw_data[4],
|
||||
instance->raw_data[5],
|
||||
instance->raw_data[6],
|
||||
instance->generic.serial,
|
||||
instance->generic.btn,
|
||||
instance->raw_data[6] & 0xF,
|
||||
instance->generic.cnt,
|
||||
instance->raw_data[7],
|
||||
instance->raw_data[8],
|
||||
instance->raw_data[9],
|
||||
instance->raw_data[10]);
|
||||
instance->raw_data[10],
|
||||
instance->raw_data[11],
|
||||
instance->raw_data[12]);
|
||||
|
||||
if(instance->base.callback) {
|
||||
instance->base.callback(&instance->base, instance->base.context);
|
||||
@@ -342,6 +550,7 @@ void subghz_protocol_decoder_fiat_marelli_feed(void* context, bool level, uint32
|
||||
instance->te_last = duration;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,14 +576,15 @@ SubGhzProtocolStatus subghz_protocol_decoder_fiat_marelli_serialize(
|
||||
subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
|
||||
if(ret == SubGhzProtocolStatusOk) {
|
||||
// Save extra data (bits 64+ right-aligned in uint32_t)
|
||||
flipper_format_write_uint32(flipper_format, "Extra", &instance->extra_data, 1);
|
||||
|
||||
// Save total bit count explicitly (generic serialize also saves it, but Extra needs context)
|
||||
uint32_t extra_bits = instance->generic.data_count_bit > 64
|
||||
? (instance->generic.data_count_bit - 64)
|
||||
: 0;
|
||||
flipper_format_write_uint32(flipper_format, "Extra_bits", &extra_bits, 1);
|
||||
|
||||
uint32_t te = instance->te_detected;
|
||||
flipper_format_write_uint32(flipper_format, "TE", &te, 1);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -395,6 +605,11 @@ SubGhzProtocolStatus subghz_protocol_decoder_fiat_marelli_deserialize(
|
||||
instance->extra_data = extra;
|
||||
}
|
||||
|
||||
uint32_t te = 0;
|
||||
if(flipper_format_read_uint32(flipper_format, "TE", &te, 1)) {
|
||||
instance->te_detected = te;
|
||||
}
|
||||
|
||||
fiat_marelli_rebuild_raw_data(instance);
|
||||
}
|
||||
|
||||
@@ -421,26 +636,31 @@ void subghz_protocol_decoder_fiat_marelli_get_string(void* context, FuriString*
|
||||
uint8_t total_bytes = (instance->bit_count + 7) / 8;
|
||||
if(total_bytes > 13) total_bytes = 13;
|
||||
|
||||
uint8_t epoch = instance->raw_data[6] & 0xF;
|
||||
uint8_t counter = (instance->raw_data[7] >> 3) & 0x1F;
|
||||
|
||||
const char* variant = (instance->te_detected &&
|
||||
instance->te_detected < FIAT_MARELLI_TE_TYPE_AB_BOUNDARY)
|
||||
? "B"
|
||||
: "A";
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Sn:%08lX Btn:%s(0x%X)\r\n"
|
||||
"Roll:%02X%02X%02X%02X%02X%02X\r\n"
|
||||
"Data:",
|
||||
"Sn:%08lX Btn:%s\r\n"
|
||||
"Ep:%X Ctr:%02d Type%s\r\n"
|
||||
"R:%02X%02X%02X%02X%02X%02X",
|
||||
instance->generic.protocol_name,
|
||||
instance->bit_count,
|
||||
instance->generic.serial,
|
||||
fiat_marelli_button_name(instance->generic.btn),
|
||||
instance->generic.btn,
|
||||
epoch,
|
||||
counter,
|
||||
variant,
|
||||
instance->raw_data[7],
|
||||
instance->raw_data[8],
|
||||
instance->raw_data[9],
|
||||
(total_bytes > 10) ? instance->raw_data[10] : 0,
|
||||
(total_bytes > 11) ? instance->raw_data[11] : 0,
|
||||
(total_bytes > 12) ? instance->raw_data[12] : 0);
|
||||
|
||||
for(uint8_t i = 0; i < total_bytes; i++) {
|
||||
furi_string_cat_printf(output, "%02X", instance->raw_data[i]);
|
||||
}
|
||||
furi_string_cat_printf(output, "\r\n");
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ SubGhzProtocolStatus
|
||||
subghz_protocol_decoder_fiat_marelli_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
void subghz_protocol_decoder_fiat_marelli_get_string(void* context, FuriString* output);
|
||||
|
||||
// Encoder stubs
|
||||
// Encoder (replay of captured frames)
|
||||
void* subghz_protocol_encoder_fiat_marelli_alloc(SubGhzEnvironment* environment);
|
||||
void subghz_protocol_encoder_fiat_marelli_free(void* context);
|
||||
SubGhzProtocolStatus
|
||||
|
||||
274
lib/subghz/protocols/honda.c
Normal file
274
lib/subghz/protocols/honda.c
Normal file
@@ -0,0 +1,274 @@
|
||||
#include "honda.h"
|
||||
|
||||
#define TAG "SubGhzProtocolHonda"
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_honda_const = {
|
||||
.te_short = 432, // Short pulse ~432µs
|
||||
.te_long = 864, // Long pulse ~864µs (2x short)
|
||||
.te_delta = 150, // Tolerance
|
||||
.min_count_bit_for_found = 64,
|
||||
};
|
||||
|
||||
typedef struct SubGhzProtocolDecoderHonda {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
uint16_t header_count;
|
||||
} SubGhzProtocolDecoderHonda;
|
||||
|
||||
typedef struct SubGhzProtocolEncoderHonda {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
} SubGhzProtocolEncoderHonda;
|
||||
|
||||
typedef enum {
|
||||
HondaDecoderStepReset = 0,
|
||||
HondaDecoderStepCheckPreamble,
|
||||
HondaDecoderStepSaveDuration,
|
||||
HondaDecoderStepCheckDuration,
|
||||
} HondaDecoderStep;
|
||||
|
||||
static void subghz_protocol_decoder_honda_reset_internal(SubGhzProtocolDecoderHonda* instance) {
|
||||
memset(&instance->decoder, 0, sizeof(instance->decoder));
|
||||
memset(&instance->generic, 0, sizeof(instance->generic));
|
||||
instance->decoder.parser_step = HondaDecoderStepReset;
|
||||
instance->header_count = 0;
|
||||
}
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_honda_decoder = {
|
||||
.alloc = subghz_protocol_decoder_honda_alloc,
|
||||
.free = subghz_protocol_decoder_honda_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_honda_feed,
|
||||
.reset = subghz_protocol_decoder_honda_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_honda_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_honda_serialize,
|
||||
.deserialize = subghz_protocol_decoder_honda_deserialize,
|
||||
.get_string = subghz_protocol_decoder_honda_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_honda_encoder = {
|
||||
.alloc = NULL,
|
||||
.free = NULL,
|
||||
|
||||
.deserialize = NULL,
|
||||
.stop = NULL,
|
||||
.yield = NULL,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_honda = {
|
||||
.name = HONDA_PROTOCOL_NAME,
|
||||
.type = SubGhzProtocolTypeDynamic, // Rolling code (vulnerable)
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
|
||||
|
||||
.decoder = &subghz_protocol_honda_decoder,
|
||||
.encoder = &subghz_protocol_honda_encoder,
|
||||
};
|
||||
|
||||
// ----------------- Allocation / Reset / Free -------------------
|
||||
|
||||
void* subghz_protocol_decoder_honda_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderHonda* instance = calloc(1, sizeof(SubGhzProtocolDecoderHonda));
|
||||
instance->base.protocol = &subghz_protocol_honda;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
subghz_protocol_decoder_honda_reset(instance);
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_honda_free(void* context) {
|
||||
furi_assert(context);
|
||||
free(context);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_honda_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHonda* instance = context;
|
||||
subghz_protocol_decoder_honda_reset_internal(instance);
|
||||
}
|
||||
|
||||
// ----------------- Honda Protocol Parsing -------------------
|
||||
|
||||
static bool subghz_protocol_honda_parse_data(SubGhzProtocolDecoderHonda* instance) {
|
||||
uint8_t* b = (uint8_t*)&instance->generic.data;
|
||||
|
||||
// Honda protocol structure (from rtl_433):
|
||||
// Bits 0-7: Preamble/sync
|
||||
// Bits 8-39: Device ID (32 bits)
|
||||
// Bits 40-55: Rolling counter (16 bits)
|
||||
// Bits 56-63: Function code (8 bits) - which button was pressed
|
||||
|
||||
// Extract device ID (bytes 1-4)
|
||||
uint32_t device_id = ((uint32_t)b[1] << 24) |
|
||||
(b[2] << 16) |
|
||||
(b[3] << 8) |
|
||||
b[4];
|
||||
|
||||
// Extract rolling counter (bytes 5-6)
|
||||
uint16_t rolling_counter = (b[5] << 8) | b[6];
|
||||
|
||||
// Extract function code (byte 7)
|
||||
uint8_t function = b[7];
|
||||
|
||||
// Store parsed data
|
||||
instance->generic.serial = device_id;
|
||||
instance->generic.cnt = rolling_counter;
|
||||
instance->generic.btn = function;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ----------------- Decoder Feed -------------------
|
||||
|
||||
void subghz_protocol_decoder_honda_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHonda* instance = context;
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case HondaDecoderStepReset:
|
||||
if(level && (DURATION_DIFF(duration, subghz_protocol_honda_const.te_short) <
|
||||
subghz_protocol_honda_const.te_delta)) {
|
||||
instance->decoder.parser_step = HondaDecoderStepCheckPreamble;
|
||||
instance->decoder.te_last = duration;
|
||||
instance->header_count = 0;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case HondaDecoderStepCheckPreamble:
|
||||
if(level) {
|
||||
if((DURATION_DIFF(duration, subghz_protocol_honda_const.te_short) <
|
||||
subghz_protocol_honda_const.te_delta)) {
|
||||
instance->decoder.te_last = duration;
|
||||
} else {
|
||||
instance->decoder.parser_step = HondaDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
// Looking for preamble pattern
|
||||
if((DURATION_DIFF(duration, subghz_protocol_honda_const.te_short) <
|
||||
subghz_protocol_honda_const.te_delta) &&
|
||||
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_honda_const.te_short) <
|
||||
subghz_protocol_honda_const.te_delta)) {
|
||||
instance->header_count++;
|
||||
} else if((DURATION_DIFF(duration, subghz_protocol_honda_const.te_long) <
|
||||
subghz_protocol_honda_const.te_delta * 2) &&
|
||||
instance->header_count >= 10) {
|
||||
// Long gap after preamble - start of data
|
||||
instance->decoder.parser_step = HondaDecoderStepSaveDuration;
|
||||
instance->decoder.decode_data = 0ULL;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
} else {
|
||||
instance->decoder.parser_step = HondaDecoderStepReset;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case HondaDecoderStepSaveDuration:
|
||||
if(level) {
|
||||
if(duration >= (subghz_protocol_honda_const.te_long * 3)) {
|
||||
// End of transmission
|
||||
if(instance->decoder.decode_count_bit >=
|
||||
subghz_protocol_honda_const.min_count_bit_for_found) {
|
||||
|
||||
instance->generic.data = instance->decoder.decode_data;
|
||||
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
|
||||
|
||||
// Parse Honda protocol structure
|
||||
if(subghz_protocol_honda_parse_data(instance)) {
|
||||
if(instance->base.callback) {
|
||||
instance->base.callback(&instance->base, instance->base.context);
|
||||
}
|
||||
}
|
||||
}
|
||||
subghz_protocol_decoder_honda_reset_internal(instance);
|
||||
} else {
|
||||
instance->decoder.te_last = duration;
|
||||
instance->decoder.parser_step = HondaDecoderStepCheckDuration;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = HondaDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
|
||||
case HondaDecoderStepCheckDuration:
|
||||
if(!level) {
|
||||
// Manchester decoding (differential)
|
||||
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_honda_const.te_short) <
|
||||
subghz_protocol_honda_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_honda_const.te_long) <
|
||||
subghz_protocol_honda_const.te_delta)) {
|
||||
// Short-Long = 0
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
instance->decoder.parser_step = HondaDecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_honda_const.te_long) <
|
||||
subghz_protocol_honda_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_honda_const.te_short) <
|
||||
subghz_protocol_honda_const.te_delta)) {
|
||||
// Long-Short = 1
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
instance->decoder.parser_step = HondaDecoderStepSaveDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = HondaDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = HondaDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------- API -------------------
|
||||
|
||||
uint8_t subghz_protocol_decoder_honda_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHonda* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_honda_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHonda* instance = context;
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_honda_deserialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHonda* instance = context;
|
||||
return subghz_block_generic_deserialize_check_count_bit(
|
||||
&instance->generic,
|
||||
flipper_format,
|
||||
subghz_protocol_honda_const.min_count_bit_for_found);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_honda_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHonda* instance = context;
|
||||
|
||||
uint32_t hi = instance->generic.data >> 32;
|
||||
uint32_t lo = instance->generic.data & 0xFFFFFFFF;
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:%08lX%08lX\r\n"
|
||||
"ID:%08lX Btn:%02X Cnt:%04X\r\n"
|
||||
"CVE:CVE-2022-27254\r\n"
|
||||
"Note:Rolling code vulnerable\r\n",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
hi,
|
||||
lo,
|
||||
instance->generic.serial,
|
||||
instance->generic.btn,
|
||||
(uint16_t)instance->generic.cnt);
|
||||
}
|
||||
77
lib/subghz/protocols/honda.h
Normal file
77
lib/subghz/protocols/honda.h
Normal file
@@ -0,0 +1,77 @@
|
||||
#pragma once
|
||||
|
||||
#include <lib/subghz/protocols/base.h>
|
||||
#include <lib/subghz/blocks/const.h>
|
||||
#include <lib/subghz/blocks/decoder.h>
|
||||
#include <lib/subghz/blocks/encoder.h>
|
||||
#include <lib/subghz/blocks/generic.h>
|
||||
#include <lib/subghz/blocks/math.h>
|
||||
|
||||
#define HONDA_PROTOCOL_NAME "Honda"
|
||||
|
||||
extern const SubGhzProtocol subghz_protocol_honda;
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_honda_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_honda_encoder;
|
||||
|
||||
/**
|
||||
* Allocates memory for the Honda protocol decoder.
|
||||
* @param environment Pointer to SubGhzEnvironment
|
||||
* @return Pointer to the allocated decoder instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_honda_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Frees memory used by the Honda protocol decoder.
|
||||
* @param context Pointer to the decoder instance
|
||||
*/
|
||||
void subghz_protocol_decoder_honda_free(void* context);
|
||||
|
||||
/**
|
||||
* Resets the Honda protocol decoder state.
|
||||
* @param context Pointer to the decoder instance
|
||||
*/
|
||||
void subghz_protocol_decoder_honda_reset(void* context);
|
||||
|
||||
/**
|
||||
* Feeds a pulse/gap into the Honda protocol decoder.
|
||||
* @param context Pointer to the decoder instance
|
||||
* @param level Signal level (true = high, false = low)
|
||||
* @param duration Duration of the level in microseconds
|
||||
*/
|
||||
void subghz_protocol_decoder_honda_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Returns a hash of the decoded Honda data.
|
||||
* @param context Pointer to the decoder instance
|
||||
* @return Hash byte
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_honda_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serializes the decoded Honda data into a FlipperFormat file.
|
||||
* @param context Pointer to the decoder instance
|
||||
* @param flipper_format Pointer to the FlipperFormat instance
|
||||
* @param preset Pointer to the radio preset
|
||||
* @return SubGhzProtocolStatus result
|
||||
*/
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_honda_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserializes Honda data from a FlipperFormat file.
|
||||
* @param context Pointer to the decoder instance
|
||||
* @param flipper_format Pointer to the FlipperFormat instance
|
||||
* @return SubGhzProtocolStatus result
|
||||
*/
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_honda_deserialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Formats the decoded Honda data into a human-readable string.
|
||||
* @param context Pointer to the decoder instance
|
||||
* @param output Pointer to the FuriString output buffer
|
||||
*/
|
||||
void subghz_protocol_decoder_honda_get_string(void* context, FuriString* output);
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "../blocks/custom_btn_i.h"
|
||||
#include <lib/toolbox/manchester_decoder.h>
|
||||
#include <flipper_format/flipper_format.h>
|
||||
#include <furi_hal_crypto.h>
|
||||
|
||||
#define TAG "SubGhzProtocolKiaV6"
|
||||
|
||||
@@ -43,29 +44,6 @@ static const uint8_t aes_sbox[256] = {
|
||||
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
|
||||
};
|
||||
|
||||
static const uint8_t aes_sbox_inv[256] = {
|
||||
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
|
||||
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
|
||||
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
|
||||
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
|
||||
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
|
||||
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
|
||||
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
|
||||
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
|
||||
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
|
||||
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
|
||||
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
|
||||
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
|
||||
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
|
||||
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
|
||||
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
|
||||
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
|
||||
};
|
||||
|
||||
static const uint8_t aes_rcon[10] = {
|
||||
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderKiaV6 {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
SubGhzBlockDecoder decoder;
|
||||
@@ -159,181 +137,6 @@ static uint8_t kia_v6_custom_to_btn(uint8_t custom) {
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t gf_mul2(uint8_t x) {
|
||||
return ((x >> 7) * 0x1b) ^ (x << 1);
|
||||
}
|
||||
|
||||
static void aes_subbytes_inv(uint8_t* state) {
|
||||
for (int row = 0; row < 4; row++) {
|
||||
for (int col = 0; col < 4; col++) {
|
||||
state[row + col * 4] = aes_sbox_inv[state[row + col * 4]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void aes_shiftrows_inv(uint8_t* state) {
|
||||
uint8_t temp;
|
||||
temp = state[13];
|
||||
state[13] = state[9];
|
||||
state[9] = state[5];
|
||||
state[5] = state[1];
|
||||
state[1] = temp;
|
||||
|
||||
temp = state[2];
|
||||
state[2] = state[10];
|
||||
state[10] = temp;
|
||||
temp = state[6];
|
||||
state[6] = state[14];
|
||||
state[14] = temp;
|
||||
|
||||
temp = state[3];
|
||||
state[3] = state[7];
|
||||
state[7] = state[11];
|
||||
state[11] = state[15];
|
||||
state[15] = temp;
|
||||
}
|
||||
|
||||
static void aes_mixcolumns_inv(uint8_t* state) {
|
||||
uint8_t a, b, c, d;
|
||||
for(int i = 0; i < 4; i++) {
|
||||
a = state[i*4];
|
||||
b = state[i*4+1];
|
||||
c = state[i*4+2];
|
||||
d = state[i*4+3];
|
||||
|
||||
uint8_t a2 = gf_mul2(a);
|
||||
uint8_t a4 = gf_mul2(a2);
|
||||
uint8_t a8 = gf_mul2(a4);
|
||||
uint8_t b2 = gf_mul2(b);
|
||||
uint8_t b4 = gf_mul2(b2);
|
||||
uint8_t b8 = gf_mul2(b4);
|
||||
uint8_t c2 = gf_mul2(c);
|
||||
uint8_t c4 = gf_mul2(c2);
|
||||
uint8_t c8 = gf_mul2(c4);
|
||||
uint8_t d2 = gf_mul2(d);
|
||||
uint8_t d4 = gf_mul2(d2);
|
||||
uint8_t d8 = gf_mul2(d4);
|
||||
|
||||
state[i*4] = (a8^a4^a2) ^ (b8^b2^b) ^ (c8^c4^c) ^ (d8^d);
|
||||
state[i*4+1] = (a8^a) ^ (b8^b4^b2) ^ (c8^c2^c) ^ (d8^d4^d);
|
||||
state[i*4+2] = (a8^a4^a) ^ (b8^b) ^ (c8^c4^c2) ^ (d8^d2^d);
|
||||
state[i*4+3] = (a8^a2^a) ^ (b8^b4^b) ^ (c8^c) ^ (d8^d4^d2);
|
||||
}
|
||||
}
|
||||
|
||||
static void aes_addroundkey(uint8_t* state, const uint8_t* round_key) {
|
||||
for (int col = 0; col < 4; col++) {
|
||||
state[col * 4] ^= round_key[col * 4];
|
||||
state[col * 4 + 1] ^= round_key[col * 4 + 1];
|
||||
state[col * 4 + 2] ^= round_key[col * 4 + 2];
|
||||
state[col * 4 + 3] ^= round_key[col * 4 + 3];
|
||||
}
|
||||
}
|
||||
|
||||
static void aes_subbytes(uint8_t* state) {
|
||||
for (int row = 0; row < 4; row++) {
|
||||
for (int col = 0; col < 4; col++) {
|
||||
state[row + col * 4] = aes_sbox[state[row + col * 4]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void aes_shiftrows(uint8_t* state) {
|
||||
uint8_t temp;
|
||||
temp = state[1];
|
||||
state[1] = state[5];
|
||||
state[5] = state[9];
|
||||
state[9] = state[13];
|
||||
state[13] = temp;
|
||||
temp = state[2];
|
||||
state[2] = state[10];
|
||||
state[10] = temp;
|
||||
temp = state[6];
|
||||
state[6] = state[14];
|
||||
state[14] = temp;
|
||||
temp = state[3];
|
||||
state[3] = state[15];
|
||||
state[15] = state[11];
|
||||
state[11] = state[7];
|
||||
state[7] = temp;
|
||||
}
|
||||
|
||||
static void aes_mixcolumns(uint8_t* state) {
|
||||
uint8_t a, b, c, d;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
a = state[i * 4];
|
||||
b = state[i * 4 + 1];
|
||||
c = state[i * 4 + 2];
|
||||
d = state[i * 4 + 3];
|
||||
state[i * 4] = gf_mul2(a) ^ gf_mul2(b) ^ b ^ c ^ d;
|
||||
state[i * 4 + 1] = a ^ gf_mul2(b) ^ gf_mul2(c) ^ c ^ d;
|
||||
state[i * 4 + 2] = a ^ b ^ gf_mul2(c) ^ gf_mul2(d) ^ d;
|
||||
state[i * 4 + 3] = gf_mul2(a) ^ a ^ b ^ c ^ gf_mul2(d);
|
||||
}
|
||||
}
|
||||
|
||||
static void aes_key_expansion(const uint8_t* key, uint8_t* round_keys) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
round_keys[i] = key[i];
|
||||
}
|
||||
for (int i = 4; i < 44; i++) {
|
||||
int prev_word_idx = (i - 1) * 4;
|
||||
uint8_t b0 = round_keys[prev_word_idx];
|
||||
uint8_t b1 = round_keys[prev_word_idx + 1];
|
||||
uint8_t b2 = round_keys[prev_word_idx + 2];
|
||||
uint8_t b3 = round_keys[prev_word_idx + 3];
|
||||
if ((i % 4) == 0) {
|
||||
uint8_t new_b0 = aes_sbox[b1] ^ aes_rcon[(i / 4) - 1];
|
||||
uint8_t new_b1 = aes_sbox[b2];
|
||||
uint8_t new_b2 = aes_sbox[b3];
|
||||
uint8_t new_b3 = aes_sbox[b0];
|
||||
b0 = new_b0; b1 = new_b1; b2 = new_b2; b3 = new_b3;
|
||||
}
|
||||
int back_word_idx = (i - 4) * 4;
|
||||
b0 ^= round_keys[back_word_idx];
|
||||
b1 ^= round_keys[back_word_idx + 1];
|
||||
b2 ^= round_keys[back_word_idx + 2];
|
||||
b3 ^= round_keys[back_word_idx + 3];
|
||||
int curr_word_idx = i * 4;
|
||||
round_keys[curr_word_idx] = b0;
|
||||
round_keys[curr_word_idx + 1] = b1;
|
||||
round_keys[curr_word_idx + 2] = b2;
|
||||
round_keys[curr_word_idx + 3] = b3;
|
||||
}
|
||||
}
|
||||
|
||||
static void aes128_decrypt(const uint8_t* expanded_key, uint8_t* data) {
|
||||
uint8_t state[16];
|
||||
memcpy(state, data, 16);
|
||||
aes_addroundkey(state, &expanded_key[160]);
|
||||
for (int round = 9; round > 0; round--) {
|
||||
aes_shiftrows_inv(state);
|
||||
aes_subbytes_inv(state);
|
||||
aes_addroundkey(state, &expanded_key[round*16]);
|
||||
aes_mixcolumns_inv(state);
|
||||
}
|
||||
aes_shiftrows_inv(state);
|
||||
aes_subbytes_inv(state);
|
||||
aes_addroundkey(state, &expanded_key[0]);
|
||||
memcpy(data, state, 16);
|
||||
}
|
||||
|
||||
static void aes128_encrypt(const uint8_t* expanded_key, uint8_t* data) {
|
||||
uint8_t state[16];
|
||||
memcpy(state, data, 16);
|
||||
aes_addroundkey(state, &expanded_key[0]);
|
||||
for (int round = 1; round < 10; round++) {
|
||||
aes_subbytes(state);
|
||||
aes_shiftrows(state);
|
||||
aes_mixcolumns(state);
|
||||
aes_addroundkey(state, &expanded_key[round * 16]);
|
||||
}
|
||||
aes_subbytes(state);
|
||||
aes_shiftrows(state);
|
||||
aes_addroundkey(state, &expanded_key[160]);
|
||||
memcpy(data, state, 16);
|
||||
}
|
||||
|
||||
static void get_kia_v6_aes_key(uint8_t* aes_key) {
|
||||
uint64_t keystore_a = 0x37CE21F8C9F862A8ULL ^ 0x5448455049524154ULL;
|
||||
uint32_t keystore_a_hi = (keystore_a >> 32) & 0xFFFFFFFF;
|
||||
@@ -381,9 +184,9 @@ static bool kia_v6_decrypt(SubGhzProtocolDecoderKiaV6* instance) {
|
||||
|
||||
uint8_t aes_key[16];
|
||||
get_kia_v6_aes_key(aes_key);
|
||||
uint8_t expanded_key[176];
|
||||
aes_key_expansion(aes_key, expanded_key);
|
||||
aes128_decrypt(expanded_key, encrypted_data);
|
||||
uint8_t decrypted_buf[16];
|
||||
furi_hal_crypto_aes128_ecb_decrypt(aes_key, encrypted_data, decrypted_buf);
|
||||
memcpy(encrypted_data, decrypted_buf, 16);
|
||||
|
||||
uint8_t *decrypted = encrypted_data;
|
||||
uint8_t calculated_crc = kia_v6_crc8(decrypted, 15, 0xFF, 0x07);
|
||||
@@ -444,9 +247,9 @@ static void kia_v6_encrypt_payload(
|
||||
|
||||
uint8_t aes_key[16];
|
||||
get_kia_v6_aes_key(aes_key);
|
||||
uint8_t expanded_key[176];
|
||||
aes_key_expansion(aes_key, expanded_key);
|
||||
aes128_encrypt(expanded_key, plain);
|
||||
uint8_t encrypted[16];
|
||||
furi_hal_crypto_aes128_ecb_encrypt(aes_key, plain, encrypted);
|
||||
memcpy(plain, encrypted, 16);
|
||||
|
||||
uint8_t fx_hi = 0x20 | (fx_field >> 4);
|
||||
uint8_t fx_lo = fx_field & 0x0F;
|
||||
|
||||
259
lib/subghz/protocols/mitsubishi_v1.c
Normal file
259
lib/subghz/protocols/mitsubishi_v1.c
Normal file
@@ -0,0 +1,259 @@
|
||||
#include "mitsubishi_v1.h"
|
||||
|
||||
#define TAG "SubGhzProtocolMitsubishi"
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_mitsubishi_const = {
|
||||
.te_short = 320, // Similar to KIA timing
|
||||
.te_long = 640, // ~2× te_short
|
||||
.te_delta = 100,
|
||||
.min_count_bit_for_found = 64,
|
||||
};
|
||||
|
||||
typedef struct SubGhzProtocolDecoderMitsubishi {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
uint16_t header_count;
|
||||
} SubGhzProtocolDecoderMitsubishi;
|
||||
|
||||
typedef struct SubGhzProtocolEncoderMitsubishi {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
} SubGhzProtocolEncoderMitsubishi;
|
||||
|
||||
typedef enum {
|
||||
MitsubishiDecoderStepReset = 0,
|
||||
MitsubishiDecoderStepCheckPreamble,
|
||||
MitsubishiDecoderStepSaveDuration,
|
||||
MitsubishiDecoderStepCheckDuration,
|
||||
} MitsubishiDecoderStep;
|
||||
|
||||
static void subghz_protocol_decoder_mitsubishi_reset_internal(SubGhzProtocolDecoderMitsubishi* instance) {
|
||||
memset(&instance->decoder, 0, sizeof(instance->decoder));
|
||||
memset(&instance->generic, 0, sizeof(instance->generic));
|
||||
instance->decoder.parser_step = MitsubishiDecoderStepReset;
|
||||
instance->header_count = 0;
|
||||
}
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_mitsubishi_decoder = {
|
||||
.alloc = subghz_protocol_decoder_mitsubishi_alloc,
|
||||
.free = subghz_protocol_decoder_mitsubishi_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_mitsubishi_feed,
|
||||
.reset = subghz_protocol_decoder_mitsubishi_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_mitsubishi_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_mitsubishi_serialize,
|
||||
.deserialize = subghz_protocol_decoder_mitsubishi_deserialize,
|
||||
.get_string = subghz_protocol_decoder_mitsubishi_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_mitsubishi_encoder = {
|
||||
.alloc = NULL,
|
||||
.free = NULL,
|
||||
|
||||
.deserialize = NULL,
|
||||
.stop = NULL,
|
||||
.yield = NULL,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_mitsubishi_v1 = {
|
||||
.name = MITSUBISHI_PROTOCOL_NAME,
|
||||
.type = SubGhzProtocolTypeDynamic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable,
|
||||
|
||||
.decoder = &subghz_protocol_mitsubishi_decoder,
|
||||
.encoder = &subghz_protocol_mitsubishi_encoder,
|
||||
};
|
||||
|
||||
// ----------------- Allocation / Reset / Free -------------------
|
||||
|
||||
void* subghz_protocol_decoder_mitsubishi_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderMitsubishi* instance = calloc(1, sizeof(SubGhzProtocolDecoderMitsubishi));
|
||||
instance->base.protocol = &subghz_protocol_mitsubishi_v1;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
subghz_protocol_decoder_mitsubishi_reset(instance);
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_mitsubishi_free(void* context) {
|
||||
furi_assert(context);
|
||||
free(context);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_mitsubishi_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderMitsubishi* instance = context;
|
||||
subghz_protocol_decoder_mitsubishi_reset_internal(instance);
|
||||
}
|
||||
|
||||
// ----------------- Helper Functions -------------------
|
||||
|
||||
// Parse Mitsubishi/KIA-Hyundai data structure
|
||||
static void subghz_protocol_mitsubishi_parse_data(SubGhzProtocolDecoderMitsubishi* instance) {
|
||||
// Structure similar to KIA/Hyundai protocol
|
||||
// Serial number in upper bits
|
||||
// Button code in middle bits
|
||||
// Counter in lower bits
|
||||
|
||||
instance->generic.serial = (uint32_t)((instance->generic.data >> 32) & 0xFFFFFFFF);
|
||||
instance->generic.btn = (instance->generic.data >> 24) & 0xFF;
|
||||
instance->generic.cnt = (instance->generic.data >> 8) & 0xFFFF;
|
||||
}
|
||||
|
||||
// ----------------- Decoder Feed -------------------
|
||||
|
||||
void subghz_protocol_decoder_mitsubishi_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderMitsubishi* instance = context;
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case MitsubishiDecoderStepReset:
|
||||
if(level && (DURATION_DIFF(duration, subghz_protocol_mitsubishi_const.te_short) <
|
||||
subghz_protocol_mitsubishi_const.te_delta)) {
|
||||
instance->decoder.parser_step = MitsubishiDecoderStepCheckPreamble;
|
||||
instance->decoder.te_last = duration;
|
||||
instance->header_count = 0;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case MitsubishiDecoderStepCheckPreamble:
|
||||
if(level) {
|
||||
if((DURATION_DIFF(duration, subghz_protocol_mitsubishi_const.te_short) <
|
||||
subghz_protocol_mitsubishi_const.te_delta) ||
|
||||
(DURATION_DIFF(duration, subghz_protocol_mitsubishi_const.te_long) <
|
||||
subghz_protocol_mitsubishi_const.te_delta)) {
|
||||
instance->decoder.te_last = duration;
|
||||
} else {
|
||||
instance->decoder.parser_step = MitsubishiDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
if((DURATION_DIFF(duration, subghz_protocol_mitsubishi_const.te_short) <
|
||||
subghz_protocol_mitsubishi_const.te_delta) &&
|
||||
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_mitsubishi_const.te_short) <
|
||||
subghz_protocol_mitsubishi_const.te_delta)) {
|
||||
instance->header_count++;
|
||||
} else if(
|
||||
(DURATION_DIFF(duration, subghz_protocol_mitsubishi_const.te_long) <
|
||||
subghz_protocol_mitsubishi_const.te_delta) &&
|
||||
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_mitsubishi_const.te_long) <
|
||||
subghz_protocol_mitsubishi_const.te_delta)) {
|
||||
if(instance->header_count > 10) {
|
||||
instance->decoder.parser_step = MitsubishiDecoderStepSaveDuration;
|
||||
instance->decoder.decode_data = 0ULL;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
} else {
|
||||
instance->decoder.parser_step = MitsubishiDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = MitsubishiDecoderStepReset;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MitsubishiDecoderStepSaveDuration:
|
||||
if(level) {
|
||||
if(duration >= (subghz_protocol_mitsubishi_const.te_long * 3)) {
|
||||
if(instance->decoder.decode_count_bit >=
|
||||
subghz_protocol_mitsubishi_const.min_count_bit_for_found) {
|
||||
instance->generic.data = instance->decoder.decode_data;
|
||||
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
|
||||
|
||||
// Parse Mitsubishi data
|
||||
subghz_protocol_mitsubishi_parse_data(instance);
|
||||
|
||||
if(instance->base.callback) {
|
||||
instance->base.callback(&instance->base, instance->base.context);
|
||||
}
|
||||
}
|
||||
subghz_protocol_decoder_mitsubishi_reset_internal(instance);
|
||||
} else {
|
||||
instance->decoder.te_last = duration;
|
||||
instance->decoder.parser_step = MitsubishiDecoderStepCheckDuration;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = MitsubishiDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
|
||||
case MitsubishiDecoderStepCheckDuration:
|
||||
if(!level) {
|
||||
// Manchester-like decoding (KIA/Hyundai style)
|
||||
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_mitsubishi_const.te_short) <
|
||||
subghz_protocol_mitsubishi_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_mitsubishi_const.te_short) <
|
||||
subghz_protocol_mitsubishi_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
instance->decoder.parser_step = MitsubishiDecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_mitsubishi_const.te_long) <
|
||||
subghz_protocol_mitsubishi_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_mitsubishi_const.te_long) <
|
||||
subghz_protocol_mitsubishi_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
instance->decoder.parser_step = MitsubishiDecoderStepSaveDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = MitsubishiDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = MitsubishiDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------- API -------------------
|
||||
|
||||
uint8_t subghz_protocol_decoder_mitsubishi_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderMitsubishi* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_mitsubishi_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderMitsubishi* instance = context;
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_mitsubishi_deserialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderMitsubishi* instance = context;
|
||||
return subghz_block_generic_deserialize_check_count_bit(
|
||||
&instance->generic,
|
||||
flipper_format,
|
||||
subghz_protocol_mitsubishi_const.min_count_bit_for_found);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_mitsubishi_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderMitsubishi* instance = context;
|
||||
|
||||
uint32_t hi = instance->generic.data >> 32;
|
||||
uint32_t lo = instance->generic.data & 0xFFFFFFFF;
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:%08lX%08lX\r\n"
|
||||
"Sn:%08lX Btn:%02X Cnt:%04lX\r\n"
|
||||
"Type:KIA/Hyundai based\r\n"
|
||||
"Models:L200,Pajero,ASX+\r\n",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
hi,
|
||||
lo,
|
||||
instance->generic.serial,
|
||||
instance->generic.btn,
|
||||
instance->generic.cnt);
|
||||
}
|
||||
77
lib/subghz/protocols/mitsubishi_v1.h
Normal file
77
lib/subghz/protocols/mitsubishi_v1.h
Normal file
@@ -0,0 +1,77 @@
|
||||
#pragma once
|
||||
|
||||
#include <lib/subghz/protocols/base.h>
|
||||
#include <lib/subghz/blocks/const.h>
|
||||
#include <lib/subghz/blocks/decoder.h>
|
||||
#include <lib/subghz/blocks/encoder.h>
|
||||
#include <lib/subghz/blocks/generic.h>
|
||||
#include <lib/subghz/blocks/math.h>
|
||||
|
||||
#define MITSUBISHI_PROTOCOL_NAME "Mitsubishi v1"
|
||||
|
||||
extern const SubGhzProtocol subghz_protocol_mitsubishi_v1;
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_mitsubishi_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_mitsubishi_encoder;
|
||||
|
||||
/**
|
||||
* Allocates memory for the Mitsubishi protocol decoder.
|
||||
* @param environment Pointer to SubGhzEnvironment
|
||||
* @return Pointer to the allocated decoder instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_mitsubishi_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Frees memory used by the Mitsubishi protocol decoder.
|
||||
* @param context Pointer to the decoder instance
|
||||
*/
|
||||
void subghz_protocol_decoder_mitsubishi_free(void* context);
|
||||
|
||||
/**
|
||||
* Resets the Mitsubishi protocol decoder state.
|
||||
* @param context Pointer to the decoder instance
|
||||
*/
|
||||
void subghz_protocol_decoder_mitsubishi_reset(void* context);
|
||||
|
||||
/**
|
||||
* Feeds a pulse/gap into the Mitsubishi protocol decoder.
|
||||
* @param context Pointer to the decoder instance
|
||||
* @param level Signal level (true = high, false = low)
|
||||
* @param duration Duration of the level in microseconds
|
||||
*/
|
||||
void subghz_protocol_decoder_mitsubishi_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Returns a hash of the decoded Mitsubishi data.
|
||||
* @param context Pointer to the decoder instance
|
||||
* @return Hash byte
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_mitsubishi_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serializes the decoded Mitsubishi data into a FlipperFormat file.
|
||||
* @param context Pointer to the decoder instance
|
||||
* @param flipper_format Pointer to the FlipperFormat instance
|
||||
* @param preset Pointer to the radio preset
|
||||
* @return SubGhzProtocolStatus result
|
||||
*/
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_mitsubishi_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserializes Mitsubishi data from a FlipperFormat file.
|
||||
* @param context Pointer to the decoder instance
|
||||
* @param flipper_format Pointer to the FlipperFormat instance
|
||||
* @return SubGhzProtocolStatus result
|
||||
*/
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_mitsubishi_deserialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Formats the decoded Mitsubishi data into a human-readable string.
|
||||
* @param context Pointer to the decoder instance
|
||||
* @param output Pointer to the FuriString output buffer
|
||||
*/
|
||||
void subghz_protocol_decoder_mitsubishi_get_string(void* context, FuriString* output);
|
||||
291
lib/subghz/protocols/peugeot.c
Normal file
291
lib/subghz/protocols/peugeot.c
Normal file
@@ -0,0 +1,291 @@
|
||||
#include "peugeot.h"
|
||||
|
||||
#define TAG "SubGhzProtocolPeugeot"
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_peugeot_const = {
|
||||
.te_short = 370, // Short pulse duration
|
||||
.te_long = 772, // Long pulse duration (~2x short)
|
||||
.te_delta = 152, // Tolerance
|
||||
.min_count_bit_for_found = 66,
|
||||
};
|
||||
|
||||
typedef struct SubGhzProtocolDecoderPeugeot {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
uint16_t header_count;
|
||||
uint8_t packet_count;
|
||||
} SubGhzProtocolDecoderPeugeot;
|
||||
|
||||
typedef struct SubGhzProtocolEncoderPeugeot {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
} SubGhzProtocolEncoderPeugeot;
|
||||
|
||||
typedef enum {
|
||||
PeugeotDecoderStepReset = 0,
|
||||
PeugeotDecoderStepCheckPreamble,
|
||||
PeugeotDecoderStepSaveDuration,
|
||||
PeugeotDecoderStepCheckDuration,
|
||||
} PeugeotDecoderStep;
|
||||
|
||||
static void subghz_protocol_decoder_peugeot_reset_internal(SubGhzProtocolDecoderPeugeot* instance) {
|
||||
memset(&instance->decoder, 0, sizeof(instance->decoder));
|
||||
memset(&instance->generic, 0, sizeof(instance->generic));
|
||||
instance->decoder.parser_step = PeugeotDecoderStepReset;
|
||||
instance->header_count = 0;
|
||||
instance->packet_count = 0;
|
||||
}
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_peugeot_decoder = {
|
||||
.alloc = subghz_protocol_decoder_peugeot_alloc,
|
||||
.free = subghz_protocol_decoder_peugeot_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_peugeot_feed,
|
||||
.reset = subghz_protocol_decoder_peugeot_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_peugeot_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_peugeot_serialize,
|
||||
.deserialize = subghz_protocol_decoder_peugeot_deserialize,
|
||||
.get_string = subghz_protocol_decoder_peugeot_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_peugeot_encoder = {
|
||||
.alloc = NULL,
|
||||
.free = NULL,
|
||||
|
||||
.deserialize = NULL,
|
||||
.stop = NULL,
|
||||
.yield = NULL,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_peugeot = {
|
||||
.name = PEUGEOT_PROTOCOL_NAME,
|
||||
.type = SubGhzProtocolTypeDynamic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
|
||||
|
||||
.decoder = &subghz_protocol_peugeot_decoder,
|
||||
.encoder = &subghz_protocol_peugeot_encoder,
|
||||
};
|
||||
|
||||
// ----------------- Allocation / Reset / Free -------------------
|
||||
|
||||
void* subghz_protocol_decoder_peugeot_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderPeugeot* instance = calloc(1, sizeof(SubGhzProtocolDecoderPeugeot));
|
||||
instance->base.protocol = &subghz_protocol_peugeot;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
subghz_protocol_decoder_peugeot_reset(instance);
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_peugeot_free(void* context) {
|
||||
furi_assert(context);
|
||||
free(context);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_peugeot_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderPeugeot* instance = context;
|
||||
subghz_protocol_decoder_peugeot_reset_internal(instance);
|
||||
}
|
||||
|
||||
// ----------------- Helper Functions -------------------
|
||||
|
||||
// Reverse 8 bits (LSB to MSB)
|
||||
static uint8_t reverse8(uint8_t byte) {
|
||||
byte = (byte & 0xF0) >> 4 | (byte & 0x0F) << 4;
|
||||
byte = (byte & 0xCC) >> 2 | (byte & 0x33) << 2;
|
||||
byte = (byte & 0xAA) >> 1 | (byte & 0x55) << 1;
|
||||
return byte;
|
||||
}
|
||||
|
||||
// Parse Keeloq data structure
|
||||
static bool subghz_protocol_peugeot_parse_data(SubGhzProtocolDecoderPeugeot* instance) {
|
||||
uint8_t* b = (uint8_t*)&instance->generic.data;
|
||||
|
||||
// Check preamble (first 12 bits should be 0xFFF)
|
||||
if(b[0] != 0xFF || (b[1] & 0xF0) != 0xF0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extract encrypted part (32 bits) - reversed
|
||||
uint32_t encrypted = ((uint32_t)reverse8(b[3]) << 24) |
|
||||
(reverse8(b[2]) << 16) |
|
||||
(reverse8(b[1] & 0x0F) << 8) |
|
||||
reverse8(b[0]);
|
||||
|
||||
// Extract serial number (28 bits) - reversed
|
||||
uint32_t serial = ((uint32_t)reverse8(b[7] & 0xF0) << 20) |
|
||||
(reverse8(b[6]) << 12) |
|
||||
(reverse8(b[5]) << 4) |
|
||||
(reverse8(b[4]) >> 4);
|
||||
|
||||
// Extract button bits (4 bits from encrypted part)
|
||||
// Note: Button bits are (MSB/first sent to LSB) S3, S0, S1, S2
|
||||
uint8_t button_bits = (encrypted >> 28) & 0x0F;
|
||||
|
||||
// Store parsed data
|
||||
instance->generic.serial = serial;
|
||||
instance->generic.btn = button_bits;
|
||||
instance->generic.cnt = (encrypted >> 16) & 0xFFFF; // Counter from encrypted part
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ----------------- Decoder Feed -------------------
|
||||
|
||||
void subghz_protocol_decoder_peugeot_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderPeugeot* instance = context;
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case PeugeotDecoderStepReset:
|
||||
if(level && (DURATION_DIFF(duration, subghz_protocol_peugeot_const.te_short) <
|
||||
subghz_protocol_peugeot_const.te_delta)) {
|
||||
instance->decoder.parser_step = PeugeotDecoderStepCheckPreamble;
|
||||
instance->decoder.te_last = duration;
|
||||
instance->header_count = 0;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case PeugeotDecoderStepCheckPreamble:
|
||||
if(level) {
|
||||
// High level - save duration
|
||||
if((DURATION_DIFF(duration, subghz_protocol_peugeot_const.te_short) <
|
||||
subghz_protocol_peugeot_const.te_delta)) {
|
||||
instance->decoder.te_last = duration;
|
||||
} else {
|
||||
instance->decoder.parser_step = PeugeotDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
// Low level - check for warm-up pulses
|
||||
if((DURATION_DIFF(duration, subghz_protocol_peugeot_const.te_short) <
|
||||
subghz_protocol_peugeot_const.te_delta) &&
|
||||
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_peugeot_const.te_short) <
|
||||
subghz_protocol_peugeot_const.te_delta)) {
|
||||
// Short pulse pair - part of warm-up
|
||||
instance->header_count++;
|
||||
} else if((DURATION_DIFF(duration, 4400) < 500) && instance->header_count >= 10) {
|
||||
// Long gap after warm-up pulses (~4400µs)
|
||||
instance->decoder.parser_step = PeugeotDecoderStepSaveDuration;
|
||||
instance->decoder.decode_data = 0ULL;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
} else {
|
||||
instance->decoder.parser_step = PeugeotDecoderStepReset;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PeugeotDecoderStepSaveDuration:
|
||||
if(level) {
|
||||
// High level - save duration
|
||||
if(duration >= (subghz_protocol_peugeot_const.te_long * 3)) {
|
||||
// Very long pulse - end of packet
|
||||
if(instance->decoder.decode_count_bit >=
|
||||
subghz_protocol_peugeot_const.min_count_bit_for_found) {
|
||||
|
||||
instance->generic.data = instance->decoder.decode_data;
|
||||
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
|
||||
|
||||
// Parse the Keeloq structure
|
||||
if(subghz_protocol_peugeot_parse_data(instance)) {
|
||||
instance->packet_count++;
|
||||
|
||||
// Call callback after receiving at least one packet
|
||||
if(instance->base.callback) {
|
||||
instance->base.callback(&instance->base, instance->base.context);
|
||||
}
|
||||
}
|
||||
}
|
||||
subghz_protocol_decoder_peugeot_reset_internal(instance);
|
||||
} else {
|
||||
instance->decoder.te_last = duration;
|
||||
instance->decoder.parser_step = PeugeotDecoderStepCheckDuration;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = PeugeotDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
|
||||
case PeugeotDecoderStepCheckDuration:
|
||||
if(!level) {
|
||||
// PWM decoding: short-long = 0, long-short = 1
|
||||
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_peugeot_const.te_short) <
|
||||
subghz_protocol_peugeot_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_peugeot_const.te_long) <
|
||||
subghz_protocol_peugeot_const.te_delta)) {
|
||||
// Short high, long low = 0
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
instance->decoder.parser_step = PeugeotDecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_peugeot_const.te_long) <
|
||||
subghz_protocol_peugeot_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_peugeot_const.te_short) <
|
||||
subghz_protocol_peugeot_const.te_delta)) {
|
||||
// Long high, short low = 1
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
instance->decoder.parser_step = PeugeotDecoderStepSaveDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = PeugeotDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = PeugeotDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------- API -------------------
|
||||
|
||||
uint8_t subghz_protocol_decoder_peugeot_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderPeugeot* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_peugeot_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderPeugeot* instance = context;
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_peugeot_deserialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderPeugeot* instance = context;
|
||||
return subghz_block_generic_deserialize_check_count_bit(
|
||||
&instance->generic,
|
||||
flipper_format,
|
||||
subghz_protocol_peugeot_const.min_count_bit_for_found);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_peugeot_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderPeugeot* instance = context;
|
||||
|
||||
uint32_t hi = instance->generic.data >> 32;
|
||||
uint32_t lo = instance->generic.data & 0xFFFFFFFF;
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:%08lX%08lX\r\n"
|
||||
"Sn:%07lX Btn:%X Cnt:%04lX\r\n"
|
||||
"Type:Keeloq/HCS\r\n",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
hi,
|
||||
lo,
|
||||
instance->generic.serial,
|
||||
instance->generic.btn,
|
||||
instance->generic.cnt);
|
||||
}
|
||||
77
lib/subghz/protocols/peugeot.h
Normal file
77
lib/subghz/protocols/peugeot.h
Normal file
@@ -0,0 +1,77 @@
|
||||
#pragma once
|
||||
|
||||
#include <lib/subghz/protocols/base.h>
|
||||
#include <lib/subghz/blocks/const.h>
|
||||
#include <lib/subghz/blocks/decoder.h>
|
||||
#include <lib/subghz/blocks/encoder.h>
|
||||
#include <lib/subghz/blocks/generic.h>
|
||||
#include <lib/subghz/blocks/math.h>
|
||||
|
||||
#define PEUGEOT_PROTOCOL_NAME "Peugeot"
|
||||
|
||||
extern const SubGhzProtocol subghz_protocol_peugeot;
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_peugeot_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_peugeot_encoder;
|
||||
|
||||
/**
|
||||
* Allocates memory for the Peugeot protocol decoder.
|
||||
* @param environment Pointer to SubGhzEnvironment
|
||||
* @return Pointer to the allocated decoder instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_peugeot_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Frees memory used by the Peugeot protocol decoder.
|
||||
* @param context Pointer to the decoder instance
|
||||
*/
|
||||
void subghz_protocol_decoder_peugeot_free(void* context);
|
||||
|
||||
/**
|
||||
* Resets the Peugeot protocol decoder state.
|
||||
* @param context Pointer to the decoder instance
|
||||
*/
|
||||
void subghz_protocol_decoder_peugeot_reset(void* context);
|
||||
|
||||
/**
|
||||
* Feeds a pulse/gap into the Peugeot protocol decoder.
|
||||
* @param context Pointer to the decoder instance
|
||||
* @param level Signal level (true = high, false = low)
|
||||
* @param duration Duration of the level in microseconds
|
||||
*/
|
||||
void subghz_protocol_decoder_peugeot_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Returns a hash of the decoded Peugeot data.
|
||||
* @param context Pointer to the decoder instance
|
||||
* @return Hash byte
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_peugeot_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serializes the decoded Peugeot data into a FlipperFormat file.
|
||||
* @param context Pointer to the decoder instance
|
||||
* @param flipper_format Pointer to the FlipperFormat instance
|
||||
* @param preset Pointer to the radio preset
|
||||
* @return SubGhzProtocolStatus result
|
||||
*/
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_peugeot_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserializes Peugeot data from a FlipperFormat file.
|
||||
* @param context Pointer to the decoder instance
|
||||
* @param flipper_format Pointer to the FlipperFormat instance
|
||||
* @return SubGhzProtocolStatus result
|
||||
*/
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_peugeot_deserialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Formats the decoded Peugeot data into a human-readable string.
|
||||
* @param context Pointer to the decoder instance
|
||||
* @param output Pointer to the FuriString output buffer
|
||||
*/
|
||||
void subghz_protocol_decoder_peugeot_get_string(void* context, FuriString* output);
|
||||
@@ -43,6 +43,8 @@ const SubGhzProtocol* const subghz_protocol_registry_items[] = {
|
||||
&subghz_protocol_kia_v2, &subghz_protocol_kia_v3_v4,
|
||||
&subghz_protocol_kia_v5, &subghz_protocol_kia_v6,
|
||||
&subghz_protocol_suzuki, &subghz_protocol_mitsubishi_v0,
|
||||
&subghz_protocol_bmw, &subghz_protocol_mitsubishi_v1, &subghz_protocol_honda,
|
||||
&subghz_protocol_citroen, &subghz_protocol_peugeot,
|
||||
};
|
||||
|
||||
const SubGhzProtocolRegistry subghz_protocol_registry = {
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
#include "fiat_v0.h"
|
||||
#include "fiat_marelli.h"
|
||||
#include "subaru.h"
|
||||
#include "bmw.h"
|
||||
#include "kia_generic.h"
|
||||
#include "kia_v0.h"
|
||||
#include "kia_v1.h"
|
||||
@@ -72,5 +73,9 @@
|
||||
#include "kia_v6.h"
|
||||
#include "suzuki.h"
|
||||
#include "mitsubishi_v0.h"
|
||||
#include "mitsubishi_v1.h"
|
||||
#include "honda.h"
|
||||
#include "citroen.h"
|
||||
#include "peugeot.h"
|
||||
#include "mazda_siemens.h"
|
||||
#include "keys.h"
|
||||
|
||||
@@ -1248,6 +1248,8 @@ Function,+,furi_hal_crypto_enclave_load_key,_Bool,"uint8_t, const uint8_t*"
|
||||
Function,+,furi_hal_crypto_enclave_store_key,_Bool,"FuriHalCryptoKey*, uint8_t*"
|
||||
Function,+,furi_hal_crypto_enclave_unload_key,_Bool,uint8_t
|
||||
Function,+,furi_hal_crypto_enclave_verify,_Bool,"uint8_t*, uint8_t*"
|
||||
Function,+,furi_hal_crypto_aes128_ecb_decrypt,_Bool,"const uint8_t*, const uint8_t*, uint8_t*"
|
||||
Function,+,furi_hal_crypto_aes128_ecb_encrypt,_Bool,"const uint8_t*, const uint8_t*, uint8_t*"
|
||||
Function,+,furi_hal_crypto_encrypt,_Bool,"const uint8_t*, uint8_t*, size_t"
|
||||
Function,+,furi_hal_crypto_gcm,_Bool,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, uint8_t*, _Bool"
|
||||
Function,+,furi_hal_crypto_gcm_decrypt_and_verify,FuriHalCryptoGCMState,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, const uint8_t*"
|
||||
|
||||
|
@@ -1454,6 +1454,8 @@ Function,+,furi_hal_crypto_enclave_load_key,_Bool,"uint8_t, const uint8_t*"
|
||||
Function,+,furi_hal_crypto_enclave_store_key,_Bool,"FuriHalCryptoKey*, uint8_t*"
|
||||
Function,+,furi_hal_crypto_enclave_unload_key,_Bool,uint8_t
|
||||
Function,+,furi_hal_crypto_enclave_verify,_Bool,"uint8_t*, uint8_t*"
|
||||
Function,+,furi_hal_crypto_aes128_ecb_decrypt,_Bool,"const uint8_t*, const uint8_t*, uint8_t*"
|
||||
Function,+,furi_hal_crypto_aes128_ecb_encrypt,_Bool,"const uint8_t*, const uint8_t*, uint8_t*"
|
||||
Function,+,furi_hal_crypto_encrypt,_Bool,"const uint8_t*, uint8_t*, size_t"
|
||||
Function,+,furi_hal_crypto_gcm,_Bool,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, uint8_t*, _Bool"
|
||||
Function,+,furi_hal_crypto_gcm_decrypt_and_verify,FuriHalCryptoGCMState,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, const uint8_t*"
|
||||
|
||||
|
@@ -21,9 +21,11 @@
|
||||
#define CRYPTO_MODE_DECRYPT (AES_CR_MODE_1)
|
||||
#define CRYPTO_MODE_DECRYPT_INIT (AES_CR_MODE_0 | AES_CR_MODE_1)
|
||||
|
||||
#define CRYPTO_DATATYPE_32B 0U
|
||||
#define CRYPTO_KEYSIZE_256B (AES_CR_KEYSIZE)
|
||||
#define CRYPTO_AES_CBC (AES_CR_CHMOD_0)
|
||||
#define CRYPTO_DATATYPE_32B 0U
|
||||
#define CRYPTO_DATATYPE_8B (AES_CR_DATATYPE_1)
|
||||
#define CRYPTO_KEYSIZE_256B (AES_CR_KEYSIZE)
|
||||
#define CRYPTO_AES_ECB 0U
|
||||
#define CRYPTO_AES_CBC (AES_CR_CHMOD_0)
|
||||
|
||||
#define CRYPTO_AES_CTR (AES_CR_CHMOD_1)
|
||||
#define CRYPTO_CTR_IV_LEN (12U)
|
||||
@@ -748,3 +750,72 @@ FuriHalCryptoGCMState furi_hal_crypto_gcm_decrypt_and_verify(
|
||||
|
||||
return FuriHalCryptoGCMStateOk;
|
||||
}
|
||||
|
||||
static void crypto_key_init_ecb128(const uint8_t* key) {
|
||||
CLEAR_BIT(AES1->CR, AES_CR_EN);
|
||||
MODIFY_REG(
|
||||
AES1->CR,
|
||||
AES_CR_DATATYPE | AES_CR_KEYSIZE | AES_CR_CHMOD,
|
||||
CRYPTO_DATATYPE_8B | CRYPTO_AES_ECB);
|
||||
|
||||
AES1->KEYR3 = ((uint32_t*)key)[0];
|
||||
AES1->KEYR2 = ((uint32_t*)key)[1];
|
||||
AES1->KEYR1 = ((uint32_t*)key)[2];
|
||||
AES1->KEYR0 = ((uint32_t*)key)[3];
|
||||
}
|
||||
|
||||
bool furi_hal_crypto_aes128_ecb_encrypt(
|
||||
const uint8_t* key,
|
||||
const uint8_t* input,
|
||||
uint8_t* output) {
|
||||
furi_check(furi_hal_crypto_mutex);
|
||||
furi_check(furi_mutex_acquire(furi_hal_crypto_mutex, FuriWaitForever) == FuriStatusOk);
|
||||
|
||||
furi_hal_bus_enable(FuriHalBusAES1);
|
||||
crypto_key_init_ecb128(key);
|
||||
|
||||
MODIFY_REG(AES1->CR, AES_CR_MODE, CRYPTO_MODE_ENCRYPT);
|
||||
SET_BIT(AES1->CR, AES_CR_EN);
|
||||
|
||||
bool state = crypto_process_block((uint32_t*)input, (uint32_t*)output, 4);
|
||||
|
||||
CLEAR_BIT(AES1->CR, AES_CR_EN);
|
||||
furi_hal_bus_disable(FuriHalBusAES1);
|
||||
furi_check(furi_mutex_release(furi_hal_crypto_mutex) == FuriStatusOk);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
bool furi_hal_crypto_aes128_ecb_decrypt(
|
||||
const uint8_t* key,
|
||||
const uint8_t* input,
|
||||
uint8_t* output) {
|
||||
furi_check(furi_hal_crypto_mutex);
|
||||
furi_check(furi_mutex_acquire(furi_hal_crypto_mutex, FuriWaitForever) == FuriStatusOk);
|
||||
|
||||
furi_hal_bus_enable(FuriHalBusAES1);
|
||||
crypto_key_init_ecb128(key);
|
||||
|
||||
MODIFY_REG(AES1->CR, AES_CR_MODE, CRYPTO_MODE_DECRYPT_INIT);
|
||||
SET_BIT(AES1->CR, AES_CR_EN);
|
||||
|
||||
if(!furi_hal_crypto_wait_flag(AES_SR_CCF)) {
|
||||
CLEAR_BIT(AES1->CR, AES_CR_EN);
|
||||
furi_hal_bus_disable(FuriHalBusAES1);
|
||||
furi_check(furi_mutex_release(furi_hal_crypto_mutex) == FuriStatusOk);
|
||||
return false;
|
||||
}
|
||||
|
||||
SET_BIT(AES1->CR, AES_CR_CCFC);
|
||||
|
||||
MODIFY_REG(AES1->CR, AES_CR_MODE, CRYPTO_MODE_DECRYPT);
|
||||
SET_BIT(AES1->CR, AES_CR_EN);
|
||||
|
||||
bool state = crypto_process_block((uint32_t*)input, (uint32_t*)output, 4);
|
||||
|
||||
CLEAR_BIT(AES1->CR, AES_CR_EN);
|
||||
furi_hal_bus_disable(FuriHalBusAES1);
|
||||
furi_check(furi_mutex_release(furi_hal_crypto_mutex) == FuriStatusOk);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -290,6 +290,32 @@ FuriHalCryptoGCMState furi_hal_crypto_gcm_decrypt_and_verify(
|
||||
size_t length,
|
||||
const uint8_t* tag);
|
||||
|
||||
/** Encrypt a single 16-byte block using AES-128-ECB
|
||||
*
|
||||
* @param[in] key pointer to 16 bytes key data
|
||||
* @param[in] input pointer to 16 bytes input data
|
||||
* @param[out] output pointer to 16 bytes output data
|
||||
*
|
||||
* @return true on success
|
||||
*/
|
||||
bool furi_hal_crypto_aes128_ecb_encrypt(
|
||||
const uint8_t* key,
|
||||
const uint8_t* input,
|
||||
uint8_t* output);
|
||||
|
||||
/** Decrypt a single 16-byte block using AES-128-ECB
|
||||
*
|
||||
* @param[in] key pointer to 16 bytes key data
|
||||
* @param[in] input pointer to 16 bytes input data
|
||||
* @param[out] output pointer to 16 bytes output data
|
||||
*
|
||||
* @return true on success
|
||||
*/
|
||||
bool furi_hal_crypto_aes128_ecb_decrypt(
|
||||
const uint8_t* key,
|
||||
const uint8_t* input,
|
||||
uint8_t* output);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user