Herolib Crypt
Comprehensive cryptography library for Rust providing asymmetric, symmetric, and HTTP signature functionality.
Overview
herolib-crypt provides comprehensive cryptographic primitives for the HeroLib ecosystem with optional feature flags for selective compilation.
Features
-
Asymmetric Cryptography
- Ed25519 digital signatures (signing/verification)
- X25519 ECDH encryption (encrypt/decrypt)
- Dual keypair architecture (separate keys for signing and encryption)
-
Symmetric Cryptography
- XChaCha20-Poly1305 authenticated encryption
- Argon2id password-based key derivation
- Secure key handling with automatic memory zeroization
-
HTTP Message Signatures
- RFC 9421/9530 compliant request authentication
- Ed25519-based digital signatures for HTTP requests
- Replay protection and tampering detection
-
Ed25519 Keys Module
- Fast, secure elliptic curve signatures
- Comprehensive key serialization utilities
- Constant-time operations for security
-
Unified Rhai Integration: Single registration function for all modules
Feature Flags
httpsig(default): HTTP Message Signaturesrhai: Enable Rhai scripting supportfull: Enable all features (httpsig+rhai)
Note: The keys module is always included as it's essential for core functionality.
Installation
Add to your Cargo.toml:
[]
# Default: httpsig enabled
= { = true }
# Everything including Rhai
= { = true, = ["full"] }
# Only core functionality (no HTTP signatures or Rhai)
= { = true, = false }
Quick Start
Asymmetric: Generate a Keypair
use generate_keypair;
let keypair = generate_keypair?;
// For signing (Ed25519)
println!;
println!;
// For encryption (X25519)
println!;
println!;
Asymmetric: Sign and Verify Messages
use ;
// Generate keypair
let keypair = generate_keypair?;
// Sign a message (uses Ed25519 signing key)
let message = "Hello, world!";
let signature = sign_message?;
// Verify the signature (uses Ed25519 public key)
let is_valid = verify_signature?;
assert!;
Asymmetric: Encrypt and Decrypt Messages
use ;
// Alice and Bob generate their keypairs
let alice = generate_keypair?;
let bob = generate_keypair?;
// Alice encrypts a message for Bob (uses Bob's X25519 encryption public key)
let message = "Secret message";
let encrypted = encrypt_message?;
// Bob decrypts with his X25519 encryption private key
let decrypted = decrypt_message?;
assert_eq!;
Symmetric: Password-Based Encryption (Recommended)
use ;
// Encrypt with just a password - salt is handled automatically!
let encrypted = encrypt_with_password?;
// Decrypt with the same password
let decrypted = decrypt_with_password?;
assert_eq!;
Symmetric: String Encryption
use ;
// Encrypt string to base64
let encrypted = encrypt_string?;
// Decrypt back to string
let decrypted = decrypt_string?;
assert_eq!;
Symmetric: Advanced - Random Key
use ;
// Generate a random key (for programmatic key management)
let key = generate;
let cipher = new;
// Encrypt and decrypt
let encrypted = cipher.encrypt?;
let decrypted = cipher.decrypt?;
Ed25519 Keys Module
use Ed25519Keypair;
Key Serialization
use Ed25519Keypair;
HTTP Message Signatures
use Ed25519Keypair;
use HttpSigner;
use Request;
Verifying HTTP Signatures
use HttpVerifier;
use Ed25519PublicKey;
use Request;
Rhai Integration
The crypt module provides unified Rhai registration:
use Engine;
use register_crypto_module;
let mut engine = new;
register_crypto_module?;
This registers all functions from both keys and httpsig modules.
Rhai Example
// Generate keypair
let keypair = ed25519_generate();
let public_key = public_key(keypair);
// Sign HTTP request
let signer = httpsig_signer_new(keypair, "user-123");
let result = httpsig_sign(
signer,
"POST",
"/api/payments",
"api.example.com",
#{},
`{"amount": 100}`
);
// Verify signature
let verifier = httpsig_verifier_new();
let verifier = httpsig_verifier_with_key(verifier, public_key);
let headers = #{
"signature-input": result.signature_input,
"signature": result.signature,
"content-digest": result.content_digest
};
let verify_result = httpsig_verify(
verifier,
"POST",
"/api/payments",
"api.example.com",
headers,
`{"amount": 100}`
);
if verify_result.verified {
print(`✓ Verified by ${verify_result.key_id}`);
}
Module Structure
herolib-crypt
├── asymmetric (always included)
│ ├── Ed25519 signing (delegates to keys module)
│ └── X25519 encryption
├── symmetric (always included)
│ ├── XChaCha20-Poly1305 encryption
│ └── Argon2id key derivation
├── keys (always included)
│ ├── Ed25519Keypair
│ ├── Ed25519PublicKey
│ ├── Signature
│ ├── KeyError
│ └── utility functions
├── httpsig (feature: httpsig)
│ ├── HttpSigner
│ ├── HttpVerifier
│ ├── HttpSigError
│ └── utility functions
└── rhai (feature: rhai)
├── register() - unified registration
├── keys::rhai::register()
└── httpsig::rhai::register()
API Reference
Asymmetric Key Generation
generate_keypair() -> CryptoResult<KeyPair>- Generate a new dual keypair (Ed25519 + X25519)public_key_from_private(private_key_hex) -> CryptoResult<String>- Derive Ed25519 public keyencryption_public_key_from_private(encryption_private_key_hex) -> CryptoResult<String>- Derive X25519 public key
Asymmetric Signing (Ed25519)
sign_message(message, private_key_hex) -> CryptoResult<String>- Sign a messageverify_signature(message, signature_hex, public_key_hex) -> CryptoResult<bool>- Verify a signature
Asymmetric Encryption (X25519 + ChaCha20-Poly1305)
encrypt_message(message, recipient_encryption_public_key_hex) -> CryptoResult<String>- Encrypt for a recipientdecrypt_message(encrypted_hex, encryption_private_key_hex) -> CryptoResult<String>- Decrypt a message
Symmetric Encryption (Simple API)
encrypt_with_password(data, password) -> SymmetricResult<Vec<u8>>- Encrypt with passworddecrypt_with_password(encrypted, password) -> SymmetricResult<Vec<u8>>- Decrypt with passwordencrypt_string(text, password) -> SymmetricResult<String>- Encrypt string to base64decrypt_string(encrypted_base64, password) -> SymmetricResult<String>- Decrypt base64 to string
Symmetric Encryption (Advanced API)
EncryptionKey::generate() -> EncryptionKey- Generate a random 256-bit keyEncryptionKey::derive_from_password(password, salt) -> SymmetricResult<EncryptionKey>- Derive key from passwordCipher::new(key) -> Cipher- Create a cipherCipher::encrypt(plaintext) -> SymmetricResult<Vec<u8>>- Encrypt dataCipher::decrypt(ciphertext) -> SymmetricResult<Vec<u8>>- Decrypt data
Keys Module
Ed25519Keypair
generate() -> Result<Ed25519Keypair, KeyError>- Generate new keypairfrom_bytes(bytes: &[u8]) -> Result<Ed25519Keypair, KeyError>- Import from bytesfrom_hex(hex: &str) -> Result<Ed25519Keypair, KeyError>- Import from hexsign(&self, message: &[u8]) -> Signature- Sign messageverify(&self, message: &[u8], signature: &Signature) -> Result<bool, KeyError>- Verify signaturepublic_key(&self) -> Ed25519PublicKey- Get public keyto_bytes(&self) -> Vec<u8>- Export private key as bytesto_hex(&self) -> String- Export private key as hexto_public_key_bytes(&self) -> Vec<u8>- Export public key as bytesto_public_key_hex(&self) -> String- Export public key as hex
Keys Utility Functions
generate_random_bytes(size: usize) -> Vec<u8>- Generate cryptographically secure random bytesencode_hex(data: &[u8]) -> String- Encode bytes to hex stringdecode_hex(hex: &str) -> Result<Vec<u8>, KeyError>- Decode hex string to bytessha256_digest(data: &[u8]) -> Vec<u8>- Compute SHA-256 hashsecure_compare(a: &[u8], b: &[u8]) -> bool- Constant-time comparisonverify_ed25519(pubkey: &[u8], message: &[u8], signature: &[u8]) -> Result<bool, KeyError>- Standalone verification
HTTP Signatures Module
HttpSigner
new(keypair: Ed25519Keypair, key_id: impl Into<String>) -> Self- Create new signerwith_headers(self, headers: Vec<String>) -> Self- Add headers to signwith_label(self, label: impl Into<String>) -> Self- Set signature label (default: "sig1")sign_request(&self, request: &mut Request<Vec<u8>>, body: &[u8]) -> Result<(), HttpSigError>- Sign HTTP request
HttpVerifier
new() -> Self- Create new verifierwith_key(self, key: Ed25519PublicKey) -> Self- Set public key for verificationwith_key_getter(self, getter: Box<dyn Fn(&str) -> Result<Ed25519PublicKey, KeyError>>) -> Self- Set dynamic key lookupwith_tolerance(self, seconds: u64) -> Self- Set timestamp tolerance (default: 60s)verify_request(&self, request: &Request<Vec<u8>>, body: &[u8]) -> Result<VerificationResult, HttpSigError>- Verify HTTP request
Wire Format
When a request is signed, it includes these headers:
POST /api/v1/payments HTTP/1.1
Host: api.example.com
Content-Type: application/json
Content-Digest: sha-256=:X48E9qHmRKpD1nxWnGjcKuXG7AEn9ELUE30CQSY9p3w=:
Signature-Input: sig1=("@method" "@path" "@authority" "content-digest");keyid="user-123";alg="ed25519";created=1735652986
Signature: sig1=:p7By5l82mNkP9qHmRKpD1nxWnGjcKuXG7AEn9ELUE30CQSY9p3w=:
{"amount": 100}
Key Formats
All keys are represented as hex strings:
| Key Type | Size (bytes) | Hex Length |
|---|---|---|
| Ed25519 Private Key | 32 | 64 chars |
| Ed25519 Public Key | 32 | 64 chars |
| X25519 Private Key | 32 | 64 chars |
| X25519 Public Key | 32 | 64 chars |
| Ed25519 Signature | 64 | 128 chars |
Security Properties
Signing (Ed25519)
- Deterministic: Same message + key = same signature
- Unforgeable: Cannot create valid signature without the private key
- Verifiable: Anyone with public key can verify authenticity
Encryption (X25519 + ChaCha20-Poly1305)
- Confidential: Only recipient's private key can decrypt
- Authenticated: Tampering is detected (AEAD)
- Forward Secure: Each message uses ephemeral keys
Symmetric (XChaCha20-Poly1305)
- Authenticated: Tampering detection via Poly1305 MAC
- Large Nonce: 192-bit nonces prevent collisions
- Memory Safe: Keys are automatically zeroized on drop
Security Considerations
Ed25519 Keys
- Private Key Storage: Never store private keys in plaintext. Use secure key management.
- Random Generation: Uses OS entropy via
rand::thread_rng()for cryptographically secure randomness. - Constant-Time Operations:
secure_compareprevents timing attacks. - WASM Compatibility: Uses
getrandomwithwasm_jsfeature for browser support.
HTTP Signatures
- ✅ Authenticity: Only the holder of the private key can create valid signatures
- ✅ Integrity: Headers and body are cryptographically bound to the signature
- ✅ Replay Protection: Configurable timestamp tolerance (default: 60s)
- ✅ Mandatory Digest: All requests include Content-Digest, even GET/DELETE
- ✅ Canonical Authority: Normalized host:port prevents proxy attacks
RFC Compliance
- RFC 9421: HTTP Message Signatures - Full compliance
- RFC 9530: Digest Fields - Content-Digest implementation
- Ed25519: Fast, secure elliptic curve signatures
Testing
# From workspace root (runs all Rust unit tests AND Rhai integration tests)
# Run Rhai examples
# Run specific Rhai examples manually
# Run Rhai tests
Test coverage:
- 119 unit tests - Core functionality
- 33 doc tests - Documentation examples
- Rhai examples - Keys and HTTP signatures
- Rhai tests - Comprehensive test suites for keys and httpsig
Architecture
The package is organized with clear module boundaries:
- Feature Flags: Compile only what you need (httpsig, rhai)
- Clean Modules: Clear boundaries between asymmetric, symmetric, keys, and httpsig
- Unified API: Single entry point for all cryptographic operations
- Mandatory Keys: Core Ed25519 functionality always available
Benefits:
- ✅ Simplified dependency management
- ✅ Selective compilation via features
- ✅ Unified Rhai integration
- ✅ Comprehensive cryptographic coverage
Dependencies
ed25519-dalek- Ed25519 signaturesx25519-dalek- X25519 key exchangechacha20poly1305- AEAD encryptionsha3- Key derivation (KDF)sha2- SHA-256 hashingargon2- Password-based key derivationzeroize- Secure memory handlinghttp(optional) - HTTP types for httpsigrhai(optional) - Scripting support
Specifications
See the detailed specifications:
- SPEC_SIGNING.md - Signing specification
- SPEC_ENCRYPTION.md - Encryption specification
License
Apache-2.0