[![crates.io](https://img.shields.io/crates/v/evidence.svg)](https://crates.io/crates/evidence) [![docs.rs](https://docs.rs/evidence/badge.svg)](https://docs.rs/evidence) [![license](https://img.shields.io/crates/l/evidence.svg)](LICENSE-MIT) # 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: ```rust 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: ```rust use evidence::digest::{Digest, sha2::Sha256}; use evidence::codec::Identity; // These are incompatible types — can't be mixed up let user_hash: Digest = Digest::hash(&user); let post_hash: Digest = Digest::hash(&post); // Compile error: expected `Digest`, found `Digest` // 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 ```rust use evidence::{ codec::Encode, digest::{Digest, sha2::Sha256} }; // Define your codec (or use the built-in Cbor / Json codecs) enum MyCbor {} impl> Encode for MyCbor { type Error = minicbor::encode::Error; fn encode(value: &T) -> Result, Self::Error> { minicbor::to_vec(value) } } // Now you can hash values let digest: Digest = 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 ```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](LICENSE-APACHE) or [MIT license](LICENSE-MIT) at your option.