#![allow(missing_docs)]
use super::ParseError;
#[derive(Debug, Clone)]
pub struct KePayload {
pub dh_group: u16,
pub key_data: Vec<u8>,
}
impl KePayload {
pub fn parse(body: &[u8]) -> Result<Self, ParseError> {
if body.len() < 4 {
return Err(ParseError::Truncated {
what: "KE payload",
need: 4,
got: body.len(),
});
}
Ok(Self {
dh_group: u16::from_be_bytes([body[0], body[1]]),
key_data: body[4..].to_vec(),
})
}
pub fn write_body(&self, out: &mut Vec<u8>) {
out.extend_from_slice(&self.dh_group.to_be_bytes());
out.extend_from_slice(&[0, 0]); out.extend_from_slice(&self.key_data);
}
pub fn encoded_len(&self) -> usize {
4 + self.key_data.len()
}
}
#[derive(Debug, Clone)]
pub struct NoncePayload(pub Vec<u8>);
impl NoncePayload {
pub fn parse(body: &[u8]) -> Result<Self, ParseError> {
Ok(Self(body.to_vec()))
}
pub fn write_body(&self, out: &mut Vec<u8>) {
out.extend_from_slice(&self.0);
}
pub fn encoded_len(&self) -> usize {
self.0.len()
}
}
pub mod notify_type {
pub const UNSUPPORTED_CRITICAL_PAYLOAD: u16 = 1;
pub const INVALID_IKE_SPI: u16 = 4;
pub const INVALID_MAJOR_VERSION: u16 = 5;
pub const INVALID_SYNTAX: u16 = 7;
pub const INVALID_MESSAGE_ID: u16 = 9;
pub const NO_PROPOSAL_CHOSEN: u16 = 14;
pub const INVALID_KE_PAYLOAD: u16 = 17;
pub const AUTHENTICATION_FAILED: u16 = 24;
pub const NAT_DETECTION_SOURCE_IP: u16 = 16388;
pub const NAT_DETECTION_DESTINATION_IP: u16 = 16389;
pub const COOKIE: u16 = 16390;
pub const REKEY_SA: u16 = 16393;
pub const ESP_TFC_PADDING_NOT_SUPPORTED: u16 = 16394;
pub const NON_FIRST_FRAGMENTS_ALSO: u16 = 16395;
pub const MOBIKE_SUPPORTED: u16 = 16396;
pub const ADDITIONAL_IP4_ADDRESS: u16 = 16397;
pub const ADDITIONAL_IP6_ADDRESS: u16 = 16398;
pub const NO_ADDITIONAL_ADDRESSES: u16 = 16399;
pub const UPDATE_SA_ADDRESSES: u16 = 16400;
pub const COOKIE2: u16 = 16401;
pub const NO_NATS_ALLOWED: u16 = 16402;
pub const FRAGMENTATION_SUPPORTED: u16 = 16430;
pub const SIGNATURE_HASH_ALGORITHMS: u16 = 16431;
pub const REDIRECT_SUPPORTED: u16 = 16406;
pub const REDIRECTED_FROM: u16 = 16408;
pub const IKEV2_FRAGMENTATION_SUPPORTED: u16 = 16430;
}
#[derive(Debug, Clone)]
pub struct NotifyPayload {
pub protocol_id: u8,
pub notify_type: u16,
pub spi: Vec<u8>,
pub data: Vec<u8>,
}
impl NotifyPayload {
pub fn parse(body: &[u8]) -> Result<Self, ParseError> {
if body.len() < 4 {
return Err(ParseError::Truncated {
what: "Notify payload",
need: 4,
got: body.len(),
});
}
let protocol_id = body[0];
let spi_size = body[1] as usize;
let notify_type = u16::from_be_bytes([body[2], body[3]]);
if body.len() < 4 + spi_size {
return Err(ParseError::Truncated {
what: "Notify SPI",
need: 4 + spi_size,
got: body.len(),
});
}
let spi = body[4..4 + spi_size].to_vec();
let data = body[4 + spi_size..].to_vec();
Ok(Self {
protocol_id,
notify_type,
spi,
data,
})
}
pub fn write_body(&self, out: &mut Vec<u8>) {
out.push(self.protocol_id);
out.push(self.spi.len() as u8);
out.extend_from_slice(&self.notify_type.to_be_bytes());
out.extend_from_slice(&self.spi);
out.extend_from_slice(&self.data);
}
pub fn encoded_len(&self) -> usize {
4 + self.spi.len() + self.data.len()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IdType {
Ipv4Addr, Fqdn, Rfc822Addr, Ipv6Addr, DerAsn1Dn, DerAsn1Gn, KeyId, Other(u8),
}
impl IdType {
pub fn from_u8(v: u8) -> Self {
match v {
1 => Self::Ipv4Addr,
2 => Self::Fqdn,
3 => Self::Rfc822Addr,
5 => Self::Ipv6Addr,
9 => Self::DerAsn1Dn,
10 => Self::DerAsn1Gn,
11 => Self::KeyId,
other => Self::Other(other),
}
}
pub fn as_u8(self) -> u8 {
match self {
Self::Ipv4Addr => 1,
Self::Fqdn => 2,
Self::Rfc822Addr => 3,
Self::Ipv6Addr => 5,
Self::DerAsn1Dn => 9,
Self::DerAsn1Gn => 10,
Self::KeyId => 11,
Self::Other(v) => v,
}
}
}
#[derive(Debug, Clone)]
pub struct IdPayload {
pub id_type: IdType,
pub data: Vec<u8>,
}
impl IdPayload {
pub fn parse(body: &[u8]) -> Result<Self, ParseError> {
if body.len() < 4 {
return Err(ParseError::Truncated {
what: "ID payload",
need: 4,
got: body.len(),
});
}
Ok(Self {
id_type: IdType::from_u8(body[0]),
data: body[4..].to_vec(),
})
}
pub fn write_body(&self, out: &mut Vec<u8>) {
out.push(self.id_type.as_u8());
out.extend_from_slice(&[0, 0, 0]); out.extend_from_slice(&self.data);
}
pub fn encoded_len(&self) -> usize {
4 + self.data.len()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AuthMethod {
RsaDigitalSignature, SharedKeyMic, DssDigitalSignature, EcdsaSha256P256, EcdsaSha384P384, EcdsaSha512P521, DigitalSignature, Other(u8),
}
impl AuthMethod {
pub fn from_u8(v: u8) -> Self {
match v {
1 => Self::RsaDigitalSignature,
2 => Self::SharedKeyMic,
3 => Self::DssDigitalSignature,
9 => Self::EcdsaSha256P256,
10 => Self::EcdsaSha384P384,
11 => Self::EcdsaSha512P521,
14 => Self::DigitalSignature,
other => Self::Other(other),
}
}
pub fn as_u8(self) -> u8 {
match self {
Self::RsaDigitalSignature => 1,
Self::SharedKeyMic => 2,
Self::DssDigitalSignature => 3,
Self::EcdsaSha256P256 => 9,
Self::EcdsaSha384P384 => 10,
Self::EcdsaSha512P521 => 11,
Self::DigitalSignature => 14,
Self::Other(v) => v,
}
}
}
#[derive(Debug, Clone)]
pub struct AuthPayload {
pub method: AuthMethod,
pub data: Vec<u8>,
}
impl AuthPayload {
pub fn parse(body: &[u8]) -> Result<Self, ParseError> {
if body.len() < 4 {
return Err(ParseError::Truncated {
what: "AUTH payload",
need: 4,
got: body.len(),
});
}
Ok(Self {
method: AuthMethod::from_u8(body[0]),
data: body[4..].to_vec(),
})
}
pub fn write_body(&self, out: &mut Vec<u8>) {
out.push(self.method.as_u8());
out.extend_from_slice(&[0, 0, 0]);
out.extend_from_slice(&self.data);
}
pub fn encoded_len(&self) -> usize {
4 + self.data.len()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TsType {
Ipv4AddrRange, Ipv6AddrRange, Other(u8),
}
impl TsType {
pub fn from_u8(v: u8) -> Self {
match v {
7 => Self::Ipv4AddrRange,
8 => Self::Ipv6AddrRange,
other => Self::Other(other),
}
}
pub fn as_u8(self) -> u8 {
match self {
Self::Ipv4AddrRange => 7,
Self::Ipv6AddrRange => 8,
Self::Other(v) => v,
}
}
}
#[derive(Debug, Clone)]
pub struct TrafficSelector {
pub ts_type: TsType,
pub ip_protocol: u8,
pub start_port: u16,
pub end_port: u16,
pub start_addr: Vec<u8>,
pub end_addr: Vec<u8>,
}
impl TrafficSelector {
fn parse(bytes: &[u8]) -> Result<(Self, usize), ParseError> {
if bytes.len() < 8 {
return Err(ParseError::Truncated {
what: "TS substructure",
need: 8,
got: bytes.len(),
});
}
let ts_type = TsType::from_u8(bytes[0]);
let ip_protocol = bytes[1];
let length = u16::from_be_bytes([bytes[2], bytes[3]]) as usize;
if length < 8 || bytes.len() < length {
return Err(ParseError::Truncated {
what: "TS body",
need: length,
got: bytes.len(),
});
}
let start_port = u16::from_be_bytes([bytes[4], bytes[5]]);
let end_port = u16::from_be_bytes([bytes[6], bytes[7]]);
let addr_len = (length - 8) / 2;
let start_addr = bytes[8..8 + addr_len].to_vec();
let end_addr = bytes[8 + addr_len..length].to_vec();
Ok((
Self {
ts_type,
ip_protocol,
start_port,
end_port,
start_addr,
end_addr,
},
length,
))
}
fn encoded_len(&self) -> usize {
8 + self.start_addr.len() + self.end_addr.len()
}
fn write_into(&self, out: &mut Vec<u8>) {
out.push(self.ts_type.as_u8());
out.push(self.ip_protocol);
out.extend_from_slice(&(self.encoded_len() as u16).to_be_bytes());
out.extend_from_slice(&self.start_port.to_be_bytes());
out.extend_from_slice(&self.end_port.to_be_bytes());
out.extend_from_slice(&self.start_addr);
out.extend_from_slice(&self.end_addr);
}
}
#[derive(Debug, Clone)]
pub struct TsPayload {
pub selectors: Vec<TrafficSelector>,
}
impl TsPayload {
pub fn parse(body: &[u8]) -> Result<Self, ParseError> {
if body.len() < 4 {
return Err(ParseError::Truncated {
what: "TS payload",
need: 4,
got: body.len(),
});
}
let num = body[0] as usize;
let mut selectors = Vec::with_capacity(num);
let mut cur = 4;
for _ in 0..num {
let (ts, used) = TrafficSelector::parse(&body[cur..])?;
selectors.push(ts);
cur += used;
}
Ok(Self { selectors })
}
pub fn write_body(&self, out: &mut Vec<u8>) {
out.push(self.selectors.len() as u8);
out.extend_from_slice(&[0, 0, 0]);
for ts in &self.selectors {
ts.write_into(out);
}
}
pub fn encoded_len(&self) -> usize {
4 + self
.selectors
.iter()
.map(|t| t.encoded_len())
.sum::<usize>()
}
}
#[derive(Debug, Clone)]
pub struct EncryptedPayload<'a> {
pub iv: &'a [u8],
pub ciphertext: &'a [u8],
pub icv: &'a [u8],
}
impl<'a> EncryptedPayload<'a> {
pub fn split(body: &'a [u8], iv_len: usize, icv_len: usize) -> Result<Self, ParseError> {
if body.len() < iv_len + icv_len {
return Err(ParseError::Truncated {
what: "SK body",
need: iv_len + icv_len,
got: body.len(),
});
}
let (iv, rest) = body.split_at(iv_len);
let (ciphertext, icv) = rest.split_at(rest.len() - icv_len);
Ok(Self {
iv,
ciphertext,
icv,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
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_ke() {
let body = from_hex(
"
00 13 00 00 76 4C 55 51 F0 73 E8 8E
AE 62 8C 82 10 32 D3 DB 10 7A 24 27 9C B4 A0 F0
A0 31 C3 FF 8E D5 10 7A 01 43 CC 3F 51 0A 66 4A
15 7D EF 81 D0 55 FD 58 60 BC 71 9A C7 FA 2C 13
EB 8A DD CA 3E 71 53 2D
",
);
let ke = KePayload::parse(&body).expect("parse");
assert_eq!(ke.dh_group, 19);
assert_eq!(ke.key_data.len(), 64);
}
#[test]
fn parse_real_notify_nat_dest() {
let body = from_hex(
"
00 00 40 05 D2 DC 28 01 6E 4C 2F 7B
2B 61 97 BD 71 00 13 35 8C 6C 4D B1
",
);
let n = NotifyPayload::parse(&body).expect("parse");
assert_eq!(n.protocol_id, 0);
assert_eq!(n.notify_type, notify_type::NAT_DETECTION_DESTINATION_IP);
assert_eq!(n.spi.len(), 0);
assert_eq!(n.data.len(), 20); }
#[test]
fn round_trip_id_and_auth() {
let id = IdPayload {
id_type: IdType::Rfc822Addr,
data: b"alice@example.com".to_vec(),
};
let mut buf = Vec::new();
id.write_body(&mut buf);
let back = IdPayload::parse(&buf).unwrap();
assert_eq!(back.id_type, IdType::Rfc822Addr);
assert_eq!(back.data, b"alice@example.com");
let auth = AuthPayload {
method: AuthMethod::SharedKeyMic,
data: vec![0xab; 32],
};
let mut buf = Vec::new();
auth.write_body(&mut buf);
let back = AuthPayload::parse(&buf).unwrap();
assert_eq!(back.method, AuthMethod::SharedKeyMic);
assert_eq!(back.data, vec![0xab; 32]);
}
}