multi-hash 1.0.7

Multihash self-describing cryptographic hash data
docs.rs failed to build multi-hash-1.0.7
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.

Build Status License Crates.io Documentation

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)

Algorithm Codec Digest Size
Blake2b-256 Blake2B256 32 bytes
Blake2b-384 Blake2B384 48 bytes
Blake2b-512 Blake2B512 64 bytes
Blake2s-256 Blake2S256 32 bytes
Blake3 Blake3 32 bytes
SHA3-256 Sha3256 32 bytes
SHA3-384 Sha3384 48 bytes
SHA3-512 Sha3512 64 bytes

See SAFE_HASH_CODECS for the constant array.

Legacy algorithms (for compatibility)

Algorithm Codec Digest Size
SHA1 Sha1 20 bytes
SHA2-224 Sha2224 28 bytes
SHA2-256 Sha2256 32 bytes
SHA2-384 Sha2384 48 bytes
SHA2-512 Sha2512 64 bytes
SHA2-512/224 Sha2512224 28 bytes
SHA2-512/256 Sha2512256 32 bytes
Blake2b-224 Blake2B224 28 bytes
Blake2s-224 Blake2S224 28 bytes
MD5 Md5 16 bytes
RIPEMD-128 Ripemd128 16 bytes
RIPEMD-160 Ripemd160 20 bytes
RIPEMD-256 Ripemd256 32 bytes
RIPEMD-320 Ripemd320 40 bytes
SHA3-224 Sha3224 28 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 hash
let 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 digest
let 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 binary
let 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 string
let base58_string = encoded.to_string();
println!("Multihash: {}", base58_string);

// Parse back from string
use 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: Base16Lower
let encoded_mh1: EncodedMultihash = mh.clone().into();

// Or choose a specific base encoding
let 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)]
struct DocumentHash {
    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 JSON
let 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 algorithms
match Builder::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 data
match Builder::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 digest
let digest = HashDigest::new(vec![0u8; 32]);
assert_eq!(digest.len(), 32);
assert_eq!(digest.as_bytes().len(), 32);

// Type-safe algorithm identifier
let 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