# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [0.2.0] - 2026-08-11
### Changed
- `ts::ts_payload_mut` now decodes `adaptation_field_control` via
`mpeg_ts::ts::TsHeader::parse` (a new dependency on `mpeg-ts`) instead of
hand-rolling the same AFC bit decode — a duplication-audit finding.
Magic-number `188`/`0x3f`/`0x80` replaced with named,
spec-cited constants (`mpeg_ts::ts::TS_PACKET_SIZE`/`SCRAMBLING_MASK` plus
a local `TSC_EVEN_KEY`). The payload byte-offset computation and the
`&mut` slicing itself stay hand-rolled, with a comment explaining why:
`mpeg_ts::ts::TsPacket` only exposes an immutable `payload: &[u8]`, and
CSA (de)scrambling needs to write back into the caller's own buffer.
Identical behaviour; no public API change.
- MSRV raised to **1.95.0** (issue #949). This removes the workspace's MSRV
split: `webrtc-runtime`'s optional `media` feature needed rustc 1.88 (via
`rcgen`), which had grown a dedicated CI job, six `--exclude` lanes and a
guard script to contain. Adopting let-chains and `is_multiple_of` where the
1.95 lints require them; no functional or API change.
## [0.1.2] - 2026-08-08
`0.1.1` was never separately published (no such version exists on crates.io,
no `dvb-csa-v0.1.1` tag) — its doc-accuracy fix is folded into this release
below, alongside the new bitsliced feature.
### Fixed
- **Documentation corrected — the published 0.1.0 advertised two things that
do not exist.** No code or behaviour change; the cipher is unaffected.
- The crates.io `description` claimed "a bitsliced fast path", and the
crate-root docs described a `bitsliced` feature "differentially tested
against the scalar reference". There is no such feature and no such code.
Both claims removed; the docs now say a bitsliced path is unimplemented.
- The crate-root docs listed a **TSDuck** scrambled-capture oracle as part
of "the gate". The fixture is committed but the control word it was
scrambled with was never recorded, so it cannot be decrypted and no test
references it. The claim is removed and the situation is now recorded in
`tests/fixtures/PROVENANCE.md`, including how to make it a real oracle.
- The remaining oracle — byte-exact known-answer vectors from libdvbcsa
1.1.0, in `tests/golden_vectors.rs` — is genuine and unchanged.
### Added
- **`bitsliced` feature — a real bitsliced fast path**, restoring (and now
earning) the claim the documentation fix above had to strip. Off by
default; purely additive, so no existing API changes and nothing breaks.
- `dvb_csa::bitsliced::{scramble_batch, descramble_batch, LANES}` process up
to `LANES` (64) **independent payloads** per pass, transposing the data and
evaluating the cipher as a branch-free boolean circuit so every gate acts
on all 64 lanes at once.
- **The unit of parallelism is the payload, not the block** — deliberately.
Scrambling's block cipher is a reverse CBC (`C[i] = E(P[i] ^ C[i+1])`) and
the stream cipher is a chained LFSR; both are strictly sequential within
one payload. Only descrambling's block half (`P[i] = D(C[i]) ^ C[i+1]`) is
independent per block, and the stream cipher — the sequential part — is
~2/3 of the work. So there is no bitsliced single-payload entry point: for
one payload there is nothing worth slicing.
- Measured on an Apple M2 Ultra (rustc 1.86.0), 64 x 184-byte TS payloads:
scramble **15.7 -> 91.3 MiB/s (5.8x)**, descramble **15.1 -> 99.8 MiB/s
(6.6x)**. `benches/throughput.rs` now runs both paths over the same batch.
- `no_std`: the fast path allocates nothing and uses no `std`.
- Circuits are generated by `tools/gen_circuits.py` from `src/tables.rs` —
the tables stay the single source of truth. The generator synthesises each
table as the cheaper of a shared ROBDD or an algebraic normal form (block
S-box 402 gates, the seven stream S-boxes 153, `STREAM_CDEF` 58), and
proves the linear tables linear rather than assuming it: `PERM` is a bit
permutation and so costs nothing bitsliced, and the S-box index selection,
`csa_stream_b_sel` and `STREAM_OUT` reduce to XOR trees.
- Three independent correctness gates: every generated circuit is checked
against its source table over its **entire** input domain
(`src/bitsliced/circuit_tests.rs`); `tests/bitsliced_differential.rs`
requires byte-identical agreement with the scalar path over randomised
payloads, batch sizes straddling `LANES`, and lengths covering the
pass-through (<8 B), single-block, non-multiple-of-8 and long cases, in
both directions; and `tests/golden_vectors.rs` now drives the libdvbcsa
known-answer vectors through the batch path too — each planted in a
different lane of a batch of unrelated decoys — so the fast path answers
to the external oracle, not merely to our own scalar code.
## [0.1.0] - 2026-08-07
### Added
- Initial `dvb-csa` crate: pure-Rust DVB Common Scrambling Algorithm (CSA2).
- 56-round block cipher (SPN on 8-byte blocks) with key-permutation schedule.
- LFSR stream cipher with dual 40-bit shift registers and S-box feedback.
- CBC-like chaining combining block + stream ciphers.
- `scramble()` / `descramble()` operating on raw payload bytes.
- `ControlWord` key type with `expand_block()` and `expand_stream()` derivations.
- `ts` module for TS-packet-level scramble/descramble (payload extraction).
- Oracle validation against libdvbcsa 1.1.0: 11 golden vectors (184-byte
payloads) + 4 inline multi-size vectors (8B, 16B, 32B, 64B).
- Criterion benchmarks for 184-byte scramble/descramble throughput.
- CLI examples: `scramble_file`, `descramble_file`.
- `no_std` support (default); optional `std` feature.