Skip to main content

dvb_ci/objects/
ca_info.rs

1//! CA Info objects — ETSI EN 50221 §8.4.3.1-8.4.3.2, Tables 23-24 (PDF p. 29).
2//!
3//! - `ca_info_enq` (`9F 80 30`, Table 23) — header-only enquiry.
4//! - `ca_info` (`9F 80 31`, Table 24) — list of supported `CA_system_id`s.
5
6use crate::error::{Error, Result};
7use crate::tag::{self, ApduTag};
8use crate::traits::ApduDef;
9use alloc::vec::Vec;
10use broadcast_common::{Parse, Serialize};
11
12/// `ca_info_enq()` — header-only enquiry (Table 23).
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize))]
15pub struct CaInfoEnq;
16
17/// `ca_info()` reply — the `CA_system_id`s this application supports (Table 24).
18#[derive(Debug, Clone, PartialEq, Eq, Default)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize))]
20pub struct CaInfo {
21    /// Supported `CA_system_id` values (ETSI TS 101 162), in wire order.
22    pub ca_system_ids: Vec<u16>,
23}
24
25impl<'a> Parse<'a> for CaInfoEnq {
26    type Error = Error;
27    fn parse(bytes: &'a [u8]) -> Result<Self> {
28        super::parse_empty_apdu(bytes, tag::CA_INFO_ENQ, "ca_info_enq")?;
29        Ok(Self)
30    }
31}
32impl Serialize for CaInfoEnq {
33    type Error = Error;
34    fn serialized_len(&self) -> usize {
35        super::empty_apdu_len()
36    }
37    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
38        super::serialize_empty_apdu(tag::CA_INFO_ENQ, buf)
39    }
40}
41impl ApduDef<'_> for CaInfoEnq {
42    const TAG: ApduTag = tag::CA_INFO_ENQ;
43    const NAME: &'static str = "CA_INFO_ENQ";
44}
45
46impl<'a> Parse<'a> for CaInfo {
47    type Error = Error;
48    fn parse(bytes: &'a [u8]) -> Result<Self> {
49        let body = super::parse_apdu_header(bytes, tag::CA_INFO, "ca_info")?;
50        if body.len() % 2 != 0 {
51            return Err(Error::InvalidObject {
52                what: "ca_info",
53                reason: "body length is not a multiple of 2",
54            });
55        }
56        let ca_system_ids = body
57            .chunks_exact(2)
58            .map(|c| u16::from_be_bytes([c[0], c[1]]))
59            .collect();
60        Ok(Self { ca_system_ids })
61    }
62}
63
64impl Serialize for CaInfo {
65    type Error = Error;
66    fn serialized_len(&self) -> usize {
67        super::apdu_len(self.ca_system_ids.len() * 2)
68    }
69    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
70        let body_len = self.ca_system_ids.len() * 2;
71        let mut pos = super::write_apdu_header(tag::CA_INFO, body_len, buf)?;
72        for id in &self.ca_system_ids {
73            buf[pos..pos + 2].copy_from_slice(&id.to_be_bytes());
74            pos += 2;
75        }
76        Ok(pos)
77    }
78}
79
80impl ApduDef<'_> for CaInfo {
81    const TAG: ApduTag = tag::CA_INFO;
82    const NAME: &'static str = "CA_INFO";
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88
89    #[test]
90    fn enq_round_trip() {
91        let bytes = CaInfoEnq.to_bytes();
92        assert_eq!(bytes, [0x9F, 0x80, 0x30, 0x00]);
93        assert_eq!(CaInfoEnq::parse(&bytes).unwrap(), CaInfoEnq);
94    }
95
96    #[test]
97    fn ca_info_multi_round_trips() {
98        let info = CaInfo {
99            ca_system_ids: alloc::vec![0x0500, 0x0B00, 0x1801],
100        };
101        let bytes = info.to_bytes();
102        assert_eq!(&bytes[..4], &[0x9F, 0x80, 0x31, 0x06]); // len 6
103        let parsed = CaInfo::parse(&bytes).unwrap();
104        assert_eq!(parsed, info);
105        assert_eq!(parsed.ca_system_ids.len(), 3);
106    }
107
108    #[test]
109    fn mutating_id_changes_bytes() {
110        let info = CaInfo {
111            ca_system_ids: alloc::vec![0x0500],
112        };
113        let a = info.to_bytes();
114        let mut other = info.clone();
115        other.ca_system_ids[0] = 0x0B00;
116        assert_ne!(a, other.to_bytes());
117    }
118}