use crate::field::Field;
use crate::packet::{Layer, LayerContext};
use crate::protocols::ipsec::ikev2::payload::{
write_generic_payload_header, IkePayload, PayloadHeaderFields, PayloadType,
};
use crate::protocols::transport::common::{impl_layer_div, impl_layer_object};
use crate::CrafterError;
use crate::Result;
pub const IKE_CERT_PAYLOAD_NAME: &str = "IkeCertPayload";
pub const IKE_CERTREQ_PAYLOAD_NAME: &str = "IkeCertReqPayload";
pub const CERT_FIXED_LEN: usize = 1;
pub const CERT_ENCODING_X509_SIGNATURE: u8 = 4;
pub const CERT_ENCODING_PKCS7_X509: u8 = 2;
pub const CERT_ENCODING_DNS_SIGNED_KEY: u8 = 7;
pub const CERT_ENCODING_HASH_URL_X509: u8 = 12;
pub const CERT_ENCODING_HASH_URL_X509_BUNDLE: u8 = 13;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CertEncoding {
Pkcs7X509,
X509Signature,
DnsSignedKey,
HashUrlX509,
HashUrlX509Bundle,
Unknown(u8),
}
impl CertEncoding {
pub fn codepoint(self) -> u8 {
match self {
Self::Pkcs7X509 => CERT_ENCODING_PKCS7_X509,
Self::X509Signature => CERT_ENCODING_X509_SIGNATURE,
Self::DnsSignedKey => CERT_ENCODING_DNS_SIGNED_KEY,
Self::HashUrlX509 => CERT_ENCODING_HASH_URL_X509,
Self::HashUrlX509Bundle => CERT_ENCODING_HASH_URL_X509_BUNDLE,
Self::Unknown(value) => value,
}
}
}
impl From<u8> for CertEncoding {
fn from(value: u8) -> Self {
match value {
CERT_ENCODING_PKCS7_X509 => Self::Pkcs7X509,
CERT_ENCODING_X509_SIGNATURE => Self::X509Signature,
CERT_ENCODING_DNS_SIGNED_KEY => Self::DnsSignedKey,
CERT_ENCODING_HASH_URL_X509 => Self::HashUrlX509,
CERT_ENCODING_HASH_URL_X509_BUNDLE => Self::HashUrlX509Bundle,
other => Self::Unknown(other),
}
}
}
impl From<CertEncoding> for u8 {
fn from(cert_encoding: CertEncoding) -> Self {
cert_encoding.codepoint()
}
}
#[derive(Debug, Clone)]
pub struct IkeCertPayload {
cert_encoding: Field<u8>,
cert_data: Vec<u8>,
header: PayloadHeaderFields,
}
impl IkeCertPayload {
pub fn new(cert_encoding: impl Into<CertEncoding>, cert_data: impl Into<Vec<u8>>) -> Self {
Self {
cert_encoding: Field::user(cert_encoding.into().codepoint()),
cert_data: cert_data.into(),
header: PayloadHeaderFields::new(),
}
}
pub fn x509_signature(cert_data: impl Into<Vec<u8>>) -> Self {
Self::new(CertEncoding::X509Signature, cert_data)
}
pub fn cert_encoding(mut self, cert_encoding: impl Into<CertEncoding>) -> Self {
self.cert_encoding
.set_user(cert_encoding.into().codepoint());
self
}
pub fn cert_data(mut self, cert_data: impl Into<Vec<u8>>) -> Self {
self.cert_data = cert_data.into();
self
}
pub fn next_payload(mut self, next_payload: u8) -> Self {
self.header.set_next_payload(next_payload);
self
}
pub fn payload_length(mut self, length: u16) -> Self {
self.header.set_length(length);
self
}
pub fn critical(mut self, critical: bool) -> Self {
self.header.set_critical(critical);
self
}
pub fn cert_encoding_value(&self) -> u8 {
self.cert_encoding.value().copied().unwrap_or(0)
}
pub fn cert_encoding_kind(&self) -> CertEncoding {
CertEncoding::from(self.cert_encoding_value())
}
pub fn cert_data_bytes(&self) -> &[u8] {
&self.cert_data
}
fn cert_body(&self) -> Vec<u8> {
let mut out = Vec::with_capacity(CERT_FIXED_LEN + self.cert_data.len());
out.push(self.cert_encoding_value());
out.extend_from_slice(&self.cert_data);
out
}
}
impl IkePayload for IkeCertPayload {
fn payload_type(&self) -> PayloadType {
PayloadType::Certificate
}
fn payload_body(&self, _ctx: &LayerContext<'_>) -> Result<Vec<u8>> {
Ok(self.cert_body())
}
fn next_payload_override(&self) -> Option<u8> {
self.header.next_payload_override()
}
fn payload_length_override(&self) -> Option<u16> {
self.header.payload_length_override()
}
fn critical(&self) -> bool {
self.header.critical()
}
}
impl Layer for IkeCertPayload {
fn name(&self) -> &'static str {
IKE_CERT_PAYLOAD_NAME
}
fn summary(&self) -> String {
format!(
"IkeCertPayload(cert_encoding={}, cert_data_len={})",
self.cert_encoding_value(),
self.cert_data.len()
)
}
fn inspection_fields(&self) -> Vec<(&'static str, String)> {
vec![
("cert_encoding", self.cert_encoding_value().to_string()),
("cert_data_len", self.cert_data.len().to_string()),
]
}
fn encoded_len(&self) -> usize {
super::GENERIC_PAYLOAD_HEADER_LEN + CERT_FIXED_LEN + self.cert_data.len()
}
fn compile(&self, ctx: &LayerContext<'_>, out: &mut Vec<u8>) -> Result<()> {
let body = self.payload_body(ctx)?;
write_generic_payload_header(
out,
ctx,
self.next_payload_override(),
self.critical(),
self.payload_length_override(),
body.len(),
)?;
out.extend_from_slice(&body);
Ok(())
}
impl_layer_object!(IkeCertPayload);
}
impl_layer_div!(IkeCertPayload);
#[derive(Debug, Clone)]
pub struct IkeCertReqPayload {
cert_encoding: Field<u8>,
ca_data: Vec<u8>,
header: PayloadHeaderFields,
}
impl IkeCertReqPayload {
pub fn new(cert_encoding: impl Into<CertEncoding>, ca_data: impl Into<Vec<u8>>) -> Self {
Self {
cert_encoding: Field::user(cert_encoding.into().codepoint()),
ca_data: ca_data.into(),
header: PayloadHeaderFields::new(),
}
}
pub fn x509_signature(ca_data: impl Into<Vec<u8>>) -> Self {
Self::new(CertEncoding::X509Signature, ca_data)
}
pub fn cert_encoding(mut self, cert_encoding: impl Into<CertEncoding>) -> Self {
self.cert_encoding
.set_user(cert_encoding.into().codepoint());
self
}
pub fn ca_data(mut self, ca_data: impl Into<Vec<u8>>) -> Self {
self.ca_data = ca_data.into();
self
}
pub fn next_payload(mut self, next_payload: u8) -> Self {
self.header.set_next_payload(next_payload);
self
}
pub fn payload_length(mut self, length: u16) -> Self {
self.header.set_length(length);
self
}
pub fn critical(mut self, critical: bool) -> Self {
self.header.set_critical(critical);
self
}
pub fn cert_encoding_value(&self) -> u8 {
self.cert_encoding.value().copied().unwrap_or(0)
}
pub fn cert_encoding_kind(&self) -> CertEncoding {
CertEncoding::from(self.cert_encoding_value())
}
pub fn ca_data_bytes(&self) -> &[u8] {
&self.ca_data
}
fn certreq_body(&self) -> Vec<u8> {
let mut out = Vec::with_capacity(CERT_FIXED_LEN + self.ca_data.len());
out.push(self.cert_encoding_value());
out.extend_from_slice(&self.ca_data);
out
}
}
impl IkePayload for IkeCertReqPayload {
fn payload_type(&self) -> PayloadType {
PayloadType::CertificateRequest
}
fn payload_body(&self, _ctx: &LayerContext<'_>) -> Result<Vec<u8>> {
Ok(self.certreq_body())
}
fn next_payload_override(&self) -> Option<u8> {
self.header.next_payload_override()
}
fn payload_length_override(&self) -> Option<u16> {
self.header.payload_length_override()
}
fn critical(&self) -> bool {
self.header.critical()
}
}
impl Layer for IkeCertReqPayload {
fn name(&self) -> &'static str {
IKE_CERTREQ_PAYLOAD_NAME
}
fn summary(&self) -> String {
format!(
"IkeCertReqPayload(cert_encoding={}, ca_data_len={})",
self.cert_encoding_value(),
self.ca_data.len()
)
}
fn inspection_fields(&self) -> Vec<(&'static str, String)> {
vec![
("cert_encoding", self.cert_encoding_value().to_string()),
("ca_data_len", self.ca_data.len().to_string()),
]
}
fn encoded_len(&self) -> usize {
super::GENERIC_PAYLOAD_HEADER_LEN + CERT_FIXED_LEN + self.ca_data.len()
}
fn compile(&self, ctx: &LayerContext<'_>, out: &mut Vec<u8>) -> Result<()> {
let body = self.payload_body(ctx)?;
write_generic_payload_header(
out,
ctx,
self.next_payload_override(),
self.critical(),
self.payload_length_override(),
body.len(),
)?;
out.extend_from_slice(&body);
Ok(())
}
impl_layer_object!(IkeCertReqPayload);
}
impl_layer_div!(IkeCertReqPayload);
pub(crate) fn parse_cert_payload_body(bytes: &[u8]) -> Result<IkeCertPayload> {
if bytes.len() < CERT_FIXED_LEN {
return Err(CrafterError::buffer_too_short(
"ikev2.cert",
CERT_FIXED_LEN,
bytes.len(),
));
}
let cert_encoding = bytes[0];
let cert_data = bytes[CERT_FIXED_LEN..].to_vec();
Ok(IkeCertPayload::new(cert_encoding, cert_data))
}
pub(crate) fn parse_certreq_payload_body(bytes: &[u8]) -> Result<IkeCertReqPayload> {
if bytes.len() < CERT_FIXED_LEN {
return Err(CrafterError::buffer_too_short(
"ikev2.certreq",
CERT_FIXED_LEN,
bytes.len(),
));
}
let cert_encoding = bytes[0];
let ca_data = bytes[CERT_FIXED_LEN..].to_vec();
Ok(IkeCertReqPayload::new(cert_encoding, ca_data))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::packet::{LayerContext, Packet, Raw};
use crate::protocols::ipsec::ikev2::payload::GENERIC_PAYLOAD_HEADER_LEN;
fn compile_payload(payload: impl Layer) -> Vec<u8> {
let packet = Packet::from_layer(payload);
let ctx = LayerContext::new(&packet, 0);
let mut out = Vec::new();
packet.get(0).unwrap().compile(&ctx, &mut out).unwrap();
out
}
fn x509_cert_payload() -> IkeCertPayload {
IkeCertPayload::x509_signature((0u8..16).collect::<Vec<u8>>())
}
#[test]
fn cert_constants_match_manifest() {
assert_eq!(CERT_FIXED_LEN, 1);
assert_eq!(CERT_ENCODING_PKCS7_X509, 2);
assert_eq!(CERT_ENCODING_X509_SIGNATURE, 4);
assert_eq!(CERT_ENCODING_DNS_SIGNED_KEY, 7);
assert_eq!(CERT_ENCODING_HASH_URL_X509, 12);
assert_eq!(CERT_ENCODING_HASH_URL_X509_BUNDLE, 13);
assert_eq!(PayloadType::Certificate.codepoint(), 37);
assert_eq!(PayloadType::CertificateRequest.codepoint(), 38);
}
#[test]
fn cert_encoding_round_trips_through_u8() {
for value in 0u8..=255 {
let cert_encoding = CertEncoding::from(value);
assert_eq!(cert_encoding.codepoint(), value);
assert_eq!(u8::from(cert_encoding), value);
}
}
#[test]
fn named_cert_encodings_map_to_codepoints() {
assert_eq!(
CertEncoding::from(CERT_ENCODING_X509_SIGNATURE),
CertEncoding::X509Signature
);
assert_eq!(
CertEncoding::from(CERT_ENCODING_PKCS7_X509),
CertEncoding::Pkcs7X509
);
assert_eq!(
CertEncoding::from(CERT_ENCODING_HASH_URL_X509),
CertEncoding::HashUrlX509
);
}
#[test]
fn unknown_cert_encoding_is_preserved() {
let unassigned = 200u8;
assert_eq!(
CertEncoding::from(unassigned),
CertEncoding::Unknown(unassigned)
);
assert_eq!(CertEncoding::Unknown(unassigned).codepoint(), unassigned);
}
#[test]
fn payload_types_and_names_are_registered() {
let cert = x509_cert_payload();
assert_eq!(cert.payload_type(), PayloadType::Certificate);
assert_eq!(cert.name(), IKE_CERT_PAYLOAD_NAME);
let certreq = IkeCertReqPayload::x509_signature(vec![0xAAu8; 20]);
assert_eq!(certreq.payload_type(), PayloadType::CertificateRequest);
assert_eq!(certreq.name(), IKE_CERTREQ_PAYLOAD_NAME);
}
#[test]
fn cert_body_lays_out_encoding_then_data() {
let payload = x509_cert_payload();
let body = payload.cert_body();
assert_eq!(body[0], CERT_ENCODING_X509_SIGNATURE);
assert_eq!(&body[CERT_FIXED_LEN..], &(0u8..16).collect::<Vec<u8>>()[..]);
assert_eq!(body.len(), CERT_FIXED_LEN + 16);
assert_eq!(payload.cert_encoding_kind(), CertEncoding::X509Signature);
}
#[test]
fn certreq_body_lays_out_encoding_then_ca_data() {
let payload = IkeCertReqPayload::x509_signature(vec![0x11u8, 0x22, 0x33]);
let body = payload.certreq_body();
assert_eq!(body[0], CERT_ENCODING_X509_SIGNATURE);
assert_eq!(&body[CERT_FIXED_LEN..], &[0x11, 0x22, 0x33]);
assert_eq!(body.len(), CERT_FIXED_LEN + 3);
}
#[test]
fn cert_compiles_generic_header_then_body() {
let payload = x509_cert_payload();
let bytes = compile_payload(payload.clone());
assert_eq!(bytes[0], 0); assert_eq!(bytes[1], 0); let payload_len = u16::from_be_bytes([bytes[2], bytes[3]]) as usize;
assert_eq!(payload_len, bytes.len());
assert_eq!(payload_len, payload.encoded_len());
assert_eq!(
&bytes[GENERIC_PAYLOAD_HEADER_LEN..],
&payload.cert_body()[..]
);
}
#[test]
fn payloads_honor_generic_header_overrides() {
let cert = x509_cert_payload()
.next_payload(39)
.critical(true)
.payload_length(0xBEEF);
let bytes = compile_payload(cert);
assert_eq!(bytes[0], 39);
assert_eq!(bytes[1], 0x80); assert_eq!(u16::from_be_bytes([bytes[2], bytes[3]]), 0xBEEF);
let certreq = IkeCertReqPayload::x509_signature(vec![0xAAu8])
.next_payload(37)
.critical(true)
.payload_length(0x00FF);
let bytes = compile_payload(certreq);
assert_eq!(bytes[0], 37);
assert_eq!(bytes[1], 0x80);
assert_eq!(u16::from_be_bytes([bytes[2], bytes[3]]), 0x00FF);
}
#[test]
fn chain_next_payload_points_at_cert_and_certreq() {
use crate::protocols::ipsec::ikev2::payload::{
following_next_payload, payload_type_for_layer_name, PAYLOAD_CERT, PAYLOAD_CERTREQ,
};
assert_eq!(
payload_type_for_layer_name(IKE_CERT_PAYLOAD_NAME),
Some(PayloadType::Certificate)
);
assert_eq!(
payload_type_for_layer_name(IKE_CERTREQ_PAYLOAD_NAME),
Some(PayloadType::CertificateRequest)
);
let packet: Packet = Packet::from_layer(Raw::from_bytes([0u8; 0])) / x509_cert_payload();
let ctx = LayerContext::new(&packet, 0);
assert_eq!(following_next_payload(&ctx), PAYLOAD_CERT);
let packet: Packet = Packet::from_layer(Raw::from_bytes([0u8; 0]))
/ IkeCertReqPayload::x509_signature(vec![0xAAu8]);
let ctx = LayerContext::new(&packet, 0);
assert_eq!(following_next_payload(&ctx), PAYLOAD_CERTREQ);
}
#[test]
fn round_trip_cert_preserves_encoding_and_data() {
let payload = x509_cert_payload();
let bytes = compile_payload(payload.clone());
let parsed = parse_cert_payload_body(&bytes[GENERIC_PAYLOAD_HEADER_LEN..]).unwrap();
assert_eq!(parsed.cert_encoding_value(), CERT_ENCODING_X509_SIGNATURE);
assert_eq!(parsed.cert_encoding_kind(), CertEncoding::X509Signature);
assert_eq!(
parsed.cert_data_bytes(),
&(0u8..16).collect::<Vec<u8>>()[..]
);
let recompiled = compile_payload(parsed);
assert_eq!(recompiled, bytes);
}
#[test]
fn round_trip_certreq_preserves_encoding_and_ca_data() {
let payload = IkeCertReqPayload::x509_signature(vec![0xDE, 0xAD, 0xBE, 0xEF]);
let bytes = compile_payload(payload.clone());
let parsed = parse_certreq_payload_body(&bytes[GENERIC_PAYLOAD_HEADER_LEN..]).unwrap();
assert_eq!(parsed.cert_encoding_value(), CERT_ENCODING_X509_SIGNATURE);
assert_eq!(parsed.ca_data_bytes(), &[0xDE, 0xAD, 0xBE, 0xEF]);
let recompiled = compile_payload(parsed);
assert_eq!(recompiled, bytes);
}
#[test]
fn parse_rejects_empty_body() {
let err = parse_cert_payload_body(&[]).unwrap_err();
assert!(matches!(err, CrafterError::BufferTooShort { .. }));
let err = parse_certreq_payload_body(&[]).unwrap_err();
assert!(matches!(err, CrafterError::BufferTooShort { .. }));
}
}