1use serde::{Deserialize, Serialize};
22use std::io::Read;
23use tls_codec::{
24 Deserialize as TlsDeserializeTrait, DeserializeBytes as TlsDeserializeBytesTrait,
25 Serialize as TlsSerializeTrait, TlsDeserialize, TlsDeserializeBytes, TlsSerialize, TlsSize,
26 VLBytes,
27};
28
29use crate::component::ComponentId;
30
31#[derive(thiserror::Error, Debug, PartialEq, Eq, Clone)]
33pub enum SafeAadError {
34 #[error("duplicate component id in SafeAAD: {0}")]
36 DuplicateComponentId(ComponentId),
37 #[error("SafeAAD items are not sorted by component id in increasing order")]
39 ItemsNotSortedAscending,
40 #[error("codec error: {0}")]
42 Codec(String),
43}
44
45#[derive(
54 Clone,
55 Debug,
56 PartialEq,
57 Eq,
58 Serialize,
59 Deserialize,
60 TlsSerialize,
61 TlsDeserialize,
62 TlsDeserializeBytes,
63 TlsSize,
64)]
65pub struct SafeAadItem {
66 component_id: ComponentId,
67 aad_item_data: VLBytes,
68}
69
70impl SafeAadItem {
71 pub fn new(component_id: ComponentId, data: Vec<u8>) -> Self {
73 Self {
74 component_id,
75 aad_item_data: data.into(),
76 }
77 }
78
79 pub fn component_id(&self) -> ComponentId {
81 self.component_id
82 }
83
84 pub fn data(&self) -> &[u8] {
86 self.aad_item_data.as_slice()
87 }
88}
89
90#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, TlsSerialize, TlsSize)]
93pub struct SafeAad {
94 aad_items: Vec<SafeAadItem>,
95}
96
97impl SafeAad {
98 pub fn from_items(items: Vec<SafeAadItem>) -> Result<Self, SafeAadError> {
103 Self::validate(&items)?;
104 Ok(Self { aad_items: items })
105 }
106
107 pub fn empty() -> Self {
109 Self {
110 aad_items: Vec::new(),
111 }
112 }
113
114 pub fn items(&self) -> &[SafeAadItem] {
116 &self.aad_items
117 }
118
119 pub fn get(&self, component_id: ComponentId) -> Option<&[u8]> {
123 self.aad_items
126 .binary_search_by_key(&component_id, SafeAadItem::component_id)
127 .ok()
128 .map(|index| self.aad_items[index].data())
129 }
130
131 #[cfg(feature = "virtual-clients-draft")]
134 pub(crate) fn upsert(&mut self, item: SafeAadItem) {
135 match self
136 .aad_items
137 .binary_search_by_key(&item.component_id(), SafeAadItem::component_id)
138 {
139 Ok(index) => self.aad_items[index] = item,
140 Err(index) => self.aad_items.insert(index, item),
141 }
142 }
143
144 pub fn is_empty(&self) -> bool {
146 self.aad_items.is_empty()
147 }
148
149 pub fn len(&self) -> usize {
151 self.aad_items.len()
152 }
153
154 fn validate(items: &[SafeAadItem]) -> Result<(), SafeAadError> {
155 let mut previous: Option<ComponentId> = None;
156 for item in items {
157 if let Some(prev) = previous {
158 if item.component_id == prev {
159 return Err(SafeAadError::DuplicateComponentId(item.component_id));
160 }
161 if item.component_id < prev {
162 return Err(SafeAadError::ItemsNotSortedAscending);
163 }
164 }
165 previous = Some(item.component_id);
166 }
167 Ok(())
168 }
169}
170
171impl TlsDeserializeTrait for SafeAad {
172 fn tls_deserialize<R: Read>(bytes: &mut R) -> Result<Self, tls_codec::Error> {
173 let aad_items = Vec::<SafeAadItem>::tls_deserialize(bytes)?;
174 SafeAad::from_items(aad_items)
175 .map_err(|err| tls_codec::Error::DecodingError(err.to_string()))
176 }
177}
178
179impl TlsDeserializeBytesTrait for SafeAad {
180 fn tls_deserialize_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), tls_codec::Error> {
181 let (aad_items, rest) = Vec::<SafeAadItem>::tls_deserialize_bytes(bytes)?;
182 let aad = SafeAad::from_items(aad_items)
183 .map_err(|err| tls_codec::Error::DecodingError(err.to_string()))?;
184 Ok((aad, rest))
185 }
186}
187
188pub(crate) fn assemble_authenticated_data(
192 safe_aad: &SafeAad,
193 tail: &[u8],
194) -> Result<Vec<u8>, SafeAadError> {
195 let mut out = safe_aad
196 .tls_serialize_detached()
197 .map_err(|err| SafeAadError::Codec(err.to_string()))?;
198 out.extend_from_slice(tail);
199 Ok(out)
200}
201
202pub(crate) fn parse_authenticated_data_prefix(
206 bytes: &[u8],
207) -> Result<(SafeAad, usize), SafeAadError> {
208 let (parsed, remainder) = SafeAad::tls_deserialize_bytes(bytes)
209 .map_err(|err| SafeAadError::Codec(err.to_string()))?;
210 let prefix_len = bytes.len() - remainder.len();
211 Ok((parsed, prefix_len))
212}
213
214#[cfg(test)]
215mod tests {
216 use super::*;
217 use tls_codec::{Deserialize, Serialize};
218
219 fn item(id: ComponentId, data: &[u8]) -> SafeAadItem {
220 SafeAadItem::new(id, data.to_vec())
221 }
222
223 #[test]
224 fn roundtrip_non_empty() {
225 let safe_aad = SafeAad::from_items(vec![
226 item(1, b"first"),
227 item(7, b""),
228 item(42, b"last item bytes"),
229 ])
230 .unwrap();
231
232 let bytes = safe_aad.tls_serialize_detached().unwrap();
233 let parsed = SafeAad::tls_deserialize_exact(&bytes).unwrap();
234
235 assert_eq!(parsed, safe_aad);
236 let reserialized = parsed.tls_serialize_detached().unwrap();
237 assert_eq!(reserialized, bytes);
238 }
239
240 #[test]
241 fn empty_is_length_prefix_only() {
242 let safe_aad = SafeAad::empty();
243 let bytes = safe_aad.tls_serialize_detached().unwrap();
244
245 assert_eq!(bytes, vec![0x00]);
247
248 let parsed = SafeAad::tls_deserialize_exact(&bytes).unwrap();
249 assert!(parsed.is_empty());
250 }
251
252 #[test]
253 fn from_items_rejects_duplicates() {
254 let err = SafeAad::from_items(vec![item(3, b"a"), item(3, b"b")]).unwrap_err();
255 assert_eq!(err, SafeAadError::DuplicateComponentId(3));
256 }
257
258 #[test]
259 fn from_items_rejects_misordered() {
260 let err = SafeAad::from_items(vec![item(9, b""), item(2, b"")]).unwrap_err();
261 assert_eq!(err, SafeAadError::ItemsNotSortedAscending);
262 }
263
264 #[test]
265 fn deserialize_rejects_misordered() {
266 let raw_items: Vec<SafeAadItem> = vec![item(5, b"x"), item(1, b"y")];
270 let raw_bytes = raw_items.tls_serialize_detached().unwrap();
271
272 let err = SafeAad::tls_deserialize_exact(&raw_bytes).unwrap_err();
273 match err {
274 tls_codec::Error::DecodingError(message) => {
275 assert!(
276 message.contains("not sorted"),
277 "unexpected error message: {message}"
278 );
279 }
280 other => panic!("unexpected error variant: {other:?}"),
281 }
282 }
283
284 #[test]
285 fn deserialize_rejects_duplicates() {
286 let raw_items: Vec<SafeAadItem> = vec![item(4, b""), item(4, b"")];
287 let raw_bytes = raw_items.tls_serialize_detached().unwrap();
288
289 let err = SafeAad::tls_deserialize_exact(&raw_bytes).unwrap_err();
290 match err {
291 tls_codec::Error::DecodingError(message) => {
292 assert!(
293 message.contains("duplicate"),
294 "unexpected error message: {message}"
295 );
296 }
297 other => panic!("unexpected error variant: {other:?}"),
298 }
299 }
300
301 #[test]
302 fn boundary_component_ids() {
303 let safe_aad = SafeAad::from_items(vec![item(0, b"min"), item(u16::MAX, b"max")]).unwrap();
304
305 let bytes = safe_aad.tls_serialize_detached().unwrap();
306 let parsed = SafeAad::tls_deserialize_exact(&bytes).unwrap();
307
308 assert_eq!(parsed.get(0), Some(b"min".as_slice()));
309 assert_eq!(parsed.get(u16::MAX), Some(b"max".as_slice()));
310 }
311
312 #[test]
313 fn get_returns_none_for_missing() {
314 let safe_aad = SafeAad::from_items(vec![item(1, b"a"), item(10, b"b")]).unwrap();
315 assert_eq!(safe_aad.get(5), None);
316 assert_eq!(safe_aad.get(1), Some(b"a".as_slice()));
317 assert_eq!(safe_aad.get(10), Some(b"b".as_slice()));
318 }
319
320 #[cfg(feature = "virtual-clients-draft")]
324 #[test]
325 fn upsert_inserts_and_replaces_in_order() {
326 let mut safe_aad = SafeAad::from_items(vec![item(1, b"a"), item(10, b"c")]).unwrap();
327
328 safe_aad.upsert(item(5, b"b"));
329 safe_aad.upsert(item(20, b"d"));
330 safe_aad.upsert(item(0, b"first"));
331 safe_aad.upsert(item(10, b"replaced"));
333
334 let ids: Vec<ComponentId> = safe_aad
335 .items()
336 .iter()
337 .map(SafeAadItem::component_id)
338 .collect();
339 assert_eq!(ids, vec![0, 1, 5, 10, 20]);
340 assert_eq!(safe_aad.get(10), Some(b"replaced".as_slice()));
341
342 let bytes = safe_aad.tls_serialize_detached().unwrap();
343 assert_eq!(SafeAad::tls_deserialize_exact(&bytes).unwrap(), safe_aad);
344 }
345
346 #[test]
347 fn assemble_and_parse_authenticated_data_roundtrip() {
348 let safe_aad =
349 SafeAad::from_items(vec![item(2, b"safe-aad-data"), item(8, b"more")]).unwrap();
350 let tail = b"caller tail bytes";
351
352 let combined = assemble_authenticated_data(&safe_aad, tail).unwrap();
353
354 let (parsed, prefix_len) = parse_authenticated_data_prefix(&combined).unwrap();
355 assert_eq!(parsed, safe_aad);
356 assert_eq!(&combined[prefix_len..], tail);
357 }
358}