blob: 2d406a1a74ac9750a98b103804adc56ddcbdd7fe [file] [log] [blame]
use super::super::*;
use crate::err::ip_auth::IcvLenError;
use arrayvec::ArrayVec;
use core::fmt::{Debug, Formatter};
/// Deprecated use [IpAuthHeader] instead.
#[deprecated(since = "0.10.1", note = "Please use the type IpAuthHeader instead")]
pub type IPv6AuthenticationHeader = IpAuthHeader;
/// Deprecated use [IpAuthHeader] instead.
#[deprecated(since = "0.14.0", note = "Please use the type IpAuthHeader instead")]
pub type IpAuthenticationHeader = IpAuthHeader;
/// IP Authentication Header (rfc4302)
#[derive(Clone)]
pub struct IpAuthHeader {
/// IP protocol number specifying the next header or transport layer protocol.
///
/// See [IpNumber] or [ip_number] for a definition of the known values.
pub next_header: IpNumber,
/// Security Parameters Index
pub spi: u32,
/// This unsigned 32-bit field contains a counter value that
/// increases by one for each packet sent.
pub sequence_number: u32,
/// Length in 4-octets (maximum valid value is 0xfe) of data filled in the
/// `raw_icv_buffer`.
raw_icv_len: u8,
/// Buffer containing the "Encoded Integrity Check Value-ICV" (variable).
/// The length of the used data can be set via the `variable` (must be a multiple of 4 bytes).
raw_icv_buffer: [u8; 0xfe * 4],
}
impl Debug for IpAuthHeader {
fn fmt(&self, formatter: &mut Formatter) -> Result<(), core::fmt::Error> {
let mut s = formatter.debug_struct("IpAuthHeader");
s.field("next_header", &self.next_header);
s.field("spi", &self.spi);
s.field("sequence_number", &self.sequence_number);
s.field("raw_icv", &self.raw_icv());
s.finish()
}
}
impl PartialEq for IpAuthHeader {
fn eq(&self, other: &Self) -> bool {
self.next_header == other.next_header
&& self.spi == other.spi
&& self.sequence_number == other.sequence_number
&& self.raw_icv() == other.raw_icv()
}
}
impl Eq for IpAuthHeader {}
impl Default for IpAuthHeader {
fn default() -> Self {
IpAuthHeader {
next_header: IpNumber(255),
spi: 0,
sequence_number: 0,
raw_icv_len: 0,
raw_icv_buffer: [0; 0xfe * 4],
}
}
}
impl<'a> IpAuthHeader {
/// Minimum length of an IP authentication header in bytes/octets.
pub const MIN_LEN: usize = 4 + 4 + 4;
/// Maximum length of an IP authentication header in bytes/octets.
///
/// This number is calculated by taking the maximum value
/// that the "payload length" field supports (0xff) adding 2 and
/// multiplying the sum by 4 as the "payload length" specifies how
/// many 4 bytes words are present in the header.
pub const MAX_LEN: usize = 4 * (0xff + 2);
/// The maximum amount of bytes/octets that can be stored in the ICV
/// part of an IP authentication header.
pub const MAX_ICV_LEN: usize = 0xfe * 4;
/// Create a new authentication header with the given parameters.
///
/// Note: The length of the raw_icv slice must be a multiple of 4
/// and the maximum allowed length is 1016 bytes
/// (`IpAuthHeader::MAX_ICV_LEN`). If the slice length does
/// not fulfill these requirements the value is not copied and an
/// [`crate::err::ip_auth::IcvLenError`] is returned.
/// If successful an Ok(()) is returned.
pub fn new(
next_header: IpNumber,
spi: u32,
sequence_number: u32,
raw_icv: &'a [u8],
) -> Result<IpAuthHeader, IcvLenError> {
use IcvLenError::*;
if raw_icv.len() > IpAuthHeader::MAX_ICV_LEN {
Err(TooBig(raw_icv.len()))
} else if 0 != raw_icv.len() % 4 {
Err(Unaligned(raw_icv.len()))
} else {
let mut result = IpAuthHeader {
next_header,
spi,
sequence_number,
raw_icv_len: (raw_icv.len() / 4) as u8,
raw_icv_buffer: [0; IpAuthHeader::MAX_ICV_LEN],
};
result.raw_icv_buffer[..raw_icv.len()].copy_from_slice(raw_icv);
Ok(result)
}
}
/// Read an authentication header from a slice and return the header & unused parts of the slice.
pub fn from_slice(
slice: &'a [u8],
) -> Result<(IpAuthHeader, &'a [u8]), err::ip_auth::HeaderSliceError> {
let s = IpAuthHeaderSlice::from_slice(slice)?;
let rest = &slice[s.slice().len()..];
let header = s.to_header();
Ok((header, rest))
}
/// Read an authentication header from the current reader position.
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub fn read<T: std::io::Read + Sized>(
reader: &mut T,
) -> Result<IpAuthHeader, err::ip_auth::HeaderReadError> {
use err::ip_auth::HeaderError::*;
use err::ip_auth::HeaderReadError::*;
let start = {
let mut start = [0; 4 + 4 + 4];
reader.read_exact(&mut start).map_err(Io)?;
start
};
let next_header = IpNumber(start[0]);
let payload_len = start[1];
// payload len must be at least 1
if payload_len < 1 {
Err(Content(ZeroPayloadLen))
} else {
// read the rest of the header
Ok(IpAuthHeader {
next_header,
spi: u32::from_be_bytes([start[4], start[5], start[6], start[7]]),
sequence_number: u32::from_be_bytes([start[8], start[9], start[10], start[11]]),
raw_icv_len: payload_len - 1,
raw_icv_buffer: {
let mut buffer = [0; 0xfe * 4];
reader
.read_exact(&mut buffer[..usize::from(payload_len - 1) * 4])
.map_err(Io)?;
buffer
},
})
}
}
/// Read an authentication header from the current reader position
/// with a limited reader.
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub fn read_limited<T: std::io::Read + Sized>(
reader: &mut crate::io::LimitedReader<T>,
) -> Result<IpAuthHeader, err::ip_auth::HeaderLimitedReadError> {
use err::{
ip_auth::HeaderError::*,
ip_auth::HeaderLimitedReadError::{self, *},
Layer,
};
fn map_err(err: err::io::LimitedReadError) -> HeaderLimitedReadError {
use err::io::LimitedReadError as I;
match err {
I::Io(err) => Io(err),
I::Len(err) => Len(err),
}
}
// notify reader of layer start
reader.start_layer(Layer::IpAuthHeader);
let start = {
let mut start = [0; 4 + 4 + 4];
reader.read_exact(&mut start).map_err(map_err)?;
start
};
let next_header = IpNumber(start[0]);
let payload_len = start[1];
// payload len must be at least 1
if payload_len < 1 {
Err(Content(ZeroPayloadLen))
} else {
// read the rest of the header
Ok(IpAuthHeader {
next_header,
spi: u32::from_be_bytes([start[4], start[5], start[6], start[7]]),
sequence_number: u32::from_be_bytes([start[8], start[9], start[10], start[11]]),
raw_icv_len: payload_len - 1,
raw_icv_buffer: {
let mut buffer = [0; 0xfe * 4];
reader
.read_exact(&mut buffer[..usize::from(payload_len - 1) * 4])
.map_err(map_err)?;
buffer
},
})
}
}
/// Returns a slice the raw icv value.
pub fn raw_icv(&self) -> &[u8] {
&self.raw_icv_buffer[..usize::from(self.raw_icv_len) * 4]
}
/// Sets the icv value to the given raw value. The length of the slice must be
/// a multiple of 4 and the maximum allowed length is 1016 bytes
/// (`IpAuthHeader::MAX_ICV_LEN`). If the slice length does
/// not fulfill these requirements the value is not copied and an
/// [`crate::err::ip_auth::IcvLenError`] is returned.
/// If successful an Ok(()) is returned.
pub fn set_raw_icv(&mut self, raw_icv: &[u8]) -> Result<(), IcvLenError> {
use IcvLenError::*;
if raw_icv.len() > IpAuthHeader::MAX_ICV_LEN {
Err(TooBig(raw_icv.len()))
} else if 0 != raw_icv.len() % 4 {
Err(Unaligned(raw_icv.len()))
} else {
self.raw_icv_buffer[..raw_icv.len()].copy_from_slice(raw_icv);
self.raw_icv_len = (raw_icv.len() / 4) as u8;
Ok(())
}
}
/// Writes the given authentication header to the current position.
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub fn write<T: std::io::Write + Sized>(&self, writer: &mut T) -> Result<(), std::io::Error> {
let spi_be = self.spi.to_be_bytes();
let sequence_number_be = self.sequence_number.to_be_bytes();
debug_assert!(self.raw_icv_len != 0xff);
writer.write_all(&[
self.next_header.0,
self.raw_icv_len + 1,
0,
0,
spi_be[0],
spi_be[1],
spi_be[2],
spi_be[3],
sequence_number_be[0],
sequence_number_be[1],
sequence_number_be[2],
sequence_number_be[3],
])?;
writer.write_all(self.raw_icv())?;
Ok(())
}
///Length of the header in bytes.
pub fn header_len(&self) -> usize {
12 + usize::from(self.raw_icv_len) * 4
}
/// Returns the serialized header.
pub fn to_bytes(&self) -> ArrayVec<u8, { IpAuthHeader::MAX_LEN }> {
let spi_be = self.spi.to_be_bytes();
let seq_be = self.sequence_number.to_be_bytes();
let mut result = ArrayVec::<u8, { IpAuthHeader::MAX_LEN }>::new();
result.extend([
self.next_header.0,
self.raw_icv_len + 1,
0,
0,
spi_be[0],
spi_be[1],
spi_be[2],
spi_be[3],
seq_be[0],
seq_be[1],
seq_be[2],
seq_be[3],
]);
result.extend(self.raw_icv_buffer);
// SAFETY: Safe as the header len can not exceed the maximum length
// of the header.
unsafe {
result.set_len(self.header_len());
}
result
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::{
err::{Layer, LenError},
io::LimitedReader,
test_gens::*,
};
use alloc::{format, vec::Vec};
use err::ip_auth::HeaderError::*;
use proptest::prelude::*;
use std::io::Cursor;
#[test]
fn default() {
let default_header = IpAuthHeader {
..Default::default()
};
assert_eq!(default_header.next_header, IpNumber(255));
assert_eq!(default_header.spi, 0);
assert_eq!(default_header.sequence_number, 0);
assert_eq!(default_header.raw_icv_len, 0);
assert_eq!(default_header.raw_icv_buffer, [0; 0xfe * 4]);
}
proptest! {
#[test]
fn debug(input in ip_auth_any()) {
assert_eq!(
&format!(
"IpAuthHeader {{ next_header: {:?}, spi: {}, sequence_number: {}, raw_icv: {:?} }}",
input.next_header,
input.spi,
input.sequence_number,
input.raw_icv()),
&format!("{:?}", input)
);
}
}
#[test]
pub fn clone() {
let a = IpAuthHeader::new(0.into(), 0, 0, &[0; 4]);
assert_eq!(a.clone(), a);
}
#[test]
pub fn partial_eq() {
let a = IpAuthHeader::new(0.into(), 0, 0, &[0; 4]);
//equal
assert!(a == IpAuthHeader::new(0.into(), 0, 0, &[0; 4]));
//not equal tests
assert!(a != IpAuthHeader::new(1.into(), 0, 0, &[0; 4]));
assert!(a != IpAuthHeader::new(0.into(), 1, 0, &[0; 4]));
assert!(a != IpAuthHeader::new(0.into(), 0, 1, &[0; 4]));
assert!(a != IpAuthHeader::new(0.into(), 0, 0, &[0, 1, 0, 0]));
assert!(a != IpAuthHeader::new(0.into(), 0, 1, &[]));
assert!(a != IpAuthHeader::new(0.into(), 0, 1, &[0; 8]));
}
#[test]
fn new_and_set_icv() {
use IcvLenError::*;
struct Test {
icv: &'static [u8],
err: Option<IcvLenError>,
}
let tests = [
// ok
Test {
icv: &[],
err: None,
},
Test {
icv: &[1, 2, 3, 4],
err: None,
},
Test {
icv: &[1, 2, 3, 4, 5, 6, 7, 8],
err: None,
},
Test {
icv: &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
err: None,
},
Test {
icv: &[0; 0xfe * 4],
err: None,
},
// unaligned
Test {
icv: &[1],
err: Some(Unaligned(1)),
},
Test {
icv: &[1, 2, 3],
err: Some(Unaligned(3)),
},
Test {
icv: &[1, 2, 3, 4, 5],
err: Some(Unaligned(5)),
},
Test {
icv: &[1, 2, 3, 4, 5, 6, 7],
err: Some(Unaligned(7)),
},
// too big
Test {
icv: &[0; 0xff * 4],
err: Some(TooBig(0xff * 4)),
},
];
for test in tests.iter() {
// new
{
let a = IpAuthHeader::new(5.into(), 6, 7, test.icv);
if let Some(err) = &test.err {
assert_eq!(Err(err.clone()), a);
} else {
let unwrapped = a.unwrap();
assert_eq!(IpNumber(5), unwrapped.next_header);
assert_eq!(6, unwrapped.spi);
assert_eq!(7, unwrapped.sequence_number);
assert_eq!(test.icv, unwrapped.raw_icv());
}
}
// set_raw_icv
{
let mut header = IpAuthHeader::new(5.into(), 6, 7, &[0; 4]).unwrap();
let result = header.set_raw_icv(test.icv);
assert_eq!(IpNumber(5), header.next_header);
assert_eq!(6, header.spi);
assert_eq!(7, header.sequence_number);
if let Some(err) = &test.err {
assert_eq!(Err(err.clone()), result);
assert_eq!(&[0; 4], header.raw_icv());
} else {
assert_eq!(Ok(()), result);
assert_eq!(test.icv, header.raw_icv());
}
}
}
}
proptest! {
#[test]
fn from_slice(header in ip_auth_any()) {
use err::ip_auth::HeaderSliceError::*;
// ok
{
let mut bytes = ArrayVec::<u8, {IpAuthHeader::MAX_LEN + 2}>::new();
bytes.extend(header.to_bytes());
bytes.push(1);
bytes.push(2);
let (actual_header, actual_slice) = IpAuthHeader::from_slice(&bytes).unwrap();
assert_eq!(header, actual_header);
assert_eq!(&[1,2], actual_slice);
}
// length error
{
let bytes = header.to_bytes();
for len in 0..header.header_len() {
assert_eq!(
IpAuthHeader::from_slice(&bytes[..len]).unwrap_err(),
Len(err::LenError{
required_len: if len < IpAuthHeader::MIN_LEN {
IpAuthHeader::MIN_LEN
} else {
header.header_len()
},
len: len,
len_source: LenSource::Slice,
layer: err::Layer::IpAuthHeader,
layer_start_offset: 0,
})
);
}
}
// payload length error
{
let mut bytes = header.to_bytes();
// set payload length to 0
bytes[1] = 0;
assert_eq!(
IpAuthHeader::from_slice(&bytes).unwrap_err(),
Content(ZeroPayloadLen)
);
}
}
}
proptest! {
#[test]
fn read(header in ip_auth_any()) {
// ok
{
let bytes = header.to_bytes();
let mut cursor = Cursor::new(&bytes);
assert_eq!(header, IpAuthHeader::read(&mut cursor).unwrap());
}
// length error
{
let bytes = header.to_bytes();
for len in 0..header.header_len() {
let mut cursor = Cursor::new(&bytes[..len]);
assert!(
IpAuthHeader::read(&mut cursor)
.unwrap_err()
.io()
.is_some()
);
}
}
// payload length error
{
let mut bytes = header.to_bytes();
// set payload length to 0
bytes[1] = 0;
let mut cursor = Cursor::new(&bytes);
assert_eq!(
IpAuthHeader::read(&mut cursor).unwrap_err().content(),
Some(ZeroPayloadLen)
);
}
}
}
proptest! {
#[test]
fn read_limited(header in ip_auth_any()) {
// ok
{
let bytes = header.to_bytes();
let mut cursor = Cursor::new(&bytes);
let mut reader = LimitedReader::new(
&mut cursor,
bytes.len(),
LenSource::Slice,
0,
Layer::Ipv4Header
);
assert_eq!(header, IpAuthHeader::read_limited(&mut reader).unwrap());
}
// length error
{
let bytes = header.to_bytes();
for len in 0..header.header_len() {
// io error
{
let mut cursor = Cursor::new(&bytes[..len]);
let mut reader = LimitedReader::new(
&mut cursor,
bytes.len(),
LenSource::Slice,
0,
Layer::Ipv4Header
);
assert!(
IpAuthHeader::read_limited(&mut reader)
.unwrap_err()
.io()
.is_some()
);
}
// limited reader error
{
let mut cursor = Cursor::new(&bytes);
let mut reader = LimitedReader::new(
&mut cursor,
len,
LenSource::Ipv4HeaderTotalLen,
0,
Layer::Ipv4Header
);
assert_eq!(
IpAuthHeader::read_limited(&mut reader)
.unwrap_err()
.len()
.unwrap(),
LenError {
required_len: if len < 12 {
12
} else {
bytes.len()
},
len,
len_source: LenSource::Ipv4HeaderTotalLen,
layer: Layer::IpAuthHeader,
layer_start_offset: 0
}
);
}
}
}
// payload length error
{
let mut bytes = header.to_bytes();
// set payload length to 0
bytes[1] = 0;
let mut cursor = Cursor::new(&bytes);
let mut reader = LimitedReader::new(
&mut cursor,
bytes.len(),
LenSource::Ipv4HeaderTotalLen,
0,
Layer::Ipv4Header
);
assert_eq!(
IpAuthHeader::read_limited(&mut reader).unwrap_err().content(),
Some(ZeroPayloadLen)
);
}
}
}
proptest! {
#[test]
fn write(header in ip_auth_any()) {
// ok case
{
let mut buffer: Vec<u8> = Vec::with_capacity(header.header_len());
header.write(&mut buffer).unwrap();
assert_eq!(header, IpAuthHeader::from_slice(&buffer).unwrap().0);
};
// io error
for len in 0..header.header_len() {
let mut buffer = [0u8;IpAuthHeader::MAX_LEN];
let mut cursor = Cursor::new(&mut buffer[..len]);
assert!(header.write(&mut cursor).is_err());
}
}
}
proptest! {
#[test]
fn header_len(header in ip_auth_any()) {
assert_eq!(header.header_len(), header.raw_icv().len() + 12);
}
}
proptest! {
#[test]
fn to_bytes(header in ip_auth_any()) {
let bytes = header.to_bytes();
assert_eq!(header.next_header.0, bytes[0]);
assert_eq!((header.header_len()/4 - 2) as u8, bytes[1]);
assert_eq!(0, bytes[2]);
assert_eq!(0, bytes[3]);
{
let spi = u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
assert_eq!(spi, header.spi);
}
{
let seq_nr = u32::from_be_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]);
assert_eq!(seq_nr, header.sequence_number);
}
assert_eq!(&bytes[12..], header.raw_icv());
}
}
}