1use crate::error::{Error, Result};
10use crate::length;
11use crate::tag::ApduTag;
12
13pub mod application_info;
14pub mod ca_info;
15pub mod ca_pmt;
16pub mod ca_pmt_reply;
17pub mod date_time;
18pub mod host_control;
19pub mod low_speed_comms;
20pub mod mmi_close;
21pub mod mmi_display;
22pub mod mmi_high;
23pub mod resource_manager;
24
25pub(crate) fn reject_trailing_body(body: &[u8], expected: usize, what: &'static str) -> Result<()> {
30 if body.len() > expected {
31 return Err(Error::InvalidObject {
32 what,
33 reason: "trailing bytes after the fixed body",
34 });
35 }
36 Ok(())
37}
38
39pub(crate) fn parse_apdu_header<'a>(
42 bytes: &'a [u8],
43 expected: ApduTag,
44 what: &'static str,
45) -> Result<&'a [u8]> {
46 if bytes.len() < 3 {
47 return Err(Error::BufferTooShort {
48 need: 3,
49 have: bytes.len(),
50 what,
51 });
52 }
53 let got = ApduTag::from_bytes(bytes[0], bytes[1], bytes[2]);
54 if got != expected {
55 return Err(Error::UnexpectedApduTag {
56 got: got.as_u24(),
57 expected: expected.as_u24(),
58 what,
59 });
60 }
61 let (len_value, len_hdr) = length::decode(&bytes[3..])?;
62 let body_start = 3 + len_hdr;
63 let body_end = body_start + len_value;
64 if bytes.len() < body_end {
65 return Err(Error::LengthMismatch {
66 what,
67 declared: len_value,
68 actual: bytes.len().saturating_sub(body_start),
69 });
70 }
71 Ok(&bytes[body_start..body_end])
72}
73
74pub(crate) fn parse_empty_apdu(bytes: &[u8], expected: ApduTag, what: &'static str) -> Result<()> {
76 let body = parse_apdu_header(bytes, expected, what)?;
77 if !body.is_empty() {
78 return Err(Error::InvalidObject {
79 what,
80 reason: "expected empty body",
81 });
82 }
83 Ok(())
84}
85
86pub(crate) fn apdu_len(body_len: usize) -> usize {
88 3 + length::encoded_len(body_len) + body_len
89}
90
91pub(crate) fn empty_apdu_len() -> usize {
93 apdu_len(0)
94}
95
96pub(crate) fn write_apdu_header(tag: ApduTag, body_len: usize, buf: &mut [u8]) -> Result<usize> {
100 let total = apdu_len(body_len);
101 if buf.len() < total {
102 return Err(Error::OutputBufferTooSmall {
103 need: total,
104 have: buf.len(),
105 });
106 }
107 buf[..3].copy_from_slice(&tag.to_bytes());
108 let n = length::encode_into(body_len, &mut buf[3..])?;
109 Ok(3 + n)
110}
111
112pub(crate) fn serialize_empty_apdu(tag: ApduTag, buf: &mut [u8]) -> Result<usize> {
114 write_apdu_header(tag, 0, buf)
115}
116
117pub(crate) fn fit_bits(value: u64, bits: u32, what: &'static str) -> Result<u64> {
124 broadcast_common::len::fit_bits(value, bits, what).map_err(|_| Error::InvalidObject {
125 what,
126 reason: overflow_reason(bits),
127 })
128}
129
130const fn overflow_reason(bits: u32) -> &'static str {
133 match bits {
134 4 => "exceeds 4-bit range (0x0F)",
135 5 => "exceeds 5-bit range (0x1F)",
136 7 => "exceeds 7-bit range (0x7F)",
137 8 => "exceeds 8-bit range (0xFF)",
138 12 => "exceeds 12-bit range (0x0FFF)",
139 13 => "exceeds 13-bit PID range (0x1FFF)",
140 14 => "exceeds 14-bit range (0x3FFF)",
141 16 => "exceeds 16-bit range (0xFFFF)",
142 24 => "exceeds 24-bit range (0xFFFFFF)",
143 _ => "exceeds field width",
144 }
145}
146
147pub(crate) struct Redacted(pub(crate) usize);
151
152impl core::fmt::Debug for Redacted {
153 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
154 write!(f, "<{} bytes redacted>", self.0)
155 }
156}
157
158#[cfg(feature = "serde")]
160pub(crate) mod bytes_serde {
161 pub fn serialize<S: serde::Serializer>(bytes: &[u8], s: S) -> Result<S::Ok, S::Error> {
162 s.serialize_bytes(bytes)
163 }
164}