use serde_json::Value;
use crate::canonicalize::to_canon_bytes_value;
use crate::error::JcsError;
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum DigestAlgorithm {
Blake3Untagged,
Blake3Keyed {
key: [u8; 32],
},
Blake3DomainSeparated {
context: String,
},
Sha256,
}
impl DigestAlgorithm {
#[must_use]
pub const fn name(&self) -> &'static str {
match self {
Self::Blake3Untagged => "blake3-untagged",
Self::Blake3Keyed { .. } => "blake3-keyed",
Self::Blake3DomainSeparated { .. } => "blake3-domain-separated",
Self::Sha256 => "sha256",
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DigestStrategy {
pub algorithm: DigestAlgorithm,
}
impl DigestStrategy {
#[must_use]
pub const fn blake3_untagged() -> Self {
Self {
algorithm: DigestAlgorithm::Blake3Untagged,
}
}
#[must_use]
pub const fn blake3_keyed(key: [u8; 32]) -> Self {
Self {
algorithm: DigestAlgorithm::Blake3Keyed { key },
}
}
#[must_use]
pub fn blake3_domain_separated(context: impl Into<String>) -> Self {
Self {
algorithm: DigestAlgorithm::Blake3DomainSeparated {
context: context.into(),
},
}
}
#[must_use]
pub const fn sha256() -> Self {
Self {
algorithm: DigestAlgorithm::Sha256,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CanonicalDigest {
pub algorithm: DigestAlgorithm,
pub bytes: Vec<u8>,
}
pub fn to_canon_digest_with(
value: &Value,
strategy: &DigestStrategy,
) -> Result<CanonicalDigest, JcsError> {
let bytes = to_canon_bytes_value(value)?;
let digest_bytes = match &strategy.algorithm {
DigestAlgorithm::Blake3Untagged => blake3::hash(&bytes).as_bytes().to_vec(),
DigestAlgorithm::Blake3Keyed { key } => blake3::keyed_hash(key, &bytes).as_bytes().to_vec(),
DigestAlgorithm::Blake3DomainSeparated { context } => {
blake3::derive_key(context, &bytes).to_vec()
}
DigestAlgorithm::Sha256 => {
return Err(JcsError::UnsupportedAlgorithm(
"SHA-256 over canonical bytes is declared in the API but not \
wired in this build; open a follow-up to add the sha2 dep"
.to_string(),
));
}
};
Ok(CanonicalDigest {
algorithm: strategy.algorithm.clone(),
bytes: digest_bytes,
})
}
pub fn to_canon_blake3_digest(value: &Value) -> Result<[u8; 32], JcsError> {
let bytes = to_canon_bytes_value(value)?;
Ok(*blake3::hash(&bytes).as_bytes())
}
pub fn to_canon_blake3_digest_from_slice(json: &[u8]) -> Result<[u8; 32], JcsError> {
let bytes = crate::to_canon_bytes_from_slice(json)?;
Ok(*blake3::hash(&bytes).as_bytes())
}