From fdf83f8681bb3531f6441f9d0a021ca9dcd36c45 Mon Sep 17 00:00:00 2001 From: edef Date: Tue, 3 May 2022 14:03:12 +0000 Subject: ripple/fossil: add digest_from_str and digest_str These decode digests to and from zbase32 for user-facing uses. Change-Id: Ibd2db960044a97812d18d1a3c107521d78bd7f24 --- ripple/fossil/src/lib.rs | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) (limited to 'ripple/fossil/src') diff --git a/ripple/fossil/src/lib.rs b/ripple/fossil/src/lib.rs index 53e4d7b..aa4821c 100644 --- a/ripple/fossil/src/lib.rs +++ b/ripple/fossil/src/lib.rs @@ -3,7 +3,7 @@ pub use crate::chunker::Chunker; use { - anyhow::{Context, Result}, + anyhow::{anyhow, bail, Context, Result}, byteorder::{BigEndian, ByteOrder}, prost::Message, sled::{transaction::ConflictableTransactionError, Transactional}, @@ -474,6 +474,44 @@ impl Directory { } } +pub fn digest_str(digest: &Digest) -> String { + let bytes = digest.as_bytes(); + zbase32::encode_full_bytes(bytes) +} + +pub fn digest_from_str(s: &str) -> Result { + let bits = DIGEST_BYTES * 8; + let chars = (bits + 5 - 1) / 5; + + if s.len() < chars { + bail!("digest too short"); + } + + let bytes_vec = zbase32::decode_str(s, bits as u64).map_err(|_| anyhow!("invalid digest"))?; + if zbase32::encode_full_bytes(&bytes_vec) != s { + // non-canonical input + bail!("non-canonical digest"); + } + + let mut bytes = [0; DIGEST_BYTES]; + bytes.copy_from_slice(&bytes_vec); + + Ok(bytes.into()) +} + +#[test] +fn digest_str_golden() { + let h = blake3::hash(b"hello fossil!"); + let s = "xow7w4moszx4w5ngp7d7w3fyfk313tpw6fndik4imdhd18ynw6so"; + assert_eq!(digest_str(&h), s); + assert_eq!(digest_from_str(s).ok(), Some(h)); +} + +#[test] +fn digest_str_short() { + assert!(digest_from_str("xow7w4moszx4w5ngp7d7w3fyfk313tpw6fndik4imdhd18ynw6s").is_err()); +} + #[track_caller] pub fn digest_from_bytes(bytes: &[u8]) -> Digest { if bytes.len() != DIGEST_BYTES { -- cgit 1.4.1