Protocol updates

This commit is contained in:
leviathan
2026-02-11 21:59:30 -05:00
parent 0767850a6f
commit 9e2565c1fc
30 changed files with 772 additions and 507 deletions
+25 -23
View File
@@ -1,12 +1,13 @@
//! PSA (Peugeot/Citroen) protocol decoder/encoder
//! PSA (Peugeot/Citroën) protocol decoder/encoder
//!
//! Ported from protopirate's psa.c
//! Aligned with ProtoPirate reference: `REFERENCES/ProtoPirate/protocols/psa.c`.
//! Decode/encode logic (Manchester, preamble, TEA, XOR, mode 0x23/0x36) matches reference.
//!
//! Protocol characteristics:
//! - Manchester encoding: 125/250µs bit timing, 250/500µs symbol timing
//! - 128 bits total (key1: 64 bits, key2: 16 bits, validation: 48 bits)
//! - TEA and XOR encryption schemes
//! - Two modes: 0x23 and 0x36
//! - Manchester encoding: 250/500µs symbol (125/250µs sub-symbol for preamble)
//! - 128 bits total: key1 (64) + validation (16) + key2/rest (48); decode uses key1 + 16-bit validation
//! - TEA decrypt/encrypt with fixed key schedules; mode 0x23 adds XOR layer
//! - Two modes: seed 0x23 (TEA + XOR), seed 0xF3/0x36 (TEA, BF2 key schedule)
use super::{DecodedSignal, ProtocolDecoder, ProtocolTiming};
use crate::duration_diff;
@@ -35,7 +36,7 @@ const BF1_KEY_SCHEDULE: [u32; 4] = [0x4A434915, 0xD6743C2B, 0x1F29D308, 0xE6B79A
// Brute-force constants for mode 0x36
const BF2_KEY_SCHEDULE: [u32; 4] = [0x4039C240, 0xEDA92CAB, 0x4306C02A, 0x02192A04];
/// Manchester decoder states
/// Manchester decoder states (matches protopirate psa.c Manchester state machine)
#[derive(Debug, Clone, Copy, PartialEq)]
enum ManchesterState {
Mid0,
@@ -44,16 +45,12 @@ enum ManchesterState {
Start1,
}
/// Decoder states
/// Decoder states (matches protopirate's PsaDecoderState)
#[derive(Debug, Clone, Copy, PartialEq)]
enum DecoderState {
/// Waiting for first edge
WaitEdge,
/// Counting preamble pattern
CountPattern,
/// Decoding Manchester data
DecodeManchester,
/// Found end of data
End,
}
@@ -94,7 +91,7 @@ impl PsaDecoder {
}
}
/// Manchester advance
/// Manchester state machine (matches psa.c event mapping)
fn manchester_advance(&mut self, is_short: bool, is_high: bool) -> Option<bool> {
let event = match (is_short, is_high) {
(true, true) => 0,
@@ -143,7 +140,7 @@ impl PsaDecoder {
}
}
/// TEA decrypt
/// TEA decrypt (matches psa.c / standard TEA)
fn tea_decrypt(v0: &mut u32, v1: &mut u32, key: &[u32; 4]) {
let mut sum = TEA_DELTA.wrapping_mul(TEA_ROUNDS);
for _ in 0..TEA_ROUNDS {
@@ -161,7 +158,7 @@ impl PsaDecoder {
}
}
/// TEA encrypt
/// TEA encrypt (matches psa.c / standard TEA)
fn tea_encrypt(v0: &mut u32, v1: &mut u32, key: &[u32; 4]) {
let mut sum: u32 = 0;
for _ in 0..TEA_ROUNDS {
@@ -179,7 +176,7 @@ impl PsaDecoder {
}
}
/// XOR decrypt (mode 0x23)
/// XOR decrypt for mode 0x23 (matches psa.c)
fn xor_decrypt(buffer: &mut [u8]) {
let e6 = buffer[8];
let e7 = buffer[9];
@@ -198,8 +195,9 @@ impl PsaDecoder {
buffer[7] = e5 ^ e6 ^ e7;
}
/// Decrypt key1 + validation: mode 0x23 (TEA+XOR) or 0x36 (TEA, BF2) — matches psa.c
fn try_decrypt(&self) -> Option<(u32, u8, u32, u16, u8)> {
// Try mode 0x23 first
// Try mode 0x23 first (seed byte 0x23)
let seed_byte = (self.key1_high >> 24) as u8;
if seed_byte >= 0x23 && seed_byte < 0x24 {
@@ -250,8 +248,9 @@ impl PsaDecoder {
None
}
/// Build DecodedSignal from key1 + validation; decrypt yields serial/button/counter (matches psa.c)
fn parse_data(&self) -> DecodedSignal {
// Combine into 128-bit data (store lower 64)
// Store key1 as 64-bit data for display/replay
let data = ((self.key1_high as u64) << 32) | (self.key1_low as u64);
if let Some((serial, btn, counter, _crc, _mode)) = self.try_decrypt() {
@@ -445,20 +444,17 @@ impl ProtocolDecoder for PsaDecoder {
let key1_low = v1;
let validation = ((buffer[8] as u16) << 8) | (buffer[9] as u16);
// Build signal
let mut signal = Vec::with_capacity(512);
// Preamble: alternating 125µs pulses
// Preamble + sync (matches protopirate psa encode)
for _ in 0..70 {
signal.push(LevelDuration::new(true, TE_SHORT_125));
signal.push(LevelDuration::new(false, TE_SHORT_125));
}
// Sync
signal.push(LevelDuration::new(true, TE_LONG_250));
signal.push(LevelDuration::new(false, TE_LONG_250));
// Key1: 64 bits Manchester encoded
// Key1: 64 bits Manchester, then validation 16 bits
let key1 = ((key1_high as u64) << 32) | (key1_low as u64);
for bit in (0..64).rev() {
if (key1 >> bit) & 1 == 1 {
@@ -487,3 +483,9 @@ impl ProtocolDecoder for PsaDecoder {
Some(signal)
}
}
impl Default for PsaDecoder {
fn default() -> Self {
Self::new()
}
}