1#[cfg(all(feature = "alloc", not(feature = "std")))]
25use alloc::vec;
26#[cfg(all(feature = "alloc", not(feature = "std")))]
27use alloc::vec::Vec;
28#[cfg(feature = "std")]
29use std::io;
30
31use crate::error::ParseError;
32
33#[derive(Clone, Debug, Eq, PartialEq)]
38pub struct ExtensionFieldRef<'a> {
39 pub field_type: u16,
41 pub value: &'a [u8],
43}
44
45pub struct ExtensionFieldIter<'a> {
50 data: &'a [u8],
51 offset: usize,
52}
53
54impl<'a> Iterator for ExtensionFieldIter<'a> {
55 type Item = Result<ExtensionFieldRef<'a>, ParseError>;
56
57 fn next(&mut self) -> Option<Self::Item> {
58 let remaining = &self.data[self.offset..];
59 if remaining.len() < 4 {
60 return None;
61 }
62
63 let field_type = u16::from_be_bytes([remaining[0], remaining[1]]);
64 let field_length = u16::from_be_bytes([remaining[2], remaining[3]]);
65
66 if field_length < 4 {
67 return Some(Err(ParseError::InvalidExtensionLength {
68 declared: field_length,
69 }));
70 }
71
72 let value_length = (field_length - 4) as usize;
73 let value_start = self.offset + 4;
74
75 if value_start + value_length > self.data.len() {
76 return Some(Err(ParseError::ExtensionOverflow));
77 }
78
79 let value = &self.data[value_start..value_start + value_length];
80
81 let padded = (field_length as usize + 3) & !3;
83 let next_offset = self.offset + padded;
84 self.offset = next_offset.min(self.data.len());
85
86 Some(Ok(ExtensionFieldRef { field_type, value }))
87 }
88}
89
90pub fn iter_extension_fields(data: &[u8]) -> ExtensionFieldIter<'_> {
95 ExtensionFieldIter { data, offset: 0 }
96}
97
98pub const MIN_EXTENSION_FIELD_LENGTH: u16 = 16;
100
101pub const UNIQUE_IDENTIFIER: u16 = 0x0104;
105
106pub const NTS_COOKIE: u16 = 0x0204;
108
109pub const NTS_COOKIE_PLACEHOLDER: u16 = 0x0304;
111
112pub const NTS_AUTHENTICATOR: u16 = 0x0404;
114
115#[cfg(any(feature = "alloc", feature = "std"))]
117#[derive(Clone, Debug, Eq, PartialEq)]
118pub struct ExtensionField {
119 pub field_type: u16,
121 pub value: Vec<u8>,
123}
124
125#[cfg(any(feature = "alloc", feature = "std"))]
130pub fn parse_extension_fields_buf(data: &[u8]) -> Result<Vec<ExtensionField>, ParseError> {
131 iter_extension_fields(data)
132 .map(|r| {
133 r.map(|ef_ref| ExtensionField {
134 field_type: ef_ref.field_type,
135 value: ef_ref.value.to_vec(),
136 })
137 })
138 .collect()
139}
140
141#[cfg(any(feature = "alloc", feature = "std"))]
146pub fn write_extension_fields_buf(
147 fields: &[ExtensionField],
148 buf: &mut [u8],
149) -> Result<usize, ParseError> {
150 let mut offset = 0;
151
152 for field in fields {
153 let field_length = 4 + field.value.len();
154 let padded = (field_length + 3) & !3;
155
156 if offset + padded > buf.len() {
157 return Err(ParseError::BufferTooShort {
158 needed: offset + padded,
159 available: buf.len(),
160 });
161 }
162
163 let fl = field_length as u16;
164 buf[offset..offset + 2].copy_from_slice(&field.field_type.to_be_bytes());
165 buf[offset + 2..offset + 4].copy_from_slice(&fl.to_be_bytes());
166 buf[offset + 4..offset + 4 + field.value.len()].copy_from_slice(&field.value);
167
168 for b in &mut buf[offset + field_length..offset + padded] {
170 *b = 0;
171 }
172
173 offset += padded;
174 }
175
176 Ok(offset)
177}
178
179#[cfg(feature = "std")]
184pub fn parse_extension_fields(data: &[u8]) -> io::Result<Vec<ExtensionField>> {
185 parse_extension_fields_buf(data).map_err(io::Error::from)
186}
187
188#[cfg(feature = "std")]
192pub fn write_extension_fields(fields: &[ExtensionField]) -> io::Result<Vec<u8>> {
193 let total: usize = fields.iter().map(|f| ((4 + f.value.len()) + 3) & !3).sum();
195 let mut buf = vec![0u8; total];
196 write_extension_fields_buf(fields, &mut buf)?;
197 Ok(buf)
198}
199
200#[cfg(any(feature = "alloc", feature = "std"))]
205#[derive(Clone, Debug, Eq, PartialEq)]
206pub struct UniqueIdentifier(pub Vec<u8>);
207
208#[cfg(any(feature = "alloc", feature = "std"))]
209impl UniqueIdentifier {
210 pub fn new(data: Vec<u8>) -> Self {
212 UniqueIdentifier(data)
213 }
214
215 pub fn to_extension_field(&self) -> ExtensionField {
217 ExtensionField {
218 field_type: UNIQUE_IDENTIFIER,
219 value: self.0.clone(),
220 }
221 }
222
223 pub fn from_extension_field(ef: &ExtensionField) -> Option<Self> {
225 if ef.field_type == UNIQUE_IDENTIFIER {
226 Some(UniqueIdentifier(ef.value.clone()))
227 } else {
228 None
229 }
230 }
231}
232
233#[cfg(any(feature = "alloc", feature = "std"))]
238#[derive(Clone, Debug, Eq, PartialEq)]
239pub struct NtsCookie(pub Vec<u8>);
240
241#[cfg(any(feature = "alloc", feature = "std"))]
242impl NtsCookie {
243 pub fn new(data: Vec<u8>) -> Self {
245 NtsCookie(data)
246 }
247
248 pub fn to_extension_field(&self) -> ExtensionField {
250 ExtensionField {
251 field_type: NTS_COOKIE,
252 value: self.0.clone(),
253 }
254 }
255
256 pub fn from_extension_field(ef: &ExtensionField) -> Option<Self> {
258 if ef.field_type == NTS_COOKIE {
259 Some(NtsCookie(ef.value.clone()))
260 } else {
261 None
262 }
263 }
264}
265
266#[cfg(any(feature = "alloc", feature = "std"))]
271#[derive(Clone, Debug, Eq, PartialEq)]
272pub struct NtsCookiePlaceholder {
273 pub size: usize,
275}
276
277#[cfg(any(feature = "alloc", feature = "std"))]
278impl NtsCookiePlaceholder {
279 pub fn new(size: usize) -> Self {
281 NtsCookiePlaceholder { size }
282 }
283
284 pub fn to_extension_field(&self) -> ExtensionField {
286 ExtensionField {
287 field_type: NTS_COOKIE_PLACEHOLDER,
288 value: vec![0u8; self.size],
289 }
290 }
291}
292
293#[cfg(any(feature = "alloc", feature = "std"))]
298#[derive(Clone, Debug, Eq, PartialEq)]
299pub struct NtsAuthenticator {
300 pub nonce: Vec<u8>,
302 pub ciphertext: Vec<u8>,
304}
305
306#[cfg(any(feature = "alloc", feature = "std"))]
307impl NtsAuthenticator {
308 pub fn new(nonce: Vec<u8>, ciphertext: Vec<u8>) -> Self {
310 NtsAuthenticator { nonce, ciphertext }
311 }
312
313 pub fn to_extension_field(&self) -> ExtensionField {
317 let mut value = Vec::new();
318 value.extend_from_slice(&(self.nonce.len() as u16).to_be_bytes());
320 value.extend_from_slice(&self.nonce);
321 let nonce_padded = (2 + self.nonce.len() + 3) & !3;
323 let nonce_pad = nonce_padded - (2 + self.nonce.len());
324 value.extend(core::iter::repeat_n(0u8, nonce_pad));
325 value.extend_from_slice(&(self.ciphertext.len() as u16).to_be_bytes());
327 value.extend_from_slice(&self.ciphertext);
328
329 ExtensionField {
330 field_type: NTS_AUTHENTICATOR,
331 value,
332 }
333 }
334
335 #[cfg(feature = "std")]
337 pub fn from_extension_field(ef: &ExtensionField) -> io::Result<Option<Self>> {
338 Self::from_extension_field_buf(ef).map_err(io::Error::from)
339 }
340
341 pub fn from_extension_field_buf(ef: &ExtensionField) -> Result<Option<Self>, ParseError> {
343 if ef.field_type != NTS_AUTHENTICATOR {
344 return Ok(None);
345 }
346
347 let data = &ef.value;
348 if data.len() < 2 {
349 return Err(ParseError::BufferTooShort {
350 needed: 2,
351 available: data.len(),
352 });
353 }
354
355 let nonce_len = u16::from_be_bytes([data[0], data[1]]) as usize;
356 let nonce_start = 2;
357
358 if nonce_start + nonce_len > data.len() {
359 return Err(ParseError::ExtensionOverflow);
360 }
361 let nonce = data[nonce_start..nonce_start + nonce_len].to_vec();
362
363 let nonce_padded = (2 + nonce_len + 3) & !3;
365 let ct_offset = nonce_padded;
366 if ct_offset + 2 > data.len() {
367 return Err(ParseError::BufferTooShort {
368 needed: ct_offset + 2,
369 available: data.len(),
370 });
371 }
372
373 let ct_len = u16::from_be_bytes([data[ct_offset], data[ct_offset + 1]]) as usize;
374 let ct_start = ct_offset + 2;
375
376 if ct_start + ct_len > data.len() {
377 return Err(ParseError::ExtensionOverflow);
378 }
379 let ciphertext = data[ct_start..ct_start + ct_len].to_vec();
380
381 Ok(Some(NtsAuthenticator { nonce, ciphertext }))
382 }
383}
384
385#[cfg(all(test, feature = "std"))]
386mod tests {
387 use super::*;
388
389 #[test]
390 fn test_parse_empty() {
391 let fields = parse_extension_fields(&[]).unwrap();
392 assert!(fields.is_empty());
393 }
394
395 #[test]
396 fn test_roundtrip_single_field() {
397 let field = ExtensionField {
398 field_type: UNIQUE_IDENTIFIER,
399 value: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
400 };
401 let buf = write_extension_fields(std::slice::from_ref(&field)).unwrap();
402 let parsed = parse_extension_fields(&buf).unwrap();
403 assert_eq!(parsed.len(), 1);
404 assert_eq!(parsed[0], field);
405 }
406
407 #[test]
408 fn test_roundtrip_multiple_fields() {
409 let fields = vec![
410 ExtensionField {
411 field_type: UNIQUE_IDENTIFIER,
412 value: vec![0xAA; 32],
413 },
414 ExtensionField {
415 field_type: NTS_COOKIE,
416 value: vec![0xBB; 64],
417 },
418 ];
419 let buf = write_extension_fields(&fields).unwrap();
420 let parsed = parse_extension_fields(&buf).unwrap();
421 assert_eq!(parsed.len(), 2);
422 assert_eq!(parsed[0], fields[0]);
423 assert_eq!(parsed[1], fields[1]);
424 }
425
426 #[test]
427 fn test_padding() {
428 let field = ExtensionField {
430 field_type: 0x1234,
431 value: vec![1, 2, 3, 4, 5],
432 };
433 let buf = write_extension_fields(&[field]).unwrap();
434 assert_eq!(buf.len(), 12); }
436
437 #[test]
438 fn test_unique_identifier_conversion() {
439 let uid = UniqueIdentifier::new(vec![0x42; 32]);
440 let ef = uid.to_extension_field();
441 assert_eq!(ef.field_type, UNIQUE_IDENTIFIER);
442 let back = UniqueIdentifier::from_extension_field(&ef).unwrap();
443 assert_eq!(back.0, vec![0x42; 32]);
444 }
445
446 #[test]
447 fn test_nts_cookie_conversion() {
448 let cookie = NtsCookie::new(vec![0xDE, 0xAD, 0xBE, 0xEF]);
449 let ef = cookie.to_extension_field();
450 assert_eq!(ef.field_type, NTS_COOKIE);
451 let back = NtsCookie::from_extension_field(&ef).unwrap();
452 assert_eq!(back.0, vec![0xDE, 0xAD, 0xBE, 0xEF]);
453 }
454
455 #[test]
456 fn test_nts_authenticator_roundtrip() {
457 let auth = NtsAuthenticator::new(vec![0x11; 16], vec![0x22; 48]);
458 let ef = auth.to_extension_field();
459 assert_eq!(ef.field_type, NTS_AUTHENTICATOR);
460 let back = NtsAuthenticator::from_extension_field(&ef)
461 .unwrap()
462 .unwrap();
463 assert_eq!(back.nonce, vec![0x11; 16]);
464 assert_eq!(back.ciphertext, vec![0x22; 48]);
465 }
466
467 #[test]
468 fn test_cookie_placeholder() {
469 let placeholder = NtsCookiePlaceholder::new(100);
470 let ef = placeholder.to_extension_field();
471 assert_eq!(ef.field_type, NTS_COOKIE_PLACEHOLDER);
472 assert_eq!(ef.value.len(), 100);
473 assert!(ef.value.iter().all(|&b| b == 0));
474 }
475
476 #[test]
477 fn test_parse_truncated_field() {
478 let data = [0x01, 0x04, 0x00];
480 let fields = parse_extension_fields(&data).unwrap();
481 assert!(fields.is_empty()); }
483
484 #[test]
485 fn test_parse_invalid_length() {
486 let data = [0x01, 0x04, 0x00, 0x02];
488 let result = parse_extension_fields(&data);
489 assert!(result.is_err());
490 }
491
492 #[test]
495 fn test_buf_parse_empty() {
496 let fields = parse_extension_fields_buf(&[]).unwrap();
497 assert!(fields.is_empty());
498 }
499
500 #[test]
501 fn test_buf_roundtrip_single_field() {
502 let field = ExtensionField {
503 field_type: UNIQUE_IDENTIFIER,
504 value: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
505 };
506
507 let io_buf = write_extension_fields(std::slice::from_ref(&field)).unwrap();
509 let mut buf = vec![0u8; 256];
510 let written = write_extension_fields_buf(std::slice::from_ref(&field), &mut buf).unwrap();
511 assert_eq!(&io_buf[..], &buf[..written]);
512
513 let parsed = parse_extension_fields_buf(&buf[..written]).unwrap();
515 assert_eq!(parsed.len(), 1);
516 assert_eq!(parsed[0], field);
517 }
518
519 #[test]
520 fn test_buf_equivalence_with_io_api() {
521 let fields = vec![
522 ExtensionField {
523 field_type: UNIQUE_IDENTIFIER,
524 value: vec![0xAA; 32],
525 },
526 ExtensionField {
527 field_type: NTS_COOKIE,
528 value: vec![0xBB; 64],
529 },
530 ];
531
532 let io_buf = write_extension_fields(&fields).unwrap();
533 let mut raw_buf = vec![0u8; 512];
534 let written = write_extension_fields_buf(&fields, &mut raw_buf).unwrap();
535
536 assert_eq!(&io_buf[..], &raw_buf[..written]);
538
539 let io_parsed = parse_extension_fields(&io_buf).unwrap();
541 let buf_parsed = parse_extension_fields_buf(&raw_buf[..written]).unwrap();
542 assert_eq!(io_parsed, buf_parsed);
543 }
544
545 #[test]
546 fn test_buf_write_buffer_too_short() {
547 let field = ExtensionField {
548 field_type: UNIQUE_IDENTIFIER,
549 value: vec![0xAA; 32],
550 };
551 let mut tiny_buf = [0u8; 4]; let result = write_extension_fields_buf(&[field], &mut tiny_buf);
553 assert!(result.is_err());
554 }
555
556 #[test]
557 fn test_buf_parse_invalid_length() {
558 let data = [0x01, 0x04, 0x00, 0x02]; let result = parse_extension_fields_buf(&data);
560 assert!(matches!(
561 result,
562 Err(ParseError::InvalidExtensionLength { declared: 2 })
563 ));
564 }
565
566 #[test]
567 fn test_iter_extension_fields() {
568 let fields = vec![
569 ExtensionField {
570 field_type: UNIQUE_IDENTIFIER,
571 value: vec![0xAA; 32],
572 },
573 ExtensionField {
574 field_type: NTS_COOKIE,
575 value: vec![0xBB; 64],
576 },
577 ];
578 let io_buf = write_extension_fields(&fields).unwrap();
579
580 let mut iter = iter_extension_fields(&io_buf);
581
582 let first = iter.next().unwrap().unwrap();
583 assert_eq!(first.field_type, UNIQUE_IDENTIFIER);
584 assert_eq!(first.value, &[0xAA; 32][..]);
585
586 let second = iter.next().unwrap().unwrap();
587 assert_eq!(second.field_type, NTS_COOKIE);
588 assert_eq!(second.value, &[0xBB; 64][..]);
589
590 assert!(iter.next().is_none());
591 }
592
593 #[test]
594 fn test_iter_extension_fields_empty() {
595 let mut iter = iter_extension_fields(&[]);
596 assert!(iter.next().is_none());
597 }
598
599 #[test]
600 fn test_nts_authenticator_buf_roundtrip() {
601 let auth = NtsAuthenticator::new(vec![0x11; 16], vec![0x22; 48]);
602 let ef = auth.to_extension_field();
603 let back = NtsAuthenticator::from_extension_field_buf(&ef)
604 .unwrap()
605 .unwrap();
606 assert_eq!(back.nonce, vec![0x11; 16]);
607 assert_eq!(back.ciphertext, vec![0x22; 48]);
608 }
609}