use std::fmt;
use sha2::{Digest, Sha256};
use crate::{
dataplane_path::{
types::PathType,
view::{ScionDpPathViewExt, ScionDpPathViewRef},
},
identifier::isd_asn::IsdAsn,
path::ScionPath,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DpPathFingerprint([u8; DpPathFingerprint::LENGTH]);
impl DpPathFingerprint {
const LENGTH: usize = 32;
const DISPLAYED_BYTES: usize = 8;
#[inline]
pub fn from_scion_path(path: &ScionPath) -> DpPathFingerprint {
Self::from_dp_path(path.dp_path.as_ref(), path.src_ia, path.dst_ia)
}
pub fn from_dp_path(
dp_path: ScionDpPathViewRef<'_>,
src_ia: IsdAsn,
dst_ia: IsdAsn,
) -> DpPathFingerprint {
let mut hasher = Sha256::new();
match dp_path {
ScionDpPathViewRef::Empty => {
hasher.update([u8::from(PathType::Empty)]);
hasher.update(src_ia.to_be_bytes());
hasher.update(dst_ia.to_be_bytes());
}
ScionDpPathViewRef::Standard(standard_path) => {
hasher.update([u8::from(PathType::Scion)]);
hasher.update(src_ia.to_be_bytes());
hasher.update(dst_ia.to_be_bytes());
standard_path.hop_fields().iter().for_each(|hf| {
hasher.update(hf.cons_ingress().to_be_bytes());
hasher.update(hf.cons_egress().to_be_bytes());
});
}
ScionDpPathViewRef::Unsupported { path_type, data } => {
hasher.update([u8::from(path_type)]);
hasher.update(src_ia.to_be_bytes());
hasher.update(dst_ia.to_be_bytes());
hasher.update(data.as_ref());
}
ScionDpPathViewRef::OneHop(onehop) => {
hasher.update([u8::from(PathType::OneHop)]);
hasher.update(src_ia.to_be_bytes());
hasher.update(dst_ia.to_be_bytes());
onehop.hop_fields().iter().for_each(|hf| {
hasher.update(hf.cons_ingress().to_be_bytes());
hasher.update(hf.cons_egress().to_be_bytes());
});
}
}
DpPathFingerprint(hasher.finalize().into())
}
#[inline]
fn format(&self, f: &mut fmt::Formatter<'_>, n_displayed: usize, lower: bool) -> fmt::Result {
for byte in &self.0[..n_displayed] {
if lower {
write!(f, "{byte:02x}")?;
} else {
write!(f, "{byte:02X}")?;
}
}
Ok(())
}
}
impl From<&ScionPath> for DpPathFingerprint {
#[inline]
fn from(path: &ScionPath) -> Self {
Self::from_scion_path(path)
}
}
impl AsRef<[u8]> for DpPathFingerprint {
#[inline]
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl TryFrom<&[u8]> for DpPathFingerprint {
type Error = std::array::TryFromSliceError;
#[inline]
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
Ok(Self(value.try_into()?))
}
}
impl From<[u8; 32]> for DpPathFingerprint {
#[inline]
fn from(value: [u8; 32]) -> Self {
Self(value)
}
}
impl fmt::Display for DpPathFingerprint {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
self.format(f, Self::LENGTH, true)
} else {
self.format(f, Self::DISPLAYED_BYTES, true)
}
}
}
impl fmt::LowerHex for DpPathFingerprint {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
f.write_str("0x")?;
}
self.format(f, Self::DISPLAYED_BYTES, true)
}
}
impl fmt::UpperHex for DpPathFingerprint {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
f.write_str("0x")?;
}
self.format(f, Self::DISPLAYED_BYTES, false)
}
}