evidence 0.1.0

Type-level tags for cryptographic primitives
Documentation

crates.io docs.rs license

evidence

Type-level tags for cryptographic primitives.

evidence wraps cryptographic output (hashes, signatures, ciphertext) with phantom type parameters that track what was processed, which algorithm was used, and how the input was serialized — all at zero runtime cost.

The Problem

Cryptographic code often looks like this:

fn verify_user(user_hash: [u8; 32], post_hash: [u8; 32]) {
    // Oops! Easy to swap these by accident
    if user_hash == post_hash { /* ... */ }
}

These are just byte arrays — the compiler can't help you distinguish a hash of a User from a hash of a Post, or a SHA-256 digest from a Blake3 digest.

The Solution

With evidence, the type system tracks what was hashed/signed and how:

use evidence::digest::{Digest, sha2::Sha256};
use evidence::codec::Identity;

// These are incompatible types — can't be mixed up
let user_hash: Digest<User, Sha256, Identity> = Digest::hash(&user);
let post_hash: Digest<Post, Sha256, Identity> = Digest::hash(&post);

// Compile error: expected `Digest<User, ...>`, found `Digest<Post, ...>`
// verify_user(post_hash);

This prevents:

  • Mixing up digests of different types
  • Comparing hashes from different algorithms
  • Accessing signed payloads before verification
  • Confusing encodings (a CBOR hash != a JSON hash of the same value)

The supported types can be extended without changing this library. That said, contributions of common primitives are always welcome.

Usage

use evidence::{
  codec::Encode,
  digest::{Digest, sha2::Sha256}
};

// Define your codec (or use the built-in Cbor / Json codecs)
enum MyCbor {}

impl<T: minicbor::Encode<()>> Encode<T> for MyCbor {
    type Error = minicbor::encode::Error<core::convert::Infallible>;

    fn encode(value: &T) -> Result<Vec<u8>, Self::Error> {
        minicbor::to_vec(value)
    }
}

// Now you can hash values
let digest: Digest<MyData, Sha256, MyCbor> = Digest::hash(&my_data);

// Access the raw bytes when needed
let bytes: &[u8] = digest.as_bytes();

Primitives

Hash

Primitive Feature Output Size
Sha256 sha2 32 bytes
Sha384 sha2 48 bytes
Sha512 sha2 64 bytes
Sha3_224 sha3 28 bytes
Sha3_256 sha3 32 bytes
Sha3_384 sha3 48 bytes
Sha3_512 sha3 64 bytes
Keccak256 sha3 32 bytes
Keccak512 sha3 64 bytes
Blake3 blake3 32 bytes

Signature

Primitive Feature
Ed25519 ed25519

Encryption (AEAD)

Primitive Feature Key Size Nonce Size
ChaCha20Poly1305 chacha20poly1305 32 bytes 12 bytes
Aes128Gcm aes-gcm 16 bytes 12 bytes
Aes256Gcm aes-gcm 32 bytes 12 bytes

MAC

Primitive Feature Tag Size
HmacSha256 hmac 32 bytes
HmacSha384 hmac 48 bytes
HmacSha512 hmac 64 bytes

Features

Feature Description
sha2 SHA-256, SHA-384, SHA-512
sha3 SHA3-224/256/384/512, Keccak256/512
blake3 Blake3
ed25519 Ed25519 signatures
hmac HMAC-SHA256/384/512
chacha20poly1305 ChaCha20-Poly1305 AEAD
aes-gcm AES-128-GCM, AES-256-GCM
minicbor Cbor codec Encode/Decode impls
serde_json Json codec Encode/Decode impls (requires std)
serde Serialize/Deserialize impls
rkyv Zero-copy Archive/Serialize/Deserialize
arbitrary Fuzzing support (arbitrary)
bolero Fuzzing support (bolero)
proptest Property testing (requires std)

No features are enabled by default.

Cargo.toml

[dependencies]
evidence = { version = "0.1", features = ["sha2"] }

# With signatures
evidence = { version = "0.1", features = ["sha2", "ed25519"] }

# With encryption
evidence = { version = "0.1", features = ["sha2", "chacha20poly1305"] }

MSRV

The minimum supported Rust version is 1.90.

no_std

This crate is #![no_std] compatible. It requires alloc for encoding.

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.