1pub mod ep;
14mod gcm;
15pub mod key;
17pub mod sa_mgmt;
19mod security_header;
20
21pub use gcm::AesGcmCrypto;
22pub use security_header::SecurityHeader;
23pub use security_header::SecurityTrailer;
24
25pub const MAX_SECURITY_HEADER_SIZE: usize = 64;
27
28pub const MAX_MAC_SIZE: usize = 64;
30
31pub const MAX_IV_SIZE: usize = 32;
33
34pub const MAX_SN_SIZE: usize = 8;
36
37pub const MAX_PL_SIZE: usize = 2;
39
40#[derive(Debug, Copy, Clone, Eq, PartialEq)]
42pub enum ServiceType {
43 Authentication,
45 Encryption,
47 AuthenticatedEncryption,
49}
50
51#[derive(Debug, Clone)]
56pub struct SecurityAssociation {
57 pub spi: u16,
59 pub service_type: ServiceType,
61 pub iv_len: u8,
63 pub sn_len: u8,
65 pub pl_len: u8,
67 pub mac_len: u8,
69 pub sequence_number: u64,
71 pub sequence_window: u64,
73 pub auth_mask: heapless::Vec<u8, 2048>,
76}
77
78impl SecurityAssociation {
79 pub fn header_size(&self) -> usize {
81 2 + self.iv_len as usize + self.sn_len as usize + self.pl_len as usize
82 }
83
84 pub fn trailer_size(&self) -> usize {
86 self.mac_len as usize
87 }
88
89 pub fn overhead(&self) -> usize {
91 self.header_size() + self.trailer_size()
92 }
93}
94
95#[derive(Debug, Copy, Clone, Eq, PartialEq, thiserror::Error)]
97pub enum Error {
98 #[error("reserved SPI: {0}")]
100 ReservedSpi(u16),
101 #[error("unknown SPI: {0}")]
103 UnknownSpi(u16),
104 #[error("buffer too small: need {required} bytes, have {available} bytes")]
106 BufferTooSmall {
107 required: usize,
109 available: usize,
111 },
112 #[error("frame too short to contain Security Header")]
114 FrameTooShort,
115 #[error("MAC verification failed")]
117 MacVerificationFailed,
118 #[error("sequence number rejected: got {received}, expected {expected}")]
120 SequenceNumberRejected {
121 received: u64,
123 expected: u64,
125 },
126 #[error("padding error")]
128 PaddingError,
129 #[error("cryptographic operation failed")]
131 CryptoError,
132 #[error("invalid SDLS-EP procedure")]
134 InvalidProcedure,
135 #[error("unknown key ID: {0}")]
137 UnknownKeyId(u16),
138 #[error("duplicate key ID")]
140 DuplicateKeyId,
141 #[error("key ring full")]
143 KeyRingFull,
144 #[error("invalid key length")]
146 InvalidKeyLength,
147 #[error("invalid key state")]
149 InvalidKeyState,
150 #[error("invalid SA state")]
152 InvalidSaState,
153 #[error("duplicate SPI: {0}")]
155 DuplicateSpi(u16),
156 #[error("SA table full")]
158 SaTableFull,
159}
160
161pub trait CryptoProvider {
171 fn encrypt(
176 &self,
177 sa: &SecurityAssociation,
178 iv: &[u8],
179 aad: &[u8],
180 data: &mut [u8],
181 tag_out: &mut [u8],
182 ) -> Result<usize, Error>;
183
184 fn decrypt(
188 &self,
189 sa: &SecurityAssociation,
190 iv: &[u8],
191 aad: &[u8],
192 data: &mut [u8],
193 tag: &[u8],
194 ) -> Result<usize, Error>;
195
196 fn compute_mac(
199 &self,
200 sa: &SecurityAssociation,
201 iv: &[u8],
202 payload: &[u8],
203 mac_out: &mut [u8],
204 ) -> Result<(), Error>;
205}
206
207pub struct ClearModeCrypto;
213
214impl CryptoProvider for ClearModeCrypto {
215 fn encrypt(
216 &self,
217 _sa: &SecurityAssociation,
218 _iv: &[u8],
219 _aad: &[u8],
220 _data: &mut [u8],
221 _tag_out: &mut [u8],
222 ) -> Result<usize, Error> {
223 Ok(0)
224 }
225
226 fn decrypt(
227 &self,
228 _sa: &SecurityAssociation,
229 _iv: &[u8],
230 _aad: &[u8],
231 _data: &mut [u8],
232 _tag: &[u8],
233 ) -> Result<usize, Error> {
234 Ok(0)
235 }
236
237 fn compute_mac(
238 &self,
239 sa: &SecurityAssociation,
240 _iv: &[u8],
241 _payload: &[u8],
242 mac_out: &mut [u8],
243 ) -> Result<(), Error> {
244 let len = sa.mac_len as usize;
245 mac_out[..len].fill(0);
246 Ok(())
247 }
248}
249
250pub fn write_security_header(
254 sa: &SecurityAssociation,
255 iv: &[u8],
256 sn: &[u8],
257 pad_len: u16,
258 out: &mut [u8],
259) -> Result<usize, Error> {
260 let hdr_size = sa.header_size();
261 if out.len() < hdr_size {
262 return Err(Error::BufferTooSmall {
263 required: hdr_size,
264 available: out.len(),
265 });
266 }
267 if sa.spi == 0 || sa.spi == 0xFFFF {
268 return Err(Error::ReservedSpi(sa.spi));
269 }
270
271 let mut pos = 0;
272
273 out[pos..pos + 2].copy_from_slice(&sa.spi.to_be_bytes());
275 pos += 2;
276
277 let iv_len = sa.iv_len as usize;
279 if iv_len > 0 {
280 out[pos..pos + iv_len].copy_from_slice(&iv[..iv_len]);
281 pos += iv_len;
282 }
283
284 let sn_len = sa.sn_len as usize;
286 if sn_len > 0 {
287 out[pos..pos + sn_len].copy_from_slice(&sn[..sn_len]);
288 pos += sn_len;
289 }
290
291 let pl_len = sa.pl_len as usize;
293 if pl_len > 0 {
294 let pl_bytes = pad_len.to_be_bytes();
295 let start = 2 - pl_len;
296 out[pos..pos + pl_len].copy_from_slice(&pl_bytes[start..]);
297 pos += pl_len;
298 }
299
300 Ok(pos)
301}
302
303pub fn read_spi(header_bytes: &[u8]) -> Result<u16, Error> {
305 if header_bytes.len() < 2 {
306 return Err(Error::FrameTooShort);
307 }
308 Ok(u16::from_be_bytes([header_bytes[0], header_bytes[1]]))
309}
310
311pub fn parse_security_header<'a>(
313 sa: &SecurityAssociation,
314 header_bytes: &'a [u8],
315) -> Result<SecurityHeader<'a>, Error> {
316 let hdr_size = sa.header_size();
317 if header_bytes.len() < hdr_size {
318 return Err(Error::FrameTooShort);
319 }
320 SecurityHeader::parse(sa, header_bytes)
321}
322
323pub fn apply_security(
333 sa: &mut SecurityAssociation,
334 crypto: &impl CryptoProvider,
335 frame: &mut [u8],
336 header_end: usize,
337 data_start: usize,
338 data_end: usize,
339) -> Result<(), Error> {
340 if sa.spi == 0 || sa.spi == 0xFFFF {
341 return Err(Error::ReservedSpi(sa.spi));
342 }
343
344 let trailer_start = data_end;
345 let mac_len = sa.mac_len as usize;
346 let trailer_end = trailer_start + mac_len;
347 if frame.len() < trailer_end {
348 return Err(Error::BufferTooSmall {
349 required: trailer_end,
350 available: frame.len(),
351 });
352 }
353
354 let mut iv_buf = [0u8; MAX_IV_SIZE];
356 let mut sn_buf = [0u8; MAX_SN_SIZE];
357 let iv_len = sa.iv_len as usize;
358 let sn_len = sa.sn_len as usize;
359
360 if iv_len > 0 {
361 let sn_bytes = sa.sequence_number.to_be_bytes();
362 let start = 8usize.saturating_sub(iv_len);
363 let copy_len = iv_len.min(8);
364 let iv_start = iv_len.saturating_sub(8);
365 iv_buf[iv_start..iv_start + copy_len].copy_from_slice(&sn_bytes[start..start + copy_len]);
366 }
367
368 if sn_len > 0 {
369 let sn_bytes = sa.sequence_number.to_be_bytes();
370 let start = 8 - sn_len;
371 sn_buf[..sn_len].copy_from_slice(&sn_bytes[start..]);
372 }
373
374 write_security_header(
377 sa,
378 &iv_buf[..iv_len],
379 &sn_buf[..sn_len],
380 0,
381 &mut frame[header_end..data_start],
382 )?;
383
384 let mut mac_buf = [0u8; MAX_MAC_SIZE];
385
386 match sa.service_type {
387 ServiceType::Authentication => {
388 let auth_end = data_end;
389 if sa.auth_mask.len() >= auth_end {
390 let mut masked = heapless::Vec::<u8, 2048>::new();
391 masked.resize(auth_end, 0).ok();
392 for i in 0..auth_end {
393 masked[i] = frame[i] & sa.auth_mask[i];
394 }
395 crypto.compute_mac(
396 sa,
397 &iv_buf[..iv_len],
398 &masked[..auth_end],
399 &mut mac_buf[..mac_len],
400 )?;
401 } else {
402 crypto.compute_mac(
403 sa,
404 &iv_buf[..iv_len],
405 &frame[..auth_end],
406 &mut mac_buf[..mac_len],
407 )?;
408 }
409 frame[trailer_start..trailer_start + mac_len].copy_from_slice(&mac_buf[..mac_len]);
410 }
411 ServiceType::Encryption => {
412 crypto.encrypt(
413 sa,
414 &iv_buf[..iv_len],
415 &[],
416 &mut frame[data_start..data_end],
417 &mut [],
418 )?;
419 }
420 ServiceType::AuthenticatedEncryption => {
421 let (aad_part, rest) = frame.split_at_mut(data_start);
423 let data_len = data_end - data_start;
424
425 if sa.auth_mask.len() >= data_start {
426 let mut masked_aad = [0u8; 256];
427 for i in 0..aad_part.len() {
428 masked_aad[i] = aad_part[i] & sa.auth_mask[i];
429 }
430 crypto.encrypt(
431 sa,
432 &iv_buf[..iv_len],
433 &masked_aad[..aad_part.len()],
434 &mut rest[..data_len],
435 &mut mac_buf[..mac_len],
436 )?;
437 } else {
438 crypto.encrypt(
439 sa,
440 &iv_buf[..iv_len],
441 aad_part,
442 &mut rest[..data_len],
443 &mut mac_buf[..mac_len],
444 )?;
445 }
446 rest[data_len..data_len + mac_len].copy_from_slice(&mac_buf[..mac_len]);
447 }
448 }
449
450 sa.sequence_number = sa.sequence_number.wrapping_add(1);
451
452 Ok(())
453}
454
455pub fn process_security(
461 sa: &mut SecurityAssociation,
462 crypto: &impl CryptoProvider,
463 frame: &mut [u8],
464 header_end: usize,
465 data_start: usize,
466 data_end: usize,
467) -> Result<(), Error> {
468 let spi = read_spi(&frame[header_end..])?;
469 if spi != sa.spi {
470 return Err(Error::UnknownSpi(spi));
471 }
472
473 let trailer_start = data_end;
474 let mac_len = sa.mac_len as usize;
475 let trailer_end = trailer_start + mac_len;
476 if frame.len() < trailer_end {
477 return Err(Error::FrameTooShort);
478 }
479
480 let (received_sn, iv_copy) = {
481 let sec_hdr = SecurityHeader::parse(sa, &frame[header_end..data_start])?;
482 let sn_val = sec_hdr.sequence_number_value();
483 let mut iv_buf = [0u8; MAX_IV_SIZE];
484 let iv = sec_hdr.iv();
485 iv_buf[..iv.len()].copy_from_slice(iv);
486 (sn_val, iv_buf)
487 };
488
489 let iv_len = sa.iv_len as usize;
490 let sn_len = sa.sn_len as usize;
491
492 match sa.service_type {
493 ServiceType::Authentication => {
494 let mut mac_buf = [0u8; MAX_MAC_SIZE];
495 let auth_end = data_end;
496
497 if sa.auth_mask.len() >= auth_end {
498 let mut masked = heapless::Vec::<u8, 2048>::new();
499 masked.resize(auth_end, 0).ok();
500 for i in 0..auth_end {
501 masked[i] = frame[i] & sa.auth_mask[i];
502 }
503 crypto.compute_mac(
504 sa,
505 &iv_copy[..iv_len],
506 &masked[..auth_end],
507 &mut mac_buf[..mac_len],
508 )?;
509 } else {
510 crypto.compute_mac(
511 sa,
512 &iv_copy[..iv_len],
513 &frame[..auth_end],
514 &mut mac_buf[..mac_len],
515 )?;
516 }
517
518 let received_mac = &frame[trailer_start..trailer_start + mac_len];
519 if received_mac != &mac_buf[..mac_len] {
520 return Err(Error::MacVerificationFailed);
521 }
522
523 if sn_len > 0 {
524 check_sequence_number(sa, received_sn)?;
525 }
526 }
527 ServiceType::Encryption => {
528 crypto.decrypt(
529 sa,
530 &iv_copy[..iv_len],
531 &[],
532 &mut frame[data_start..data_end],
533 &[],
534 )?;
535 }
536 ServiceType::AuthenticatedEncryption => {
537 let mut tag = [0u8; MAX_MAC_SIZE];
539 tag[..mac_len].copy_from_slice(&frame[trailer_start..trailer_start + mac_len]);
540
541 let (aad_part, rest) = frame.split_at_mut(data_start);
543 let data_len = data_end - data_start;
544
545 if sa.auth_mask.len() >= data_start {
546 let mut masked_aad = [0u8; 256];
547 for i in 0..aad_part.len() {
548 masked_aad[i] = aad_part[i] & sa.auth_mask[i];
549 }
550 crypto.decrypt(
551 sa,
552 &iv_copy[..iv_len],
553 &masked_aad[..aad_part.len()],
554 &mut rest[..data_len],
555 &tag[..mac_len],
556 )?;
557 } else {
558 crypto.decrypt(
559 sa,
560 &iv_copy[..iv_len],
561 aad_part,
562 &mut rest[..data_len],
563 &tag[..mac_len],
564 )?;
565 }
566
567 if sn_len > 0 {
568 check_sequence_number(sa, received_sn)?;
569 }
570 }
571 }
572
573 Ok(())
574}
575
576fn check_sequence_number(sa: &mut SecurityAssociation, received: u64) -> Result<(), Error> {
577 let expected = sa.sequence_number;
578 if received < expected {
579 return Err(Error::SequenceNumberRejected { received, expected });
580 }
581 let window = sa.sequence_window;
582 if window > 0 && received > expected.wrapping_add(window) {
583 return Err(Error::SequenceNumberRejected { received, expected });
584 }
585 sa.sequence_number = received.wrapping_add(1);
586 Ok(())
587}
588
589#[cfg(test)]
590mod tests {
591 use super::*;
592
593 fn test_sa() -> SecurityAssociation {
594 SecurityAssociation {
595 spi: 1,
596 service_type: ServiceType::Authentication,
597 iv_len: 4,
598 sn_len: 4,
599 pl_len: 0,
600 mac_len: 16,
601 sequence_number: 0,
602 sequence_window: 100,
603 auth_mask: heapless::Vec::new(),
604 }
605 }
606
607 #[test]
608 fn sa_sizes() {
609 let sa = test_sa();
610 assert_eq!(sa.header_size(), 10);
612 assert_eq!(sa.trailer_size(), 16);
613 assert_eq!(sa.overhead(), 26);
614 }
615
616 #[test]
617 fn write_and_read_spi() {
618 let sa = test_sa();
619 let mut buf = [0u8; 64];
620 let iv = [0u8; 4];
621 let sn = [0u8; 4];
622 write_security_header(&sa, &iv, &sn, 0, &mut buf).unwrap();
623
624 let spi = read_spi(&buf).unwrap();
625 assert_eq!(spi, 1);
626 }
627
628 #[test]
629 fn reserved_spi_rejected() {
630 let mut sa = test_sa();
631 sa.spi = 0;
632 let mut buf = [0u8; 64];
633 let err = write_security_header(&sa, &[0; 4], &[0; 4], 0, &mut buf).unwrap_err();
634 assert_eq!(err, Error::ReservedSpi(0));
635
636 sa.spi = 0xFFFF;
637 let err = write_security_header(&sa, &[0; 4], &[0; 4], 0, &mut buf).unwrap_err();
638 assert_eq!(err, Error::ReservedSpi(0xFFFF));
639 }
640
641 #[test]
642 fn header_roundtrip() {
643 let sa = test_sa();
644 let mut buf = [0u8; 64];
645 let iv = [0x01, 0x02, 0x03, 0x04];
646 let sn = [0x00, 0x00, 0x00, 0x05];
647 let written = write_security_header(&sa, &iv, &sn, 0, &mut buf).unwrap();
648 assert_eq!(written, 10);
649
650 let hdr = SecurityHeader::parse(&sa, &buf[..written]).unwrap();
651 assert_eq!(hdr.spi(), 1);
652 assert_eq!(hdr.iv(), &[0x01, 0x02, 0x03, 0x04]);
653 assert_eq!(hdr.sequence_number(), &[0x00, 0x00, 0x00, 0x05]);
654 assert_eq!(hdr.sequence_number_value(), 5);
655 assert_eq!(hdr.pad_length(), 0);
656 }
657
658 #[test]
659 fn clear_mode_apply_and_process() {
660 let mut sa_send = test_sa();
662 let mut sa_recv = test_sa();
663 let crypto = ClearModeCrypto;
664
665 let header_end = 5;
666 let data_start = header_end + sa_send.header_size(); let data_end = data_start + 10; let mut frame = [0u8; 64];
669 frame[0..5].copy_from_slice(&[0xC0, 0x00, 0x2A, 0x00, 0x28]);
671 frame[data_start..data_end].copy_from_slice(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
673
674 apply_security(
675 &mut sa_send,
676 &crypto,
677 &mut frame,
678 header_end,
679 data_start,
680 data_end,
681 )
682 .unwrap();
683
684 assert_eq!(read_spi(&frame[header_end..]).unwrap(), 1);
686 assert_eq!(sa_send.sequence_number, 1);
688
689 process_security(
691 &mut sa_recv,
692 &crypto,
693 &mut frame,
694 header_end,
695 data_start,
696 data_end,
697 )
698 .unwrap();
699
700 assert_eq!(sa_recv.sequence_number, 1);
702 assert_eq!(
704 &frame[data_start..data_end],
705 &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
706 );
707 }
708
709 #[test]
710 fn encryption_only_sa() {
711 let sa = SecurityAssociation {
712 spi: 2,
713 service_type: ServiceType::Encryption,
714 iv_len: 12,
715 sn_len: 0,
716 pl_len: 1,
717 mac_len: 0,
718 sequence_number: 0,
719 sequence_window: 0,
720 auth_mask: heapless::Vec::new(),
721 };
722 assert_eq!(sa.header_size(), 15);
724 assert_eq!(sa.trailer_size(), 0);
725 }
726
727 #[test]
728 fn authenticated_encryption_sa() {
729 let sa = SecurityAssociation {
730 spi: 3,
731 service_type: ServiceType::AuthenticatedEncryption,
732 iv_len: 12,
733 sn_len: 0,
734 pl_len: 1,
735 mac_len: 16,
736 sequence_number: 0,
737 sequence_window: 100,
738 auth_mask: heapless::Vec::new(),
739 };
740 assert_eq!(sa.header_size(), 15);
742 assert_eq!(sa.trailer_size(), 16);
744 assert_eq!(sa.overhead(), 31);
745 }
746
747 #[test]
748 fn spi_read_too_short() {
749 let err = read_spi(&[0x00]).unwrap_err();
750 assert_eq!(err, Error::FrameTooShort);
751 }
752
753 #[test]
754 fn header_with_pad_length() {
755 let sa = SecurityAssociation {
756 spi: 10,
757 service_type: ServiceType::Encryption,
758 iv_len: 0,
759 sn_len: 0,
760 pl_len: 2,
761 mac_len: 0,
762 sequence_number: 0,
763 sequence_window: 0,
764 auth_mask: heapless::Vec::new(),
765 };
766 let mut buf = [0u8; 16];
767 let written = write_security_header(&sa, &[], &[], 42, &mut buf).unwrap();
768 assert_eq!(written, 4);
770
771 let hdr = SecurityHeader::parse(&sa, &buf[..written]).unwrap();
772 assert_eq!(hdr.spi(), 10);
773 assert_eq!(hdr.pad_length(), 42);
774 }
775
776 fn aes_gcm_sa() -> SecurityAssociation {
777 SecurityAssociation {
778 spi: 5,
779 service_type: ServiceType::AuthenticatedEncryption,
780 iv_len: 12,
781 sn_len: 0,
782 pl_len: 0,
783 mac_len: 16,
784 sequence_number: 1,
785 sequence_window: 100,
786 auth_mask: heapless::Vec::new(),
787 }
788 }
789
790 #[test]
791 fn aes_gcm_128_roundtrip() {
792 let key = [0x42u8; 16];
793 let crypto = AesGcmCrypto::new_128(&key);
794 let sa = aes_gcm_sa();
795
796 let iv = [0u8; 12];
797 let aad = [0xC0, 0x00, 0x2A];
798 let original = [1u8, 2, 3, 4, 5, 6, 7, 8];
799 let mut data = original;
800 let mut tag = [0u8; 16];
801
802 crypto.encrypt(&sa, &iv, &aad, &mut data, &mut tag).unwrap();
803 assert_ne!(data, original);
804
805 crypto.decrypt(&sa, &iv, &aad, &mut data, &tag).unwrap();
806 assert_eq!(data, original);
807 }
808
809 #[test]
810 fn aes_gcm_256_roundtrip() {
811 let key = [0x7Fu8; 32];
812 let crypto = AesGcmCrypto::new_256(&key);
813 let sa = aes_gcm_sa();
814
815 let iv = [0u8; 12];
816 let aad = b"header";
817 let original = *b"secret!!";
818 let mut data = original;
819 let mut tag = [0u8; 16];
820
821 crypto.encrypt(&sa, &iv, aad, &mut data, &mut tag).unwrap();
822 crypto.decrypt(&sa, &iv, aad, &mut data, &tag).unwrap();
823 assert_eq!(data, original);
824 }
825
826 #[test]
827 fn aes_gcm_tampered_ciphertext() {
828 let key = [0x42u8; 16];
829 let crypto = AesGcmCrypto::new_128(&key);
830 let sa = aes_gcm_sa();
831
832 let iv = [0u8; 12];
833 let aad = [0xC0];
834 let mut data = [1u8, 2, 3, 4];
835 let mut tag = [0u8; 16];
836
837 crypto.encrypt(&sa, &iv, &aad, &mut data, &mut tag).unwrap();
838
839 data[0] ^= 0xFF; let err = crypto.decrypt(&sa, &iv, &aad, &mut data, &tag).unwrap_err();
841 assert_eq!(err, Error::MacVerificationFailed);
842 }
843
844 #[test]
845 fn aes_gcm_tampered_aad() {
846 let key = [0x42u8; 16];
847 let crypto = AesGcmCrypto::new_128(&key);
848 let sa = aes_gcm_sa();
849
850 let iv = [0u8; 12];
851 let aad = [0xC0];
852 let mut data = [1u8, 2, 3, 4];
853 let mut tag = [0u8; 16];
854
855 crypto.encrypt(&sa, &iv, &aad, &mut data, &mut tag).unwrap();
856
857 let bad_aad = [0xC1]; let err = crypto
859 .decrypt(&sa, &iv, &bad_aad, &mut data, &tag)
860 .unwrap_err();
861 assert_eq!(err, Error::MacVerificationFailed);
862 }
863
864 #[test]
865 fn aes_gcm_apply_process_roundtrip() {
866 let key = [0xABu8; 16];
867 let crypto = AesGcmCrypto::new_128(&key);
868
869 let mut sa_send = aes_gcm_sa();
870 let mut sa_recv = aes_gcm_sa();
871
872 let header_end = 5;
876 let data_start = header_end + sa_send.header_size();
877 let data_end = data_start + 10;
878 let total = data_end + sa_send.trailer_size();
879 let mut frame = [0u8; 64];
880
881 frame[0..5].copy_from_slice(&[0xC0, 0x00, 0x2A, 0x00, 0x28]);
883 frame[data_start..data_end].copy_from_slice(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
885
886 apply_security(
887 &mut sa_send,
888 &crypto,
889 &mut frame,
890 header_end,
891 data_start,
892 data_end,
893 )
894 .unwrap();
895
896 assert_eq!(sa_send.sequence_number, 2);
897 assert_ne!(
899 &frame[data_start..data_end],
900 &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
901 );
902 assert_ne!(&frame[data_end..total], &[0u8; 16]);
904
905 process_security(
907 &mut sa_recv,
908 &crypto,
909 &mut frame,
910 header_end,
911 data_start,
912 data_end,
913 )
914 .unwrap();
915
916 assert_eq!(
918 &frame[data_start..data_end],
919 &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
920 );
921 }
922
923 #[test]
924 fn aes_gcm_detect_tampered_frame() {
925 let key = [0xABu8; 16];
926 let crypto = AesGcmCrypto::new_128(&key);
927
928 let mut sa_send = aes_gcm_sa();
929 let mut sa_recv = aes_gcm_sa();
930
931 let header_end = 5;
932 let data_start = header_end + sa_send.header_size();
933 let data_end = data_start + 10;
934 let mut frame = [0u8; 64];
935
936 frame[0..5].copy_from_slice(&[0xC0, 0x00, 0x2A, 0x00, 0x28]);
937 frame[data_start..data_end].copy_from_slice(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
938
939 apply_security(
940 &mut sa_send,
941 &crypto,
942 &mut frame,
943 header_end,
944 data_start,
945 data_end,
946 )
947 .unwrap();
948
949 frame[data_start] ^= 0xFF;
951
952 let err = process_security(
953 &mut sa_recv,
954 &crypto,
955 &mut frame,
956 header_end,
957 data_start,
958 data_end,
959 )
960 .unwrap_err();
961 assert_eq!(err, Error::MacVerificationFailed);
962 }
963}