#![allow(missing_docs)]
use super::ParseError;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ProtocolId {
Ike, Ah, Esp, Other(u8),
}
impl ProtocolId {
pub fn from_u8(v: u8) -> Self {
match v {
1 => Self::Ike,
2 => Self::Ah,
3 => Self::Esp,
other => Self::Other(other),
}
}
pub fn as_u8(self) -> u8 {
match self {
Self::Ike => 1,
Self::Ah => 2,
Self::Esp => 3,
Self::Other(v) => v,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TransformType {
Encr, Prf, Integ, Dh, Esn, Other(u8),
}
impl TransformType {
pub fn from_u8(v: u8) -> Self {
match v {
1 => Self::Encr,
2 => Self::Prf,
3 => Self::Integ,
4 => Self::Dh,
5 => Self::Esn,
other => Self::Other(other),
}
}
pub fn as_u8(self) -> u8 {
match self {
Self::Encr => 1,
Self::Prf => 2,
Self::Integ => 3,
Self::Dh => 4,
Self::Esn => 5,
Self::Other(v) => v,
}
}
}
#[derive(Debug, Clone)]
pub struct Attribute {
pub attr_type: u16,
pub value: AttrValue,
}
#[derive(Debug, Clone)]
pub enum AttrValue {
Tv(u16),
Tlv(Vec<u8>),
}
impl Attribute {
pub const KEY_LENGTH: u16 = 14;
fn parse(bytes: &[u8]) -> Result<(Self, usize), ParseError> {
if bytes.len() < 4 {
return Err(ParseError::Truncated {
what: "transform attribute",
need: 4,
got: bytes.len(),
});
}
let af_and_type = u16::from_be_bytes([bytes[0], bytes[1]]);
let attr_type = af_and_type & 0x7FFF;
let af = af_and_type & 0x8000 != 0;
if af {
let value = u16::from_be_bytes([bytes[2], bytes[3]]);
Ok((
Self {
attr_type,
value: AttrValue::Tv(value),
},
4,
))
} else {
let len = u16::from_be_bytes([bytes[2], bytes[3]]) as usize;
let total = 4 + len;
if bytes.len() < total {
return Err(ParseError::Truncated {
what: "TLV attribute body",
need: total,
got: bytes.len(),
});
}
Ok((
Self {
attr_type,
value: AttrValue::Tlv(bytes[4..total].to_vec()),
},
total,
))
}
}
pub fn write_into(&self, out: &mut Vec<u8>) {
match &self.value {
AttrValue::Tv(v) => {
out.extend_from_slice(&(self.attr_type | 0x8000).to_be_bytes());
out.extend_from_slice(&v.to_be_bytes());
}
AttrValue::Tlv(b) => {
out.extend_from_slice(&(self.attr_type & 0x7FFF).to_be_bytes());
out.extend_from_slice(&(b.len() as u16).to_be_bytes());
out.extend_from_slice(b);
}
}
}
pub fn encoded_len(&self) -> usize {
match &self.value {
AttrValue::Tv(_) => 4,
AttrValue::Tlv(b) => 4 + b.len(),
}
}
}
#[derive(Debug, Clone)]
pub struct Transform {
pub transform_type: TransformType,
pub transform_id: u16,
pub attributes: Vec<Attribute>,
}
impl Transform {
pub fn key_length(&self) -> Option<u16> {
self.attributes.iter().find_map(|a| {
if a.attr_type == Attribute::KEY_LENGTH
&& let AttrValue::Tv(v) = a.value
{
return Some(v);
}
None
})
}
fn parse(bytes: &[u8]) -> Result<(Self, u8, usize), ParseError> {
if bytes.len() < 8 {
return Err(ParseError::Truncated {
what: "transform header",
need: 8,
got: bytes.len(),
});
}
let last = bytes[0];
let length = u16::from_be_bytes([bytes[2], bytes[3]]) as usize;
if length < 8 || bytes.len() < length {
return Err(ParseError::Truncated {
what: "transform body",
need: length,
got: bytes.len(),
});
}
let transform_type = TransformType::from_u8(bytes[4]);
let transform_id = u16::from_be_bytes([bytes[6], bytes[7]]);
let mut attrs = Vec::new();
let mut cur = 8;
while cur < length {
let (attr, used) = Attribute::parse(&bytes[cur..length])?;
attrs.push(attr);
cur += used;
}
Ok((
Self {
transform_type,
transform_id,
attributes: attrs,
},
last,
length,
))
}
pub fn write_into(&self, out: &mut Vec<u8>, last_substruc: u8) {
let attr_len: usize = self.attributes.iter().map(|a| a.encoded_len()).sum();
let total = 8 + attr_len;
out.push(last_substruc);
out.push(0); out.extend_from_slice(&(total as u16).to_be_bytes());
out.push(self.transform_type.as_u8());
out.push(0); out.extend_from_slice(&self.transform_id.to_be_bytes());
for a in &self.attributes {
a.write_into(out);
}
}
}
#[derive(Debug, Clone)]
pub struct Proposal {
pub proposal_num: u8,
pub protocol: ProtocolId,
pub spi: Vec<u8>,
pub transforms: Vec<Transform>,
}
impl Proposal {
fn parse(bytes: &[u8]) -> Result<(Self, u8, usize), ParseError> {
if bytes.len() < 8 {
return Err(ParseError::Truncated {
what: "proposal header",
need: 8,
got: bytes.len(),
});
}
let last = bytes[0];
let length = u16::from_be_bytes([bytes[2], bytes[3]]) as usize;
let proposal_num = bytes[4];
let protocol = ProtocolId::from_u8(bytes[5]);
let spi_size = bytes[6] as usize;
let num_xforms = bytes[7] as usize;
if length < 8 + spi_size || bytes.len() < length {
return Err(ParseError::Truncated {
what: "proposal body",
need: length,
got: bytes.len(),
});
}
let spi = bytes[8..8 + spi_size].to_vec();
let mut transforms = Vec::with_capacity(num_xforms);
let mut cur = 8 + spi_size;
for _ in 0..num_xforms {
if cur >= length {
return Err(ParseError::BadLength {
what: "proposal transforms exceed proposal length",
value: length,
});
}
let (xform, _last, used) = Transform::parse(&bytes[cur..length])?;
transforms.push(xform);
cur += used;
}
Ok((
Self {
proposal_num,
protocol,
spi,
transforms,
},
last,
length,
))
}
pub fn encoded_len(&self) -> usize {
let xform_len: usize = self
.transforms
.iter()
.map(|t| 8 + t.attributes.iter().map(|a| a.encoded_len()).sum::<usize>())
.sum();
8 + self.spi.len() + xform_len
}
pub fn write_into(&self, out: &mut Vec<u8>, last_substruc: u8) {
let total = self.encoded_len();
out.push(last_substruc);
out.push(0);
out.extend_from_slice(&(total as u16).to_be_bytes());
out.push(self.proposal_num);
out.push(self.protocol.as_u8());
out.push(self.spi.len() as u8);
out.push(self.transforms.len() as u8);
out.extend_from_slice(&self.spi);
for (i, t) in self.transforms.iter().enumerate() {
let last = if i + 1 == self.transforms.len() { 0 } else { 3 };
t.write_into(out, last);
}
}
}
#[derive(Debug, Clone)]
pub struct SaPayload {
pub proposals: Vec<Proposal>,
}
impl SaPayload {
pub fn parse(body: &[u8]) -> Result<Self, ParseError> {
let mut proposals = Vec::new();
let mut cur = 0;
loop {
if cur >= body.len() {
break;
}
let (prop, last, used) = Proposal::parse(&body[cur..])?;
proposals.push(prop);
cur += used;
if last == 0 {
break;
}
}
Ok(Self { proposals })
}
pub fn encoded_len(&self) -> usize {
self.proposals.iter().map(|p| p.encoded_len()).sum()
}
pub fn write_body(&self, out: &mut Vec<u8>) {
for (i, p) in self.proposals.iter().enumerate() {
let last = if i + 1 == self.proposals.len() { 0 } else { 2 };
p.write_into(out, last);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
const SA_BODY_HEX: &str = "
02 00 00 2C 01 01 00 04 03 00 00 0C 01 00 00 14
80 0E 01 00 03 00 00 08 02 00 00 05 03 00 00 08
06 00 00 24 00 00 00 08 04 00 00 13 02 00 00 2C
02 01 00 04 03 00 00 0C 01 00 00 14 80 0E 01 00
03 00 00 08 02 00 00 05 03 00 00 08 06 00 00 24
00 00 00 08 04 00 00 0E 02 00 00 34 03 01 00 05
03 00 00 0C 01 00 00 0C 80 0E 01 00 03 00 00 08
03 00 00 0C 03 00 00 08 02 00 00 05 03 00 00 08
06 00 00 24 00 00 00 08 04 00 00 13 02 00 00 34
04 01 00 05 03 00 00 0C 01 00 00 0C 80 0E 01 00
03 00 00 08 03 00 00 0C 03 00 00 08 02 00 00 05
03 00 00 08 06 00 00 24 00 00 00 08 04 00 00 0E
02 00 00 24 05 01 00 03 03 00 00 0C 01 00 00 14
80 0E 01 00 03 00 00 08 02 00 00 05 00 00 00 08
04 00 00 13 02 00 00 24 06 01 00 03 03 00 00 0C
01 00 00 14 80 0E 01 00 03 00 00 08 02 00 00 05
00 00 00 08 04 00 00 0E 02 00 00 2C 07 01 00 04
03 00 00 0C 01 00 00 0C 80 0E 01 00 03 00 00 08
03 00 00 0C 03 00 00 08 02 00 00 05 00 00 00 08
04 00 00 13 00 00 00 2C 08 01 00 04 03 00 00 0C
01 00 00 0C 80 0E 01 00 03 00 00 08 03 00 00 0C
03 00 00 08 02 00 00 05 00 00 00 08 04 00 00 0E
";
fn from_hex(s: &str) -> Vec<u8> {
s.split_ascii_whitespace()
.map(|h| u8::from_str_radix(h, 16).unwrap())
.collect()
}
#[test]
fn parse_real_ios_sa_body() {
let body = from_hex(SA_BODY_HEX);
let sa = SaPayload::parse(&body).expect("parse");
assert_eq!(sa.proposals.len(), 8);
let p1 = &sa.proposals[0];
assert_eq!(p1.proposal_num, 1);
assert_eq!(p1.protocol, ProtocolId::Ike);
assert_eq!(p1.spi.len(), 0);
assert_eq!(p1.transforms.len(), 4);
assert_eq!(p1.transforms[0].transform_type, TransformType::Encr);
assert_eq!(p1.transforms[0].transform_id, 20); assert_eq!(p1.transforms[0].key_length(), Some(256));
assert_eq!(p1.transforms[1].transform_type, TransformType::Prf);
assert_eq!(p1.transforms[1].transform_id, 5); assert_eq!(p1.transforms[3].transform_type, TransformType::Dh);
assert_eq!(p1.transforms[3].transform_id, 19); }
#[test]
fn round_trip_real_ios_sa_body() {
let body = from_hex(SA_BODY_HEX);
let sa = SaPayload::parse(&body).expect("parse");
let mut buf = Vec::new();
sa.write_body(&mut buf);
assert_eq!(buf, body);
}
}