Version 1.0.0

This commit is contained in:
leviathan
2026-02-07 17:35:27 -05:00
parent c8bff9afd7
commit 4339895b41
43 changed files with 12218 additions and 3 deletions
+290
View File
@@ -0,0 +1,290 @@
//! AUT64 block cipher implementation
//!
//! Ported from protopirate's aut64.c
//!
//! AUT64 algorithm: 12 rounds, 8-byte block/key size
//! Based on: Reference AUT64 implementation
//! See: https://www.usenix.org/system/files/conference/usenixsecurity16/sec16_paper_garcia.pdf
pub const AUT64_NUM_ROUNDS: usize = 12;
pub const AUT64_BLOCK_SIZE: usize = 8;
pub const AUT64_KEY_SIZE: usize = 8;
pub const AUT64_PBOX_SIZE: usize = 8;
pub const AUT64_SBOX_SIZE: usize = 16;
#[allow(dead_code)]
pub const AUT64_KEY_STRUCT_PACKED_SIZE: usize = 16;
/// AUT64 key structure
#[derive(Debug, Clone)]
pub struct Aut64Key {
pub index: u8,
pub key: [u8; AUT64_KEY_SIZE],
pub pbox: [u8; AUT64_PBOX_SIZE],
pub sbox: [u8; AUT64_SBOX_SIZE],
}
impl Default for Aut64Key {
fn default() -> Self {
Self {
index: 0,
key: [0u8; AUT64_KEY_SIZE],
pbox: [0u8; AUT64_PBOX_SIZE],
sbox: [0u8; AUT64_SBOX_SIZE],
}
}
}
/// Round-dependent upper-nibble lookup table
static TABLE_LN: [[u8; 8]; AUT64_NUM_ROUNDS] = [
[0x4, 0x5, 0x6, 0x7, 0x0, 0x1, 0x2, 0x3], // Round 0
[0x5, 0x4, 0x7, 0x6, 0x1, 0x0, 0x3, 0x2], // Round 1
[0x6, 0x7, 0x4, 0x5, 0x2, 0x3, 0x0, 0x1], // Round 2
[0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0], // Round 3
[0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7], // Round 4
[0x1, 0x0, 0x3, 0x2, 0x5, 0x4, 0x7, 0x6], // Round 5
[0x2, 0x3, 0x0, 0x1, 0x6, 0x7, 0x4, 0x5], // Round 6
[0x3, 0x2, 0x1, 0x0, 0x7, 0x6, 0x5, 0x4], // Round 7
[0x5, 0x4, 0x7, 0x6, 0x1, 0x0, 0x3, 0x2], // Round 8
[0x4, 0x5, 0x6, 0x7, 0x0, 0x1, 0x2, 0x3], // Round 9
[0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0], // Round 10
[0x6, 0x7, 0x4, 0x5, 0x2, 0x3, 0x0, 0x1], // Round 11
];
/// Round-dependent lower-nibble lookup table
static TABLE_UN: [[u8; 8]; AUT64_NUM_ROUNDS] = [
[0x1, 0x0, 0x3, 0x2, 0x5, 0x4, 0x7, 0x6], // Round 0
[0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7], // Round 1
[0x3, 0x2, 0x1, 0x0, 0x7, 0x6, 0x5, 0x4], // Round 2
[0x2, 0x3, 0x0, 0x1, 0x6, 0x7, 0x4, 0x5], // Round 3
[0x5, 0x4, 0x7, 0x6, 0x1, 0x0, 0x3, 0x2], // Round 4
[0x4, 0x5, 0x6, 0x7, 0x0, 0x1, 0x2, 0x3], // Round 5
[0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0], // Round 6
[0x6, 0x7, 0x4, 0x5, 0x2, 0x3, 0x0, 0x1], // Round 7
[0x3, 0x2, 0x1, 0x0, 0x7, 0x6, 0x5, 0x4], // Round 8
[0x2, 0x3, 0x0, 0x1, 0x6, 0x7, 0x4, 0x5], // Round 9
[0x1, 0x0, 0x3, 0x2, 0x5, 0x4, 0x7, 0x6], // Round 10
[0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7], // Round 11
];
/// GF(2^4) multiplication table (nibble offset table)
#[rustfmt::skip]
static TABLE_OFFSET: [u8; 256] = [
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, // 1
0x0, 0x2, 0x4, 0x6, 0x8, 0xA, 0xC, 0xE, 0x3, 0x1, 0x7, 0x5, 0xB, 0x9, 0xF, 0xD, // 2
0x0, 0x3, 0x6, 0x5, 0xC, 0xF, 0xA, 0x9, 0xB, 0x8, 0xD, 0xE, 0x7, 0x4, 0x1, 0x2, // 3
0x0, 0x4, 0x8, 0xC, 0x3, 0x7, 0xB, 0xF, 0x6, 0x2, 0xE, 0xA, 0x5, 0x1, 0xD, 0x9, // 4
0x0, 0x5, 0xA, 0xF, 0x7, 0x2, 0xD, 0x8, 0xE, 0xB, 0x4, 0x1, 0x9, 0xC, 0x3, 0x6, // 5
0x0, 0x6, 0xC, 0xA, 0xB, 0xD, 0x7, 0x1, 0x5, 0x3, 0x9, 0xF, 0xE, 0x8, 0x2, 0x4, // 6
0x0, 0x7, 0xE, 0x9, 0xF, 0x8, 0x1, 0x6, 0xD, 0xA, 0x3, 0x4, 0x2, 0x5, 0xC, 0xB, // 7
0x0, 0x8, 0x3, 0xB, 0x6, 0xE, 0x5, 0xD, 0xC, 0x4, 0xF, 0x7, 0xA, 0x2, 0x9, 0x1, // 8
0x0, 0x9, 0x1, 0x8, 0x2, 0xB, 0x3, 0xA, 0x4, 0xD, 0x5, 0xC, 0x6, 0xF, 0x7, 0xE, // 9
0x0, 0xA, 0x7, 0xD, 0xE, 0x4, 0x9, 0x3, 0xF, 0x5, 0x8, 0x2, 0x1, 0xB, 0x6, 0xC, // A
0x0, 0xB, 0x5, 0xE, 0xA, 0x1, 0xF, 0x4, 0x7, 0xC, 0x2, 0x9, 0xD, 0x6, 0x8, 0x3, // B
0x0, 0xC, 0xB, 0x7, 0x5, 0x9, 0xE, 0x2, 0xA, 0x6, 0x1, 0xD, 0xF, 0x3, 0x4, 0x8, // C
0x0, 0xD, 0x9, 0x4, 0x1, 0xC, 0x8, 0x5, 0x2, 0xF, 0xB, 0x6, 0x3, 0xE, 0xA, 0x7, // D
0x0, 0xE, 0xF, 0x1, 0xD, 0x3, 0x2, 0xC, 0x9, 0x7, 0x6, 0x8, 0x4, 0xA, 0xB, 0x5, // E
0x0, 0xF, 0xD, 0x2, 0x9, 0x6, 0x4, 0xB, 0x1, 0xE, 0xC, 0x3, 0x8, 0x7, 0x5, 0xA, // F
];
/// S-box substitution table
static TABLE_SUB: [u8; 16] = [
0x0, 0x1, 0x9, 0xE, 0xD, 0xB, 0x7, 0x6,
0xF, 0x2, 0xC, 0x5, 0xA, 0x4, 0x3, 0x8,
];
/// Key nibble operation: apply key-dependent GF offset
fn key_nibble(key: &Aut64Key, nibble: u8, table: &[u8; 8], iteration: usize) -> u8 {
let key_value = key.key[table[iteration] as usize];
let offset = ((key_value as usize) << 4) | (nibble as usize);
TABLE_OFFSET[offset]
}
/// Compute round key from state
fn round_key(key: &Aut64Key, state: &[u8], round_n: usize) -> u8 {
let mut result_hi: u8 = 0;
let mut result_lo: u8 = 0;
for i in 0..(AUT64_BLOCK_SIZE - 1) {
result_hi ^= key_nibble(key, state[i] >> 4, &TABLE_UN[round_n], i);
result_lo ^= key_nibble(key, state[i] & 0x0F, &TABLE_LN[round_n], i);
}
(result_hi << 4) | result_lo
}
/// Final byte nibble for key schedule
fn final_byte_nibble(key: &Aut64Key, table: &[u8; 8]) -> u8 {
let key_value = key.key[table[AUT64_BLOCK_SIZE - 1] as usize];
TABLE_SUB[key_value as usize] << 4
}
/// Encrypt final byte nibble (inverse S-box lookup through offset table)
fn encrypt_final_byte_nibble(key: &Aut64Key, nibble: u8, table: &[u8; 8]) -> u8 {
let offset = final_byte_nibble(key, table) as usize;
for i in 0u8..16 {
if TABLE_OFFSET[offset + i as usize] == nibble {
return i;
}
}
0 // Should not reach here for valid inputs
}
/// Encrypt compress: compute encrypted output byte for a round
fn encrypt_compress(key: &Aut64Key, state: &[u8], round_n: usize) -> u8 {
let round_k = round_key(key, state, round_n);
let mut result_hi = round_k >> 4;
let mut result_lo = round_k & 0x0F;
result_hi ^= encrypt_final_byte_nibble(key, state[AUT64_BLOCK_SIZE - 1] >> 4, &TABLE_UN[round_n]);
result_lo ^= encrypt_final_byte_nibble(key, state[AUT64_BLOCK_SIZE - 1] & 0x0F, &TABLE_LN[round_n]);
(result_hi << 4) | result_lo
}
/// Decrypt final byte nibble (forward S-box through offset table)
fn decrypt_final_byte_nibble(key: &Aut64Key, nibble: u8, table: &[u8; 8], result: u8) -> u8 {
let offset = final_byte_nibble(key, table) as usize;
TABLE_OFFSET[(result ^ nibble) as usize + offset]
}
/// Decrypt compress: compute decrypted output byte for a round
fn decrypt_compress(key: &Aut64Key, state: &[u8], round_n: usize) -> u8 {
let round_k = round_key(key, state, round_n);
let result_hi = round_k >> 4;
let result_lo = round_k & 0x0F;
let hi = decrypt_final_byte_nibble(
key,
state[AUT64_BLOCK_SIZE - 1] >> 4,
&TABLE_UN[round_n],
result_hi,
);
let lo = decrypt_final_byte_nibble(
key,
state[AUT64_BLOCK_SIZE - 1] & 0x0F,
&TABLE_LN[round_n],
result_lo,
);
(hi << 4) | lo
}
/// S-box substitution on a full byte (applies S-box to each nibble independently)
fn substitute(key: &Aut64Key, byte: u8) -> u8 {
(key.sbox[(byte >> 4) as usize] << 4) | key.sbox[(byte & 0x0F) as usize]
}
/// Byte-level permutation using P-box
fn permute_bytes(key: &Aut64Key, state: &mut [u8]) {
let mut result = [0u8; AUT64_PBOX_SIZE];
for i in 0..AUT64_PBOX_SIZE {
result[key.pbox[i] as usize] = state[i];
}
state[..AUT64_PBOX_SIZE].copy_from_slice(&result);
}
/// Bit-level permutation using P-box
fn permute_bits(key: &Aut64Key, byte: u8) -> u8 {
let mut result: u8 = 0;
for i in 0..8 {
if byte & (1 << i) != 0 {
result |= 1 << key.pbox[i];
}
}
result
}
/// Compute inverse permutation box
fn reverse_box(box_in: &[u8], len: usize) -> Vec<u8> {
let mut reversed = vec![0u8; len];
for i in 0..len {
for j in 0..len {
if box_in[j] == i as u8 {
reversed[i] = j as u8;
break;
}
}
}
reversed
}
/// AUT64 encrypt: 12 rounds of the cipher
pub fn aut64_encrypt(key: &Aut64Key, message: &mut [u8]) {
// Create reverse key for encryption
let mut reverse_key = key.clone();
let rev_pbox = reverse_box(&key.pbox, AUT64_PBOX_SIZE);
let rev_sbox = reverse_box(&key.sbox, AUT64_SBOX_SIZE);
reverse_key.pbox.copy_from_slice(&rev_pbox);
reverse_key.sbox.copy_from_slice(&rev_sbox);
for i in 0..AUT64_NUM_ROUNDS {
permute_bytes(&reverse_key, message);
message[7] = encrypt_compress(&reverse_key, message, i);
message[7] = substitute(&reverse_key, message[7]);
message[7] = permute_bits(&reverse_key, message[7]);
message[7] = substitute(&reverse_key, message[7]);
}
}
/// AUT64 decrypt: 12 rounds of the cipher (reverse order)
pub fn aut64_decrypt(key: &Aut64Key, message: &mut [u8]) {
for i in (0..AUT64_NUM_ROUNDS).rev() {
message[7] = substitute(key, message[7]);
message[7] = permute_bits(key, message[7]);
message[7] = substitute(key, message[7]);
message[7] = decrypt_compress(key, message, i);
permute_bytes(key, message);
}
}
/// Pack an AUT64 key structure into a 16-byte array
#[allow(dead_code)]
pub fn aut64_pack(src: &Aut64Key) -> [u8; AUT64_KEY_STRUCT_PACKED_SIZE] {
let mut dest = [0u8; AUT64_KEY_STRUCT_PACKED_SIZE];
dest[0] = src.index;
for i in 0..(src.key.len() / 2) {
dest[i + 1] = (src.key[i * 2] << 4) | src.key[i * 2 + 1];
}
let mut pbox: u32 = 0;
for i in 0..src.pbox.len() {
pbox = (pbox << 3) | src.pbox[i] as u32;
}
dest[5] = (pbox >> 16) as u8;
dest[6] = ((pbox >> 8) & 0xFF) as u8;
dest[7] = (pbox & 0xFF) as u8;
for i in 0..(src.sbox.len() / 2) {
dest[i + 8] = (src.sbox[i * 2] << 4) | src.sbox[i * 2 + 1];
}
dest
}
/// Unpack a 16-byte array into an AUT64 key structure
#[allow(dead_code)]
pub fn aut64_unpack(src: &[u8]) -> Aut64Key {
let mut dest = Aut64Key::default();
dest.index = src[0];
for i in 0..(dest.key.len() / 2) {
dest.key[i * 2] = src[i + 1] >> 4;
dest.key[i * 2 + 1] = src[i + 1] & 0xF;
}
let pbox: u32 = ((src[5] as u32) << 16) | ((src[6] as u32) << 8) | src[7] as u32;
for i in (0..dest.pbox.len()).rev() {
dest.pbox[i] = ((pbox >> ((dest.pbox.len() - 1 - i) * 3)) & 0x7) as u8;
}
for i in 0..(dest.sbox.len() / 2) {
dest.sbox[i * 2] = src[i + 8] >> 4;
dest.sbox[i * 2 + 1] = src[i + 8] & 0xF;
}
dest
}
+90
View File
@@ -0,0 +1,90 @@
//! Common utilities for protocol implementations.
/// Decoded signal information
#[derive(Debug, Clone)]
pub struct DecodedSignal {
/// Serial number / device ID
pub serial: Option<u32>,
/// Button code
pub button: Option<u8>,
/// Rolling counter
pub counter: Option<u16>,
/// CRC is valid
pub crc_valid: bool,
/// Raw data (up to 64 bits)
pub data: u64,
/// Number of bits in data
pub data_count_bit: usize,
/// Whether encoding is supported
pub encoder_capable: bool,
}
impl DecodedSignal {
#[allow(dead_code)]
pub fn new(data: u64, bit_count: usize) -> Self {
Self {
serial: None,
button: None,
counter: None,
crc_valid: false,
data,
data_count_bit: bit_count,
encoder_capable: false,
}
}
}
/// CRC8 calculation with custom polynomial
///
/// # Arguments
/// * `data` - Data bytes to calculate CRC over
/// * `poly` - CRC polynomial
/// * `init` - Initial CRC value
pub fn crc8(data: &[u8], poly: u8, init: u8) -> u8 {
let mut crc = init;
for &byte in data {
crc ^= byte;
for _ in 0..8 {
if (crc & 0x80) != 0 {
crc = (crc << 1) ^ poly;
} else {
crc <<= 1;
}
}
}
crc
}
/// CRC8 for Kia protocol (polynomial 0x7F, init 0x00)
pub fn crc8_kia(data: &[u8]) -> u8 {
crc8(data, 0x7F, 0x00)
}
/// Add a bit to the decoder's data accumulator
#[inline]
pub fn add_bit(data: &mut u64, count: &mut usize, bit: bool) {
*data = (*data << 1) | (bit as u64);
*count += 1;
}
/// Button names for common keyfob buttons
#[allow(dead_code)]
pub fn get_button_name(btn: u8) -> &'static str {
match btn {
0x01 => "Lock",
0x02 => "Unlock",
0x03 => "Lock+Unlock",
0x04 => "Trunk",
0x08 => "Panic",
_ => "Unknown",
}
}
/// Button code constants
#[allow(dead_code)]
pub mod buttons {
pub const LOCK: u8 = 0x01;
pub const UNLOCK: u8 = 0x02;
pub const TRUNK: u8 = 0x04;
pub const PANIC: u8 = 0x08;
}
+326
View File
@@ -0,0 +1,326 @@
//! Fiat V0 protocol decoder/encoder
//!
//! Ported from protopirate's fiat_v0.c
//!
//! Protocol characteristics:
//! - Differential Manchester encoding: 200/400µs timing
//! - 64-bit data (cnt:32 | serial:32) + 6-bit button
//! - 150 preamble pairs, 800µs gap, 3 bursts
use super::{DecodedSignal, ProtocolDecoder, ProtocolTiming};
use crate::duration_diff;
use crate::radio::demodulator::LevelDuration;
const TE_SHORT: u32 = 200;
const TE_LONG: u32 = 400;
const TE_DELTA: u32 = 100;
#[allow(dead_code)]
const MIN_COUNT_BIT: usize = 64;
const PREAMBLE_PAIRS: u16 = 150;
const GAP_US: u32 = 800;
const TOTAL_BURSTS: u8 = 3;
const INTER_BURST_GAP: u32 = 25000;
/// Manchester decoder states
#[derive(Debug, Clone, Copy, PartialEq)]
enum ManchesterState {
Mid0,
Mid1,
Start0,
Start1,
}
/// Decoder states
#[derive(Debug, Clone, Copy, PartialEq)]
enum DecoderStep {
Reset,
Preamble,
Data,
}
/// Fiat V0 protocol decoder
pub struct FiatV0Decoder {
step: DecoderStep,
preamble_count: u16,
manchester_state: ManchesterState,
data_low: u32,
data_high: u32,
bit_count: u8,
cnt: u32,
serial: u32,
btn: u8,
te_last: u32,
}
impl FiatV0Decoder {
pub fn new() -> Self {
Self {
step: DecoderStep::Reset,
preamble_count: 0,
manchester_state: ManchesterState::Mid1,
data_low: 0,
data_high: 0,
bit_count: 0,
cnt: 0,
serial: 0,
btn: 0,
te_last: 0,
}
}
/// Manchester advance - returns decoded bit or None
fn manchester_advance(&mut self, is_short: bool, is_high: bool) -> Option<bool> {
let event = match (is_short, is_high) {
(true, true) => 0,
(true, false) => 1,
(false, true) => 2,
(false, false) => 3,
};
let (new_state, output) = match (self.manchester_state, event) {
(ManchesterState::Mid0, 0) | (ManchesterState::Mid1, 0) => {
(ManchesterState::Start1, None)
}
(ManchesterState::Mid0, 1) | (ManchesterState::Mid1, 1) => {
(ManchesterState::Start0, None)
}
(ManchesterState::Start1, 1) => (ManchesterState::Mid1, Some(true)),
(ManchesterState::Start1, 3) => (ManchesterState::Start0, Some(true)),
(ManchesterState::Start0, 0) => (ManchesterState::Mid0, Some(false)),
(ManchesterState::Start0, 2) => (ManchesterState::Start1, Some(false)),
_ => (ManchesterState::Mid1, None),
};
self.manchester_state = new_state;
output
}
fn manchester_reset(&mut self) {
self.manchester_state = ManchesterState::Mid1;
}
fn add_manchester_bit(&mut self, bit: bool) {
let new_bit = if bit { 1u32 } else { 0u32 };
let carry = (self.data_low >> 31) & 1;
self.data_low = (self.data_low << 1) | new_bit;
self.data_high = (self.data_high << 1) | carry;
self.bit_count += 1;
if self.bit_count == 0x40 {
self.serial = self.data_low;
self.cnt = self.data_high;
self.data_low = 0;
self.data_high = 0;
}
}
fn parse_data(&self) -> DecodedSignal {
let data = ((self.cnt as u64) << 32) | (self.serial as u64);
DecodedSignal {
serial: Some(self.serial),
button: Some(self.btn),
counter: Some(self.cnt as u16),
crc_valid: true, // No CRC in Fiat V0
data,
data_count_bit: 71,
encoder_capable: true,
}
}
}
impl ProtocolDecoder for FiatV0Decoder {
fn name(&self) -> &'static str {
"Fiat V0"
}
fn timing(&self) -> ProtocolTiming {
ProtocolTiming {
te_short: TE_SHORT,
te_long: TE_LONG,
te_delta: TE_DELTA,
min_count_bit: MIN_COUNT_BIT,
}
}
fn supported_frequencies(&self) -> &[u32] {
&[433_920_000]
}
fn reset(&mut self) {
self.step = DecoderStep::Reset;
self.preamble_count = 0;
self.data_low = 0;
self.data_high = 0;
self.bit_count = 0;
self.cnt = 0;
self.serial = 0;
self.btn = 0;
self.te_last = 0;
self.manchester_reset();
}
fn feed(&mut self, level: bool, duration: u32) -> Option<DecodedSignal> {
match self.step {
DecoderStep::Reset => {
if !level {
return None;
}
if duration_diff!(duration, TE_SHORT) < TE_DELTA {
self.data_low = 0;
self.data_high = 0;
self.step = DecoderStep::Preamble;
self.te_last = duration;
self.preamble_count = 0;
self.bit_count = 0;
self.manchester_reset();
}
}
DecoderStep::Preamble => {
// Count short pulses in preamble, look for gap
if duration_diff!(duration, TE_SHORT) < TE_DELTA {
self.preamble_count += 1;
self.te_last = duration;
} else if self.preamble_count >= PREAMBLE_PAIRS {
// Check for gap
if duration_diff!(duration, GAP_US) < TE_DELTA {
self.step = DecoderStep::Data;
self.preamble_count = 0;
self.data_low = 0;
self.data_high = 0;
self.bit_count = 0;
self.te_last = duration;
return None;
} else {
self.step = DecoderStep::Reset;
}
} else {
self.step = DecoderStep::Reset;
}
}
DecoderStep::Data => {
let is_short = duration_diff!(duration, TE_SHORT) < TE_DELTA;
let is_long = duration_diff!(duration, TE_LONG) < TE_DELTA;
if is_short || is_long {
if let Some(bit) = self.manchester_advance(is_short, level) {
self.add_manchester_bit(bit);
if self.bit_count > 0x46 {
self.btn = ((self.data_low << 1) | 1) as u8;
let result = self.parse_data();
self.data_low = 0;
self.data_high = 0;
self.bit_count = 0;
self.step = DecoderStep::Reset;
return Some(result);
}
}
} else if duration > TE_LONG * 3 {
// End of signal
self.step = DecoderStep::Reset;
}
self.te_last = duration;
}
}
None
}
fn supports_encoding(&self) -> bool {
true
}
fn encode(&self, decoded: &DecodedSignal, button: u8) -> Option<Vec<LevelDuration>> {
let serial = decoded.serial?;
let cnt = decoded.counter.unwrap_or(0) as u32;
let data = ((cnt as u64) << 32) | (serial as u64);
// Reverse the decoder's btn fix: decoder does (x << 1) | 1
let btn_to_send = button >> 1;
let mut signal = Vec::with_capacity(1024);
for burst in 0..TOTAL_BURSTS {
if burst > 0 {
signal.push(LevelDuration::new(false, INTER_BURST_GAP));
}
// Preamble
for i in 0..PREAMBLE_PAIRS {
signal.push(LevelDuration::new(true, TE_SHORT));
if i < PREAMBLE_PAIRS - 1 {
signal.push(LevelDuration::new(false, TE_SHORT));
} else {
signal.push(LevelDuration::new(false, GAP_US));
}
}
// First bit (bit 63)
let first_bit = (data >> 63) & 1 == 1;
if first_bit {
signal.push(LevelDuration::new(true, TE_LONG));
} else {
signal.push(LevelDuration::new(true, TE_SHORT));
signal.push(LevelDuration::new(false, TE_LONG));
}
let mut prev_bit = first_bit;
// Remaining 63 data bits using differential Manchester
for bit in (0..63).rev() {
let curr_bit = (data >> bit) & 1 == 1;
match (prev_bit, curr_bit) {
(false, false) => {
signal.push(LevelDuration::new(true, TE_SHORT));
signal.push(LevelDuration::new(false, TE_SHORT));
}
(false, true) => {
signal.push(LevelDuration::new(true, TE_LONG));
}
(true, false) => {
signal.push(LevelDuration::new(false, TE_LONG));
}
(true, true) => {
signal.push(LevelDuration::new(false, TE_SHORT));
signal.push(LevelDuration::new(true, TE_SHORT));
}
}
prev_bit = curr_bit;
}
// 6 button bits
for bit in (0..6).rev() {
let curr_bit = (btn_to_send >> bit) & 1 == 1;
match (prev_bit, curr_bit) {
(false, false) => {
signal.push(LevelDuration::new(true, TE_SHORT));
signal.push(LevelDuration::new(false, TE_SHORT));
}
(false, true) => {
signal.push(LevelDuration::new(true, TE_LONG));
}
(true, false) => {
signal.push(LevelDuration::new(false, TE_LONG));
}
(true, true) => {
signal.push(LevelDuration::new(false, TE_SHORT));
signal.push(LevelDuration::new(true, TE_SHORT));
}
}
prev_bit = curr_bit;
}
// End marker
if prev_bit {
signal.push(LevelDuration::new(false, TE_SHORT));
}
signal.push(LevelDuration::new(false, TE_SHORT * 8));
}
Some(signal)
}
}
+320
View File
@@ -0,0 +1,320 @@
//! Ford V0 protocol decoder
//!
//! Ported from protopirate's ford_v0.c
//!
//! Protocol characteristics:
//! - Manchester encoding: 250/500µs timing
//! - 64 bits total
//! - Matrix-based CRC
use super::{ProtocolDecoder, ProtocolTiming, DecodedSignal};
use crate::radio::demodulator::LevelDuration;
use crate::duration_diff;
const TE_SHORT: u32 = 250;
const TE_LONG: u32 = 500;
const TE_DELTA: u32 = 100;
const MIN_COUNT_BIT: usize = 64;
/// Manchester decoder states
#[derive(Debug, Clone, Copy, PartialEq)]
enum ManchesterState {
Mid0,
Mid1,
Start0,
Start1,
}
/// Decoder states
#[derive(Debug, Clone, Copy, PartialEq)]
enum DecoderStep {
Reset,
CheckPreamble,
SaveDuration,
CheckDuration,
}
/// Ford V0 protocol decoder
pub struct FordV0Decoder {
step: DecoderStep,
te_last: u32,
header_count: u16,
decode_data: u64,
decode_count_bit: usize,
manchester_state: ManchesterState,
}
impl FordV0Decoder {
pub fn new() -> Self {
Self {
step: DecoderStep::Reset,
te_last: 0,
header_count: 0,
decode_data: 0,
decode_count_bit: 0,
manchester_state: ManchesterState::Mid1,
}
}
/// Manchester decode: advance state machine
fn manchester_advance(&mut self, is_short: bool, is_high: bool) -> Option<bool> {
let event = match (is_short, is_high) {
(true, true) => 0, // Short High
(true, false) => 1, // Short Low
(false, true) => 2, // Long High
(false, false) => 3, // Long Low
};
let (new_state, output) = match (self.manchester_state, event) {
// From Mid0 or Mid1
(ManchesterState::Mid0, 0) | (ManchesterState::Mid1, 0) =>
(ManchesterState::Start1, None),
(ManchesterState::Mid0, 1) | (ManchesterState::Mid1, 1) =>
(ManchesterState::Start0, None),
// From Start1
(ManchesterState::Start1, 1) => (ManchesterState::Mid1, Some(true)),
(ManchesterState::Start1, 3) => (ManchesterState::Start0, Some(true)),
// From Start0
(ManchesterState::Start0, 0) => (ManchesterState::Mid0, Some(false)),
(ManchesterState::Start0, 2) => (ManchesterState::Start1, Some(false)),
// Reset on invalid transitions
_ => (ManchesterState::Mid1, None),
};
self.manchester_state = new_state;
output
}
/// CRC matrix for Ford
const CRC_MATRIX: [[u8; 4]; 8] = [
[0x0C, 0xBB, 0x51, 0x25],
[0x18, 0xB1, 0x62, 0xCB],
[0x30, 0xA7, 0x44, 0x57],
[0x60, 0x89, 0x88, 0xAE],
[0xC0, 0xD6, 0xD4, 0x97],
[0x25, 0x49, 0x6D, 0xE1],
[0x4A, 0x92, 0xDA, 0x03],
[0x94, 0xE1, 0x71, 0x06],
];
/// Calculate Ford CRC
fn calculate_crc(data: u64) -> u8 {
let mut crc = 0u8;
for byte_idx in 0..7 {
let byte = ((data >> (56 - byte_idx * 8)) & 0xFF) as u8;
for bit in 0..8 {
if (byte >> (7 - bit)) & 1 == 1 {
crc ^= Self::CRC_MATRIX[bit][byte_idx % 4];
}
}
}
crc
}
/// Parse decoded data
fn parse_data(data: u64) -> DecodedSignal {
// Ford V0 format:
// Bits 60-63: Prefix (0x5)
// Bits 32-59: Serial (28 bits)
// Bits 28-31: Button (4 bits)
// Bits 16-27: Counter (12 bits)
// Bits 8-15: Encrypted data
// Bits 0-7: CRC
let serial = ((data >> 32) & 0x0FFFFFFF) as u32;
let button = ((data >> 28) & 0x0F) as u8;
let counter = ((data >> 16) & 0x0FFF) as u16;
let received_crc = (data & 0xFF) as u8;
let calculated_crc = Self::calculate_crc(data);
DecodedSignal {
serial: Some(serial),
button: Some(button),
counter: Some(counter),
crc_valid: received_crc == calculated_crc,
data,
data_count_bit: MIN_COUNT_BIT,
encoder_capable: true,
}
}
}
impl ProtocolDecoder for FordV0Decoder {
fn name(&self) -> &'static str {
"Ford V0"
}
fn timing(&self) -> ProtocolTiming {
ProtocolTiming {
te_short: TE_SHORT,
te_long: TE_LONG,
te_delta: TE_DELTA,
min_count_bit: MIN_COUNT_BIT,
}
}
fn supported_frequencies(&self) -> &[u32] {
&[315_000_000, 433_920_000] // 315 MHz (US) and 433.92 MHz (EU)
}
fn reset(&mut self) {
self.step = DecoderStep::Reset;
self.te_last = 0;
self.header_count = 0;
self.decode_data = 0;
self.decode_count_bit = 0;
self.manchester_state = ManchesterState::Mid1;
}
fn feed(&mut self, level: bool, duration: u32) -> Option<DecodedSignal> {
let is_short = duration_diff!(duration, TE_SHORT) < TE_DELTA;
let is_long = duration_diff!(duration, TE_LONG) < TE_DELTA;
match self.step {
DecoderStep::Reset => {
if level && is_short {
self.step = DecoderStep::CheckPreamble;
self.header_count = 1;
self.manchester_state = ManchesterState::Mid1;
}
}
DecoderStep::CheckPreamble => {
if is_short {
self.header_count += 1;
if self.header_count > 20 && !level {
// Enough preamble, start looking for data
self.step = DecoderStep::SaveDuration;
self.decode_data = 0;
self.decode_count_bit = 0;
self.manchester_state = ManchesterState::Mid1;
}
} else if is_long {
if self.header_count > 10 {
self.step = DecoderStep::SaveDuration;
self.decode_data = 0;
self.decode_count_bit = 0;
self.manchester_state = ManchesterState::Mid1;
// Process this long pulse
if let Some(bit) = self.manchester_advance(false, level) {
self.decode_data = (self.decode_data << 1) | (bit as u64);
self.decode_count_bit += 1;
}
} else {
self.step = DecoderStep::Reset;
}
} else {
self.step = DecoderStep::Reset;
}
}
DecoderStep::SaveDuration => {
self.te_last = duration;
self.step = DecoderStep::CheckDuration;
}
DecoderStep::CheckDuration => {
let last_short = duration_diff!(self.te_last, TE_SHORT) < TE_DELTA;
let last_long = duration_diff!(self.te_last, TE_LONG) < TE_DELTA;
// Check for end of transmission
if duration > TE_LONG * 3 {
if self.decode_count_bit >= MIN_COUNT_BIT {
let result = Self::parse_data(self.decode_data);
self.step = DecoderStep::Reset;
return Some(result);
}
self.step = DecoderStep::Reset;
return None;
}
// Manchester decode
if last_short {
if let Some(bit) = self.manchester_advance(true, !level) {
self.decode_data = (self.decode_data << 1) | (bit as u64);
self.decode_count_bit += 1;
}
} else if last_long {
if let Some(bit) = self.manchester_advance(false, !level) {
self.decode_data = (self.decode_data << 1) | (bit as u64);
self.decode_count_bit += 1;
}
}
if is_short || is_long {
if let Some(bit) = self.manchester_advance(is_short, level) {
self.decode_data = (self.decode_data << 1) | (bit as u64);
self.decode_count_bit += 1;
}
self.step = DecoderStep::SaveDuration;
} else {
self.step = DecoderStep::Reset;
}
// Check if we have enough bits
if self.decode_count_bit >= MIN_COUNT_BIT {
let result = Self::parse_data(self.decode_data);
self.step = DecoderStep::Reset;
return Some(result);
}
}
}
None
}
fn supports_encoding(&self) -> bool {
true
}
fn encode(&self, decoded: &DecodedSignal, button: u8) -> Option<Vec<LevelDuration>> {
let serial = decoded.serial?;
let counter = decoded.counter.unwrap_or(0);
// Build data packet
let mut data: u64 = 0;
data |= 0x5 << 60; // Prefix
data |= ((serial as u64) & 0x0FFFFFFF) << 32;
data |= ((button as u64) & 0x0F) << 28;
data |= ((counter as u64) & 0x0FFF) << 16;
data |= ((decoded.data >> 8) & 0xFF) << 8; // Keep encrypted byte
data |= Self::calculate_crc(data) as u64;
let mut signal = Vec::with_capacity(256);
// Preamble
for _ in 0..30 {
signal.push(LevelDuration::new(true, TE_SHORT));
signal.push(LevelDuration::new(false, TE_SHORT));
}
// Sync
signal.push(LevelDuration::new(true, TE_LONG));
signal.push(LevelDuration::new(false, TE_LONG));
// Data: Manchester encoded, 64 bits MSB first
for bit_num in (0..64).rev() {
let bit = (data >> bit_num) & 1 == 1;
if bit {
// Manchester 1: low-high
signal.push(LevelDuration::new(false, TE_SHORT));
signal.push(LevelDuration::new(true, TE_SHORT));
} else {
// Manchester 0: high-low
signal.push(LevelDuration::new(true, TE_SHORT));
signal.push(LevelDuration::new(false, TE_SHORT));
}
}
// End
signal.push(LevelDuration::new(false, TE_LONG * 4));
Some(signal)
}
}
+149
View File
@@ -0,0 +1,149 @@
//! KeeLoq common encryption/decryption routines
//!
//! Shared by Kia V3/V4, Star Line, and other KeeLoq-based protocols.
//! Based on the NLF (Non-Linear Feedback) function with constant 0x3A5C742E.
/// The KeeLoq NLF constant
const KEELOQ_NLF: u32 = 0x3A5C742E;
/// KeeLoq decrypt: 528 rounds of the KeeLoq cipher (decrypt direction)
pub fn keeloq_decrypt(data: u32, key: u64) -> u32 {
let mut block = data;
let mut tkey = key;
for _ in 0..528 {
let lutkey = ((block >> 0) & 1)
| ((block >> 7) & 2)
| ((block >> 17) & 4)
| ((block >> 22) & 8)
| ((block >> 26) & 16);
let lsb = ((block >> 31)
^ ((block >> 15) & 1)
^ ((KEELOQ_NLF >> lutkey) & 1)
^ (((tkey >> 15) & 1) as u32)) as u32;
block = ((block & 0x7FFFFFFF) << 1) | lsb;
tkey = ((tkey & 0x7FFFFFFFFFFFFFFF) << 1) | (tkey >> 63);
}
block
}
/// KeeLoq encrypt: 528 rounds of the KeeLoq cipher (encrypt direction)
pub fn keeloq_encrypt(data: u32, key: u64) -> u32 {
let mut block = data;
let mut tkey = key;
for _ in 0..528 {
let lutkey = ((block >> 1) & 1)
| ((block >> 8) & 2)
| ((block >> 18) & 4)
| ((block >> 23) & 8)
| ((block >> 27) & 16);
let msb = ((block >> 0)
^ ((block >> 16) & 1)
^ ((KEELOQ_NLF >> lutkey) & 1)
^ (((tkey >> 0) & 1) as u32)) as u32;
block = ((block >> 1) & 0x7FFFFFFF) | (msb << 31);
tkey = ((tkey >> 1) & 0x7FFFFFFFFFFFFFFF) | ((tkey & 1) << 63);
}
block
}
/// Normal learning key derivation
/// Derives a 64-bit key from a 32-bit fix code and a 64-bit manufacturer key
pub fn keeloq_normal_learning(fix: u32, manufacturer_key: u64) -> u64 {
let serial_low = fix & 0xFFFF;
let serial_high = (fix >> 16) & 0xFFFF;
let key_low = keeloq_decrypt(serial_low as u32 | 0x20000000, manufacturer_key);
let key_high = keeloq_decrypt(serial_high as u32 | 0x60000000, manufacturer_key);
((key_high as u64) << 32) | (key_low as u64)
}
/// Reverse the bits in a 64-bit key (for protocols that store data MSB-first)
pub fn reverse_key(key: u64, bit_count: usize) -> u64 {
let mut result: u64 = 0;
for i in 0..bit_count {
if (key >> i) & 1 == 1 {
result |= 1 << (bit_count - 1 - i);
}
}
result
}
/// Reverse bits in a byte
#[allow(dead_code)]
pub fn reverse8(byte: u8) -> u8 {
let mut b = byte;
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
b
}
/// Secure learning key derivation
/// Derives a 64-bit key from a serial, seed, and manufacturer key
#[allow(dead_code)]
pub fn keeloq_secure_learning(data: u32, seed: u32, key: u64) -> u64 {
let serial = data & 0x0FFFFFFF;
let k1 = keeloq_decrypt(serial, key);
let k2 = keeloq_decrypt(seed, key);
((k1 as u64) << 32) | (k2 as u64)
}
/// FAAC SLH (Spa) learning key derivation
/// Derives a 64-bit key from a seed and manufacturer key
#[allow(dead_code)]
pub fn keeloq_faac_learning(seed: u32, key: u64) -> u64 {
let hs = (seed >> 16) as u16;
let ending: u16 = 0x544D;
let lsb = ((hs as u32) << 16) | (ending as u32);
((keeloq_encrypt(seed, key) as u64) << 32) | (keeloq_encrypt(lsb, key) as u64)
}
/// Magic XOR Type 1 learning key derivation
#[allow(dead_code)]
pub fn keeloq_magic_xor_type1_learning(data: u32, xor: u64) -> u64 {
let serial = data & 0x0FFFFFFF;
(((serial as u64) << 32) | (serial as u64)) ^ xor
}
/// Magic Serial Type 1 learning key derivation
#[allow(dead_code)]
pub fn keeloq_magic_serial_type1_learning(data: u32, man: u64) -> u64 {
(man & 0xFFFFFFFF)
| ((data as u64) << 40)
| (((((data & 0xFF).wrapping_add((data >> 8) & 0xFF)) & 0xFF) as u64) << 32)
}
/// Magic Serial Type 2 learning key derivation
#[allow(dead_code)]
pub fn keeloq_magic_serial_type2_learning(data: u32, man: u64) -> u64 {
let p = data.to_le_bytes();
let mut m = man.to_le_bytes();
m[7] = p[0];
m[6] = p[1];
m[5] = p[2];
m[4] = p[3];
u64::from_le_bytes(m)
}
/// Magic Serial Type 3 learning key derivation
#[allow(dead_code)]
pub fn keeloq_magic_serial_type3_learning(data: u32, man: u64) -> u64 {
(man & 0xFFFFFFFFFF000000) | ((data & 0xFFFFFF) as u64)
}
/// KeeLoq learning type constants
#[allow(dead_code)]
pub mod learning_types {
pub const UNKNOWN: u32 = 0;
pub const SIMPLE: u32 = 1;
pub const NORMAL: u32 = 2;
// pub const SECURE: u32 = 3;
pub const MAGIC_XOR_TYPE_1: u32 = 4;
// pub const FAAC: u32 = 5;
pub const MAGIC_SERIAL_TYPE_1: u32 = 6;
pub const MAGIC_SERIAL_TYPE_2: u32 = 7;
pub const MAGIC_SERIAL_TYPE_3: u32 = 8;
}
+177
View File
@@ -0,0 +1,177 @@
//! Key management module for protocol encryption/decryption
//!
//! Ported from protopirate's keys.c
//!
//! Manages manufacturer keys used by various protocols:
//! - KIA V3/V4: kia_mf_key (manufacturer key for KeeLoq)
//! - KIA V5: kia_v5_key (custom mixer cipher key)
//! - KIA V6: kia_v6_a_key, kia_v6_b_key (AES-128 XOR mask keys)
//! - VAG: AUT64 keys loaded from keystore files
use super::aut64::{self, Aut64Key, AUT64_KEY_STRUCT_PACKED_SIZE};
use std::path::Path;
use std::sync::{OnceLock, RwLock};
use tracing::{info, warn, error};
/// Key type identifiers (matches protopirate's keystore types)
const KIA_KEY1: u32 = 10; // kia_mf_key
const KIA_KEY2: u32 = 11; // kia_v6_a_key
const KIA_KEY3: u32 = 12; // kia_v6_b_key
const KIA_KEY4: u32 = 13; // kia_v5_key
/// Maximum number of VAG AUT64 keys
const VAG_KEYS_COUNT: usize = 3;
/// Global key store - thread-safe access to loaded keys
pub struct KeyStore {
/// KIA manufacturer key (for KeeLoq-based V3/V4)
pub kia_mf_key: u64,
/// KIA V6 AES key A
pub kia_v6_a_key: u64,
/// KIA V6 AES key B
pub kia_v6_b_key: u64,
/// KIA V5 mixer key
pub kia_v5_key: u64,
/// VAG AUT64 keys
pub vag_keys: Vec<Aut64Key>,
/// Whether VAG keys have been loaded
pub vag_keys_loaded: bool,
}
impl Default for KeyStore {
fn default() -> Self {
Self {
kia_mf_key: 0,
kia_v6_a_key: 0,
kia_v6_b_key: 0,
kia_v5_key: 0,
vag_keys: Vec::new(),
vag_keys_loaded: false,
}
}
}
impl KeyStore {
/// Create a new empty key store
pub fn new() -> Self {
Self::default()
}
/// Load KIA keys from a key entries list
/// Each entry is (type_id, key_value)
pub fn load_kia_keys(&mut self, entries: &[(u32, u64)]) {
for &(key_type, key_value) in entries {
match key_type {
KIA_KEY1 => self.kia_mf_key = key_value,
KIA_KEY2 => self.kia_v6_a_key = key_value,
KIA_KEY3 => self.kia_v6_b_key = key_value,
KIA_KEY4 => self.kia_v5_key = key_value,
_ => {}
}
}
}
/// Load VAG AUT64 keys from raw binary data
/// The data should contain packed AUT64 key structures (16 bytes each)
pub fn load_vag_keys_from_data(&mut self, data: &[u8]) {
if self.vag_keys_loaded {
return;
}
self.vag_keys.clear();
for i in 0..VAG_KEYS_COUNT {
let offset = i * AUT64_KEY_STRUCT_PACKED_SIZE;
if offset + AUT64_KEY_STRUCT_PACKED_SIZE > data.len() {
error!("VAG key data too short for key {}", i);
break;
}
let key = aut64::aut64_unpack(&data[offset..offset + AUT64_KEY_STRUCT_PACKED_SIZE]);
self.vag_keys.push(key);
}
self.vag_keys_loaded = true;
info!("Loaded {} VAG keys", self.vag_keys.len());
}
/// Load VAG AUT64 keys from a file path
pub fn load_vag_keys_from_file(&mut self, path: &str) {
if self.vag_keys_loaded {
return;
}
let file_path = Path::new(path);
if !file_path.exists() {
warn!("VAG key file not found: {}", path);
return;
}
match std::fs::read(file_path) {
Ok(data) => {
self.load_vag_keys_from_data(&data);
}
Err(e) => {
error!("Failed to read VAG key file {}: {}", path, e);
}
}
}
/// Get a VAG AUT64 key by its internal index field
pub fn get_vag_key(&self, index: u8) -> Option<&Aut64Key> {
self.vag_keys.iter().find(|k| k.index == index)
}
/// Get a VAG AUT64 key by array position (0-based)
pub fn get_vag_key_by_position(&self, position: usize) -> Option<&Aut64Key> {
self.vag_keys.get(position)
}
/// Get the KIA manufacturer key
pub fn get_kia_mf_key(&self) -> u64 {
self.kia_mf_key
}
/// Get the KIA V6 AES key A
pub fn get_kia_v6_keystore_a(&self) -> u64 {
self.kia_v6_a_key
}
/// Get the KIA V6 AES key B
pub fn get_kia_v6_keystore_b(&self) -> u64 {
self.kia_v6_b_key
}
/// Get the KIA V5 mixer key
pub fn get_kia_v5_key(&self) -> u64 {
self.kia_v5_key
}
}
/// Global singleton keystore
fn global_keystore() -> &'static RwLock<KeyStore> {
static GLOBAL_KEYSTORE: OnceLock<RwLock<KeyStore>> = OnceLock::new();
GLOBAL_KEYSTORE.get_or_init(|| RwLock::new(KeyStore::new()))
}
/// Get a read reference to the global keystore
pub fn get_keystore() -> std::sync::RwLockReadGuard<'static, KeyStore> {
global_keystore().read().unwrap()
}
/// Get a write reference to the global keystore
pub fn get_keystore_mut() -> std::sync::RwLockWriteGuard<'static, KeyStore> {
global_keystore().write().unwrap()
}
/// Initialize the global keystore with KIA keys
pub fn load_keys(kia_entries: &[(u32, u64)]) {
let mut store = get_keystore_mut();
store.load_kia_keys(kia_entries);
}
/// Initialize VAG keys from file
pub fn load_vag_keys(path: &str) {
let mut store = get_keystore_mut();
store.load_vag_keys_from_file(path);
}
+259
View File
@@ -0,0 +1,259 @@
//! Kia V0 protocol decoder
//!
//! Ported from protopirate's kia_v0.c
//!
//! Protocol characteristics:
//! - PWM encoding: short pulse (250µs) = 0, long pulse (500µs) = 1
//! - 61 bits total
//! - Preamble: alternating short pulses
//! - Sync: long-long pattern
//! - Data: 59 bits (4-bit prefix + 16-bit counter + 28-bit serial + 4-bit button + 8-bit CRC)
use super::{ProtocolDecoder, ProtocolTiming, DecodedSignal};
use super::common::{crc8_kia, add_bit};
use crate::radio::demodulator::LevelDuration;
use crate::duration_diff;
const TE_SHORT: u32 = 250;
const TE_LONG: u32 = 500;
const TE_DELTA: u32 = 100;
const MIN_COUNT_BIT: usize = 61;
/// Decoder states
#[derive(Debug, Clone, Copy, PartialEq)]
enum DecoderStep {
Reset,
CheckPreamble,
SaveDuration,
CheckDuration,
}
/// Kia V0 protocol decoder
pub struct KiaV0Decoder {
step: DecoderStep,
te_last: u32,
header_count: u16,
decode_data: u64,
decode_count_bit: usize,
}
impl KiaV0Decoder {
pub fn new() -> Self {
Self {
step: DecoderStep::Reset,
te_last: 0,
header_count: 0,
decode_data: 0,
decode_count_bit: 0,
}
}
/// Calculate CRC for Kia data packet
fn calculate_crc(data: u64) -> u8 {
let crc_data = [
((data >> 48) & 0xFF) as u8,
((data >> 40) & 0xFF) as u8,
((data >> 32) & 0xFF) as u8,
((data >> 24) & 0xFF) as u8,
((data >> 16) & 0xFF) as u8,
((data >> 8) & 0xFF) as u8,
];
crc8_kia(&crc_data)
}
/// Verify CRC of received data
fn verify_crc(data: u64) -> bool {
let received_crc = (data & 0xFF) as u8;
let calculated_crc = Self::calculate_crc(data);
received_crc == calculated_crc
}
/// Extract fields from decoded data
fn parse_data(data: u64) -> DecodedSignal {
let serial = ((data >> 12) & 0x0FFFFFFF) as u32;
let button = ((data >> 8) & 0x0F) as u8;
let counter = ((data >> 40) & 0xFFFF) as u16;
let crc_valid = Self::verify_crc(data);
DecodedSignal {
serial: Some(serial),
button: Some(button),
counter: Some(counter),
crc_valid,
data,
data_count_bit: MIN_COUNT_BIT,
encoder_capable: true,
}
}
}
impl ProtocolDecoder for KiaV0Decoder {
fn name(&self) -> &'static str {
"Kia V0"
}
fn timing(&self) -> ProtocolTiming {
ProtocolTiming {
te_short: TE_SHORT,
te_long: TE_LONG,
te_delta: TE_DELTA,
min_count_bit: MIN_COUNT_BIT,
}
}
fn supported_frequencies(&self) -> &[u32] {
&[433_920_000] // 433.92 MHz
}
fn reset(&mut self) {
self.step = DecoderStep::Reset;
self.te_last = 0;
self.header_count = 0;
self.decode_data = 0;
self.decode_count_bit = 0;
}
fn feed(&mut self, level: bool, duration: u32) -> Option<DecodedSignal> {
match self.step {
DecoderStep::Reset => {
if level && duration_diff!(duration, TE_SHORT) < TE_DELTA {
self.step = DecoderStep::CheckPreamble;
self.te_last = duration;
self.header_count = 0;
}
}
DecoderStep::CheckPreamble => {
if level {
if duration_diff!(duration, TE_SHORT) < TE_DELTA ||
duration_diff!(duration, TE_LONG) < TE_DELTA {
self.te_last = duration;
} else {
self.step = DecoderStep::Reset;
}
} else if duration_diff!(duration, TE_SHORT) < TE_DELTA &&
duration_diff!(self.te_last, TE_SHORT) < TE_DELTA {
// Short-short pair in preamble
self.header_count += 1;
} else if duration_diff!(duration, TE_LONG) < TE_DELTA &&
duration_diff!(self.te_last, TE_LONG) < TE_DELTA {
// Long-long sync pattern
if self.header_count > 15 {
self.step = DecoderStep::SaveDuration;
self.decode_data = 0;
self.decode_count_bit = 1;
// Add first bit (the sync is also a '1' bit)
add_bit(&mut self.decode_data, &mut self.decode_count_bit, true);
} else {
self.step = DecoderStep::Reset;
}
} else {
self.step = DecoderStep::Reset;
}
}
DecoderStep::SaveDuration => {
if level {
if duration >= TE_LONG + TE_DELTA * 2 {
// End of transmission
self.step = DecoderStep::Reset;
if self.decode_count_bit == MIN_COUNT_BIT {
return Some(Self::parse_data(self.decode_data));
}
} else {
self.te_last = duration;
self.step = DecoderStep::CheckDuration;
}
} else {
self.step = DecoderStep::Reset;
}
}
DecoderStep::CheckDuration => {
if !level {
if duration_diff!(self.te_last, TE_SHORT) < TE_DELTA &&
duration_diff!(duration, TE_SHORT) < TE_DELTA {
// Short-short = bit 0
add_bit(&mut self.decode_data, &mut self.decode_count_bit, false);
self.step = DecoderStep::SaveDuration;
} else if duration_diff!(self.te_last, TE_LONG) < TE_DELTA &&
duration_diff!(duration, TE_LONG) < TE_DELTA {
// Long-long = bit 1
add_bit(&mut self.decode_data, &mut self.decode_count_bit, true);
self.step = DecoderStep::SaveDuration;
} else {
self.step = DecoderStep::Reset;
}
} else {
self.step = DecoderStep::Reset;
}
}
}
None
}
fn supports_encoding(&self) -> bool {
true
}
fn encode(&self, decoded: &DecodedSignal, button: u8) -> Option<Vec<LevelDuration>> {
let serial = decoded.serial?;
let counter = decoded.counter.unwrap_or(0);
// Build data packet
let mut data: u64 = 0;
// Bits 56-59: Preserve from original (usually 0xF)
data |= decoded.data & 0x0F00000000000000;
// Bits 40-55: Counter (16 bits)
data |= ((counter as u64) & 0xFFFF) << 40;
// Bits 12-39: Serial (28 bits)
data |= ((serial as u64) & 0x0FFFFFFF) << 12;
// Bits 8-11: Button (4 bits)
data |= ((button as u64) & 0x0F) << 8;
// Bits 0-7: CRC
let crc = Self::calculate_crc(data);
data |= crc as u64;
let mut signal = Vec::with_capacity(256);
// Generate 2 bursts
for burst in 0..2 {
if burst > 0 {
// Inter-burst gap
signal.push(LevelDuration::new(false, 25000));
}
// Preamble: 32 alternating short pulses
for i in 0..32 {
let is_high = (i % 2) == 0;
signal.push(LevelDuration::new(is_high, TE_SHORT));
}
// Sync: long-long
signal.push(LevelDuration::new(true, TE_LONG));
signal.push(LevelDuration::new(false, TE_LONG));
// Data: 59 bits (MSB first)
for bit_num in 0..59 {
let bit_mask = 1u64 << (58 - bit_num);
let bit = (data & bit_mask) != 0;
let duration = if bit { TE_LONG } else { TE_SHORT };
signal.push(LevelDuration::new(true, duration));
signal.push(LevelDuration::new(false, duration));
}
// End marker
signal.push(LevelDuration::new(true, TE_LONG * 2));
}
Some(signal)
}
}
+296
View File
@@ -0,0 +1,296 @@
//! Kia V1 protocol decoder
//!
//! Ported from protopirate's kia_v1.c
//!
//! Protocol characteristics:
//! - Manchester encoding: 800/1600µs timing
//! - 57 bits total
//! - Long preamble of ~90 pulses
//! - CRC4 checksum
use super::{ProtocolDecoder, ProtocolTiming, DecodedSignal};
use crate::radio::demodulator::LevelDuration;
use crate::duration_diff;
const TE_SHORT: u32 = 800;
const TE_LONG: u32 = 1600;
const TE_DELTA: u32 = 200;
const MIN_COUNT_BIT: usize = 57;
/// Manchester states
#[derive(Debug, Clone, Copy, PartialEq)]
enum ManchesterState {
Mid0,
Mid1,
Start0,
Start1,
}
/// Decoder states
#[derive(Debug, Clone, Copy, PartialEq)]
enum DecoderStep {
Reset,
CheckPreamble,
DecodeData,
}
/// Kia V1 protocol decoder
pub struct KiaV1Decoder {
step: DecoderStep,
te_last: u32,
header_count: u16,
decode_data: u64,
decode_count_bit: usize,
manchester_state: ManchesterState,
}
impl KiaV1Decoder {
pub fn new() -> Self {
Self {
step: DecoderStep::Reset,
te_last: 0,
header_count: 0,
decode_data: 0,
decode_count_bit: 0,
manchester_state: ManchesterState::Mid1,
}
}
/// CRC4 calculation for Kia V1
fn crc4(bytes: &[u8], offset: u8) -> u8 {
let mut crc: u8 = 0;
for &byte in bytes {
crc ^= (byte & 0x0F) ^ (byte >> 4);
}
(crc.wrapping_add(offset)) & 0x0F
}
/// Manchester state machine
fn manchester_advance(&mut self, is_short: bool, is_high: bool) -> Option<bool> {
let event = match (is_short, is_high) {
(true, false) => 0, // Short Low
(true, true) => 1, // Short High
(false, false) => 2, // Long Low
(false, true) => 3, // Long High
};
let (new_state, output) = match (self.manchester_state, event) {
(ManchesterState::Mid0, 0) | (ManchesterState::Mid1, 0) =>
(ManchesterState::Start0, None),
(ManchesterState::Mid0, 1) | (ManchesterState::Mid1, 1) =>
(ManchesterState::Start1, None),
(ManchesterState::Start1, 0) => (ManchesterState::Mid1, Some(true)),
(ManchesterState::Start1, 2) => (ManchesterState::Start0, Some(true)),
(ManchesterState::Start0, 1) => (ManchesterState::Mid0, Some(false)),
(ManchesterState::Start0, 3) => (ManchesterState::Start1, Some(false)),
_ => (ManchesterState::Mid1, None),
};
self.manchester_state = new_state;
output
}
/// Parse decoded data
fn parse_data(&self) -> DecodedSignal {
let data = self.decode_data;
// Extract fields per kia_v1.c
let serial = (data >> 24) as u32;
let button = ((data >> 16) & 0xFF) as u8;
let cnt_low = ((data >> 8) & 0xFF) as u16;
let cnt_high = ((data >> 4) & 0x0F) as u16;
let counter = (cnt_high << 8) | cnt_low;
let received_crc = (data & 0x0F) as u8;
// Calculate CRC
let mut char_data = [0u8; 7];
char_data[0] = ((serial >> 24) & 0xFF) as u8;
char_data[1] = ((serial >> 16) & 0xFF) as u8;
char_data[2] = ((serial >> 8) & 0xFF) as u8;
char_data[3] = (serial & 0xFF) as u8;
char_data[4] = button;
char_data[5] = (counter & 0xFF) as u8;
let crc = if cnt_high == 0 {
let offset = if counter >= 0x098 { button } else { 1 };
Self::crc4(&char_data[..6], offset)
} else if cnt_high >= 0x6 {
char_data[6] = cnt_high as u8;
Self::crc4(&char_data, 1)
} else {
Self::crc4(&char_data[..6], 1)
};
DecodedSignal {
serial: Some(serial),
button: Some(button),
counter: Some(counter),
crc_valid: received_crc == crc,
data,
data_count_bit: MIN_COUNT_BIT,
encoder_capable: true,
}
}
}
impl ProtocolDecoder for KiaV1Decoder {
fn name(&self) -> &'static str {
"Kia V1"
}
fn timing(&self) -> ProtocolTiming {
ProtocolTiming {
te_short: TE_SHORT,
te_long: TE_LONG,
te_delta: TE_DELTA,
min_count_bit: MIN_COUNT_BIT,
}
}
fn supported_frequencies(&self) -> &[u32] {
&[315_000_000, 433_920_000]
}
fn reset(&mut self) {
self.step = DecoderStep::Reset;
self.te_last = 0;
self.header_count = 0;
self.decode_data = 0;
self.decode_count_bit = 0;
self.manchester_state = ManchesterState::Mid1;
}
fn feed(&mut self, level: bool, duration: u32) -> Option<DecodedSignal> {
let is_short = duration_diff!(duration, TE_SHORT) < TE_DELTA;
let is_long = duration_diff!(duration, TE_LONG) < TE_DELTA;
match self.step {
DecoderStep::Reset => {
if level && is_long {
self.step = DecoderStep::CheckPreamble;
self.te_last = duration;
self.header_count = 0;
self.decode_data = 0;
self.decode_count_bit = 0;
self.manchester_state = ManchesterState::Mid1;
}
}
DecoderStep::CheckPreamble => {
if !level {
if is_long && duration_diff!(self.te_last, TE_LONG) < TE_DELTA {
self.header_count += 1;
self.te_last = duration;
} else {
self.step = DecoderStep::Reset;
}
}
if self.header_count > 70 {
if !level && is_short && duration_diff!(self.te_last, TE_LONG) < TE_DELTA {
self.decode_count_bit = 1;
self.decode_data = 1; // Add first bit
self.header_count = 0;
self.step = DecoderStep::DecodeData;
}
}
}
DecoderStep::DecodeData => {
if is_short {
if let Some(bit) = self.manchester_advance(true, level) {
self.decode_data = (self.decode_data << 1) | (bit as u64);
self.decode_count_bit += 1;
}
} else if is_long {
if let Some(bit) = self.manchester_advance(false, level) {
self.decode_data = (self.decode_data << 1) | (bit as u64);
self.decode_count_bit += 1;
}
} else {
self.step = DecoderStep::Reset;
return None;
}
if self.decode_count_bit >= MIN_COUNT_BIT {
let result = self.parse_data();
self.step = DecoderStep::Reset;
return Some(result);
}
}
}
None
}
fn supports_encoding(&self) -> bool {
true
}
fn encode(&self, decoded: &DecodedSignal, button: u8) -> Option<Vec<LevelDuration>> {
let serial = decoded.serial?;
let counter = decoded.counter.unwrap_or(0);
// Calculate CRC
let cnt_high = ((counter >> 8) & 0x0F) as u8;
let mut char_data = [0u8; 7];
char_data[0] = ((serial >> 24) & 0xFF) as u8;
char_data[1] = ((serial >> 16) & 0xFF) as u8;
char_data[2] = ((serial >> 8) & 0xFF) as u8;
char_data[3] = (serial & 0xFF) as u8;
char_data[4] = button;
char_data[5] = (counter & 0xFF) as u8;
let crc = if cnt_high == 0 {
let offset = if counter >= 0x098 { button } else { 1 };
Self::crc4(&char_data[..6], offset)
} else if cnt_high >= 0x6 {
char_data[6] = cnt_high;
Self::crc4(&char_data, 1)
} else {
Self::crc4(&char_data[..6], 1)
};
// Build data
let data: u64 = ((serial as u64) << 24) |
((button as u64) << 16) |
(((counter & 0xFF) as u64) << 8) |
((cnt_high as u64) << 4) |
(crc as u64);
let mut signal = Vec::with_capacity(600);
// Generate 3 bursts
for burst in 0..3 {
if burst > 0 {
signal.push(LevelDuration::new(false, 25000));
}
// Preamble: 90 long pairs
for _ in 0..90 {
signal.push(LevelDuration::new(false, TE_LONG));
signal.push(LevelDuration::new(true, TE_LONG));
}
// Short gap
signal.push(LevelDuration::new(false, TE_SHORT));
// Data: Manchester encoded, MSB first
for bit_num in (1..MIN_COUNT_BIT).rev() {
let bit = ((data >> (bit_num - 1)) & 1) == 1;
if bit {
signal.push(LevelDuration::new(true, TE_SHORT));
signal.push(LevelDuration::new(false, TE_SHORT));
} else {
signal.push(LevelDuration::new(false, TE_SHORT));
signal.push(LevelDuration::new(true, TE_SHORT));
}
}
}
Some(signal)
}
}
+275
View File
@@ -0,0 +1,275 @@
//! Kia V2 protocol decoder
//!
//! Ported from protopirate's kia_v2.c
//!
//! Protocol characteristics:
//! - Manchester encoding: 500/1000µs timing
//! - 53 bits total
//! - Long preamble of 252+ pairs
//! - CRC4 checksum
use super::{ProtocolDecoder, ProtocolTiming, DecodedSignal};
use crate::radio::demodulator::LevelDuration;
use crate::duration_diff;
const TE_SHORT: u32 = 500;
const TE_LONG: u32 = 1000;
const TE_DELTA: u32 = 150;
const MIN_COUNT_BIT: usize = 53;
/// Manchester states
#[derive(Debug, Clone, Copy, PartialEq)]
enum ManchesterState {
Mid0,
Mid1,
Start0,
Start1,
}
/// Decoder states
#[derive(Debug, Clone, Copy, PartialEq)]
enum DecoderStep {
Reset,
CheckPreamble,
CollectRawBits,
}
/// Kia V2 protocol decoder
pub struct KiaV2Decoder {
step: DecoderStep,
te_last: u32,
header_count: u16,
decode_data: u64,
decode_count_bit: usize,
manchester_state: ManchesterState,
}
impl KiaV2Decoder {
pub fn new() -> Self {
Self {
step: DecoderStep::Reset,
te_last: 0,
header_count: 0,
decode_data: 0,
decode_count_bit: 0,
manchester_state: ManchesterState::Mid1,
}
}
/// Calculate CRC for Kia V2
fn calculate_crc(data: u64) -> u8 {
let serial = ((data >> 20) & 0xFFFFFFFF) as u32;
let u_var4 = (data & 0xFFFFFFFF) as u32;
let mut bytes = [0u8; 6];
bytes[0] = (u_var4 >> 20) as u8;
bytes[1] = ((u_var4 >> 28) | ((serial & 0x0F) << 4)) as u8;
bytes[2] = (serial >> 4) as u8;
bytes[3] = (serial >> 12) as u8;
bytes[4] = (u_var4 >> 4) as u8;
bytes[5] = (u_var4 >> 12) as u8;
let mut crc: u8 = 0;
for &byte in &bytes {
crc ^= (byte & 0x0F) ^ (byte >> 4);
}
(crc.wrapping_add(1)) & 0x0F
}
/// Manchester state machine
fn manchester_advance(&mut self, is_short: bool, is_high: bool) -> Option<bool> {
let event = match (is_short, is_high) {
(true, false) => 0, // Short Low
(true, true) => 1, // Short High
(false, false) => 2, // Long Low
(false, true) => 3, // Long High
};
let (new_state, output) = match (self.manchester_state, event) {
(ManchesterState::Mid0, 0) | (ManchesterState::Mid1, 0) =>
(ManchesterState::Start0, None),
(ManchesterState::Mid0, 1) | (ManchesterState::Mid1, 1) =>
(ManchesterState::Start1, None),
(ManchesterState::Start1, 0) => (ManchesterState::Mid1, Some(true)),
(ManchesterState::Start1, 2) => (ManchesterState::Start0, Some(true)),
(ManchesterState::Start0, 1) => (ManchesterState::Mid0, Some(false)),
(ManchesterState::Start0, 3) => (ManchesterState::Start1, Some(false)),
_ => (ManchesterState::Mid1, None),
};
self.manchester_state = new_state;
output
}
/// Parse decoded data
fn parse_data(&self) -> DecodedSignal {
let data = self.decode_data;
let serial = ((data >> 20) & 0xFFFFFFFF) as u32;
let button = ((data >> 16) & 0x0F) as u8;
// Counter has byte-swapped format
let raw_count = ((data >> 4) & 0xFFF) as u16;
let counter = ((raw_count >> 4) | (raw_count << 8)) & 0xFFF;
let received_crc = (data & 0x0F) as u8;
let calculated_crc = Self::calculate_crc(data);
DecodedSignal {
serial: Some(serial),
button: Some(button),
counter: Some(counter),
crc_valid: received_crc == calculated_crc,
data,
data_count_bit: MIN_COUNT_BIT,
encoder_capable: true,
}
}
}
impl ProtocolDecoder for KiaV2Decoder {
fn name(&self) -> &'static str {
"Kia V2"
}
fn timing(&self) -> ProtocolTiming {
ProtocolTiming {
te_short: TE_SHORT,
te_long: TE_LONG,
te_delta: TE_DELTA,
min_count_bit: MIN_COUNT_BIT,
}
}
fn supported_frequencies(&self) -> &[u32] {
&[315_000_000, 433_920_000]
}
fn reset(&mut self) {
self.step = DecoderStep::Reset;
self.te_last = 0;
self.header_count = 0;
self.decode_data = 0;
self.decode_count_bit = 0;
self.manchester_state = ManchesterState::Mid1;
}
fn feed(&mut self, level: bool, duration: u32) -> Option<DecodedSignal> {
let is_short = duration_diff!(duration, TE_SHORT) < TE_DELTA;
let is_long = duration_diff!(duration, TE_LONG) < TE_DELTA;
match self.step {
DecoderStep::Reset => {
if level && is_long {
self.step = DecoderStep::CheckPreamble;
self.te_last = duration;
self.header_count = 0;
self.manchester_state = ManchesterState::Mid1;
}
}
DecoderStep::CheckPreamble => {
if level {
if is_long {
self.te_last = duration;
self.header_count += 1;
} else if is_short && self.header_count >= 100 {
self.header_count = 0;
self.decode_data = 0;
self.decode_count_bit = 1;
self.step = DecoderStep::CollectRawBits;
self.decode_data = 1; // First bit
} else {
self.step = DecoderStep::Reset;
}
} else {
if is_long {
self.header_count += 1;
self.te_last = duration;
} else if !is_short {
self.step = DecoderStep::Reset;
}
}
}
DecoderStep::CollectRawBits => {
if is_short {
if let Some(bit) = self.manchester_advance(true, level) {
self.decode_data = (self.decode_data << 1) | (bit as u64);
self.decode_count_bit += 1;
}
} else if is_long {
if let Some(bit) = self.manchester_advance(false, level) {
self.decode_data = (self.decode_data << 1) | (bit as u64);
self.decode_count_bit += 1;
}
} else {
self.step = DecoderStep::Reset;
return None;
}
if self.decode_count_bit >= MIN_COUNT_BIT {
let result = self.parse_data();
self.step = DecoderStep::Reset;
return Some(result);
}
}
}
None
}
fn supports_encoding(&self) -> bool {
true
}
fn encode(&self, decoded: &DecodedSignal, button: u8) -> Option<Vec<LevelDuration>> {
let serial = decoded.serial?;
let counter = decoded.counter.unwrap_or(0);
// Reconstruct data in V2 format
let u_var6 = ((counter & 0xFF) as u32) << 8 |
((button & 0x0F) as u32) << 16 |
(((counter >> 4) & 0xF0) as u32);
let mut new_data: u64 = 1u64 << 52; // Start bit
new_data |= ((serial as u64) << 20) & 0xFFFFFFFFF00000;
new_data |= u_var6 as u64;
// Calculate and apply CRC
let crc = Self::calculate_crc(new_data);
new_data = (new_data & !0x0F) | (crc as u64);
let mut signal = Vec::with_capacity(700);
// Generate 2 bursts
for _burst in 0..2 {
// Preamble: 252 long pairs
for _ in 0..252 {
signal.push(LevelDuration::new(false, TE_LONG));
signal.push(LevelDuration::new(true, TE_LONG));
}
// Short gap
signal.push(LevelDuration::new(false, TE_SHORT));
// Data: Manchester encoded, MSB first
for bit_num in (1..MIN_COUNT_BIT).rev() {
let bit = ((new_data >> (bit_num - 1)) & 1) == 1;
if bit {
signal.push(LevelDuration::new(true, TE_SHORT));
signal.push(LevelDuration::new(false, TE_SHORT));
} else {
signal.push(LevelDuration::new(false, TE_SHORT));
signal.push(LevelDuration::new(true, TE_SHORT));
}
}
}
Some(signal)
}
}
+386
View File
@@ -0,0 +1,386 @@
//! Kia V3/V4 protocol decoder
//!
//! Ported from protopirate's kia_v3_v4.c
//!
//! Protocol characteristics:
//! - PWM encoding: 400/800µs timing
//! - 68 bits total
//! - Short preamble of 16 pairs
//! - KeeLoq encryption (requires manufacturer key)
//! - V3 and V4 differ only in sync polarity
use super::{ProtocolDecoder, ProtocolTiming, DecodedSignal};
use crate::radio::demodulator::LevelDuration;
use crate::duration_diff;
const TE_SHORT: u32 = 400;
const TE_LONG: u32 = 800;
const TE_DELTA: u32 = 150;
const MIN_COUNT_BIT: usize = 68;
const SYNC_DURATION: u32 = 1200;
const INTER_BURST_GAP_US: u32 = 10000;
const PREAMBLE_PAIRS: usize = 16;
const TOTAL_BURSTS: usize = 3;
/// Decoder states
#[derive(Debug, Clone, Copy, PartialEq)]
enum DecoderStep {
Reset,
CheckPreamble,
CollectRawBits,
}
/// Kia V3/V4 protocol decoder
pub struct KiaV3V4Decoder {
step: DecoderStep,
te_last: u32,
header_count: u16,
raw_bits: [u8; 32],
raw_bit_count: u16,
is_v3_sync: bool,
}
impl KiaV3V4Decoder {
pub fn new() -> Self {
Self {
step: DecoderStep::Reset,
te_last: 0,
header_count: 0,
raw_bits: [0; 32],
raw_bit_count: 0,
is_v3_sync: false,
}
}
/// Reverse bits in a byte
fn reverse8(byte: u8) -> u8 {
let mut byte = byte;
byte = (byte & 0xF0) >> 4 | (byte & 0x0F) << 4;
byte = (byte & 0xCC) >> 2 | (byte & 0x33) << 2;
byte = (byte & 0xAA) >> 1 | (byte & 0x55) << 1;
byte
}
/// Add a raw bit to the buffer
fn add_raw_bit(&mut self, bit: bool) {
if self.raw_bit_count < 256 {
let byte_idx = (self.raw_bit_count / 8) as usize;
let bit_idx = 7 - (self.raw_bit_count % 8);
if bit {
self.raw_bits[byte_idx] |= 1 << bit_idx;
} else {
self.raw_bits[byte_idx] &= !(1 << bit_idx);
}
self.raw_bit_count += 1;
}
}
/// CRC4 calculation
fn calculate_crc(bytes: &[u8]) -> u8 {
let mut crc: u8 = 0;
for &byte in bytes.iter().take(8) {
crc ^= (byte & 0x0F) ^ (byte >> 4);
}
crc & 0x0F
}
/// KeeLoq decrypt
fn keeloq_decrypt(data: u32, key: u64) -> u32 {
let mut block = data;
let mut tkey = key;
for _ in 0..528 {
let lutkey = ((block >> 0) & 1) |
((block >> 7) & 2) |
((block >> 17) & 4) |
((block >> 22) & 8) |
((block >> 26) & 16);
let lsb = ((block >> 31) ^
((block >> 15) & 1) ^
((0x3A5C742E_u32 >> lutkey) & 1) ^
(((tkey >> 15) & 1) as u32)) as u32;
block = ((block & 0x7FFFFFFF) << 1) | lsb;
tkey = ((tkey & 0x7FFFFFFFFFFFFFFF) << 1) | (tkey >> 63);
}
block
}
/// KeeLoq encrypt
fn keeloq_encrypt(data: u32, key: u64) -> u32 {
let mut block = data;
let mut tkey = key;
for _ in 0..528 {
let lutkey = ((block >> 1) & 1) |
((block >> 8) & 2) |
((block >> 18) & 4) |
((block >> 23) & 8) |
((block >> 27) & 16);
let msb = ((block >> 0) ^
((block >> 16) & 1) ^
((0x3A5C742E_u32 >> lutkey) & 1) ^
(((tkey >> 0) & 1) as u32)) as u32;
block = ((block >> 1) & 0x7FFFFFFF) | (msb << 31);
tkey = ((tkey >> 1) & 0x7FFFFFFFFFFFFFFF) | ((tkey & 1) << 63);
}
block
}
/// Get manufacturer key (placeholder - in real use, this would be loaded from config)
fn get_mf_key() -> u64 {
// This is a placeholder - actual key should be loaded from secure storage
0x0000000000000000
}
/// Process the collected buffer and validate
fn process_buffer(&self) -> Option<DecodedSignal> {
if self.raw_bit_count < 68 {
return None;
}
let mut b = self.raw_bits;
// V3 sync means data is inverted
if self.is_v3_sync {
let num_bytes = ((self.raw_bit_count + 7) / 8) as usize;
for i in 0..num_bytes {
b[i] = !b[i];
}
}
let _crc = (b[8] >> 4) & 0x0F;
let encrypted = ((Self::reverse8(b[3]) as u32) << 24) |
((Self::reverse8(b[2]) as u32) << 16) |
((Self::reverse8(b[1]) as u32) << 8) |
(Self::reverse8(b[0]) as u32);
let serial = ((Self::reverse8(b[7] & 0xF0) as u32) << 24) |
((Self::reverse8(b[6]) as u32) << 16) |
((Self::reverse8(b[5]) as u32) << 8) |
(Self::reverse8(b[4]) as u32);
let button = (Self::reverse8(b[7]) & 0xF0) >> 4;
let our_serial_lsb = (serial & 0xFF) as u8;
let mf_key = Self::get_mf_key();
let decrypted = Self::keeloq_decrypt(encrypted, mf_key);
let dec_btn = ((decrypted >> 28) & 0x0F) as u8;
let dec_serial_lsb = ((decrypted >> 16) & 0xFF) as u8;
// Validate decryption (may fail if key is wrong)
let crc_valid = if mf_key != 0 {
dec_btn == button && dec_serial_lsb == our_serial_lsb
} else {
// Can't validate without key
true
};
let counter = (decrypted & 0xFFFF) as u16;
// Build key data
let key_data = ((b[0] as u64) << 56) |
((b[1] as u64) << 48) |
((b[2] as u64) << 40) |
((b[3] as u64) << 32) |
((b[4] as u64) << 24) |
((b[5] as u64) << 16) |
((b[6] as u64) << 8) |
(b[7] as u64);
Some(DecodedSignal {
serial: Some(serial),
button: Some(button),
counter: Some(counter),
crc_valid,
data: key_data,
data_count_bit: MIN_COUNT_BIT,
encoder_capable: true,
})
}
}
impl ProtocolDecoder for KiaV3V4Decoder {
fn name(&self) -> &'static str {
"Kia V3/V4"
}
fn timing(&self) -> ProtocolTiming {
ProtocolTiming {
te_short: TE_SHORT,
te_long: TE_LONG,
te_delta: TE_DELTA,
min_count_bit: MIN_COUNT_BIT,
}
}
fn supported_frequencies(&self) -> &[u32] {
&[315_000_000, 433_920_000]
}
fn reset(&mut self) {
self.step = DecoderStep::Reset;
self.te_last = 0;
self.header_count = 0;
self.raw_bits = [0; 32];
self.raw_bit_count = 0;
self.is_v3_sync = false;
}
fn feed(&mut self, level: bool, duration: u32) -> Option<DecodedSignal> {
let is_short = duration_diff!(duration, TE_SHORT) < TE_DELTA;
let is_long = duration_diff!(duration, TE_LONG) < TE_DELTA;
let is_sync = duration > 1000 && duration < 1500;
let is_very_long = duration > 1500;
match self.step {
DecoderStep::Reset => {
if level && is_short {
self.step = DecoderStep::CheckPreamble;
self.te_last = duration;
self.header_count = 1;
}
}
DecoderStep::CheckPreamble => {
if level {
if is_short {
self.te_last = duration;
} else if is_sync && self.header_count >= 8 {
// V4 sync: long HIGH
self.step = DecoderStep::CollectRawBits;
self.raw_bit_count = 0;
self.is_v3_sync = false;
self.raw_bits = [0; 32];
} else {
self.step = DecoderStep::Reset;
}
} else {
if is_sync && self.header_count >= 8 {
// V3 sync: long LOW
self.step = DecoderStep::CollectRawBits;
self.raw_bit_count = 0;
self.is_v3_sync = true;
self.raw_bits = [0; 32];
} else if is_short && duration_diff!(self.te_last, TE_SHORT) < TE_DELTA {
self.header_count += 1;
} else if is_very_long {
self.step = DecoderStep::Reset;
}
}
}
DecoderStep::CollectRawBits => {
if level {
if is_sync || is_very_long {
// End of data
let result = self.process_buffer();
self.step = DecoderStep::Reset;
return result;
} else if is_short {
self.add_raw_bit(false);
} else if is_long {
self.add_raw_bit(true);
} else {
self.step = DecoderStep::Reset;
}
} else {
if is_sync || is_very_long {
let result = self.process_buffer();
self.step = DecoderStep::Reset;
return result;
}
// LOW durations don't carry data in PWM
}
}
}
None
}
fn supports_encoding(&self) -> bool {
true
}
fn encode(&self, decoded: &DecodedSignal, button: u8) -> Option<Vec<LevelDuration>> {
let serial = decoded.serial?;
let counter = decoded.counter.unwrap_or(0);
// Build plaintext for encryption
let plaintext = (counter as u32) |
((serial & 0xFF) << 16) |
(0x1 << 24) |
(((button & 0x0F) as u32) << 28);
let mf_key = Self::get_mf_key();
let encrypted = Self::keeloq_encrypt(plaintext, mf_key);
// Build raw bytes
let mut raw_bytes = [0u8; 9];
raw_bytes[0] = Self::reverse8((encrypted >> 0) as u8);
raw_bytes[1] = Self::reverse8((encrypted >> 8) as u8);
raw_bytes[2] = Self::reverse8((encrypted >> 16) as u8);
raw_bytes[3] = Self::reverse8((encrypted >> 24) as u8);
let serial_btn = (serial & 0x0FFFFFFF) | (((button & 0x0F) as u32) << 28);
raw_bytes[4] = Self::reverse8((serial_btn >> 0) as u8);
raw_bytes[5] = Self::reverse8((serial_btn >> 8) as u8);
raw_bytes[6] = Self::reverse8((serial_btn >> 16) as u8);
raw_bytes[7] = Self::reverse8((serial_btn >> 24) as u8);
let crc = Self::calculate_crc(&raw_bytes);
raw_bytes[8] = crc << 4;
// Use V4 encoding by default
let version = 0;
if version == 1 {
// V3: invert data
for byte in raw_bytes.iter_mut() {
*byte = !*byte;
}
}
let mut signal = Vec::with_capacity(600);
for burst in 0..TOTAL_BURSTS {
if burst > 0 {
signal.push(LevelDuration::new(false, INTER_BURST_GAP_US));
}
// Preamble
for _ in 0..PREAMBLE_PAIRS {
signal.push(LevelDuration::new(true, TE_SHORT));
signal.push(LevelDuration::new(false, TE_SHORT));
}
// Sync pulse
if version == 0 {
// V4: long HIGH, short LOW
signal.push(LevelDuration::new(true, SYNC_DURATION));
signal.push(LevelDuration::new(false, TE_SHORT));
} else {
// V3: short HIGH, long LOW
signal.push(LevelDuration::new(true, TE_SHORT));
signal.push(LevelDuration::new(false, SYNC_DURATION));
}
// Data bits
for byte_idx in 0..9 {
let bits_in_byte = if byte_idx == 8 { 4 } else { 8 };
for bit_idx in (8 - bits_in_byte..8).rev() {
let bit = (raw_bytes[byte_idx] >> bit_idx) & 1 != 0;
if bit {
signal.push(LevelDuration::new(true, TE_LONG));
signal.push(LevelDuration::new(false, TE_SHORT));
} else {
signal.push(LevelDuration::new(true, TE_SHORT));
signal.push(LevelDuration::new(false, TE_LONG));
}
}
}
}
Some(signal)
}
}
+312
View File
@@ -0,0 +1,312 @@
//! Kia V5 protocol decoder
//!
//! Ported from protopirate's kia_v5.c
//!
//! Protocol characteristics:
//! - Manchester encoding: 400/800µs timing
//! - 64 bits total (+3 bit CRC)
//! - Preamble of ~40+ short pairs
//! - Custom "mixer" encryption
//! - Decode-only (no encoder)
use super::{ProtocolDecoder, ProtocolTiming, DecodedSignal};
use crate::radio::demodulator::LevelDuration;
use crate::duration_diff;
const TE_SHORT: u32 = 400;
const TE_LONG: u32 = 800;
const TE_DELTA: u32 = 150;
const MIN_COUNT_BIT: usize = 64;
/// Manchester states
#[derive(Debug, Clone, Copy, PartialEq)]
enum ManchesterState {
Mid0,
Mid1,
Start0,
Start1,
}
/// Decoder states
#[derive(Debug, Clone, Copy, PartialEq)]
enum DecoderStep {
Reset,
CheckPreamble,
Data,
}
/// Kia V5 protocol decoder
pub struct KiaV5Decoder {
step: DecoderStep,
te_last: u32,
header_count: u16,
bit_count: u8,
decoded_data: u64,
saved_key: u64,
manchester_state: ManchesterState,
}
impl KiaV5Decoder {
pub fn new() -> Self {
Self {
step: DecoderStep::Reset,
te_last: 0,
header_count: 0,
bit_count: 0,
decoded_data: 0,
saved_key: 0,
manchester_state: ManchesterState::Mid1,
}
}
/// Get encryption key (placeholder)
fn get_v5_key() -> u64 {
0x0000000000000000
}
/// Custom mixer decryption
fn mixer_decode(encrypted: u32) -> u16 {
let mut s0 = (encrypted & 0xFF) as u8;
let mut s1 = ((encrypted >> 8) & 0xFF) as u8;
let mut s2 = ((encrypted >> 16) & 0xFF) as u8;
let mut s3 = ((encrypted >> 24) & 0xFF) as u8;
let key = Self::get_v5_key();
let mut keystore_bytes = [0u8; 8];
for i in 0..8 {
keystore_bytes[i] = ((key >> ((7 - i) * 8)) & 0xFF) as u8;
}
let mut round_index: usize = 1;
for _ in 0..18 {
let mut r = keystore_bytes[round_index];
let mut steps = 8;
while steps > 0 {
let base = if (s3 & 0x40) == 0 {
if (s3 & 0x02) == 0 { 0x74 } else { 0x2E }
} else {
if (s3 & 0x02) == 0 { 0x3A } else { 0x5C }
};
let mut base = base;
if s2 & 0x08 != 0 {
base = ((base >> 4) & 0x0F) | ((base & 0x0F) << 4);
}
if s1 & 0x01 != 0 {
base = (base & 0x3F) << 2;
}
if s0 & 0x01 != 0 {
base = base << 1;
}
let temp = (s3 ^ s1) & 0xFF;
s3 = (s3 & 0x7F) << 1;
if s2 & 0x80 != 0 {
s3 |= 0x01;
}
s2 = (s2 & 0x7F) << 1;
if s1 & 0x80 != 0 {
s2 |= 0x01;
}
s1 = (s1 & 0x7F) << 1;
if s0 & 0x80 != 0 {
s1 |= 0x01;
}
s0 = (s0 & 0x7F) << 1;
let chk = (base ^ (r ^ temp)) & 0xFF;
if chk & 0x80 != 0 {
s0 |= 0x01;
}
r = (r & 0x7F) << 1;
steps -= 1;
}
round_index = (round_index.wrapping_sub(1)) & 0x7;
}
((s0 as u16) + ((s1 as u16) << 8)) & 0xFFFF
}
/// Reverse bits in 64-bit value
fn compute_yek(key: u64) -> u64 {
let mut yek: u64 = 0;
for i in 0..8 {
let byte = ((key >> (i * 8)) & 0xFF) as u8;
let mut reversed: u8 = 0;
for b in 0..8 {
if byte & (1 << b) != 0 {
reversed |= 1 << (7 - b);
}
}
yek |= (reversed as u64) << ((7 - i) * 8);
}
yek
}
/// Manchester state machine
fn manchester_advance(&mut self, is_short: bool, is_high: bool) -> Option<bool> {
let event = match (is_short, is_high) {
(true, true) => 0, // Short High
(true, false) => 1, // Short Low
(false, true) => 2, // Long High
(false, false) => 3, // Long Low
};
let (new_state, output) = match (self.manchester_state, event) {
(ManchesterState::Mid0, 1) | (ManchesterState::Mid1, 1) =>
(ManchesterState::Start0, None),
(ManchesterState::Mid0, 0) | (ManchesterState::Mid1, 0) =>
(ManchesterState::Start1, None),
(ManchesterState::Start1, 1) => (ManchesterState::Mid1, Some(true)),
(ManchesterState::Start1, 3) => (ManchesterState::Start0, Some(true)),
(ManchesterState::Start0, 0) => (ManchesterState::Mid0, Some(false)),
(ManchesterState::Start0, 2) => (ManchesterState::Start1, Some(false)),
_ => (ManchesterState::Mid1, None),
};
self.manchester_state = new_state;
output
}
/// Parse decoded data
fn parse_data(&self) -> Option<DecodedSignal> {
if self.bit_count < MIN_COUNT_BIT as u8 {
return None;
}
let key = self.saved_key;
let yek = Self::compute_yek(key);
let serial = ((yek >> 32) & 0x0FFFFFFF) as u32;
let button = ((yek >> 60) & 0x0F) as u8;
let encrypted = (yek & 0xFFFFFFFF) as u32;
let counter = Self::mixer_decode(encrypted);
let _crc = (self.decoded_data & 0x07) as u8;
Some(DecodedSignal {
serial: Some(serial),
button: Some(button),
counter: Some(counter),
crc_valid: true, // V5 doesn't have a standard CRC validation
data: key,
data_count_bit: MIN_COUNT_BIT,
encoder_capable: false, // V5 is decode-only
})
}
}
impl ProtocolDecoder for KiaV5Decoder {
fn name(&self) -> &'static str {
"Kia V5"
}
fn timing(&self) -> ProtocolTiming {
ProtocolTiming {
te_short: TE_SHORT,
te_long: TE_LONG,
te_delta: TE_DELTA,
min_count_bit: MIN_COUNT_BIT,
}
}
fn supported_frequencies(&self) -> &[u32] {
&[433_920_000]
}
fn reset(&mut self) {
self.step = DecoderStep::Reset;
self.te_last = 0;
self.header_count = 0;
self.bit_count = 0;
self.decoded_data = 0;
self.saved_key = 0;
self.manchester_state = ManchesterState::Mid1;
}
fn feed(&mut self, level: bool, duration: u32) -> Option<DecodedSignal> {
let is_short = duration_diff!(duration, TE_SHORT) < TE_DELTA;
let is_long = duration_diff!(duration, TE_LONG) < TE_DELTA;
match self.step {
DecoderStep::Reset => {
if level && is_short {
self.step = DecoderStep::CheckPreamble;
self.te_last = duration;
self.header_count = 1;
self.bit_count = 0;
self.decoded_data = 0;
self.manchester_state = ManchesterState::Mid1;
}
}
DecoderStep::CheckPreamble => {
if level {
if is_long {
if self.header_count > 40 {
self.step = DecoderStep::Data;
self.bit_count = 0;
self.decoded_data = 0;
self.saved_key = 0;
self.header_count = 0;
} else {
self.te_last = duration;
}
} else if is_short {
self.te_last = duration;
} else {
self.step = DecoderStep::Reset;
}
} else {
if (is_short && duration_diff!(self.te_last, TE_SHORT) < TE_DELTA) ||
(is_long && duration_diff!(self.te_last, TE_SHORT) < TE_DELTA) ||
(duration_diff!(self.te_last, TE_LONG) < TE_DELTA) {
self.header_count += 1;
} else {
self.step = DecoderStep::Reset;
}
self.te_last = duration;
}
}
DecoderStep::Data => {
if !is_short && !is_long {
// End of data - try to parse
if self.bit_count >= MIN_COUNT_BIT as u8 {
let result = self.parse_data();
self.step = DecoderStep::Reset;
return result;
}
self.step = DecoderStep::Reset;
return None;
}
if self.bit_count <= 66 {
if let Some(bit) = self.manchester_advance(is_short, level) {
self.decoded_data = (self.decoded_data << 1) | (bit as u64);
self.bit_count += 1;
if self.bit_count == 64 {
self.saved_key = self.decoded_data;
self.decoded_data = 0;
}
}
}
self.te_last = duration;
}
}
None
}
fn supports_encoding(&self) -> bool {
false // V5 is decode-only in protopirate
}
fn encode(&self, _decoded: &DecodedSignal, _button: u8) -> Option<Vec<LevelDuration>> {
None // V5 doesn't support encoding
}
}
+553
View File
@@ -0,0 +1,553 @@
//! Kia V6 protocol decoder
//!
//! Ported from protopirate's kia_v6.c
//!
//! Protocol characteristics:
//! - Manchester encoding: 200/400µs timing
//! - 144 bits total (split into 3 parts)
//! - Long preamble of 600+ pairs
//! - AES-128 encryption
//! - Decode-only (no encoder)
use super::{ProtocolDecoder, ProtocolTiming, DecodedSignal};
use crate::radio::demodulator::LevelDuration;
use crate::duration_diff;
const TE_SHORT: u32 = 200;
const TE_LONG: u32 = 400;
const TE_DELTA: u32 = 100;
const MIN_COUNT_BIT: usize = 144;
const PREAMBLE_COUNT: u16 = 601;
const XOR_MASK_LOW: u32 = 0x84AF25FB;
const XOR_MASK_HIGH: u32 = 0x638766AB;
/// AES S-box
const AES_SBOX: [u8; 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,
];
/// AES inverse S-box
const AES_SBOX_INV: [u8; 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,
];
const AES_RCON: [u8; 10] = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36];
/// Manchester states
#[derive(Debug, Clone, Copy, PartialEq)]
enum ManchesterState {
Mid0,
Mid1,
Start0,
Start1,
}
/// Decoder states
#[derive(Debug, Clone, Copy, PartialEq)]
enum DecoderStep {
Reset,
WaitFirstHigh,
WaitLongHigh,
Data,
}
/// Kia V6 protocol decoder
pub struct KiaV6Decoder {
step: DecoderStep,
te_last: u32,
header_count: u16,
manchester_state: ManchesterState,
data_part1_low: u32,
data_part1_high: u32,
stored_part1_low: u32,
stored_part1_high: u32,
stored_part2_low: u32,
stored_part2_high: u32,
data_part3: u16,
bit_count: u8,
}
impl KiaV6Decoder {
pub fn new() -> Self {
Self {
step: DecoderStep::Reset,
te_last: 0,
header_count: 0,
manchester_state: ManchesterState::Mid1,
data_part1_low: 0,
data_part1_high: 0,
stored_part1_low: 0,
stored_part1_high: 0,
stored_part2_low: 0,
stored_part2_high: 0,
data_part3: 0,
bit_count: 0,
}
}
/// Get keystore A (placeholder)
fn get_keystore_a() -> u64 {
0x0000000000000000
}
/// Get keystore B (placeholder)
fn get_keystore_b() -> u64 {
0x0000000000000000
}
/// CRC8 calculation
fn crc8(data: &[u8], init: u8, polynomial: u8) -> u8 {
let mut crc = init;
for &byte in data {
crc ^= byte;
for _ in 0..8 {
let b = crc << 1;
if (crc & 0x80) != 0 {
crc = b ^ polynomial;
} else {
crc = b;
}
}
}
crc
}
/// GF(2^8) multiply by 2
fn gf_mul2(x: u8) -> u8 {
((x >> 7).wrapping_mul(0x1b)) ^ (x << 1)
}
/// AES inverse SubBytes
fn aes_subbytes_inv(state: &mut [u8; 16]) {
for i in 0..16 {
state[i] = AES_SBOX_INV[state[i] as usize];
}
}
/// AES inverse ShiftRows
fn aes_shiftrows_inv(state: &mut [u8; 16]) {
let temp = state[13];
state[13] = state[9];
state[9] = state[5];
state[5] = state[1];
state[1] = temp;
let temp = state[2];
state[2] = state[10];
state[10] = temp;
let temp = state[6];
state[6] = state[14];
state[14] = temp;
let temp = state[3];
state[3] = state[7];
state[7] = state[11];
state[11] = state[15];
state[15] = temp;
}
/// AES inverse MixColumns
fn aes_mixcolumns_inv(state: &mut [u8; 16]) {
for i in 0..4 {
let a = state[i * 4];
let b = state[i * 4 + 1];
let c = state[i * 4 + 2];
let d = state[i * 4 + 3];
let a2 = Self::gf_mul2(a);
let a4 = Self::gf_mul2(a2);
let a8 = Self::gf_mul2(a4);
let b2 = Self::gf_mul2(b);
let b4 = Self::gf_mul2(b2);
let b8 = Self::gf_mul2(b4);
let c2 = Self::gf_mul2(c);
let c4 = Self::gf_mul2(c2);
let c8 = Self::gf_mul2(c4);
let d2 = Self::gf_mul2(d);
let d4 = Self::gf_mul2(d2);
let d8 = Self::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);
}
}
/// AES AddRoundKey
fn aes_addroundkey(state: &mut [u8; 16], round_key: &[u8]) {
for i in 0..16 {
state[i] ^= round_key[i];
}
}
/// AES key expansion
fn aes_key_expansion(key: &[u8; 16]) -> [u8; 176] {
let mut round_keys = [0u8; 176];
round_keys[..16].copy_from_slice(key);
for i in 4..44 {
let prev_word_idx = (i - 1) * 4;
let mut b0 = round_keys[prev_word_idx];
let mut b1 = round_keys[prev_word_idx + 1];
let mut b2 = round_keys[prev_word_idx + 2];
let mut b3 = round_keys[prev_word_idx + 3];
if (i % 4) == 0 {
let new_b0 = AES_SBOX[b1 as usize] ^ AES_RCON[(i / 4) - 1];
let new_b1 = AES_SBOX[b2 as usize];
let new_b2 = AES_SBOX[b3 as usize];
let new_b3 = AES_SBOX[b0 as usize];
b0 = new_b0;
b1 = new_b1;
b2 = new_b2;
b3 = new_b3;
}
let 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];
let 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;
}
round_keys
}
/// AES-128 decrypt
fn aes128_decrypt(expanded_key: &[u8; 176], data: &mut [u8; 16]) {
let mut state = *data;
Self::aes_addroundkey(&mut state, &expanded_key[160..176]);
for round in (1..10).rev() {
Self::aes_shiftrows_inv(&mut state);
Self::aes_subbytes_inv(&mut state);
Self::aes_addroundkey(&mut state, &expanded_key[round * 16..(round + 1) * 16]);
Self::aes_mixcolumns_inv(&mut state);
}
Self::aes_shiftrows_inv(&mut state);
Self::aes_subbytes_inv(&mut state);
Self::aes_addroundkey(&mut state, &expanded_key[0..16]);
*data = state;
}
/// Get AES key from keystores
fn get_aes_key() -> [u8; 16] {
let keystore_a = Self::get_keystore_a();
let keystore_a_hi = ((keystore_a >> 32) & 0xFFFFFFFF) as u32;
let keystore_a_lo = (keystore_a & 0xFFFFFFFF) as u32;
let u_var15_a = keystore_a_lo ^ XOR_MASK_LOW;
let u_var5_a = XOR_MASK_HIGH ^ keystore_a_hi;
let val64_a = ((u_var5_a as u64) << 32) | (u_var15_a as u64);
let keystore_b = Self::get_keystore_b();
let keystore_b_hi = ((keystore_b >> 32) & 0xFFFFFFFF) as u32;
let keystore_b_lo = (keystore_b & 0xFFFFFFFF) as u32;
let u_var15_b = keystore_b_lo ^ XOR_MASK_LOW;
let u_var5_b = XOR_MASK_HIGH ^ keystore_b_hi;
let val64_b = ((u_var5_b as u64) << 32) | (u_var15_b as u64);
let mut aes_key = [0u8; 16];
for i in 0..8 {
aes_key[i] = ((val64_a >> (56 - i * 8)) & 0xFF) as u8;
}
for i in 0..8 {
aes_key[i + 8] = ((val64_b >> (56 - i * 8)) & 0xFF) as u8;
}
aes_key
}
/// Decrypt the stored data
fn decrypt(&self) -> Option<(u32, u8, u32, bool)> {
let mut encrypted_data = [0u8; 16];
encrypted_data[0] = ((self.stored_part1_high >> 8) & 0xFF) as u8;
encrypted_data[1] = (self.stored_part1_high & 0xFF) as u8;
encrypted_data[2] = ((self.stored_part1_low >> 24) & 0xFF) as u8;
encrypted_data[3] = ((self.stored_part1_low >> 16) & 0xFF) as u8;
encrypted_data[4] = ((self.stored_part1_low >> 8) & 0xFF) as u8;
encrypted_data[5] = (self.stored_part1_low & 0xFF) as u8;
encrypted_data[6] = ((self.stored_part2_high >> 24) & 0xFF) as u8;
encrypted_data[7] = ((self.stored_part2_high >> 16) & 0xFF) as u8;
encrypted_data[8] = ((self.stored_part2_high >> 8) & 0xFF) as u8;
encrypted_data[9] = (self.stored_part2_high & 0xFF) as u8;
encrypted_data[10] = ((self.stored_part2_low >> 24) & 0xFF) as u8;
encrypted_data[11] = ((self.stored_part2_low >> 16) & 0xFF) as u8;
encrypted_data[12] = ((self.stored_part2_low >> 8) & 0xFF) as u8;
encrypted_data[13] = (self.stored_part2_low & 0xFF) as u8;
encrypted_data[14] = ((self.data_part3 >> 8) & 0xFF) as u8;
encrypted_data[15] = (self.data_part3 & 0xFF) as u8;
let aes_key = Self::get_aes_key();
let expanded_key = Self::aes_key_expansion(&aes_key);
Self::aes128_decrypt(&expanded_key, &mut encrypted_data);
let decrypted = &encrypted_data;
let calculated_crc = Self::crc8(&decrypted[..15], 0xFF, 0x07);
let stored_crc = decrypted[15];
let crc_valid = (calculated_crc ^ stored_crc) < 2;
// Serial: bytes 4-6 as 24-bit big-endian
let serial = ((decrypted[4] as u32) << 16) | ((decrypted[5] as u32) << 8) | (decrypted[6] as u32);
let button = decrypted[7];
let counter = ((decrypted[8] as u32) << 24) |
((decrypted[9] as u32) << 16) |
((decrypted[10] as u32) << 8) |
(decrypted[11] as u32);
Some((serial, button, counter, crc_valid))
}
/// Manchester state machine
fn manchester_advance(&mut self, is_short: bool, is_high: bool) -> Option<bool> {
let event = match (is_short, is_high) {
(true, true) => 0, // Short High
(true, false) => 2, // Short Low
(false, true) => 6, // Long High
(false, false) => 4, // Long Low
};
let (new_state, output) = match (self.manchester_state, event) {
(ManchesterState::Mid0, 2) | (ManchesterState::Mid1, 2) =>
(ManchesterState::Start0, None),
(ManchesterState::Mid0, 0) | (ManchesterState::Mid1, 0) =>
(ManchesterState::Start1, None),
(ManchesterState::Start1, 2) => (ManchesterState::Mid1, Some(true)),
(ManchesterState::Start1, 4) => (ManchesterState::Start0, Some(true)),
(ManchesterState::Start0, 0) => (ManchesterState::Mid0, Some(false)),
(ManchesterState::Start0, 6) => (ManchesterState::Start1, Some(false)),
_ => (ManchesterState::Mid1, None),
};
self.manchester_state = new_state;
output
}
/// Add initial sync bits
fn add_sync_bits(&mut self) {
// Add 1, 1, 0, 1 as initial bits
for bit in [true, true, false, true] {
let carry = self.data_part1_low >> 31;
self.data_part1_low = (self.data_part1_low << 1) | (bit as u32);
self.data_part1_high = (self.data_part1_high << 1) | carry;
self.bit_count += 1;
}
}
}
impl ProtocolDecoder for KiaV6Decoder {
fn name(&self) -> &'static str {
"Kia V6"
}
fn timing(&self) -> ProtocolTiming {
ProtocolTiming {
te_short: TE_SHORT,
te_long: TE_LONG,
te_delta: TE_DELTA,
min_count_bit: MIN_COUNT_BIT,
}
}
fn supported_frequencies(&self) -> &[u32] {
&[433_920_000]
}
fn reset(&mut self) {
self.step = DecoderStep::Reset;
self.te_last = 0;
self.header_count = 0;
self.manchester_state = ManchesterState::Mid1;
self.data_part1_low = 0;
self.data_part1_high = 0;
self.stored_part1_low = 0;
self.stored_part1_high = 0;
self.stored_part2_low = 0;
self.stored_part2_high = 0;
self.data_part3 = 0;
self.bit_count = 0;
}
fn feed(&mut self, level: bool, duration: u32) -> Option<DecodedSignal> {
let is_short = duration_diff!(duration, TE_SHORT) < TE_DELTA;
let is_long = duration_diff!(duration, TE_LONG) < TE_DELTA;
match self.step {
DecoderStep::Reset => {
if level && is_short {
self.step = DecoderStep::WaitFirstHigh;
self.te_last = duration;
self.header_count = 0;
self.manchester_state = ManchesterState::Mid1;
}
}
DecoderStep::WaitFirstHigh => {
if level {
return None;
}
let diff_short = duration_diff!(duration, TE_SHORT);
let diff_long = duration_diff!(duration, TE_LONG);
if diff_long < TE_DELTA && diff_long < diff_short {
if self.header_count >= PREAMBLE_COUNT {
self.header_count = 0;
self.te_last = duration;
self.step = DecoderStep::WaitLongHigh;
return None;
}
}
if diff_short >= TE_DELTA && diff_long >= TE_DELTA {
self.step = DecoderStep::Reset;
return None;
}
if duration_diff!(self.te_last, TE_SHORT) < TE_DELTA {
self.te_last = duration;
self.header_count += 1;
} else {
self.step = DecoderStep::Reset;
}
}
DecoderStep::WaitLongHigh => {
if !level {
self.step = DecoderStep::Reset;
return None;
}
let diff_long = duration_diff!(duration, TE_LONG);
let diff_short = duration_diff!(duration, TE_SHORT);
if diff_long >= TE_DELTA && diff_short >= TE_DELTA {
self.step = DecoderStep::Reset;
return None;
}
if duration_diff!(self.te_last, TE_LONG) >= TE_DELTA {
self.step = DecoderStep::Reset;
return None;
}
self.data_part1_low = 0;
self.data_part1_high = 0;
self.bit_count = 0;
self.add_sync_bits();
self.step = DecoderStep::Data;
}
DecoderStep::Data => {
if !is_short && !is_long {
self.step = DecoderStep::Reset;
return None;
}
if let Some(bit) = self.manchester_advance(is_short, level) {
let carry = self.data_part1_low >> 31;
self.data_part1_low = (self.data_part1_low << 1) | (bit as u32);
self.data_part1_high = (self.data_part1_high << 1) | carry;
self.bit_count += 1;
if self.bit_count == 64 {
self.stored_part1_low = !self.data_part1_low;
self.stored_part1_high = !self.data_part1_high;
self.data_part1_low = 0;
self.data_part1_high = 0;
} else if self.bit_count == 128 {
self.stored_part2_low = !self.data_part1_low;
self.stored_part2_high = !self.data_part1_high;
self.data_part1_low = 0;
self.data_part1_high = 0;
}
}
self.te_last = duration;
if self.bit_count as usize == MIN_COUNT_BIT {
self.data_part3 = !(self.data_part1_low as u16);
if let Some((serial, button, counter, crc_valid)) = self.decrypt() {
let key_data = ((self.stored_part1_high as u64) << 32) |
(self.stored_part1_low as u64);
self.step = DecoderStep::Reset;
return Some(DecodedSignal {
serial: Some(serial),
button: Some(button),
counter: Some((counter & 0xFFFF) as u16), // V6 has 32-bit counter but we only store 16
crc_valid,
data: key_data,
data_count_bit: MIN_COUNT_BIT,
encoder_capable: false,
});
}
self.step = DecoderStep::Reset;
}
}
}
None
}
fn supports_encoding(&self) -> bool {
false // V6 is decode-only
}
fn encode(&self, _decoded: &DecodedSignal, _button: u8) -> Option<Vec<LevelDuration>> {
None
}
}
+184
View File
@@ -0,0 +1,184 @@
//! Protocol decoders and encoders for various keyfob systems.
//!
//! This module implements decoders for various keyfob protocols, ported from
//! protopirate. Each protocol processes level+duration pairs from the demodulator.
mod common;
pub mod keeloq_common;
#[allow(dead_code)]
pub mod aut64;
#[allow(dead_code)]
pub mod keys;
mod kia_v0;
mod kia_v1;
mod kia_v2;
mod kia_v3_v4;
mod kia_v5;
mod kia_v6;
mod subaru;
mod ford_v0;
mod vag;
mod fiat_v0;
mod suzuki;
mod scher_khan;
mod star_line;
mod psa;
pub use common::DecodedSignal;
use crate::capture::Capture;
use crate::radio::demodulator::LevelDuration;
/// Protocol timing constants
#[derive(Debug, Clone, Copy)]
#[allow(dead_code)]
pub struct ProtocolTiming {
/// Short pulse duration in µs
pub te_short: u32,
/// Long pulse duration in µs
pub te_long: u32,
/// Tolerance for timing matching in µs
pub te_delta: u32,
/// Minimum bit count for valid decode
pub min_count_bit: usize,
}
/// Trait for protocol decoders
///
/// Each protocol implements a state machine that processes level+duration pairs.
pub trait ProtocolDecoder: Send + Sync {
/// Get the protocol name
fn name(&self) -> &'static str;
/// Get timing constants
#[allow(dead_code)]
fn timing(&self) -> ProtocolTiming;
/// Get supported frequencies in Hz
fn supported_frequencies(&self) -> &[u32];
/// Reset the decoder state machine
fn reset(&mut self);
/// Feed a level+duration pair to the decoder
/// Returns Some(DecodedSignal) when a complete valid signal is decoded
fn feed(&mut self, level: bool, duration_us: u32) -> Option<DecodedSignal>;
/// Check if this protocol supports encoding
fn supports_encoding(&self) -> bool;
/// Encode a signal with the given button command
fn encode(&self, decoded: &DecodedSignal, button: u8) -> Option<Vec<LevelDuration>>;
}
/// Registry of all supported protocols
pub struct ProtocolRegistry {
decoders: Vec<Box<dyn ProtocolDecoder>>,
}
impl ProtocolRegistry {
/// Create a new protocol registry with all built-in protocols
pub fn new() -> Self {
let decoders: Vec<Box<dyn ProtocolDecoder>> = vec![
// Kia protocols
Box::new(kia_v0::KiaV0Decoder::new()),
Box::new(kia_v1::KiaV1Decoder::new()),
Box::new(kia_v2::KiaV2Decoder::new()),
Box::new(kia_v3_v4::KiaV3V4Decoder::new()),
Box::new(kia_v5::KiaV5Decoder::new()),
Box::new(kia_v6::KiaV6Decoder::new()),
// Other protocols
Box::new(subaru::SubaruDecoder::new()),
Box::new(ford_v0::FordV0Decoder::new()),
Box::new(vag::VagDecoder::new()),
Box::new(fiat_v0::FiatV0Decoder::new()),
Box::new(suzuki::SuzukiDecoder::new()),
Box::new(scher_khan::ScherKhanDecoder::new()),
Box::new(star_line::StarLineDecoder::new()),
Box::new(psa::PsaDecoder::new()),
];
Self { decoders }
}
/// Process level+duration pairs from demodulator
/// Returns decoded signal info if any protocol matches
pub fn process_signal(&mut self, pairs: &[LevelDuration], frequency: u32) -> Option<(String, DecodedSignal)> {
// Reset all decoders
for decoder in &mut self.decoders {
decoder.reset();
}
// Feed pairs to all decoders that support this frequency
for pair in pairs {
for decoder in &mut self.decoders {
// Check frequency support
let freq_supported = decoder
.supported_frequencies()
.iter()
.any(|&f| {
let diff = if f > frequency { f - frequency } else { frequency - f };
diff < (f / 50) // 2% tolerance
});
if !freq_supported {
continue;
}
if let Some(decoded) = decoder.feed(pair.level, pair.duration_us) {
return Some((decoder.name().to_string(), decoded));
}
}
}
None
}
/// Try to decode a capture (for compatibility with old interface)
#[allow(dead_code)]
pub fn try_decode(&mut self, capture: &Capture) -> Option<(String, DecodedSignal)> {
// Convert raw pairs to LevelDuration and process
if capture.raw_pairs.is_empty() {
return None;
}
let pairs: Vec<LevelDuration> = capture.raw_pairs
.iter()
.map(|p| LevelDuration::new(p.level, p.duration_us))
.collect();
self.process_signal(&pairs, capture.frequency)
}
/// Get a decoder by name
pub fn get(&self, name: &str) -> Option<&dyn ProtocolDecoder> {
self.decoders
.iter()
.find(|d| d.name().eq_ignore_ascii_case(name))
.map(|d| d.as_ref())
}
/// List all protocol names
#[allow(dead_code)]
pub fn list_protocols(&self) -> Vec<&'static str> {
self.decoders.iter().map(|d| d.name()).collect()
}
}
impl Default for ProtocolRegistry {
fn default() -> Self {
Self::new()
}
}
/// Helper macro for duration comparison (matches protopirate's DURATION_DIFF)
#[macro_export]
macro_rules! duration_diff {
($actual:expr, $expected:expr) => {
if $actual > $expected {
$actual - $expected
} else {
$expected - $actual
}
};
}
+489
View File
@@ -0,0 +1,489 @@
//! PSA (Peugeot/Citroen) protocol decoder/encoder
//!
//! Ported from protopirate's psa.c
//!
//! 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
use super::{DecodedSignal, ProtocolDecoder, ProtocolTiming};
use crate::duration_diff;
use crate::radio::demodulator::LevelDuration;
const TE_SHORT: u32 = 250;
const TE_LONG: u32 = 500;
const TE_DELTA: u32 = 100;
const MIN_COUNT_BIT: usize = 128;
// Internal timing for Manchester sub-symbol detection
const TE_SHORT_125: u32 = 125;
const TE_LONG_250: u32 = 250;
const TE_TOLERANCE_49: u32 = 49;
const TE_TOLERANCE_50: u32 = 50;
const TE_TOLERANCE_99: u32 = 99;
const TE_END_1000: u32 = 1000;
// TEA constants
const TEA_DELTA: u32 = 0x9E3779B9;
const TEA_ROUNDS: u32 = 32;
// Brute-force constants for mode 0x23
const BF1_KEY_SCHEDULE: [u32; 4] = [0x4A434915, 0xD6743C2B, 0x1F29D308, 0xE6B79A64];
// Brute-force constants for mode 0x36
const BF2_KEY_SCHEDULE: [u32; 4] = [0x4039C240, 0xEDA92CAB, 0x4306C02A, 0x02192A04];
/// Manchester decoder states
#[derive(Debug, Clone, Copy, PartialEq)]
enum ManchesterState {
Mid0,
Mid1,
Start0,
Start1,
}
/// Decoder states
#[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,
}
/// PSA protocol decoder
pub struct PsaDecoder {
state: DecoderState,
prev_duration: u32,
manchester_state: ManchesterState,
pattern_counter: u16,
data_low: u32,
data_high: u32,
bit_count: u8,
// Decoded fields
key1_low: u32,
key1_high: u32,
validation_field: u16,
key2_low: u32,
key2_high: u32,
seed: u32,
}
impl PsaDecoder {
pub fn new() -> Self {
Self {
state: DecoderState::WaitEdge,
prev_duration: 0,
manchester_state: ManchesterState::Mid1,
pattern_counter: 0,
data_low: 0,
data_high: 0,
bit_count: 0,
key1_low: 0,
key1_high: 0,
validation_field: 0,
key2_low: 0,
key2_high: 0,
seed: 0,
}
}
/// Manchester advance
fn manchester_advance(&mut self, is_short: bool, is_high: bool) -> Option<bool> {
let event = match (is_short, is_high) {
(true, true) => 0,
(true, false) => 1,
(false, true) => 2,
(false, false) => 3,
};
let (new_state, output) = match (self.manchester_state, event) {
(ManchesterState::Mid0, 0) | (ManchesterState::Mid1, 0) => {
(ManchesterState::Start1, None)
}
(ManchesterState::Mid0, 1) | (ManchesterState::Mid1, 1) => {
(ManchesterState::Start0, None)
}
(ManchesterState::Start1, 1) => (ManchesterState::Mid1, Some(true)),
(ManchesterState::Start1, 3) => (ManchesterState::Start0, Some(true)),
(ManchesterState::Start0, 0) => (ManchesterState::Mid0, Some(false)),
(ManchesterState::Start0, 2) => (ManchesterState::Start1, Some(false)),
_ => (ManchesterState::Mid1, None),
};
self.manchester_state = new_state;
output
}
fn add_bit(&mut self, bit: bool) {
let new_bit = if bit { 1u32 } else { 0u32 };
let carry = (self.data_low >> 31) & 1;
self.data_low = (self.data_low << 1) | new_bit;
self.data_high = (self.data_high << 1) | carry;
self.bit_count += 1;
// Extract key1 at 64 bits
if self.bit_count == 64 {
self.key1_low = self.data_low;
self.key1_high = self.data_high;
self.data_low = 0;
self.data_high = 0;
}
// Extract validation at 80 bits (16 more)
else if self.bit_count == 80 {
self.validation_field = self.data_low as u16;
self.data_low = 0;
self.data_high = 0;
}
}
/// TEA decrypt
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 {
*v1 = v1.wrapping_sub(
(v0.wrapping_shl(4).wrapping_add(key[2]))
^ (v0.wrapping_add(sum))
^ (v0.wrapping_shr(5).wrapping_add(key[3])),
);
*v0 = v0.wrapping_sub(
(v1.wrapping_shl(4).wrapping_add(key[0]))
^ (v1.wrapping_add(sum))
^ (v1.wrapping_shr(5).wrapping_add(key[1])),
);
sum = sum.wrapping_sub(TEA_DELTA);
}
}
/// TEA encrypt
fn tea_encrypt(v0: &mut u32, v1: &mut u32, key: &[u32; 4]) {
let mut sum: u32 = 0;
for _ in 0..TEA_ROUNDS {
sum = sum.wrapping_add(TEA_DELTA);
*v0 = v0.wrapping_add(
(v1.wrapping_shl(4).wrapping_add(key[0]))
^ (v1.wrapping_add(sum))
^ (v1.wrapping_shr(5).wrapping_add(key[1])),
);
*v1 = v1.wrapping_add(
(v0.wrapping_shl(4).wrapping_add(key[2]))
^ (v0.wrapping_add(sum))
^ (v0.wrapping_shr(5).wrapping_add(key[3])),
);
}
}
/// XOR decrypt (mode 0x23)
fn xor_decrypt(buffer: &mut [u8]) {
let e6 = buffer[8];
let e7 = buffer[9];
let e5 = buffer[7];
let e0 = buffer[2];
let e1 = buffer[3];
let e2 = buffer[4];
let e3 = buffer[5];
let e4 = buffer[6];
buffer[2] = e0 ^ e5;
buffer[3] = e1 ^ (e0 ^ e5 ^ e6 ^ e7);
buffer[4] = e2 ^ e0;
buffer[5] = e3 ^ (e0 ^ e5 ^ e6 ^ e7);
buffer[6] = e4 ^ e2;
buffer[7] = e5 ^ e6 ^ e7;
}
fn try_decrypt(&self) -> Option<(u32, u8, u32, u16, u8)> {
// Try mode 0x23 first
let seed_byte = (self.key1_high >> 24) as u8;
if seed_byte >= 0x23 && seed_byte < 0x24 {
// Mode 0x23 - TEA + XOR
let mut v0 = self.key1_high;
let mut v1 = self.key1_low;
Self::tea_decrypt(&mut v0, &mut v1, &BF1_KEY_SCHEDULE);
let mut buffer = [0u8; 10];
buffer[0] = (v0 >> 24) as u8;
buffer[1] = (v0 >> 16) as u8;
buffer[2] = (v0 >> 8) as u8;
buffer[3] = (v0 >> 0) as u8;
buffer[4] = (v1 >> 24) as u8;
buffer[5] = (v1 >> 16) as u8;
buffer[6] = (v1 >> 8) as u8;
buffer[7] = (v1 >> 0) as u8;
buffer[8] = (self.validation_field >> 8) as u8;
buffer[9] = (self.validation_field & 0xFF) as u8;
Self::xor_decrypt(&mut buffer);
let serial = ((buffer[2] as u32) << 16)
| ((buffer[3] as u32) << 8)
| (buffer[4] as u32);
let counter = ((buffer[5] as u32) << 8) | (buffer[6] as u32);
let crc = buffer[7] as u16;
let btn = buffer[8] & 0x0F;
return Some((serial, btn, counter, crc, 0x23));
}
if seed_byte >= 0xF3 && seed_byte < 0xF4 {
// Mode 0x36 - TEA + different key schedule
let mut v0 = self.key1_high;
let mut v1 = self.key1_low;
Self::tea_decrypt(&mut v0, &mut v1, &BF2_KEY_SCHEDULE);
let serial = ((v0 >> 8) & 0xFFFF00) | ((v0 & 0xFF) as u32);
let counter = v1 >> 16;
let btn = ((v1 >> 8) & 0xF) as u8;
let crc = (v1 & 0xFF) as u16;
return Some((serial, btn, counter, crc, 0x36));
}
// Cannot decrypt - return raw data
None
}
fn parse_data(&self) -> DecodedSignal {
// Combine into 128-bit data (store lower 64)
let data = ((self.key1_high as u64) << 32) | (self.key1_low as u64);
if let Some((serial, btn, counter, _crc, _mode)) = self.try_decrypt() {
DecodedSignal {
serial: Some(serial),
button: Some(btn),
counter: Some(counter as u16),
crc_valid: true,
data,
data_count_bit: MIN_COUNT_BIT,
encoder_capable: true,
}
} else {
DecodedSignal {
serial: None,
button: None,
counter: None,
crc_valid: false,
data,
data_count_bit: MIN_COUNT_BIT,
encoder_capable: false,
}
}
}
}
impl ProtocolDecoder for PsaDecoder {
fn name(&self) -> &'static str {
"PSA"
}
fn timing(&self) -> ProtocolTiming {
ProtocolTiming {
te_short: TE_SHORT,
te_long: TE_LONG,
te_delta: TE_DELTA,
min_count_bit: MIN_COUNT_BIT,
}
}
fn supported_frequencies(&self) -> &[u32] {
&[433_920_000]
}
fn reset(&mut self) {
self.state = DecoderState::WaitEdge;
self.prev_duration = 0;
self.manchester_state = ManchesterState::Mid1;
self.pattern_counter = 0;
self.data_low = 0;
self.data_high = 0;
self.bit_count = 0;
self.key1_low = 0;
self.key1_high = 0;
self.validation_field = 0;
self.key2_low = 0;
self.key2_high = 0;
self.seed = 0;
}
fn feed(&mut self, level: bool, duration: u32) -> Option<DecodedSignal> {
match self.state {
DecoderState::WaitEdge => {
if level && duration_diff!(duration, TE_SHORT_125) < TE_TOLERANCE_49 {
self.state = DecoderState::CountPattern;
self.prev_duration = duration;
self.pattern_counter = 0;
}
}
DecoderState::CountPattern => {
let diff_125 = duration_diff!(duration, TE_SHORT_125);
let diff_250 = duration_diff!(duration, TE_LONG_250);
if diff_125 < TE_TOLERANCE_50 {
self.pattern_counter += 1;
self.prev_duration = duration;
} else if diff_250 < TE_TOLERANCE_99 && self.pattern_counter >= 0x46 {
// Found end of preamble, start Manchester decoding
self.state = DecoderState::DecodeManchester;
self.data_low = 0;
self.data_high = 0;
self.bit_count = 0;
self.manchester_state = ManchesterState::Mid1;
self.prev_duration = duration;
} else if self.pattern_counter < 2 {
self.state = DecoderState::WaitEdge;
} else {
self.prev_duration = duration;
}
}
DecoderState::DecodeManchester => {
let is_short = duration_diff!(duration, TE_SHORT) < TE_DELTA;
let is_long = duration_diff!(duration, TE_LONG) < TE_DELTA;
let is_end = duration > TE_END_1000;
if is_end || self.bit_count >= 121 {
// End of data
self.state = DecoderState::End;
if self.bit_count >= 96 {
// Got enough data
let result = self.parse_data();
self.state = DecoderState::WaitEdge;
return Some(result);
}
self.state = DecoderState::WaitEdge;
return None;
}
if is_short || is_long {
if let Some(bit) = self.manchester_advance(is_short, level) {
self.add_bit(bit);
}
} else {
self.state = DecoderState::WaitEdge;
}
self.prev_duration = duration;
}
DecoderState::End => {
self.state = DecoderState::WaitEdge;
}
}
None
}
fn supports_encoding(&self) -> bool {
true
}
fn encode(&self, decoded: &DecodedSignal, button: u8) -> Option<Vec<LevelDuration>> {
let serial = decoded.serial?;
let counter = decoded.counter.unwrap_or(0).wrapping_add(1) as u32;
// Build plaintext buffer for mode 0x23
let mut buffer = [0u8; 10];
buffer[0] = 0x23;
buffer[1] = 0x00;
buffer[2] = (serial >> 16) as u8;
buffer[3] = (serial >> 8) as u8;
buffer[4] = serial as u8;
buffer[5] = (counter >> 8) as u8;
buffer[6] = counter as u8;
buffer[7] = 0; // CRC placeholder
buffer[8] = button & 0x0F;
buffer[9] = 0;
// XOR encrypt
{
let e6 = buffer[8];
let e7 = buffer[9];
let p0 = buffer[2];
let p1 = buffer[3];
let p2 = buffer[4];
let p3 = buffer[5];
let p4 = buffer[6];
let p5 = buffer[7];
let ne5 = p5 ^ e7 ^ e6;
let ne0 = p2 ^ ne5;
let ne2 = p4 ^ ne0;
let ne4 = p3 ^ ne2;
let ne3 = p0 ^ ne5;
let ne1 = p1 ^ ne3;
buffer[2] = ne0;
buffer[3] = ne1;
buffer[4] = ne2;
buffer[5] = ne3;
buffer[6] = ne4;
buffer[7] = ne5;
}
// TEA encrypt
let mut v0 = ((buffer[0] as u32) << 24)
| ((buffer[1] as u32) << 16)
| ((buffer[2] as u32) << 8)
| (buffer[3] as u32);
let mut v1 = ((buffer[4] as u32) << 24)
| ((buffer[5] as u32) << 16)
| ((buffer[6] as u32) << 8)
| (buffer[7] as u32);
Self::tea_encrypt(&mut v0, &mut v1, &BF1_KEY_SCHEDULE);
let key1_high = v0;
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
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
let key1 = ((key1_high as u64) << 32) | (key1_low as u64);
for bit in (0..64).rev() {
if (key1 >> bit) & 1 == 1 {
signal.push(LevelDuration::new(false, TE_SHORT));
signal.push(LevelDuration::new(true, TE_SHORT));
} else {
signal.push(LevelDuration::new(true, TE_SHORT));
signal.push(LevelDuration::new(false, TE_SHORT));
}
}
// Validation: 16 bits Manchester encoded
for bit in (0..16).rev() {
if (validation >> bit) & 1 == 1 {
signal.push(LevelDuration::new(false, TE_SHORT));
signal.push(LevelDuration::new(true, TE_SHORT));
} else {
signal.push(LevelDuration::new(true, TE_SHORT));
signal.push(LevelDuration::new(false, TE_SHORT));
}
}
// End marker
signal.push(LevelDuration::new(false, TE_END_1000));
Some(signal)
}
}
+201
View File
@@ -0,0 +1,201 @@
//! Scher-Khan protocol decoder
//!
//! Ported from protopirate's scher_khan.c
//!
//! Protocol characteristics:
//! - PWM encoding: 750/1100µs timing
//! - Variable bit count (35, 51, 57, 63, 64, 81, 82)
//! - Decode-only (no encoder)
//!
//! References:
//! - https://phreakerclub.com/72
use super::{DecodedSignal, ProtocolDecoder, ProtocolTiming};
use crate::duration_diff;
use crate::radio::demodulator::LevelDuration;
const TE_SHORT: u32 = 750;
const TE_LONG: u32 = 1100;
const TE_DELTA: u32 = 160;
const MIN_COUNT_BIT: usize = 35;
/// Decoder states
#[derive(Debug, Clone, Copy, PartialEq)]
enum DecoderStep {
Reset,
CheckPreamble,
SaveDuration,
CheckDuration,
}
/// Scher-Khan protocol decoder
pub struct ScherKhanDecoder {
step: DecoderStep,
te_last: u32,
header_count: u16,
decode_data: u64,
decode_count_bit: usize,
}
impl ScherKhanDecoder {
pub fn new() -> Self {
Self {
step: DecoderStep::Reset,
te_last: 0,
header_count: 0,
decode_data: 0,
decode_count_bit: 0,
}
}
fn parse_data(data: u64, bit_count: usize) -> DecodedSignal {
let (serial, btn, cnt) = match bit_count {
51 => {
// MAGIC CODE, Dynamic
let serial =
((data >> 24) & 0xFFFFFF0) as u32 | ((data >> 20) & 0x0F) as u32;
let btn = ((data >> 24) & 0x0F) as u8;
let cnt = (data & 0xFFFF) as u16;
(Some(serial), Some(btn), Some(cnt))
}
_ => (None, None, None),
};
DecodedSignal {
serial,
button: btn,
counter: cnt,
crc_valid: true,
data,
data_count_bit: bit_count,
encoder_capable: false,
}
}
}
impl ProtocolDecoder for ScherKhanDecoder {
fn name(&self) -> &'static str {
"Scher-Khan"
}
fn timing(&self) -> ProtocolTiming {
ProtocolTiming {
te_short: TE_SHORT,
te_long: TE_LONG,
te_delta: TE_DELTA,
min_count_bit: MIN_COUNT_BIT,
}
}
fn supported_frequencies(&self) -> &[u32] {
&[433_920_000]
}
fn reset(&mut self) {
self.step = DecoderStep::Reset;
self.te_last = 0;
self.header_count = 0;
self.decode_data = 0;
self.decode_count_bit = 0;
}
fn feed(&mut self, level: bool, duration: u32) -> Option<DecodedSignal> {
match self.step {
DecoderStep::Reset => {
if level && duration_diff!(duration, TE_SHORT * 2) < TE_DELTA {
self.step = DecoderStep::CheckPreamble;
self.te_last = duration;
self.header_count = 0;
}
}
DecoderStep::CheckPreamble => {
if level {
if duration_diff!(duration, TE_SHORT * 2) < TE_DELTA
|| duration_diff!(duration, TE_SHORT) < TE_DELTA
{
self.te_last = duration;
} else {
self.step = DecoderStep::Reset;
}
} else if duration_diff!(duration, TE_SHORT * 2) < TE_DELTA
|| duration_diff!(duration, TE_SHORT) < TE_DELTA
{
if duration_diff!(self.te_last, TE_SHORT * 2) < TE_DELTA {
self.header_count += 1;
} else if duration_diff!(self.te_last, TE_SHORT) < TE_DELTA {
// Found start bit
if self.header_count >= 2 {
self.step = DecoderStep::SaveDuration;
self.decode_data = 0;
self.decode_count_bit = 1;
} else {
self.step = DecoderStep::Reset;
}
} else {
self.step = DecoderStep::Reset;
}
} else {
self.step = DecoderStep::Reset;
}
}
DecoderStep::SaveDuration => {
if level {
if duration >= (TE_DELTA * 2 + TE_LONG) {
// Found stop bit
self.step = DecoderStep::Reset;
if self.decode_count_bit >= MIN_COUNT_BIT {
let result =
Self::parse_data(self.decode_data, self.decode_count_bit);
self.decode_data = 0;
self.decode_count_bit = 0;
return Some(result);
}
self.decode_data = 0;
self.decode_count_bit = 0;
} else {
self.te_last = duration;
self.step = DecoderStep::CheckDuration;
}
} else {
self.step = DecoderStep::Reset;
}
}
DecoderStep::CheckDuration => {
if !level {
if duration_diff!(self.te_last, TE_SHORT) < TE_DELTA
&& duration_diff!(duration, TE_SHORT) < TE_DELTA
{
// Bit 0
self.decode_data = (self.decode_data << 1) | 0;
self.decode_count_bit += 1;
self.step = DecoderStep::SaveDuration;
} else if duration_diff!(self.te_last, TE_LONG) < TE_DELTA
&& duration_diff!(duration, TE_LONG) < TE_DELTA
{
// Bit 1
self.decode_data = (self.decode_data << 1) | 1;
self.decode_count_bit += 1;
self.step = DecoderStep::SaveDuration;
} else {
self.step = DecoderStep::Reset;
}
} else {
self.step = DecoderStep::Reset;
}
}
}
None
}
fn supports_encoding(&self) -> bool {
false
}
fn encode(&self, _decoded: &DecodedSignal, _button: u8) -> Option<Vec<LevelDuration>> {
None
}
}
+269
View File
@@ -0,0 +1,269 @@
//! Star Line protocol decoder/encoder
//!
//! Ported from protopirate's star_line.c
//!
//! Protocol characteristics:
//! - PWM encoding: 250/500µs timing
//! - 64 bits total
//! - Header: 6 pairs of 1000µs HIGH + 1000µs LOW
//! - KeeLoq encryption (requires manufacturer key)
use super::keeloq_common::{keeloq_decrypt, keeloq_encrypt, keeloq_normal_learning, reverse_key};
use super::{DecodedSignal, ProtocolDecoder, ProtocolTiming};
use crate::duration_diff;
use crate::radio::demodulator::LevelDuration;
const TE_SHORT: u32 = 250;
const TE_LONG: u32 = 500;
const TE_DELTA: u32 = 120;
const MIN_COUNT_BIT: usize = 64;
const HEADER_DURATION: u32 = 1000; // te_long * 2
/// Decoder states
#[derive(Debug, Clone, Copy, PartialEq)]
enum DecoderStep {
Reset,
CheckPreamble,
SaveDuration,
CheckDuration,
}
/// Star Line protocol decoder
pub struct StarLineDecoder {
step: DecoderStep,
te_last: u32,
header_count: u16,
decode_data: u64,
decode_count_bit: usize,
}
impl StarLineDecoder {
pub fn new() -> Self {
Self {
step: DecoderStep::Reset,
te_last: 0,
header_count: 0,
decode_data: 0,
decode_count_bit: 0,
}
}
/// Get manufacturer key (placeholder)
fn get_mf_key() -> u64 {
0x0000000000000000
}
fn parse_data(data: u64) -> DecodedSignal {
// Data is stored MSB-first in the air, reverse to get fix|hop
let reversed = reverse_key(data, MIN_COUNT_BIT);
let key_fix = (reversed >> 32) as u32;
let key_hop = (reversed & 0xFFFFFFFF) as u32;
let serial = key_fix & 0x00FFFFFF;
let btn = (key_fix >> 24) as u8;
// Attempt KeeLoq decryption
let mf_key = Self::get_mf_key();
let counter = if mf_key != 0 {
// Try simple learning first
let decrypt = keeloq_decrypt(key_hop, mf_key);
let dec_btn = (decrypt >> 24) as u8;
let dec_serial_lsb = ((decrypt >> 16) & 0xFF) as u8;
let serial_lsb = (serial & 0xFF) as u8;
if dec_btn == btn && dec_serial_lsb == serial_lsb {
Some((decrypt & 0xFFFF) as u16)
} else {
// Try normal learning
let man_key = keeloq_normal_learning(key_fix, mf_key);
let decrypt = keeloq_decrypt(key_hop, man_key);
let dec_btn = (decrypt >> 24) as u8;
let dec_serial_lsb = ((decrypt >> 16) & 0xFF) as u8;
if dec_btn == btn && dec_serial_lsb == serial_lsb {
Some((decrypt & 0xFFFF) as u16)
} else {
None
}
}
} else {
None
};
DecodedSignal {
serial: Some(serial),
button: Some(btn),
counter: counter.or(Some(0)),
crc_valid: counter.is_some() || mf_key == 0,
data,
data_count_bit: MIN_COUNT_BIT,
encoder_capable: true,
}
}
}
impl ProtocolDecoder for StarLineDecoder {
fn name(&self) -> &'static str {
"Star Line"
}
fn timing(&self) -> ProtocolTiming {
ProtocolTiming {
te_short: TE_SHORT,
te_long: TE_LONG,
te_delta: TE_DELTA,
min_count_bit: MIN_COUNT_BIT,
}
}
fn supported_frequencies(&self) -> &[u32] {
&[433_920_000]
}
fn reset(&mut self) {
self.step = DecoderStep::Reset;
self.te_last = 0;
self.header_count = 0;
self.decode_data = 0;
self.decode_count_bit = 0;
}
fn feed(&mut self, level: bool, duration: u32) -> Option<DecodedSignal> {
match self.step {
DecoderStep::Reset => {
if level {
if duration_diff!(duration, HEADER_DURATION) < TE_DELTA * 2 {
self.step = DecoderStep::CheckPreamble;
self.header_count += 1;
} else if self.header_count > 4 {
self.decode_data = 0;
self.decode_count_bit = 0;
self.te_last = duration;
self.step = DecoderStep::CheckDuration;
}
} else {
self.header_count = 0;
}
}
DecoderStep::CheckPreamble => {
if !level && duration_diff!(duration, HEADER_DURATION) < TE_DELTA * 2 {
// Found preamble pair
self.step = DecoderStep::Reset;
} else {
self.header_count = 0;
self.step = DecoderStep::Reset;
}
}
DecoderStep::SaveDuration => {
if level {
if duration >= (TE_LONG + TE_DELTA) {
// End of data - check if we have enough bits
self.step = DecoderStep::Reset;
if self.decode_count_bit >= MIN_COUNT_BIT
&& self.decode_count_bit <= MIN_COUNT_BIT + 2
{
let result = Self::parse_data(self.decode_data);
self.decode_data = 0;
self.decode_count_bit = 0;
self.header_count = 0;
return Some(result);
}
self.decode_data = 0;
self.decode_count_bit = 0;
self.header_count = 0;
} else {
self.te_last = duration;
self.step = DecoderStep::CheckDuration;
}
} else {
self.step = DecoderStep::Reset;
}
}
DecoderStep::CheckDuration => {
if !level {
if duration_diff!(self.te_last, TE_SHORT) < TE_DELTA
&& duration_diff!(duration, TE_SHORT) < TE_DELTA
{
// Bit 0: short HIGH + short LOW
if self.decode_count_bit < MIN_COUNT_BIT {
self.decode_data = (self.decode_data << 1) | 0;
self.decode_count_bit += 1;
} else {
self.decode_count_bit += 1;
}
self.step = DecoderStep::SaveDuration;
} else if duration_diff!(self.te_last, TE_LONG) < TE_DELTA
&& duration_diff!(duration, TE_LONG) < TE_DELTA
{
// Bit 1: long HIGH + long LOW
if self.decode_count_bit < MIN_COUNT_BIT {
self.decode_data = (self.decode_data << 1) | 1;
self.decode_count_bit += 1;
} else {
self.decode_count_bit += 1;
}
self.step = DecoderStep::SaveDuration;
} else {
self.step = DecoderStep::Reset;
}
} else {
self.step = DecoderStep::Reset;
}
}
}
None
}
fn supports_encoding(&self) -> bool {
true
}
fn encode(&self, decoded: &DecodedSignal, button: u8) -> Option<Vec<LevelDuration>> {
let serial = decoded.serial?;
let counter = decoded.counter.unwrap_or(0).wrapping_add(1);
let fix = ((button as u32) << 24) | (serial & 0x00FFFFFF);
let plaintext = ((button as u32) << 24)
| (((serial & 0xFF) as u32) << 16)
| (counter as u32);
let mf_key = Self::get_mf_key();
let hop = if mf_key != 0 {
keeloq_encrypt(plaintext, mf_key)
} else {
// Without a key, replay the original hop
let reversed = reverse_key(decoded.data, MIN_COUNT_BIT);
(reversed & 0xFFFFFFFF) as u32
};
let yek = ((fix as u64) << 32) | (hop as u64);
let data = reverse_key(yek, MIN_COUNT_BIT);
let mut signal = Vec::with_capacity(256);
// Header: 6 pairs of LONG*2 HIGH + LONG*2 LOW
for _ in 0..6 {
signal.push(LevelDuration::new(true, HEADER_DURATION));
signal.push(LevelDuration::new(false, HEADER_DURATION));
}
// Data: 64 bits, MSB first
for bit in (0..64).rev() {
if (data >> bit) & 1 == 1 {
// Bit 1: LONG HIGH + LONG LOW
signal.push(LevelDuration::new(true, TE_LONG));
signal.push(LevelDuration::new(false, TE_LONG));
} else {
// Bit 0: SHORT HIGH + SHORT LOW
signal.push(LevelDuration::new(true, TE_SHORT));
signal.push(LevelDuration::new(false, TE_SHORT));
}
}
Some(signal)
}
}
+323
View File
@@ -0,0 +1,323 @@
//! Subaru protocol decoder
//!
//! Ported from protopirate's subaru.c
//!
//! Protocol characteristics:
//! - PWM encoding: short HIGH (800µs) = 1, long HIGH (1600µs) = 0
//! - 64 bits total
//! - Long preamble of 1600µs pulses
//! - Gap and sync pattern
//! - Complex counter encoding
use super::{ProtocolDecoder, ProtocolTiming, DecodedSignal};
use crate::radio::demodulator::LevelDuration;
use crate::duration_diff;
const TE_SHORT: u32 = 800;
const TE_LONG: u32 = 1600;
const TE_DELTA: u32 = 200;
#[allow(dead_code)]
const MIN_COUNT_BIT: usize = 64;
const GAP_US: u32 = 2800;
const SYNC_US: u32 = 2800;
/// Decoder states
#[derive(Debug, Clone, Copy, PartialEq)]
enum DecoderStep {
Reset,
CheckPreamble,
FoundGap,
FoundSync,
SaveDuration,
CheckDuration,
}
/// Subaru protocol decoder
pub struct SubaruDecoder {
step: DecoderStep,
te_last: u32,
header_count: u16,
data: [u8; 8],
bit_count: usize,
}
impl SubaruDecoder {
pub fn new() -> Self {
Self {
step: DecoderStep::Reset,
te_last: 0,
header_count: 0,
data: [0u8; 8],
bit_count: 0,
}
}
/// Add a bit to the data buffer
fn add_bit(&mut self, bit: bool) {
if self.bit_count < 64 {
let byte_idx = self.bit_count / 8;
let bit_idx = 7 - (self.bit_count % 8);
if bit {
self.data[byte_idx] |= 1 << bit_idx;
} else {
self.data[byte_idx] &= !(1 << bit_idx);
}
self.bit_count += 1;
}
}
/// Decode the counter from the complex Subaru encoding
fn decode_counter(kb: &[u8; 8]) -> u16 {
let mut lo: u8 = 0;
if (kb[4] & 0x40) == 0 { lo |= 0x01; }
if (kb[4] & 0x80) == 0 { lo |= 0x02; }
if (kb[5] & 0x01) == 0 { lo |= 0x04; }
if (kb[5] & 0x02) == 0 { lo |= 0x08; }
if (kb[6] & 0x01) == 0 { lo |= 0x10; }
if (kb[6] & 0x02) == 0 { lo |= 0x20; }
if (kb[5] & 0x40) == 0 { lo |= 0x40; }
if (kb[5] & 0x80) == 0 { lo |= 0x80; }
let mut reg_sh1 = (kb[7] << 4) & 0xF0;
if kb[5] & 0x04 != 0 { reg_sh1 |= 0x04; }
if kb[5] & 0x08 != 0 { reg_sh1 |= 0x08; }
if kb[6] & 0x80 != 0 { reg_sh1 |= 0x02; }
if kb[6] & 0x40 != 0 { reg_sh1 |= 0x01; }
let reg_sh2 = ((kb[6] << 2) & 0xF0) | ((kb[7] >> 4) & 0x0F);
let mut ser0 = kb[3];
let mut ser1 = kb[1];
let mut ser2 = kb[2];
let total_rot = 4 + lo;
for _ in 0..total_rot {
let t_bit = (ser0 >> 7) & 1;
ser0 = ((ser0 << 1) & 0xFE) | ((ser1 >> 7) & 1);
ser1 = ((ser1 << 1) & 0xFE) | ((ser2 >> 7) & 1);
ser2 = ((ser2 << 1) & 0xFE) | t_bit;
}
let t1 = ser1 ^ reg_sh1;
let t2 = ser2 ^ reg_sh2;
let mut hi: u8 = 0;
if (t1 & 0x10) == 0 { hi |= 0x04; }
if (t1 & 0x20) == 0 { hi |= 0x08; }
if (t2 & 0x80) == 0 { hi |= 0x02; }
if (t2 & 0x40) == 0 { hi |= 0x01; }
if (t1 & 0x01) == 0 { hi |= 0x40; }
if (t1 & 0x02) == 0 { hi |= 0x80; }
if (t2 & 0x08) == 0 { hi |= 0x20; }
if (t2 & 0x04) == 0 { hi |= 0x10; }
((hi as u16) << 8) | (lo as u16)
}
/// Process the decoded data
fn process_data(&self) -> Option<DecodedSignal> {
if self.bit_count < 64 {
return None;
}
let b = &self.data;
// Build 64-bit key
let key = ((b[0] as u64) << 56) | ((b[1] as u64) << 48) |
((b[2] as u64) << 40) | ((b[3] as u64) << 32) |
((b[4] as u64) << 24) | ((b[5] as u64) << 16) |
((b[6] as u64) << 8) | (b[7] as u64);
let serial = ((b[1] as u32) << 16) | ((b[2] as u32) << 8) | (b[3] as u32);
let button = b[0] & 0x0F;
let counter = Self::decode_counter(&self.data);
Some(DecodedSignal {
serial: Some(serial),
button: Some(button),
counter: Some(counter),
crc_valid: true, // Subaru doesn't use CRC
data: key,
data_count_bit: 64,
encoder_capable: true,
})
}
}
impl ProtocolDecoder for SubaruDecoder {
fn name(&self) -> &'static str {
"Subaru"
}
fn timing(&self) -> ProtocolTiming {
ProtocolTiming {
te_short: TE_SHORT,
te_long: TE_LONG,
te_delta: TE_DELTA,
min_count_bit: MIN_COUNT_BIT,
}
}
fn supported_frequencies(&self) -> &[u32] {
&[433_920_000]
}
fn reset(&mut self) {
self.step = DecoderStep::Reset;
self.te_last = 0;
self.header_count = 0;
self.data = [0u8; 8];
self.bit_count = 0;
}
fn feed(&mut self, level: bool, duration: u32) -> Option<DecodedSignal> {
match self.step {
DecoderStep::Reset => {
if level && duration_diff!(duration, TE_LONG) < TE_DELTA {
self.step = DecoderStep::CheckPreamble;
self.te_last = duration;
self.header_count = 1;
}
}
DecoderStep::CheckPreamble => {
if !level {
if duration_diff!(duration, TE_LONG) < TE_DELTA {
self.header_count += 1;
} else if duration > 2000 && duration < 3500 {
// Gap detected
if self.header_count > 20 {
self.step = DecoderStep::FoundGap;
} else {
self.step = DecoderStep::Reset;
}
} else {
self.step = DecoderStep::Reset;
}
} else {
if duration_diff!(duration, TE_LONG) < TE_DELTA {
self.te_last = duration;
self.header_count += 1;
} else {
self.step = DecoderStep::Reset;
}
}
}
DecoderStep::FoundGap => {
if level && duration > 2000 && duration < 3500 {
self.step = DecoderStep::FoundSync;
} else {
self.step = DecoderStep::Reset;
}
}
DecoderStep::FoundSync => {
if !level && duration_diff!(duration, TE_LONG) < TE_DELTA {
self.step = DecoderStep::SaveDuration;
self.bit_count = 0;
self.data = [0u8; 8];
} else {
self.step = DecoderStep::Reset;
}
}
DecoderStep::SaveDuration => {
if level {
if duration_diff!(duration, TE_SHORT) < TE_DELTA {
// Short HIGH = bit 1
self.add_bit(true);
self.te_last = duration;
self.step = DecoderStep::CheckDuration;
} else if duration_diff!(duration, TE_LONG) < TE_DELTA {
// Long HIGH = bit 0
self.add_bit(false);
self.te_last = duration;
self.step = DecoderStep::CheckDuration;
} else if duration > 3000 {
// End of transmission
if self.bit_count >= 64 {
let result = self.process_data();
self.step = DecoderStep::Reset;
return result;
}
self.step = DecoderStep::Reset;
} else {
self.step = DecoderStep::Reset;
}
} else {
self.step = DecoderStep::Reset;
}
}
DecoderStep::CheckDuration => {
if !level {
if duration_diff!(duration, TE_SHORT) < TE_DELTA ||
duration_diff!(duration, TE_LONG) < TE_DELTA {
self.step = DecoderStep::SaveDuration;
} else if duration > 3000 {
// Gap - end of packet
if self.bit_count >= 64 {
let result = self.process_data();
self.step = DecoderStep::Reset;
return result;
}
self.step = DecoderStep::Reset;
} else {
self.step = DecoderStep::Reset;
}
} else {
self.step = DecoderStep::Reset;
}
}
}
None
}
fn supports_encoding(&self) -> bool {
true
}
fn encode(&self, decoded: &DecodedSignal, _button: u8) -> Option<Vec<LevelDuration>> {
let key = decoded.data;
let mut signal = Vec::with_capacity(512);
// Generate 3 bursts
for burst in 0..3 {
if burst > 0 {
signal.push(LevelDuration::new(false, 25000));
}
// Preamble: 80 long HIGH/LOW pairs
for _ in 0..80 {
signal.push(LevelDuration::new(true, TE_LONG));
signal.push(LevelDuration::new(false, TE_LONG));
}
// Gap
signal.push(LevelDuration::new(false, GAP_US));
// Sync
signal.push(LevelDuration::new(true, SYNC_US));
signal.push(LevelDuration::new(false, TE_LONG));
// Data: 64 bits (MSB first)
// Short HIGH = 1, Long HIGH = 0
for bit in (0..64).rev() {
if (key >> bit) & 1 == 1 {
signal.push(LevelDuration::new(true, TE_SHORT));
} else {
signal.push(LevelDuration::new(true, TE_LONG));
}
signal.push(LevelDuration::new(false, TE_SHORT));
}
// End marker
signal.push(LevelDuration::new(false, TE_LONG * 2));
}
Some(signal)
}
}
+203
View File
@@ -0,0 +1,203 @@
//! Suzuki protocol decoder/encoder
//!
//! Ported from protopirate's suzuki.c
//!
//! Protocol characteristics:
//! - PWM encoding: 250/500µs timing
//! - 64 bits total
//! - 350 preamble pairs
//! - 2000µs gap between transmissions
use super::{DecodedSignal, ProtocolDecoder, ProtocolTiming};
use crate::duration_diff;
use crate::radio::demodulator::LevelDuration;
const TE_SHORT: u32 = 250;
const TE_LONG: u32 = 500;
const TE_DELTA: u32 = 99;
const MIN_COUNT_BIT: usize = 64;
const PREAMBLE_COUNT: u16 = 350;
const GAP_TIME: u32 = 2000;
const GAP_DELTA: u32 = 399;
/// Decoder states
#[derive(Debug, Clone, Copy, PartialEq)]
enum DecoderStep {
Reset,
CountPreamble,
DecodeData,
}
/// Suzuki protocol decoder
pub struct SuzukiDecoder {
step: DecoderStep,
header_count: u16,
decode_data: u64,
decode_count_bit: usize,
te_last: u32,
}
impl SuzukiDecoder {
pub fn new() -> Self {
Self {
step: DecoderStep::Reset,
header_count: 0,
decode_data: 0,
decode_count_bit: 0,
te_last: 0,
}
}
fn add_bit(&mut self, bit: u8) {
self.decode_data = (self.decode_data << 1) | (bit as u64);
self.decode_count_bit += 1;
}
fn parse_data(data: u64) -> DecodedSignal {
let data_high = (data >> 32) as u32;
let data_low = data as u32;
let serial = ((data_high & 0xFFF) << 16) | (data_low >> 16);
let btn = ((data_low >> 12) & 0xF) as u8;
let cnt = ((data_high << 4) >> 16) as u16;
DecodedSignal {
serial: Some(serial),
button: Some(btn),
counter: Some(cnt),
crc_valid: true, // CRC checked via structure
data,
data_count_bit: MIN_COUNT_BIT,
encoder_capable: true,
}
}
}
impl ProtocolDecoder for SuzukiDecoder {
fn name(&self) -> &'static str {
"Suzuki"
}
fn timing(&self) -> ProtocolTiming {
ProtocolTiming {
te_short: TE_SHORT,
te_long: TE_LONG,
te_delta: TE_DELTA,
min_count_bit: MIN_COUNT_BIT,
}
}
fn supported_frequencies(&self) -> &[u32] {
&[433_920_000]
}
fn reset(&mut self) {
self.step = DecoderStep::Reset;
self.header_count = 0;
self.decode_data = 0;
self.decode_count_bit = 0;
self.te_last = 0;
}
fn feed(&mut self, level: bool, duration: u32) -> Option<DecodedSignal> {
match self.step {
DecoderStep::Reset => {
if !level {
return None;
}
if duration_diff!(duration, TE_SHORT) > TE_DELTA {
return None;
}
self.decode_data = 0;
self.decode_count_bit = 0;
self.step = DecoderStep::CountPreamble;
self.header_count = 0;
}
DecoderStep::CountPreamble => {
if level {
// HIGH pulse
if self.header_count >= 300 {
if duration_diff!(duration, TE_LONG) <= TE_DELTA {
self.step = DecoderStep::DecodeData;
self.add_bit(1);
}
}
} else {
// LOW pulse
if duration_diff!(duration, TE_SHORT) <= TE_DELTA {
self.te_last = duration;
self.header_count += 1;
} else {
self.step = DecoderStep::Reset;
}
}
}
DecoderStep::DecodeData => {
if level {
// HIGH pulse - determines bit value
let diff_long = duration_diff!(duration, TE_LONG);
let diff_short = duration_diff!(duration, TE_SHORT);
if diff_long <= TE_DELTA {
self.add_bit(1);
} else if diff_short <= TE_DELTA {
self.add_bit(0);
}
} else {
// LOW pulse - check for gap (end of transmission)
let diff_gap = duration_diff!(duration, GAP_TIME);
if diff_gap <= GAP_DELTA {
if self.decode_count_bit == MIN_COUNT_BIT {
let result = Self::parse_data(self.decode_data);
self.decode_data = 0;
self.decode_count_bit = 0;
self.step = DecoderStep::Reset;
return Some(result);
}
self.decode_data = 0;
self.decode_count_bit = 0;
self.step = DecoderStep::Reset;
}
}
}
}
None
}
fn supports_encoding(&self) -> bool {
true
}
fn encode(&self, decoded: &DecodedSignal, _button: u8) -> Option<Vec<LevelDuration>> {
let data = decoded.data;
let mut signal = Vec::with_capacity(1024);
// Preamble: SHORT HIGH / SHORT LOW pairs
for _ in 0..PREAMBLE_COUNT {
signal.push(LevelDuration::new(true, TE_SHORT));
signal.push(LevelDuration::new(false, TE_SHORT));
}
// Data: 64 bits, MSB first
// SHORT HIGH (~250µs) = 0, LONG HIGH (~500µs) = 1
for bit in (0..64).rev() {
if (data >> bit) & 1 == 1 {
signal.push(LevelDuration::new(true, TE_LONG));
} else {
signal.push(LevelDuration::new(true, TE_SHORT));
}
signal.push(LevelDuration::new(false, TE_SHORT));
}
// End gap
signal.push(LevelDuration::new(false, GAP_TIME));
Some(signal)
}
}
+1137
View File
File diff suppressed because it is too large Load Diff