1use crate::error::Error;
17
18#[allow(non_camel_case_types)]
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
23#[repr(u8)]
24pub enum ScsType {
25 Scs11 = 0x11,
27 Scs12 = 0x12,
29 Scs13 = 0x13,
31 Scs14 = 0x14,
33 Scs15 = 0x15,
35 Scs16 = 0x16,
37 Scs17 = 0x17,
39 Scs18 = 0x18,
41}
42
43impl ScsType {
44 pub const fn from_byte(b: u8) -> Result<Self, Error> {
46 Ok(match b {
47 0x11 => Self::Scs11,
48 0x12 => Self::Scs12,
49 0x13 => Self::Scs13,
50 0x14 => Self::Scs14,
51 0x15 => Self::Scs15,
52 0x16 => Self::Scs16,
53 0x17 => Self::Scs17,
54 0x18 => Self::Scs18,
55 other => return Err(Error::BadSecurityBlock(other)),
56 })
57 }
58
59 pub const fn as_byte(self) -> u8 {
61 self as u8
62 }
63
64 pub const fn has_mac(self) -> bool {
68 matches!(self, Self::Scs15 | Self::Scs16 | Self::Scs17 | Self::Scs18)
69 }
70
71 pub const fn is_encrypted(self) -> bool {
73 matches!(self, Self::Scs17 | Self::Scs18)
74 }
75
76 pub const fn is_handshake(self) -> bool {
78 matches!(self, Self::Scs11 | Self::Scs12 | Self::Scs13 | Self::Scs14)
79 }
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq)]
84pub struct ScbView<'a> {
85 pub ty: ScsType,
87 pub data: &'a [u8],
89}
90
91impl<'a> ScbView<'a> {
92 pub fn wire_len(&self) -> usize {
94 self.data.len() + 2
95 }
96
97 pub fn parse(buf: &'a [u8]) -> Result<Self, Error> {
99 if buf.len() < 2 {
100 return Err(Error::Truncated {
101 have: buf.len(),
102 need: 2,
103 });
104 }
105 let len = buf[0] as usize;
106 if len < 2 {
107 return Err(Error::BadSecurityBlockLength(buf[1]));
108 }
109 if buf.len() < len {
110 return Err(Error::Truncated {
111 have: buf.len(),
112 need: len,
113 });
114 }
115 let ty = ScsType::from_byte(buf[1])?;
116 Ok(Self {
117 ty,
118 data: &buf[2..len],
119 })
120 }
121}
122
123#[cfg(feature = "alloc")]
124mod alloc_impls {
125 use super::*;
126 use alloc::vec::Vec;
127
128 #[derive(Debug, Clone, PartialEq, Eq)]
130 pub struct Scb {
131 pub ty: ScsType,
133 pub data: Vec<u8>,
135 }
136
137 impl Scb {
138 pub fn new(ty: ScsType, data: impl Into<Vec<u8>>) -> Self {
140 Self {
141 ty,
142 data: data.into(),
143 }
144 }
145
146 pub fn as_view(&self) -> ScbView<'_> {
148 ScbView {
149 ty: self.ty,
150 data: &self.data,
151 }
152 }
153
154 pub fn encode(&self, out: &mut Vec<u8>) {
156 let len = (self.data.len() + 2) as u8;
157 out.push(len);
158 out.push(self.ty.as_byte());
159 out.extend_from_slice(&self.data);
160 }
161 }
162
163 impl From<ScbView<'_>> for Scb {
164 fn from(v: ScbView<'_>) -> Self {
165 Self {
166 ty: v.ty,
167 data: v.data.to_vec(),
168 }
169 }
170 }
171}
172
173#[cfg(feature = "alloc")]
174pub use alloc_impls::Scb;
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179
180 #[test]
181 fn scs_type_roundtrip() {
182 for byte in [0x11u8, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18] {
183 assert_eq!(ScsType::from_byte(byte).unwrap().as_byte(), byte);
184 }
185 }
186
187 #[test]
188 fn scs_type_rejects_unknown() {
189 assert!(ScsType::from_byte(0x10).is_err());
190 assert!(ScsType::from_byte(0x19).is_err());
191 assert!(ScsType::from_byte(0x00).is_err());
192 }
193
194 #[test]
195 fn parse_minimal() {
196 let buf = [0x02u8, 0x11];
197 let scb = ScbView::parse(&buf).unwrap();
198 assert_eq!(scb.ty, ScsType::Scs11);
199 assert!(scb.data.is_empty());
200 assert_eq!(scb.wire_len(), 2);
201 }
202
203 #[test]
204 fn parse_with_data() {
205 let buf = [0x05u8, 0x12, 0xAA, 0xBB, 0xCC];
206 let scb = ScbView::parse(&buf).unwrap();
207 assert_eq!(scb.ty, ScsType::Scs12);
208 assert_eq!(scb.data, &[0xAA, 0xBB, 0xCC]);
209 }
210
211 #[test]
212 fn parse_rejects_short_len() {
213 assert!(ScbView::parse(&[0x01u8, 0x11]).is_err());
214 assert!(ScbView::parse(&[]).is_err());
215 }
216
217 #[test]
218 fn mac_predicate() {
219 for ty in [
220 ScsType::Scs15,
221 ScsType::Scs16,
222 ScsType::Scs17,
223 ScsType::Scs18,
224 ] {
225 assert!(ty.has_mac());
226 }
227 for ty in [
228 ScsType::Scs11,
229 ScsType::Scs12,
230 ScsType::Scs13,
231 ScsType::Scs14,
232 ] {
233 assert!(!ty.has_mac());
234 }
235 }
236}