| Crates.io | herolib-crypt |
| lib.rs | herolib-crypt |
| version | 0.3.13 |
| created_at | 2025-12-28 20:48:37.017369+00 |
| updated_at | 2026-01-24 05:24:12.86242+00 |
| description | Simple and secure asymmetric cryptography: signing and encryption using Ed25519 |
| homepage | |
| repository | https://github.com/herolib/herolib_rust |
| max_upload_size | |
| id | 2009365 |
| size | 314,063 |
Comprehensive cryptography library for Rust providing asymmetric, symmetric, and HTTP signature functionality.
herolib-crypt provides comprehensive cryptographic primitives for the HeroLib ecosystem with optional feature flags for selective compilation.
Asymmetric Cryptography
Symmetric Cryptography
HTTP Message Signatures
Ed25519 Keys Module
Unified Rhai Integration: Single registration function for all modules
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.
Add to your Cargo.toml:
[dependencies]
# Default: httpsig enabled
herolib-crypt = { workspace = true }
# Everything including Rhai
herolib-crypt = { workspace = true, features = ["full"] }
# Only core functionality (no HTTP signatures or Rhai)
herolib-crypt = { workspace = true, default-features = false }
use herolib_crypt::generate_keypair;
let keypair = generate_keypair()?;
// For signing (Ed25519)
println!("Signing Private: {}", keypair.private_key_hex);
println!("Signing Public: {}", keypair.public_key_hex);
// For encryption (X25519)
println!("Encryption Private: {}", keypair.encryption_private_key_hex);
println!("Encryption Public: {}", keypair.encryption_public_key_hex);
use herolib_crypt::{generate_keypair, sign_message, verify_signature};
// Generate keypair
let keypair = generate_keypair()?;
// Sign a message (uses Ed25519 signing key)
let message = "Hello, world!";
let signature = sign_message(message, &keypair.private_key_hex)?;
// Verify the signature (uses Ed25519 public key)
let is_valid = verify_signature(message, &signature, &keypair.public_key_hex)?;
assert!(is_valid);
use herolib_crypt::{generate_keypair, encrypt_message, decrypt_message};
// 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(message, &bob.encryption_public_key_hex)?;
// Bob decrypts with his X25519 encryption private key
let decrypted = decrypt_message(&encrypted, &bob.encryption_private_key_hex)?;
assert_eq!(decrypted, message);
use herolib_crypt::symmetric::{encrypt_with_password, decrypt_with_password};
// Encrypt with just a password - salt is handled automatically!
let encrypted = encrypt_with_password(b"secret data", "my-password")?;
// Decrypt with the same password
let decrypted = decrypt_with_password(&encrypted, "my-password")?;
assert_eq!(decrypted, b"secret data");
use herolib_crypt::symmetric::{encrypt_string, decrypt_string};
// Encrypt string to base64
let encrypted = encrypt_string("Hello, World!", "my-password")?;
// Decrypt back to string
let decrypted = decrypt_string(&encrypted, "my-password")?;
assert_eq!(decrypted, "Hello, World!");
use herolib_crypt::symmetric::{EncryptionKey, Cipher};
// Generate a random key (for programmatic key management)
let key = EncryptionKey::generate();
let cipher = Cipher::new(key);
// Encrypt and decrypt
let encrypted = cipher.encrypt(b"secret data")?;
let decrypted = cipher.decrypt(&encrypted)?;
use herolib_crypt::keys::Ed25519Keypair;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Generate a keypair
let keypair = Ed25519Keypair::generate()?;
// Sign a message
let message = b"Hello, World!";
let signature = keypair.sign(message);
// Verify the signature
let public_key = keypair.public_key();
let valid = public_key.verify(message, &signature)?;
assert!(valid);
Ok(())
}
use herolib_crypt::keys::Ed25519Keypair;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let keypair = Ed25519Keypair::generate()?;
// Export as hex
let private_hex = keypair.to_hex();
let public_hex = keypair.to_public_key_hex();
// Import from hex
let restored = Ed25519Keypair::from_hex(&private_hex)?;
// Export as bytes
let private_bytes = keypair.to_bytes();
let public_bytes = keypair.to_public_key_bytes();
Ok(())
}
use herolib_crypt::keys::Ed25519Keypair;
use herolib_crypt::httpsig::HttpSigner;
use http::Request;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Generate keypair
let keypair = Ed25519Keypair::generate()?;
let signer = HttpSigner::new(keypair, "user-123");
// Sign an HTTP request
let body = b"{\"amount\": 100}";
let mut request = Request::post("https://api.example.com/payments")
.header("content-type", "application/json")
.body(body.to_vec())?;
signer.sign_request(&mut request, body)?;
// Request now has Signature-Input, Signature, and Content-Digest headers
Ok(())
}
use herolib_crypt::httpsig::HttpVerifier;
use herolib_crypt::keys::Ed25519PublicKey;
use http::Request;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Get the public key (e.g., from hex string stored in database)
let public_key_hex = "..."; // The signer's public key
let public_key = Ed25519PublicKey::from_hex(public_key_hex)?;
// Create verifier with a public key
let verifier = HttpVerifier::new()
.with_key(public_key)
.with_tolerance(60); // 1 minute tolerance
// Verify the request (with signature headers from client)
let body = b"{\"amount\": 100}";
let request = Request::post("https://api.example.com/payments")
.header("content-type", "application/json")
.header("signature-input", "sig1=(...)")
.header("signature", "sig1=:...:")
.header("content-digest", "sha-256=:...:")
.body(body.to_vec())?;
let result = verifier.verify_request(&request, body)?;
println!("✓ Verified! Key ID: {}", result.key_id);
Ok(())
}
The crypt module provides unified Rhai registration:
use rhai::Engine;
use herolib_crypt::rhai::register_crypto_module;
let mut engine = Engine::new();
register_crypto_module(&mut engine)?;
This registers all functions from both keys and httpsig modules.
// 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}`);
}
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()
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 keysign_message(message, private_key_hex) -> CryptoResult<String> - Sign a messageverify_signature(message, signature_hex, public_key_hex) -> CryptoResult<bool> - Verify a signatureencrypt_message(message, recipient_encryption_public_key_hex) -> CryptoResult<String> - Encrypt for a recipientdecrypt_message(encrypted_hex, encryption_private_key_hex) -> CryptoResult<String> - Decrypt a messageencrypt_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 stringEncryptionKey::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 dataEd25519Keypairgenerate() -> 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 hexgenerate_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 verificationHttpSignernew(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 requestHttpVerifiernew() -> 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 requestWhen 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}
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 |
rand::thread_rng() for cryptographically secure randomness.secure_compare prevents timing attacks.getrandom with wasm_js feature for browser support.# From workspace root (runs all Rust unit tests AND Rhai integration tests)
cargo test -p herolib-crypt --features full
# Run Rhai examples
cargo run --features full --example run_rhai
# Run specific Rhai examples manually
cargo run --features full --example run_rhai -- packages/crypt/examples_rhai/keys/basic_signing.rhai
cargo run --features full --example run_rhai -- packages/crypt/examples_rhai/httpsig/basic_usage.rhai
# Run Rhai tests
cargo run --features full --example run_rhai -- packages/crypt/rhai_tests/keys/run_all_tests.rhai
cargo run --features full --example run_rhai -- packages/crypt/rhai_tests/httpsig/run_all_tests.rhai
Test coverage:
The package is organized with clear module boundaries:
Benefits:
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 supportSee the detailed specifications:
Apache-2.0