[](https://crates.io/crates/evidence)
[](https://docs.rs/evidence)
[](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<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
```rust
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
| `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
| `Ed25519` | `ed25519` |
### Encryption (AEAD)
| `ChaCha20Poly1305` | `chacha20poly1305` | 32 bytes | 12 bytes |
| `Aes128Gcm` | `aes-gcm` | 16 bytes | 12 bytes |
| `Aes256Gcm` | `aes-gcm` | 32 bytes | 12 bytes |
### MAC
| `HmacSha256` | `hmac` | 32 bytes |
| `HmacSha384` | `hmac` | 48 bytes |
| `HmacSha512` | `hmac` | 64 bytes |
## Features
| `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.