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