Cryptographic Operations
The tenzro-crypto crate provides all cryptographic primitives used across the network. Every operation uses real implementations — no stubs — with keys generated from OsRng (CSPRNG) exclusively.
Key Generation and Signing
| Algorithm | Use Case | Key Size |
|---|---|---|
| Ed25519 | Default signing (transactions, messages, attestations) | 32 bytes |
| Secp256k1 | Ethereum-compatible signing (EVM transactions) | 32 bytes |
| BLS12-381 | Aggregate signatures (consensus, multi-validator) | 32 bytes (secret), 48 bytes (public) |
use tenzro_crypto::{KeyPair, Ed25519Signer, Secp256k1Signer, Signer, Verifier};
// Ed25519 key generation and signing
let ed_keypair = KeyPair::generate_ed25519()?;
let message = b"Hello, Tenzro!";
let signature = ed_keypair.sign(message)?;
assert!(ed_keypair.verify(message, &signature)?);
println!("Ed25519 address: {}", ed_keypair.address());
println!("Public key: {}", hex::encode(ed_keypair.public_key()));
// Secp256k1 key generation and signing (Ethereum-compatible)
let secp_keypair = KeyPair::generate_secp256k1()?;
let signature = secp_keypair.sign(message)?;
assert!(secp_keypair.verify(message, &signature)?);
// Verify a standalone signature
use tenzro_crypto::signatures::verify;
let valid = verify(&public_key, message, &signature)?;BLS12-381 Aggregate Signatures
use tenzro_crypto::bls::{BlsKeyPair, AggregateSignature, AggregatePublicKey};
// Generate BLS key pairs for multiple validators
let validator1 = BlsKeyPair::generate()?;
let validator2 = BlsKeyPair::generate()?;
let validator3 = BlsKeyPair::generate()?;
// Each validator signs the same message
let message = b"block-hash-at-height-42";
let sig1 = validator1.sign(message)?;
let sig2 = validator2.sign(message)?;
let sig3 = validator3.sign(message)?;
// Aggregate signatures into a single compact signature
let agg_sig = AggregateSignature::aggregate(&[sig1, sig2, sig3])?;
let agg_pubkey = AggregatePublicKey::aggregate(&[
validator1.public_key(),
validator2.public_key(),
validator3.public_key(),
])?;
// Verify the aggregate (one pairing check instead of three)
assert!(agg_sig.verify(message, &agg_pubkey)?);Hashing
use tenzro_crypto::hash::{sha256, keccak256};
// SHA-256 (used for block hashes, Merkle trees, content addressing)
let hash = sha256(b"Hello, Tenzro!");
println!("SHA-256: {}", hex::encode(hash));
// Keccak-256 (used for Ethereum-compatible address derivation, EVM)
let hash = keccak256(b"Hello, Tenzro!");
println!("Keccak-256: {}", hex::encode(hash));Encryption
AES-256-GCM (Symmetric)
use tenzro_crypto::encryption::{aes_gcm_encrypt, aes_gcm_decrypt};
// Generate a random 256-bit key
let key = tenzro_crypto::rng::random_array::<32>();
// Encrypt with AES-256-GCM (authenticated encryption)
let plaintext = b"sensitive data";
let ciphertext = aes_gcm_encrypt(&key, plaintext)?;
// Output: nonce(12) || ciphertext || tag(16)
// Decrypt and verify authenticity
let decrypted = aes_gcm_decrypt(&key, &ciphertext)?;
assert_eq!(decrypted, plaintext);X25519 Key Exchange
use tenzro_crypto::key_exchange::{X25519KeyPair, derive_shared_secret};
// Two parties generate ephemeral X25519 key pairs
let alice = X25519KeyPair::generate()?;
let bob = X25519KeyPair::generate()?;
// Derive shared secret (same result on both sides)
let alice_secret = derive_shared_secret(&alice, bob.public_key())?;
let bob_secret = derive_shared_secret(&bob, alice.public_key())?;
assert_eq!(alice_secret, bob_secret);
// Use shared secret as AES-256-GCM key for encrypted communicationEnvelope Encryption
use tenzro_crypto::encryption::envelope::{envelope_encrypt, envelope_decrypt};
// Envelope encryption: encrypt data with a random DEK,
// then encrypt the DEK with the recipient's public key
let plaintext = b"large payload of sensitive data";
let envelope = envelope_encrypt(plaintext, &recipient_public_key)?;
// envelope.encrypted_dek — DEK encrypted with recipient's key
// envelope.ciphertext — Data encrypted with DEK (AES-256-GCM)
// Recipient decrypts with their private key
let decrypted = envelope_decrypt(&envelope, &recipient_private_key)?;
assert_eq!(decrypted, plaintext);MPC Threshold Signatures
use tenzro_crypto::mpc::{generate_key_shares, partial_sign, combine_signatures};
// Generate 3 shares with threshold 2
let shares = generate_key_shares(2, 3)?;
// Two parties produce partial signatures
let partial1 = partial_sign(&shares[0], message)?;
let partial2 = partial_sign(&shares[1], message)?;
// Combine any 2 partial signatures into a valid full signature
let signature = combine_signatures(&[partial1, partial2], 2)?;
// The combined signature verifies against the original public key
assert!(verify(&public_key, message, &signature)?);Argon2id Key Derivation
// Argon2id parameters (used for keystore encryption)
// Memory: 64 MB
// Iterations: 3
// Parallelism: 4
// Output: 32 bytes (256-bit key)
// Used internally by tenzro-wallet for keystore encryption:
// password → Argon2id(64MB, 3, p=4) → 256-bit AES key
// key + OsRng nonce → AES-256-GCM(key_material) → encrypted keystoreCSPRNG
use tenzro_crypto::rng::{secure_rng, fill_random_bytes, random_array, verify_csprng};
// All randomness uses OsRng (operating system CSPRNG)
let mut rng = secure_rng();
// Fill a buffer with random bytes
let mut buffer = [0u8; 32];
fill_random_bytes(&mut buffer);
// Generate a random array of fixed size
let nonce: [u8; 12] = random_array();
// Runtime self-test to verify CSPRNG quality
verify_csprng()?;Related Documentation
Key Custody — MPC wallets and encrypted keystores
TEE Attestation — Hardware-sealed key storage
ZK Proofs — Groth16 SNARKs on BN254
Secure Messaging — Encrypted agent messaging