dvb_csa/lib.rs
1//! DVB Common Scrambling Algorithm (CSA2) — the cipher underneath conditional
2//! access on DVB-S, DVB-T, and DVB-C.
3//!
4//! # Status: reverse-engineered, not spec-cited
5//!
6//! **DVB-CSA has no public normative specification.** The algorithm was
7//! confidential and licensed through the ETSI custodian; every open
8//! implementation is reverse-engineered. This crate therefore cannot follow
9//! the workspace's usual cite-the-spec-clause discipline. Correctness is
10//! established by agreement with **independent implementations** instead of
11//! a standard reference:
12//!
13//! - **libdvbcsa** 1.1.0 — VideoLAN's reference free implementation.
14//! Committed known-answer vectors: encrypt with libdvbcsa, require
15//! byte-identical output from this crate (and the reverse). See
16//! `tests/golden_vectors.rs`.
17//!
18//! A round-trip test (`descramble(scramble(x)) == x`) proves nothing for a
19//! cipher: it passes for any invertible function. The libdvbcsa known-answer
20//! vectors are the gate.
21//!
22//! A second oracle (a TSDuck-scrambled capture) is **not** currently wired
23//! up: `tests/fixtures/france-tnt-scrambled-0x02d0.ts` is committed, but the
24//! control word it was scrambled with was never recorded, so the fixture
25//! cannot be decrypted and no test references it. See its `PROVENANCE.md`.
26//!
27//! # Algorithm overview
28//!
29//! DVB-CSA2 combines a **block cipher** and a **stream cipher**, both keyed
30//! by the same 8-byte control word:
31//!
32//! - **Block cipher**: 56-round substitution/permutation network on 64-bit
33//! (8-byte) blocks, applied in a CBC-like chained mode across all
34//! complete 8-byte blocks of the payload.
35//! - **Stream cipher**: LFSR-based byte-stream generator seeded from the
36//! nibble-swapped control word and the encrypted first block as IV,
37//! XOR'd with bytes 8..end of the payload.
38//!
39//! The combination order matters:
40//! - **Encrypt**: block-cipher CBC (last block first), then stream-cipher XOR.
41//! - **Decrypt**: stream-cipher XOR first, then block-cipher CBC undo.
42//!
43//! Payloads shorter than 8 bytes are passed through unchanged (per
44//! libdvbcsa's behaviour).
45//!
46//! # Performance
47//!
48//! The scalar path processes one 64-bit block at a time. The optional
49//! `bitsliced` feature adds [`bitsliced`], a batch fast path that transposes
50//! the data and evaluates the cipher as a boolean circuit, so every gate acts
51//! on [`bitsliced::LANES`] (64) payloads at once.
52//!
53//! **The unit of parallelism is the payload, not the block.** CSA2 offers
54//! little independence *within* one payload: scrambling's block cipher is a
55//! reverse CBC (`C[i] = E(P[i] ^ C[i+1])`) and the stream cipher is a chained
56//! LFSR, both strictly sequential. Only descrambling's block half is
57//! independent per block, and the stream cipher — the sequential part — is
58//! about two thirds of the work. So [`bitsliced`] exposes
59//! [`scramble_batch`](bitsliced::scramble_batch) /
60//! [`descramble_batch`](bitsliced::descramble_batch) over up to 64
61//! *independent* payloads, and there is deliberately no bitsliced
62//! single-payload entry point.
63//!
64//! Measured by `benches/throughput.rs` on an Apple M2 Ultra, rustc 1.86.0,
65//! over a batch of 64 x 184-byte TS payloads:
66//!
67//! | Operation | Scalar | Bitsliced | Speed-up |
68//! |------------|---------------|---------------|----------|
69//! | scramble | 15.7 MiB/s | 91.3 MiB/s | **5.8x** |
70//! | descramble | 15.1 MiB/s | 99.8 MiB/s | **6.6x** |
71//!
72//! A batch materially smaller than 64 payloads leaves lanes idle and scales
73//! down accordingly; a single payload gains nothing.
74//!
75//! The bitsliced path is bit-exact with the scalar path — see
76//! [`bitsliced`] for the three gates that hold it there.
77#![cfg_attr(not(feature = "std"), no_std)]
78#![cfg_attr(docsrs, feature(doc_cfg))]
79#![warn(missing_docs)]
80
81#[cfg(feature = "bitsliced")]
82#[cfg_attr(docsrs, doc(cfg(feature = "bitsliced")))]
83pub mod bitsliced;
84mod block;
85pub mod csa;
86pub mod error;
87pub mod key;
88mod stream;
89mod tables;
90pub mod ts;
91
92pub use csa::{descramble, scramble};
93pub use error::Error;
94pub use key::ControlWord;