Skip to main content

dvb_simulcrypt/
lib.rs

1//! DVB SimulCrypt — head-end CA message framing (ETSI TS 103 197 V1.5.1).
2//!
3//! The DVB SimulCrypt head-end carries control/response messages between its
4//! conditional-access components over TCP. This crate is a **codec for those
5//! messages** — it does not open sockets. It implements the generic message
6//! structure and the two CA-bearing interfaces:
7//!
8//! - [`SimulcryptMessage`] — the generic `generic_message` (TS 103 197 §4.4.1,
9//!   Table 1b): a 5-byte header (`protocol_version` + `message_type` +
10//!   `message_length`, big-endian) followed by an ordered list of TLV
11//!   [`Parameter`]s (`parameter_type` + `parameter_length` + value).
12//!   `message_length` and every `parameter_length` are **recomputed on
13//!   serialize** from the typed fields — there is no raw passthrough.
14//! - **ECMG ⇔ SCS** (clause 5): [`EcmgScsMessageType`] (channel/stream setup,
15//!   test, status, close, error, plus `CW_provision` `0x0201` and
16//!   `ECM_response` `0x0202`) and the Table 5 [`EcmgScsParameterType`] registry
17//!   (`Super_CAS_id` `0x0001` … `ECM_id` `0x0019`, `error_status` `0x7000`,
18//!   `error_information` `0x7001`) + [`EcmgErrorStatus`] (Table 6).
19//! - **EMMG/PDG ⇔ MUX** (clause 6): [`EmmgMuxMessageType`] (channel/stream
20//!   messages, `stream_BW_request`/`allocation`, `data_provision` `0x0211`) and
21//!   the Table 7 [`EmmgMuxParameterType`] registry + [`EmmgErrorStatus`]
22//!   (Table 8), plus the [`DataType`] (§6.2.3) and [`SectionTspktFlag`] value
23//!   tables.
24//! - **C(P)SIG ⇔ (P)SIG** (clause 8): [`CpSigMessageType`] (channel/stream
25//!   messages, trigger/table/descriptor/PID‑provision messages `0x0301`–`0x0321`)
26//!   and the Table 36 [`CpSigParameterType`] registry (`bouquet_id` `0x0100` …
27//!   `flow_stream_type` `0x012C`, `error_status` `0x7000`, `error_information`
28//!   `0x7001`).
29//!
30//! # Interface scoping
31//!
32//! The 16-bit `message_type`/`parameter_type` spaces are **interface-scoped**:
33//! the same value means different things on different interfaces, and the
34//! interface is not on the wire — it is fixed by which TCP connection the
35//! message arrived on (analogous to a resource scope). So
36//! [`SimulcryptMessage::parse_on`] takes an [`Interface`] hint and decodes the
37//! raw values into the matching interface-tagged [`MessageType`] /
38//! [`ParameterType`] enums.
39//!
40//! # Signalling only — no crypto
41//!
42//! The control words in `CP_CW_combination`/`CW_encryption`, the ECMs in
43//! `ECM_datagram`, and the EMM/private data in `datagram` are carried as
44//! **opaque borrowed bytes**. This crate frames and parses them; it never
45//! decrypts or interprets them. The non-implemented interfaces (C(P)SIG⇔(P)SIG,
46//! EIS⇔SCS, (P)SIG⇔MUX, ACG⇔EIS, SIMCOMP⇔MUXCONFIG) share the same framing but
47//! are not modelled.
48//!
49//! `#![no_std]` + `alloc`; depends only on `broadcast-common`.
50//!
51//! # Examples
52//!
53//! Build an ECMG⇔SCS `channel_setup` from typed fields and round-trip it:
54//!
55//! ```
56//! use dvb_simulcrypt::{
57//!     EcmgScsMessageType, EcmgScsParameterType, Interface, MessageType, Parameter,
58//!     ParameterType, SimulcryptMessage,
59//! };
60//! use broadcast_common::traits::{Parse, Serialize};
61//!
62//! let ecm_channel_id = [0x00, 0x2A]; // 0x002A
63//! let super_cas_id = [0x00, 0x01, 0x00, 0x02]; // CA_system_id | subsystem_id
64//! let msg = SimulcryptMessage::new(
65//!     Interface::EcmgScs.protocol_version(),
66//!     MessageType::EcmgScs(EcmgScsMessageType::ChannelSetup),
67//!     vec![
68//!         Parameter::new(
69//!             ParameterType::EcmgScs(EcmgScsParameterType::EcmChannelId),
70//!             &ecm_channel_id,
71//!         ),
72//!         Parameter::new(
73//!             ParameterType::EcmgScs(EcmgScsParameterType::SuperCasId),
74//!             &super_cas_id,
75//!         ),
76//!     ],
77//! );
78//!
79//! let mut buf = vec![0u8; msg.serialized_len()];
80//! msg.serialize_into(&mut buf).unwrap();
81//! assert_eq!(SimulcryptMessage::parse_on(Interface::EcmgScs, &buf).unwrap(), msg);
82//! ```
83#![no_std]
84#![cfg_attr(docsrs, feature(doc_cfg))]
85#![warn(missing_docs)]
86// Runnable examples, embedded so they render on docs.rs and stay in sync with
87// the actual `examples/*.rs` files (shown, not compiled).
88#![doc = "\n## Runnable examples\n"]
89#![doc = "Run with `cargo run -p dvb-simulcrypt --example <name>`.\n"]
90#![doc = "\n### `build_channel_setup`\n\n```rust,ignore"]
91#![doc = include_str!("../examples/build_channel_setup.rs")]
92#![doc = "```\n\n### `parse_cw_provision`\n\n```rust,ignore"]
93#![doc = include_str!("../examples/parse_cw_provision.rs")]
94#![doc = "```"]
95
96extern crate alloc;
97
98mod error;
99mod message;
100mod registry;
101
102pub use error::{Error, Result};
103pub use message::{HEADER_LEN, PARAMETER_HEADER_LEN, Parameter, SimulcryptMessage};
104pub use registry::{
105    CpSigMessageType, CpSigParameterType, DataType, EcmgErrorStatus, EcmgScsMessageType,
106    EcmgScsParameterType, EmmgErrorStatus, EmmgMuxMessageType, EmmgMuxParameterType, Interface,
107    MessageType, ParameterType, SectionTspktFlag,
108};