Skip to content

Repository files navigation

Build StatusLicenseCrates.ioDocumentation

multi-hash

Rust implementation of the Multihash specification for self-describing cryptographic hash digests.

Multihash is a self-describing format. It pairs a hash algorithm identifier (a multicodec tag) with the raw digest bytes. This lets systems switch hash algorithms without a break in compatibility. The crate gives 23 supported hash algorithms, type-safe wrappers, serde integration, and multibase encoding via the multi-util crate stack.

Table of Contents

Features

  • 23 hash algorithms. SHA1, SHA2 family, SHA3 family, Blake2, Blake3, MD5, RIPEMD.
  • Builder pattern. A fluent API to create multihashes from raw data or existing digests.
  • Multibase encoding. The EncodedMultihash smart pointer gives a base-encoded string representation via BaseEncoded from multi-util.
  • Serde support. JSON gives the codec name string. Binary gives the varint bytes. The serde feature gates it.
  • Binary round-trip. Into<Vec<u8>> and TryFrom<&[u8]> for the raw wire format.
  • Type-safe newtypes. HashDigest and AlgorithmId wrappers.
  • Zero unsafe code. #![deny(unsafe_code)] is set at the crate root.
  • Thread-safe. All types are Send + Sync.

Install

Add this to your Cargo.toml:

[dependencies]
multi-hash = "1.0"

To disable serde support:

[dependencies]
multi-hash = { version = "1.0", default-features = false }

MSRV: Rust 1.85 (Edition 2024).

Supported Algorithms

Secure algorithms (recommended for cryptographic use)

AlgorithmCodecDigest Size
Blake2b-256Blake2B25632 bytes
Blake2b-384Blake2B38448 bytes
Blake2b-512Blake2B51264 bytes
Blake2s-256Blake2S25632 bytes
Blake3Blake332 bytes
SHA3-256Sha325632 bytes
SHA3-384Sha338448 bytes
SHA3-512Sha351264 bytes

See SAFE_HASH_CODECS for the constant array.

Legacy algorithms (for compatibility)

AlgorithmCodecDigest Size
SHA1Sha120 bytes
SHA2-224Sha222428 bytes
SHA2-256Sha225632 bytes
SHA2-384Sha238448 bytes
SHA2-512Sha251264 bytes
SHA2-512/224Sha251222428 bytes
SHA2-512/256Sha251225632 bytes
Blake2b-224Blake2B22428 bytes
Blake2s-224Blake2S22428 bytes
MD5Md516 bytes
RIPEMD-128Ripemd12816 bytes
RIPEMD-160Ripemd16020 bytes
RIPEMD-256Ripemd25632 bytes
RIPEMD-320Ripemd32040 bytes
SHA3-224Sha322428 bytes

See HASH_CODECS for the constant array of all 23 supported codecs.

Usage

Computing a Hash

use multi_hash::Builder;use multi_codec::Codec;// Compute a SHA2-256 hashlet multihash = Builder::new_from_bytes(Codec::Sha2256,b"hello world").unwrap().try_build().unwrap();assert_eq!(multihash.codec(),Codec::Sha2256);assert_eq!(multihash.as_ref().len(),32);// SHA2-256 outputs 32 bytes

Building from an Existing Digest

If you already have a hash digest, for example from an external hashing library:

use multi_hash::Builder;use multi_codec::Codec;let digest = vec![0u8;32];// pre-computed SHA2-256 digestlet multihash = Builder::new(Codec::Sha2256).with_hash(digest).try_build().unwrap();

Encoding and Decoding

Multihashes encode as codec || length || hash (varint-prefixed):

use multi_hash::{Builder,Multihash};use multi_codec::Codec;let mh1 = Builder::new_from_bytes(Codec::Sha2256,b"data").unwrap().try_build().unwrap();// Encode to binary (varint wire format)let bytes:Vec<u8> = mh1.clone().into();// Decode from binarylet mh2 = Multihash::try_from(bytes.as_ref()).unwrap();assert_eq!(mh1, mh2);

Base Encoding

Use try_build_encoded() with a specific base. It gives an EncodedMultihash that supports Display and TryFrom<&str>:

use multi_hash::Builder;use multi_codec::Codec;use multi_base::Base;let encoded = Builder::new_from_bytes(Codec::Sha2256,b"data").unwrap().with_base_encoding(Base::Base58Btc).try_build_encoded().unwrap();// Display as a base58-encoded multihash stringlet base58_string = encoded.to_string();println!("Multihash: {}", base58_string);// Parse back from stringuse multi_hash::EncodedMultihash;let decoded:EncodedMultihash = EncodedMultihash::try_from(base58_string.as_str()).unwrap();assert_eq!(encoded, decoded);

Converting to EncodedMultihash

You can convert a Multihash to an EncodedMultihash with .into(). The default base is Base16Lower. You can also use EncodedMultihash::new() with a base of your choice:

use multi_hash::{Builder,EncodedMultihash};use multi_base::Base;use multi_codec::Codec;let mh = Builder::new_from_bytes(Codec::Sha3384,b"for great justice, move every zig!").unwrap().try_build().unwrap();// Uses the preferred encoding for multihash objects: Base16Lowerlet encoded_mh1:EncodedMultihash = mh.clone().into();// Or choose a specific base encodinglet encoded_mh2:EncodedMultihash = EncodedMultihash::new(Base::Base32Upper, mh);

Serde Integration

With the serde feature on by default, Multihash implements Serialize and Deserialize. Human-readable formats give the codec name and the hex digest. Binary formats give the varint bytes:

use multi_hash::Builder;use multi_codec::Codec;use serde::{Serialize,Deserialize};#[derive(Serialize,Deserialize,Debug,PartialEq)]structDocumentHash{hash: multi_hash::Multihash,timestamp:u64,}let doc = DocumentHash{hash:Builder::new_from_bytes(Codec::Sha2256,b"document content").unwrap().try_build().unwrap(),timestamp:1234567890,};// Serialize to JSON (human-readable - codec name + hex digest)let json = serde_json::to_string(&doc).unwrap();println!("{}", json);// Deserialize from JSONlet deserialized:DocumentHash = serde_json::from_str(&json).unwrap();assert_eq!(doc, deserialized);

Error Handling

All conversion and builder errors return Result with a structured Error enum:

use multi_hash::{Builder,Error};use multi_codec::Codec;// Handle unsupported algorithmsmatchBuilder::new_from_bytes(Codec::Identity,b"data"){Err(Error::UnsupportedHash{ codec }) => {eprintln!("Algorithm {:?} not supported", codec);}Err(e) => eprintln!("Other error: {}", e),Ok(_) => unreachable!(),}// Handle missing hash datamatchBuilder::new(Codec::Sha2256).try_build(){Err(Error::MissingHash) => {eprintln!("Must call with_hash() before build()");}Err(e) => eprintln!("Other error: {}", e),Ok(_) => unreachable!(),}

Type-Safe Newtypes

For more type safety, use the newtype wrappers:

use multi_hash::types::{HashDigest,AlgorithmId};use multi_codec::Codec;// Type-safe hash digestlet digest = HashDigest::new(vec![0u8;32]);assert_eq!(digest.len(),32);assert_eq!(digest.as_bytes().len(),32);// Type-safe algorithm identifierlet algo = AlgorithmId::new(Codec::Sha2256);assert_eq!(algo.codec(),Codec::Sha2256);assert_eq!(algo.name(),"sha2-256");assert_eq!(algo.code(),0x12);

Testing

The crate has 110 tests across unit, integration, property-based, security, and doc-test suites:

# Run all tests
cargo test --all-features
# Run specific test suites
cargo test --test edge_case_tests
cargo test --test integration_tests
cargo test --test proptest_tests
cargo test --test security_tests
# Run benchmarks
cargo bench

Linting and formatting:

cargo fmt --check
cargo clippy --all-targets --all-features -- -D warnings

CI collects coverage with cargo-llvm-cov and uploads the result to Codecov.

Feature Flags

  • serde (default). Enables serde serialization and deserialization. When on, Multihash implements Serialize and Deserialize. Human-readable formats give the codec name and the hex digest. Binary formats give the varint bytes.

Disabling Default Features

[dependencies]
multi-hash = { version = "1.0", default-features = false }

Security

  • #![deny(unsafe_code)] is set at the crate root.
  • All errors return Result. No path panics on invalid input.
  • All types are Send + Sync with no shared mutable state.
  • Hash computation uses vetted cryptographic libraries from the RustCrypto ecosystem.
  • impl subtle::ConstantTimeEq for Multihash is available for timing-sensitive comparisons.
  • The Varbytes decode path enforces a decoded-size cap (16 MiB) and buffer-length checks. This mitigates CWE-400 and CWE-125.

See SECURITY.md for the full security policy.

Maintainers

This repo: @dgrantham.

Contribute

Contributions are welcome. Please check out the issues.

Development Guidelines

  • Run cargo fmt before you commit.
  • Run cargo clippy -- -D warnings to check for issues.
  • Add tests for new features.
  • Update documentation for API changes.
  • Run the full test suite: cargo test --all-features.

License

Apache-2.0 (c) Cryptid Technologies

About

Multiformats multihash implementation without size in the type signature

Resources

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages