Something went wrong. Try again.
Typelevel cryptographic algo tracking crates.io/crates/evidence
typelevel cryptography util
Something went wrong. Try again.
Hacking on Evidence #
Development Setup #
# Enter the development shell (provides Rust toolchain, cargo tools, etc.)
nix develop
# Or without Nix, ensure you have Rust 1.90+
rustup update stable
Project Structure #
src/
├── codec.rs
├── digest.rs (blake3, sha2, sha3)
├── encrypted.rs
├── encryption.rs (aes_gcm, chacha20poly1305)
├── fingerprint.rs
├── mac.rs (hmac)
├── signature.rs (ed25519)
├── signed.rs
└── verified.rs
tests/ Property-based (bolero)
Commands #
The nix shell provides a menu command listing all available tasks:
menu # Show all commands
# Build & Check
cargo check # Type-check
cargo build # Build
cargo build --release # Release build
# Test
cargo test # All tests
cargo test --doc # Doc tests only
cargo test --lib # Unit tests only
# Lint & Format
cargo clippy # Lints
cargo fmt # Format code
cargo fmt --check # Check formatting
# Docs
cargo doc --open # Build and open docs
Feature Flags #
# Check with all features
cargo check --all-features
# Check with no default features
cargo check --no-default-features
# Check specific feature
cargo check --no-default-features --features blake3
| Feature | Default | Provides |
|---|---|---|
sha2 |
No | Sha256, Sha384, Sha512 |
sha3 |
No | Sha3_224, Sha3_256, Sha3_384, Sha3_512, Keccak256, Keccak512 |
blake3 |
No | Blake3 |
serde |
No | Serialize/Deserialize impls |
arbitrary |
No | Arbitrary impl for fuzzing |
bolero |
No | TypeGenerator impl for bolero |
proptest |
No | Arbitrary impl for proptest (requires std) |
rkyv |
No | rkyv Archive/Serialize/Deserialize impls |
minicbor |
No | Cbor codec Encode/Decode impls |
serde_json |
No | Json codec Encode/Decode impls (requires std) |
ed25519 |
No | Ed25519 |
hmac |
No | HmacSha256, HmacSha384, HmacSha512 |
chacha20poly1305 |
No | ChaCha20Poly1305 |
aes-gcm |
No | Aes128Gcm, Aes256Gcm |
Architecture #
Core Types #
┌────────────────────────────────────────────────────────┐
│ Digest<T, P, C> │
│ ┌─────────────────────────────────────────────────┐ │
│ │ bytes: Array<u8, P::Size> │ │
│ │ _marker: PhantomData<fn() -> (T, C)> │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ T = value type (phantom) │
│ P = DigestPrimitive (e.g., Sha256) │
│ C = Codec (phantom) │
└────────────────────────────────────────────────────────┘
Trait Hierarchy #
DigestPrimitive SignaturePrimitive EncryptionPrimitive MacPrimitive
│ │ │ │
Sha256 Ed25519 ChaCha20Poly1305 HmacSha256
Sha384 ... Aes256Gcm HmacSha384
Sha512 Aes128Gcm HmacSha512
Sha3_256
Keccak256
Blake3
Signer<S> AsyncSigner<S, K> Encode<T> Decode<T>
│ │ │ │
SigningKey SigningKey Identity Identity
(K: FutureForm)
Sendable = Send futures
Local = !Send futures
Escape Hatches #
Extension traits for bypassing normal construction. Intentionally not re-exported at crate root — users must explicitly import:
use evidence::digest::DigestUnchecked; // Digest from raw bytes
use evidence::encrypted::EncryptedUnchecked; // Encrypted from parts
use evidence::fingerprint::FingerprintUnchecked; // Fingerprint from raw bytes
use evidence::mac::MacUnchecked; // Mac from parts
use evidence::signed::SignedUnchecked; // Signed from parts
use evidence::verified::VerifiedUnchecked; // Verified without verification
This follows "Parse, Don't Validate" — the main API makes invalid states hard to reach, but escape hatches exist for legitimate use cases (deserialization, testing, trusted contexts).
Adding a New Hash Primitive #
- Add the dependency to
Cargo.toml(optional, feature-gated) - Create a module in
src/digest/:
// src/digest/my_hash.rs
use super::DigestPrimitive;
use hybrid_array::typenum::U32;
use hybrid_array::Array;
pub enum MyHash {}
impl DigestPrimitive for MyHash {
type Size = U32;
fn hash(data: &[u8]) -> Array<u8, Self::Size> {
// ... implementation
}
}
- Add
pub mod my_hash;tosrc/digest.rs(feature-gated) - Add to the feature table in README.md
Adding a New Signature Primitive #
- Add the dependency to
Cargo.toml(optional, feature-gated) - Create a module in
src/signature/:
// src/signature/my_sig.rs
use super::{SignaturePrimitive, Signer, AsyncSigner};
use future_form::{FutureForm, Local, Sendable};
#[derive(Debug, Clone, Copy)]
pub enum MySig {}
impl SignaturePrimitive for MySig {
type VerifyingKey = my_crate::PublicKey;
type Signature = my_crate::Signature;
type SigningKey = my_crate::SecretKey;
type Error = my_crate::Error;
fn sign(key: &Self::SigningKey, message: &[u8]) -> Self::Signature {
key.sign(message)
}
fn verify(
key: &Self::VerifyingKey,
message: &[u8],
signature: &Self::Signature,
) -> Result<(), Self::Error> {
key.verify(message, signature)
}
}
impl Signer<MySig> for my_crate::SecretKey {
fn sign(&self, message: &[u8]) -> my_crate::Signature { ... }
fn verifying_key(&self) -> my_crate::PublicKey { ... }
}
// Use the proc macro to generate both Sendable and Local impls
#[future_form::future_form(Sendable, Local)]
impl<K: FutureForm> AsyncSigner<MySig, K> for my_crate::SecretKey {
fn sign<'a>(&'a self, message: &'a [u8]) -> K::Future<'a, my_crate::Signature> {
K::ready(self.inner_sign(message))
}
fn verifying_key(&self) -> my_crate::PublicKey { ... }
}
- Add
pub mod my_sig;tosrc/signature.rs(feature-gated) - Add to the feature table in README.md
Adding a New Encryption Primitive #
- Add the dependency to
Cargo.toml(optional, feature-gated) - Create a module in
src/encryption/:
// src/encryption/my_aead.rs
use alloc::vec::Vec;
use super::EncryptionPrimitive;
use hybrid_array::Array;
pub enum MyAead {}
impl EncryptionPrimitive for MyAead {
type Key = my_crate::Key;
type NonceSize = hybrid_array::typenum::U12;
type Error = my_crate::Error;
fn encrypt(key: &Self::Key, plaintext: &[u8]) -> (Vec<u8>, Array<u8, Self::NonceSize>) {
// ... generate nonce, encrypt
}
fn encrypt_with_nonce(
key: &Self::Key,
nonce: &Array<u8, Self::NonceSize>,
plaintext: &[u8],
) -> Vec<u8> {
// ... encrypt with given nonce
}
fn decrypt(
key: &Self::Key,
nonce: &Array<u8, Self::NonceSize>,
ciphertext: &[u8],
) -> Result<Vec<u8>, Self::Error> {
// ... decrypt
}
}
- Add
pub mod my_aead;tosrc/encryption.rs(feature-gated) - Add to the feature table in README.md
Adding a New MAC Primitive #
- Add the dependency to
Cargo.toml(optional, feature-gated) - Create a module in
src/mac/:
// src/mac/my_mac.rs
use super::MacPrimitive;
pub enum MyMac {}
impl MacPrimitive for MyMac {
type Key = my_crate::Key;
type Tag = [u8; 32]; // or appropriate size
type Error = my_crate::Error;
fn mac(key: &Self::Key, message: &[u8]) -> Self::Tag {
// ... compute MAC
}
fn verify(key: &Self::Key, message: &[u8], tag: &Self::Tag) -> Result<(), Self::Error> {
// ... verify MAC (use constant-time comparison!)
}
}
- Add
pub mod my_mac;tosrc/mac.rs(feature-gated) - Add to the feature table in README.md
Adding a New Codec #
Implement Encode<T> (and optionally Decode<T>, Canonical):
pub enum MyCbor {}
impl<T: minicbor::Encode<()>> Encode<T> for MyCbor {
type Error = minicbor::encode::Error<Infallible>;
fn encode(value: &T) -> Result<Vec<u8>, Self::Error> {
minicbor::to_vec(value)
}
}
impl Canonical for MyCbor {}
Testing #
Property-Based Tests #
We use bolero for property-based testing:
# Run property tests (randomized)
cargo test --test digest
# Run with more iterations
BOLERO_RANDOM_ITERATIONS=10000 cargo test --test digest
# Fuzz a specific test (requires cargo-bolero)
cargo bolero test digest::digest_deterministic
Doc Tests #
cargo test --doc
Code Style #
#![no_std]— avoid std, useallocwhen needed#![forbid(unsafe_code)]— no unsafe in this crate- Prefer
expect("reason")overunwrap() - Manual trait impls to avoid spurious bounds on phantom types
- Feature-gate optional dependencies
- Prefer
pub modoverpub usefor submodules
Commit Messages #
<type>: <subject>
<body>
Co-Authored-By: ...
Types: feat, fix, docs, refactor, test, chore