use std::fmt;
use sha2::{Digest, Sha256};
use crate::{identifier::isd_asn::IsdAsn, path::ScionPath};
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[error("interface metadata is required to compute path fingerprints")]
pub struct FingerprintError;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PathFingerprint([u8; PathFingerprint::LENGTH]);
impl PathFingerprint {
const LENGTH: usize = 32;
const DISPLAYED_BYTES: usize = 8;
#[inline]
pub(crate) fn try_from_scion_path(
path: &ScionPath,
) -> Result<PathFingerprint, FingerprintError> {
if path.src_ia == path.dst_ia {
Ok(PathFingerprint::local(path.src_ia))
} else {
let if_metadata = path
.metadata
.as_ref()
.and_then(|metadata| metadata.interfaces.as_ref())
.ok_or(FingerprintError)?;
let mut hasher = Sha256::new();
for metadata in if_metadata {
hasher.update(metadata.interface.isd_asn.to_u64().to_be_bytes());
hasher.update(u64::from(metadata.interface.id).to_be_bytes());
}
Ok(Self(hasher.finalize().into()))
}
}
#[inline]
pub fn local(local_ia: IsdAsn) -> Self {
Self(
Sha256::new_with_prefix(local_ia.to_u64().to_be_bytes())
.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 AsRef<[u8]> for PathFingerprint {
#[inline]
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl TryFrom<&[u8]> for PathFingerprint {
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 PathFingerprint {
#[inline]
fn from(value: [u8; 32]) -> Self {
Self(value)
}
}
impl fmt::Display for PathFingerprint {
#[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 PathFingerprint {
#[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 PathFingerprint {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
f.write_str("0x")?;
}
self.format(f, Self::DISPLAYED_BYTES, false)
}
}