v1.2.0: Add 4 new protocols, fix 5 existing, port Kia V6 encoder

New protocols (from ProtoPirate):
- Mazda V0: 433MHz, pair-based decoding, XOR deobfuscation
- Mitsubishi V0: 868MHz, PWM, bit negation + XOR unscrambling
- Porsche Touareg: 433/868MHz, sync preamble, 24-bit rotate cipher
- Fiat V1 (Magneti Marelli BSI): 433MHz, auto-detected timing variants

Protocol fixes aligned with ProtoPirate reference:
- Kia V1: Fix Manchester level mapping (inverted convention), off-by-one
  bit count, CRC4 simplified to 7 bytes + offset 1
- Kia V2: Fix CRC byte layout (was double-counting nibbles), off-by-one
  bit count, preamble short-HIGH handling
- Fiat V0: Fix endbyte transform (remove (<<1)|1), standard Manchester
  encoder (was differential), 7 btn bits (was 6), gap path fallback
- PSA: Fix modified TEA (XTEA-like dynamic key selection), XOR decrypt
  byte mapping, critical key2_low construction bug, encoder polarity and
  preamble, add key1_high nibble validation, dual preamble patterns
- Ford V0: Add calculate_checksum (sums all serial+count bytes) for encoder

Kia V6 encoder ported:
- Forward AES-128 (SubBytes, ShiftRows, MixColumns, encrypt)
- encrypt_payload: build plaintext, AES encrypt, pack into 3 parts
- Two-pass Manchester upload (640 + 38 preamble pairs)
- fx_field extraction stored in DecodedSignal.extra for encode roundtrip

Updated README, protocol docs, and version bump to 1.2.0.
This commit is contained in:
KaraZajac
2026-03-22 12:05:01 -04:00
parent 84b75ee6b5
commit 48ee413c9b
22 changed files with 2318 additions and 305 deletions
+4
View File
@@ -16,6 +16,10 @@ This folder describes how each keyfob protocol supported by KAT works. Each docu
| Subaru | `subaru.rs` | [subaru.md](subaru.md) |
| VAG | `vag.rs` | [vag.md](vag.md) |
| Fiat V0 | `fiat_v0.rs` | [fiat_v0.md](fiat_v0.md) |
| Fiat V1 | `fiat_v1.rs` | [fiat_v1.md](fiat_v1.md) |
| Mazda V0 | `mazda_v0.rs` | [mazda_v0.md](mazda_v0.md) |
| Mitsubishi V0 | `mitsubishi_v0.rs` | [mitsubishi_v0.md](mitsubishi_v0.md) |
| Porsche Touareg | `porsche_touareg.rs` | [porsche_touareg.md](porsche_touareg.md) |
| Suzuki | `suzuki.rs` | [suzuki.md](suzuki.md) |
| Scher-Khan | `scher_khan.rs` | [scher_khan.md](scher_khan.md) |
| Star Line | `star_line.rs` | [star_line.md](star_line.md) |
+60
View File
@@ -0,0 +1,60 @@
---
layout: default
---
# Fiat V1 Protocol (Magneti Marelli BSI / PCF7946)
**Rust module:** `src/protocols/fiat_v1.rs`
**Reference:** `REFERENCES/ProtoPirate/protocols/fiat_v1.c`
## Overview
Fiat V1 is the Magneti Marelli BSI keyfob protocol (PCF7946), found on Fiat Panda, Grande Punto, and possibly other Fiat/Lancia/Alfa ~2003-2012. Uses Manchester encoding with auto-detected timing from preamble pulse averaging. Two timing variants: Type A (~260 us, e.g. Panda) and Type B (~100 us, e.g. Grande Punto). This is a different protocol from Fiat V0.
## Timing
| Parameter | Default | Notes |
|-----------|---------|-------|
| Short | 260 us | Auto-detected from preamble |
| Long | 520 us | 2x detected TE |
| Delta | 80 us | Minimum 30 us |
| Preamble | 80+ pulses | 50-350 us range |
| Min bits | 80 | Max 104 bits |
### Auto-Detection
Preamble pulses (50-350 us) are accumulated. After 80+ pulses, `te_detected = te_sum / te_count` becomes the reference TE. Type A/B boundary is at 180 us.
## Frame Layout (80-104 bits = 10-13 bytes)
| Bytes | Content |
|-------|---------|
| 0-1 | Preamble residue (0xFFFF/0xFFFC) |
| 2-5 | Serial (32 bits) |
| 6 | Button:4 \| Epoch:4 |
| 7 | Counter:5 \| Scramble:2 \| Fixed:1 |
| 8-12 | Encrypted payload (40 bits) |
## Decoder Steps
1. **Reset** -- Wait for HIGH pulse in 50-350 us range. Also detect retransmission gaps (>5000 us).
2. **Preamble** -- Count pulses, accumulate for TE averaging. On gap >= te_detected * 4, transition to Sync.
3. **Sync** -- Expect HIGH sync pulse of te_detected * 4 to te_detected * 12 duration.
4. **Data** -- Manchester decode up to 104 bits into 13-byte raw buffer.
5. **RetxSync** -- After gap >5000 us, look for sync pulse 400-2800 us (retransmission).
## Buttons
| Code | Name |
|------|--------|
| 0x7 | Lock |
| 0xB | Unlock |
| 0xD | Trunk |
## Encoder
Not supported (decode-only).
## Frequencies
433.92 MHz.
+2 -2
View File
@@ -9,7 +9,7 @@ layout: default
## Overview
Kia V1 uses Manchester encoding at 800/1600 µs. 57 bits total: 32 serial + 8 button + 12 counter + 4 CRC. Long preamble (~90 long pairs). CRC4 with offset rules (cnt_high 0 vs ≥ 6).
Kia V1 uses Manchester encoding at 800/1600 us. 57 bits total: 32 serial + 8 button + 12 counter + 4 CRC. Long preamble (~90 long pairs). CRC4 uses XOR of nibbles over 7 bytes (serial + button + cnt_low + cnt_high) with offset 1.
## Timing
@@ -28,7 +28,7 @@ Manchester: symbol duration short or long; bit value from transition direction.
- 32 bits: serial
- 8 bits: button
- 12 bits: counter
- 4 bits: CRC4 (checksum with offset rules)
- 4 bits: CRC4 (XOR of nibbles, 7 bytes, offset 1)
## Decoder Steps
+2 -2
View File
@@ -30,7 +30,7 @@ Manchester (event mapping 0/2/4/6 for level convention). Three 64/64/16-bit segm
- Part2: 64 bits (inverted)
- Part3: 16 bits (inverted)
AES-128 decrypt with key = f(keystore_a, keystore_b, XOR_MASK_LOW, XOR_MASK_HIGH). Serial/button/counter and CRC extracted after decryption.
AES-128 decrypt with key = f(keystore_a, keystore_b, XOR_MASK_LOW, XOR_MASK_HIGH). Serial/button/counter, CRC, and fx_field extracted after decryption. The fx_field is derived from the top 2 bytes of stored_part1_high and stored in `DecodedSignal.extra`.
## Decoder Steps
@@ -40,7 +40,7 @@ AES-128 decrypt with key = f(keystore_a, keystore_b, XOR_MASK_LOW, XOR_MASK_HIGH
## Encoder
Decode-only in KAT (no encoder in reference).
Supported. Ported from ProtoPirate (`ENABLE_EMULATE_FEATURE`). Builds plaintext (fx_field, serial, button, counter, S-box CRC, CRC8), AES-128 encrypts, packs into 3 parts, then Manchester encodes with two-pass preamble (640 pairs + data, gap, 38 pairs + data). Requires fx_field from decoded signal's `extra` field.
## Frequencies
+73
View File
@@ -0,0 +1,73 @@
---
layout: default
---
# Mazda V0 Protocol
**Rust module:** `src/protocols/mazda_v0.rs`
**Reference:** `REFERENCES/ProtoPirate/protocols/mazda_v0.c`
## Overview
Mazda V0 uses a custom pair-based encoding (not standard Manchester). The `level` parameter is ignored; the decoder processes raw duration pairs. Preamble: 13+ short/short pairs followed by a short+long transition into data. Data bits are collected using a prev_state tracker with inverted polarity, then XOR-deobfuscated, checksum-validated, and parsed.
## Timing
| Parameter | Value | Notes |
|-----------|--------|--------------|
| Short | 250 us | +/-100 us |
| Long | 500 us | +/-100 us |
| Min bits | 64 | |
| Completion| 80-105 bits | |
## Frame Layout (64 bits after deobfuscation)
Raw data collects into a 14-byte buffer. First byte is discarded (sync); bytes [1..9] form the 8-byte data frame.
After XOR deobfuscation:
- Bytes 0-3: Serial (32 bits)
- Byte 4: Button (8 bits)
- Bytes 5-6: Counter (16 bits)
- Byte 7: Checksum (additive sum of bytes 0-6)
## Pair-Based Bit Decoding
| Pair (te_last, duration) | Action |
|--------------------------|--------|
| Long + Short | Collect bit(0), bit(1), prev_state=1 |
| Short + Long | Collect bit(1), prev_state=0 |
| Short + Short | Collect bit(prev_state) |
| Long + Long | Collect bit(0), bit(1), prev_state=0 |
Bit collection uses inverted polarity: state_bit=0 stores a 1.
## XOR Deobfuscation
1. Compute parity of data[7] (XOR fold to single bit).
2. If parity is odd: XOR bytes [0..6] with data[6] as mask.
3. If parity is even: XOR bytes [0..5] with data[5] as mask, also XOR data[6] with data[5].
4. Bit interleave swap bytes 5 and 6: swap even/odd bit positions between them.
## Decoder Steps
1. **Reset** -- Wait for short pulse; save and go to PreambleCheck.
2. **PreambleSave** -- Save duration, go to PreambleCheck.
3. **PreambleCheck** -- Count short+short pairs; on short+long with count >= 13, start data.
4. **DataSave** -- Save duration, go to DataCheck.
5. **DataCheck** -- Process pair; if valid continue; if invalid, check completion (80-105 bits + checksum).
## Buttons
| Code | Name |
|------|--------|
| 0x10 | Lock |
| 0x20 | Unlock |
| 0x40 | Trunk |
## Encoder
Not supported (decode-only).
## Frequencies
433.92 MHz.
+59
View File
@@ -0,0 +1,59 @@
---
layout: default
---
# Mitsubishi V0 Protocol
**Rust module:** `src/protocols/mitsubishi_v0.rs`
**Reference:** `REFERENCES/ProtoPirate/protocols/mitsubishi_v0.c`
## Overview
Mitsubishi V0 uses PWM encoding at 250/500 us. 96-bit frame (12 bytes). Level-aware: HIGH pulses are saved, LOW pulses complete the pair. After collection, data is unscrambled via bitwise NOT + counter-derived XOR mask.
## Timing
| Parameter | Value | Notes |
|-----------|--------|--------------|
| Short | 250 us | +/-100 us |
| Long | 500 us | +/-100 us |
| Min bits | 80 | |
| Frame | 96 bits| 12 bytes |
## PWM Bit Encoding
| Pair (HIGH, LOW) | Bit |
|------------------|-----|
| Short HIGH + Long LOW | 1 |
| Long HIGH + Short LOW | 0 |
Bits collected MSB-first into a 12-byte buffer.
## Frame Layout (96 bits)
After unscrambling:
- Bytes 0-3: Serial (32 bits, big-endian)
- Bytes 4-5: Counter (16 bits)
- Byte 6: Button (8 bits)
- Bytes 7-11: Remaining payload
## Unscramble Algorithm
1. Bitwise NOT the first 8 bytes.
2. Extract counter from bytes [4..5]: `hi = (counter >> 8) & 0xFF`, `lo = counter & 0xFF`.
3. Compute masks: `mask1 = (hi & 0xAA) | (lo & 0x55)`, `mask2 = (lo & 0xAA) | (hi & 0x55)`, `mask3 = mask1 ^ mask2`.
4. XOR bytes [0..5] with mask3.
## Decoder Steps
1. **Reset** -- Wait for HIGH pulse; save duration.
2. **DataSave** -- On HIGH: save duration. On LOW (unexpected): reset.
3. **DataCheck** -- On LOW: decode pair. If 96 bits collected, unscramble and return. Otherwise continue.
## Encoder
Not supported (decode-only).
## Frequencies
868.35 MHz.
+72
View File
@@ -0,0 +1,72 @@
---
layout: default
---
# Porsche Touareg Protocol
**Rust module:** `src/protocols/porsche_touareg.rs`
**Reference:** `REFERENCES/ProtoPirate/protocols/porsche_touareg.c`
## Overview
Porsche Touareg uses PWM encoding with very long timing (1680/3370 us). 64-bit frame with a sync preamble (at least 15 sync pulses at 3370 us). Counter is recovered via brute-force using a 24-bit rotation cipher. Originally designed for the Porsche Cayenne.
## Timing
| Parameter | Value | Notes |
|------------|----------|--------------|
| Short | 1680 us | +/-500 us |
| Long | 3370 us | +/-500 us |
| Sync | 3370 us | Same as long |
| Gap | 5930 us | +/-500 us |
| Sync min | 15 pulses| |
| Min bits | 64 | |
## PWM Bit Encoding
| Pair (LOW, HIGH) | Bit |
|------------------|-----|
| Short LOW + Long HIGH | 0 |
| Long LOW + Short HIGH | 1 |
## Frame Layout (64 bits = 8 bytes)
| Byte | Content |
|------|---------|
| pkt[0] | (button << 4) \| (frame_type & 0x07) |
| pkt[1] | serial bits [23:16] |
| pkt[2] | serial bits [15:8] |
| pkt[3] | serial bits [7:0] |
| pkt[4..7] | Encrypted counter/rolling code |
- **Serial**: 24 bits from pkt[1..3]
- **Button**: 4 bits (pkt[0] >> 4)
- **Frame type**: 3 bits (pkt[0] & 0x07): 0x02=First, 0x01=Cont, 0x04=Final
## Counter Recovery (Brute-Force)
Counter is not in plaintext. The decoder tries counter values 1-256, calling `compute_frame()` for each, and checks if computed bytes [4..7] match received bytes [4..7].
### Compute Frame Algorithm (24-bit Rotate Cipher)
1. Initialize 24-bit rotate register from serial bytes: r_h=b3, r_m=b1, r_l=b2.
2. ROTATE24: circular left shift across 3 bytes (h<-m, m<-l, l<-h).
3. Rotate 4 times + counter_low more times.
4. Compute encrypted bytes a9a/a9b/a9c from rotated values XOR'd with inverted counter bits.
5. Assemble pkt[4..7] using bitfield packing.
## Decoder Steps
1. **Reset** -- Wait for LOW pulse matching sync (3370 us).
2. **Sync** -- Count sync pulses (both HIGH and LOW). When count >= 15 and gap detected (5930 us), transition to GapHigh or GapLow.
3. **GapHigh** -- Expect HIGH gap (5930 us); init data on match.
4. **GapLow** -- Expect LOW gap (5930 us); init data on match.
5. **Data** -- Decode bit pairs (LOW saved, HIGH completes). At 64 bits, parse data and brute-force counter.
## Encoder
Not supported (decode-only).
## Frequencies
433.92 MHz, 868.35 MHz.
+48 -19
View File
@@ -2,46 +2,75 @@
layout: default
---
# PSA (Peugeot/Citroën) Protocol
# PSA (Peugeot/Citroen) Protocol
**Rust module:** `src/protocols/psa.rs`
**Rust module:** `src/protocols/psa.rs`
**Reference:** `REFERENCES/ProtoPirate/protocols/psa.c`
## Overview
PSA uses Manchester at 250/500 µs symbol (125/250 µs sub-symbol for preamble). 128 bits total: key1 (64) + validation (16) + key2/rest (48); decode uses key1 + 16-bit validation. TEA decrypt/encrypt with fixed key schedules; mode 0x23 adds an XOR layer. Two modes: seed 0x23 (TEA + XOR), seed 0xF3/0x36 (TEA, BF2 key schedule).
PSA uses Manchester encoding with dual preamble pattern support. 128 bits total: key1 (64) + validation (16) + key2/rest (48). Modified TEA (XTEA-like) with dynamic key selection (`key[sum & 3]` and `key[(sum >> 11) & 3]`). Two decode modes: mode 0x23 (direct XOR decrypt with checksum validation) and mode 0x36 (TEA with BF1/BF2 key schedules). Brute-force decryption for mode 0x36 is deferred/partial.
## Timing
| Parameter | Value | Notes |
|------------|--------|---------|
| Symbol short | 250 µs | ±100 µs |
| Symbol long | 500 µs | ±100 µs |
| Preamble | 125/250 µs sub-symbols | |
| Parameter | Value | Notes |
|-------------|--------|---------|
| Symbol short | 250 us | +/-100 us |
| Symbol long | 500 us | +/-100 us |
| Preamble P1 | 250 us sub-symbols | Pattern 1 |
| Preamble P2 | 125 us sub-symbols | Pattern 2 |
| End marker | 1000 us (P1), 500 us (P2) | |
| Min bits | 128 | |
## Encoding
## Dual Preamble Patterns
Manchester; preamble uses 125/250 µs; then 250/500 µs symbols. TEA encrypt; mode 0x23 adds XOR.
- **Pattern 1 (250 us):** Preamble uses 250 us sub-symbols; Manchester decode at 250/500 us. Threshold: 70+ transitions.
- **Pattern 2 (125 us):** Preamble uses 125 us sub-symbols; Manchester decode at 125/250 us. Threshold: 69+ transitions.
Pattern type is auto-detected from the first preamble pulse duration.
## Frame Layout (128 bits)
- key1: 64 bits
- validation: 16 bits
- key2/rest: 48 bits
- key1: 64 bits
- validation: 16 bits
- key2/rest: 48 bits
TEA decrypt key1 (and validation); mode 0x23: XOR with BF1 key schedule; mode 0x36: TEA with BF2 key schedule. Serial/button/counter extracted from decrypted key1.
## Encryption
### Modified TEA (XTEA-like)
Uses dynamic key word selection per round:
- Encrypt: `k_idx1 = sum & 3`, then `sum += DELTA`, then `k_idx2 = (sum >> 11) & 3`
- Decrypt: `k_idx2 = (sum >> 11) & 3`, then `sum -= DELTA`, then `k_idx1 = sum & 3`
- Round function: `(key[k_idx] + sum) ^ (((v >> 5) ^ (v << 4)) + v)`
### Mode 0x23 (Direct XOR)
1. Setup byte buffer from key1/key2 (little-endian unpack).
2. Calculate checksum over buffer[2..8] (nibble sum * 16).
3. Validate: `(checksum ^ key2_high_byte) & 0xF0 == 0`.
4. XOR decrypt using `psa_copy_reverse` byte reordering.
5. Extract: serial (24-bit), counter (16-bit), button (4-bit), CRC.
### Mode 0x36 (TEA Brute-Force)
Direct TEA decrypt attempted with BF1 and BF2 key schedules. Full brute-force (16M iterations per schedule) is available in ProtoPirate but not fully ported to KAT.
Key schedules:
- BF1: `[0x4A434915, 0xD6743C2B, 0x1F29D308, 0xE6B79A64]`
- BF2: `[0x4039C240, 0xEDA92CAB, 0x4306C02A, 0x02192A04]`
## Decoder Steps
1. **WaitEdge** — Wait for edge/preamble.
2. **CountPattern** Count preamble pattern (125/250 µs).
3. **DecodeManchester** Manchester decode 128 bits; TEA decrypt; apply mode (0x23 XOR or 0x36); extract fields.
4. **End** — End marker (e.g. 1000 µs); return decode.
1. **WaitEdge (State0)** -- Detect preamble pattern type from first HIGH pulse (250 us -> Pattern 1, 125 us -> Pattern 2).
2. **CountPattern250 (State1)** -- Count 250 us preamble pairs; on long pulse with count > 70, transition to Manchester decode.
3. **DecodeManchester250 (State2)** -- Manchester decode at 250/500 us. End on 1000 us marker or 121+ bits.
4. **CountPattern125 (State3)** -- Count 125 us preamble pairs; on 250 us pulse with count >= 69, transition to Manchester decode.
5. **DecodeManchester125 (State4)** -- Manchester decode at 125/250 us. End on 500 us marker or 121+ bits.
## Encoder
Supported; preamble, Manchester 128 bits, TEA (+ XOR for 0x23).
Supported (mode 0x23 only). XOR encrypt, then modified TEA encrypt with BF1 key schedule. Preamble: 70 cycles of 125/125 us + 250/250 us sync. Manchester encoded key1 (64 bits) + validation (16 bits). End marker: 1000 us LOW.
## Frequencies