diff options
-rw-r--r-- | ripple/Cargo.lock | 7 | ||||
-rw-r--r-- | ripple/fossil/Cargo.toml | 1 | ||||
-rw-r--r-- | ripple/fossil/src/lib.rs | 40 |
3 files changed, 47 insertions, 1 deletions
diff --git a/ripple/Cargo.lock b/ripple/Cargo.lock index 760317a..c5aae4b 100644 --- a/ripple/Cargo.lock +++ b/ripple/Cargo.lock @@ -370,6 +370,7 @@ dependencies = [ "prost", "prost-build", "sled", + "zbase32", ] [[package]] @@ -1254,6 +1255,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] +name = "zbase32" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9079049688da5871a7558ddacb7f04958862c703e68258594cb7a862b5e33f" + +[[package]] name = "zerocopy" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/ripple/fossil/Cargo.toml b/ripple/fossil/Cargo.toml index a1f39fb..6e1bf7b 100644 --- a/ripple/fossil/Cargo.toml +++ b/ripple/fossil/Cargo.toml @@ -20,6 +20,7 @@ lazy_static = "1.4.0" bao = "0.12.0" anyhow = "1.0.53" clap = { version = "3.1.15", features = ["derive"] } +zbase32 = "0.1.2" [build-dependencies] prost-build = "0.8.0" 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<Digest> { + 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 { |