use crate::core::matter::code::CesrCode;
use crate::core::matter::code::DigestCode;
use crate::core::matter::sizage::SizeType;
use crate::core::primitives::Saider;
use crate::crypto::digest::digest;
#[cfg(feature = "alloc")]
#[allow(
unused_imports,
reason = "alloc prelude items; subset used per cfg/feature combination"
)]
use alloc::{borrow::ToOwned, string::String, string::ToString};
use crate::serder::error::SerderError;
use crate::serder::primitives::to_qb64_string;
pub const DUMMY_CHAR: char = '#';
pub fn said_placeholder(code: DigestCode) -> Result<String, SerderError> {
let sizage = code.get_sizage();
let len = match *sizage.fs() {
SizeType::Fixed(n) => usize::from(n),
SizeType::Small | SizeType::Large => {
return Err(SerderError::DigestError(
"digest code has variable size, expected fixed".to_owned(),
));
}
};
Ok(core::iter::repeat_n(DUMMY_CHAR, len).collect())
}
pub fn compute_digest(data: &[u8], code: DigestCode) -> Result<Saider<'static>, SerderError> {
digest(code, data).map_err(|e| SerderError::DigestError(e.to_string()))
}
pub fn verify_said(raw: &[u8], code: DigestCode) -> Result<bool, SerderError> {
let mut value: serde_json::Value = serde_json::from_slice(raw)?;
let obj = value
.as_object_mut()
.ok_or(SerderError::MissingField("d"))?;
let original_said = obj
.get("d")
.and_then(serde_json::Value::as_str)
.ok_or(SerderError::MissingField("d"))?
.to_owned();
let placeholder = said_placeholder(code)?;
obj.insert("d".to_owned(), serde_json::Value::String(placeholder));
let reser = serde_json::to_string(&value)?;
let computed = compute_digest(reser.as_bytes(), code)?;
let computed_qb64 = to_qb64_string(&computed);
Ok(original_said == computed_qb64)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::matter::code::DigestCode;
#[test]
fn placeholder_blake3_256_is_44_chars() {
let ph = said_placeholder(DigestCode::Blake3_256).expect("fixed-size code");
assert_eq!(ph.len(), 44);
assert!(ph.chars().all(|c| c == DUMMY_CHAR));
}
#[test]
fn placeholder_sha3_256_is_44_chars() {
let ph = said_placeholder(DigestCode::SHA3_256).expect("fixed-size code");
assert_eq!(ph.len(), 44);
assert!(ph.chars().all(|c| c == DUMMY_CHAR));
}
#[test]
fn compute_digest_produces_valid_qb64() {
let data = b"hello KERI world";
let saider = compute_digest(data, DigestCode::Blake3_256).expect("digest should succeed");
let qb64 = to_qb64_string(&saider);
assert_eq!(qb64.len(), 44);
assert!(
qb64.starts_with('E'),
"Blake3_256 qb64 should start with 'E'"
);
}
#[test]
fn compute_digest_deterministic() {
let data = b"deterministic input";
let a = compute_digest(data, DigestCode::Blake3_256).expect("digest a");
let b = compute_digest(data, DigestCode::Blake3_256).expect("digest b");
assert_eq!(to_qb64_string(&a), to_qb64_string(&b));
}
#[test]
fn different_data_different_said() {
let a = compute_digest(b"alpha", DigestCode::Blake3_256).expect("digest alpha");
let b = compute_digest(b"bravo", DigestCode::Blake3_256).expect("digest bravo");
assert_ne!(to_qb64_string(&a), to_qb64_string(&b));
}
}