hbkr-rs 0.3.2

Hashblock Key Rotation
Documentation
//! hbkr - Prefix for self-addressing

use std::{fmt, str::FromStr};

use base64::decode_config;
use serde::{Deserialize, Deserializer, Serialize, Serializer};

use crate::{derivation::DerivationCode, errors::KrError, Prefix};

/// Self Addressing Derivations
///
/// Self-addressing is a digest/hash of some inception data
#[derive(Debug, PartialEq, Clone, Hash)]
pub enum SelfAddressing {
    Blake3_256,
    Blake3_512,
}

impl SelfAddressing {
    pub fn digest(&self, data: &[u8]) -> Vec<u8> {
        match self {
            Self::Blake3_256 => blake3_256_digest(data),
            Self::Blake3_512 => blake3_512_digest(data),
        }
    }

    pub fn derive(&self, data: &[u8]) -> SelfAddressingPrefix {
        SelfAddressingPrefix::new(self.to_owned(), self.digest(data))
    }
}

impl Default for SelfAddressing {
    fn default() -> Self {
        Self::Blake3_256
    }
}

impl DerivationCode for SelfAddressing {
    fn to_str(&self) -> String {
        match self {
            Self::Blake3_256 => "E",
            Self::Blake3_512 => "0D",
        }
        .into()
    }

    fn code_len(&self) -> usize {
        match self {
            Self::Blake3_256 => 1,
            Self::Blake3_512 => 2,
        }
    }

    fn derivative_b64_len(&self) -> usize {
        match self {
            Self::Blake3_256 => 43,
            Self::Blake3_512 => 86,
        }
    }
}

impl FromStr for SelfAddressing {
    type Err = KrError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s
            .get(..1)
            .ok_or_else(|| KrError::DeserializeError("Empty prefix".into()))?
        {
            "E" => Ok(Self::Blake3_256),
            "0" => match &s[1..2] {
                "D" => Ok(Self::Blake3_512),
                _ => Err(KrError::DeserializeError("Unknown hash code".into())),
            },
            _ => Err(KrError::DeserializeError(
                "Unknown hash algorithm code".into(),
            )),
        }
    }
}

#[derive(Debug, PartialEq, Clone, Hash)]
pub struct SelfAddressingPrefix {
    pub derivation: SelfAddressing,
    pub digest: Vec<u8>,
}

impl SelfAddressingPrefix {
    pub fn new(code: SelfAddressing, digest: Vec<u8>) -> Self {
        Self {
            derivation: code,
            digest,
        }
    }

    pub fn verify_binding(&self, sed: &[u8]) -> bool {
        self.derivation.digest(sed) == self.digest
    }
}

impl FromStr for SelfAddressingPrefix {
    type Err = KrError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let code = SelfAddressing::from_str(s)?;
        let c_len = code.code_len();
        let p_len = code.prefix_b64_len();
        if s.len() == code.prefix_b64_len() {
            Ok(Self::new(
                code,
                decode_config(&s[c_len..p_len], base64::URL_SAFE)?,
            ))
        } else {
            Err(KrError::SemanticError(format!(
                "Incorrect Prefix Length: {}",
                s
            )))
        }
    }
}

/// Serde compatible Serialize
impl Serialize for SelfAddressingPrefix {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(&self.to_str())
    }
}

/// Serde compatible Deserialize
impl<'de> Deserialize<'de> for SelfAddressingPrefix {
    fn deserialize<D>(deserializer: D) -> Result<SelfAddressingPrefix, D::Error>
    where
        D: Deserializer<'de>,
    {
        let s = String::deserialize(deserializer)?;

        SelfAddressingPrefix::from_str(&s).map_err(serde::de::Error::custom)
    }
}
impl Default for SelfAddressingPrefix {
    fn default() -> Self {
        Self {
            derivation: SelfAddressing::Blake3_512,
            digest: vec![],
        }
    }
}

impl fmt::Display for SelfAddressingPrefix {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.to_str())
    }
}

impl Prefix for SelfAddressingPrefix {
    fn derivative(&self) -> Vec<u8> {
        self.digest.to_owned()
    }
    fn derivation_code(&self) -> String {
        self.derivation.to_str()
    }
}

fn blake3_256_digest(input: &[u8]) -> Vec<u8> {
    blake3::hash(input).as_bytes().to_vec()
}

fn blake3_512_digest(input: &[u8]) -> Vec<u8> {
    let mut out = [0u8; 64];
    let mut h = blake3::Hasher::new();
    h.update(input);
    h.finalize_xof().fill(&mut out);
    out.to_vec()
}