use aes::cipher::{consts::U16, generic_array::GenericArray};
use anyhow::bail;
use scion_proto::path::{HopField, InfoField};
pub type ForwardingKey = GenericArray<u8, U16>;
pub fn calculate_hop_mac(
mac_chain_beta: u16,
timestamp: u32,
exp_time: u8,
cons_ingress: u16,
cons_egress: u16,
key: &ForwardingKey,
) -> [u8; 6] {
use cmac::Mac;
let mut mac_input_data = [0u8; 16];
mac_input_data[2..4].copy_from_slice(&mac_chain_beta.to_be_bytes());
mac_input_data[4..8].copy_from_slice(×tamp.to_be_bytes());
mac_input_data[9] = exp_time;
mac_input_data[10..12].copy_from_slice(&cons_ingress.to_be_bytes());
mac_input_data[12..14].copy_from_slice(&cons_egress.to_be_bytes());
let mut maccer = cmac::Cmac::<aes::Aes128>::new(key);
maccer.update(&mac_input_data);
let mac: [u8; 16] = maccer.finalize().into_bytes().into();
let mut result = [0u8; 6];
result.copy_from_slice(&mac[..6]);
result
}
#[allow(unused)]
pub fn mac_chaining_beta(segment_id: u16, hop_macs: impl Iterator<Item = [u8; 6]>) -> u16 {
let mut accumulator = segment_id;
for hop_mac in hop_macs {
let partial_mac = u16::from_be_bytes([hop_mac[0], hop_mac[1]]); accumulator ^= partial_mac;
}
accumulator
}
pub fn mac_chaining_step(accumulator: u16, hop_mac: [u8; 6]) -> u16 {
let partial_mac = u16::from_be_bytes([hop_mac[0], hop_mac[1]]); accumulator ^ partial_mac
}
#[allow(unused)]
pub fn validate_segment_macs(
info: &InfoField,
fields: &[(HopField, ForwardingKey)],
is_construction_direction: bool,
) -> anyhow::Result<()> {
let mut accumulator = if is_construction_direction {
info.seg_id
} else {
mac_chaining_beta(info.seg_id, fields.iter().map(|(hop, _)| hop.mac))
};
let iter: Box<dyn DoubleEndedIterator<Item = _>> = if is_construction_direction {
Box::new(fields.iter())
} else {
Box::new(fields.iter().rev())
};
for (i, (hop, hop_key)) in iter.enumerate() {
if !is_construction_direction {
accumulator = mac_chaining_step(accumulator, hop.mac);
}
let expected_mac = calculate_hop_mac(
accumulator,
info.timestamp_epoch,
hop.exp_time,
hop.cons_ingress,
hop.cons_egress,
hop_key,
);
if expected_mac != hop.mac {
bail!(
"MAC mismatch at hop {i}: {hop:?} expected {expected_mac:?} got {:?} current accumulator {accumulator} construction direction {is_construction_direction}",
hop.mac
);
}
if is_construction_direction {
accumulator = mac_chaining_step(accumulator, hop.mac);
}
}
Ok(())
}