Cryptography
The tenzro-crypto crate provides foundational cryptographic primitives for the Tenzro Network. It supports multiple signature schemes, hashing algorithms, symmetric and asymmetric encryption, multi-party computation (MPC) threshold signatures, and BLS12-381 signature aggregation for efficient validator consensus.
Key Generation and Address Derivation
Tenzro supports two primary signature schemes: Ed25519 for efficiency and Secp256k1 for Ethereum compatibility. Key generation produces cryptographically secure keypairs, and addresses are derived using scheme-specific hashing.
use tenzro_crypto::{
keys::{Ed25519KeyPair, Secp256k1KeyPair},
address::derive_address,
};
// Generate Ed25519 keypair (default for Tenzro)
let ed25519_keypair = Ed25519KeyPair::generate();
let ed25519_address = derive_address(
&ed25519_keypair.public_key(),
SignatureScheme::Ed25519
);
// Generate Secp256k1 keypair (Ethereum-compatible)
let secp256k1_keypair = Secp256k1KeyPair::generate();
let secp256k1_address = derive_address(
&secp256k1_keypair.public_key(),
SignatureScheme::Secp256k1
);
Ed25519 addresses use SHA-256 hashing, while Secp256k1 addresses use Keccak-256 for Ethereum compatibility. All addresses are represented as variable-length byte vectors with the signature scheme encoded in the first byte.
Signing and Verification
Tenzro implements trait-based abstractions for signing and verification. The Signer and Verifier traits provide a uniform interface across different cryptographic schemes.
use tenzro_crypto::signatures::{
Signer, Verifier,
Ed25519Signer, Ed25519VerifierImpl,
Secp256k1Signer, Secp256k1VerifierImpl,
};
// Sign a message with Ed25519
let message = b"Transfer 100 TNZO to Alice";
let signer = Ed25519Signer::new(ed25519_keypair.clone());
let signature = signer.sign(message)?;
// Verify the signature
let verifier = Ed25519VerifierImpl::new(
ed25519_keypair.public_key().clone()
);
assert!(verifier.verify(message, &signature).is_ok());
// Sign with Secp256k1 (Ethereum-compatible)
let secp_signer = Secp256k1Signer::new(secp256k1_keypair.clone());
let secp_signature = secp_signer.sign(message)?;
// Verify Secp256k1 signature
let secp_verifier = Secp256k1VerifierImpl::new(
secp256k1_keypair.public_key().clone()
);
assert!(secp_verifier.verify(message, &secp_signature).is_ok());
All signature operations use the tenzro_crypto::signatures::verify() function for automatic post-signing cryptographic verification. This is used throughout the stack, including in wallet transaction validation and bridge message verification.
Hashing Algorithms
Tenzro provides two primary hashing algorithms: SHA-256 for general-purpose hashing and Keccak-256 for Ethereum compatibility. Both are used in different contexts throughout the protocol.
use tenzro_crypto::hashing::{sha256, keccak256};
// SHA-256 hashing (default for Tenzro)
let data = b"Block header content";
let hash = sha256(data);
println!("SHA-256: {:?}", hash.as_bytes());
// Keccak-256 hashing (Ethereum compatibility)
let eth_data = b"Contract deployment bytecode";
let eth_hash = keccak256(eth_data);
println!("Keccak-256: {:?}", eth_hash.as_bytes());
Implementation: The Hash::combine() function uses SHA-256 for collision-resistant hash combination. It concatenates two hashes and returns the SHA-256 hash of the result, providing cryptographic security for Merkle tree construction and other hash-based data structures.
Symmetric Encryption
AES-256-GCM provides authenticated encryption with associated data (AEAD). It is used extensively in the wallet keystore for password-protected key storage with Argon2id key derivation.
use tenzro_crypto::encryption::{
aes_encrypt, aes_decrypt, generate_key,
};
// Generate a random AES-256 key
let key = generate_key();
// Encrypt sensitive data
let plaintext = b"Private key shares for threshold wallet";
let ciphertext = aes_encrypt(&key, plaintext)?;
// Decrypt
let recovered = aes_decrypt(&key, &ciphertext)?;
assert_eq!(plaintext, recovered.as_slice());
The wallet keystore uses Argon2id for password-based key derivation with the following parameters: 64MB memory, 3 iterations, parallelism 4. This provides strong protection against brute-force attacks.
Asymmetric Encryption and Key Exchange
X25519 provides Diffie-Hellman key exchange for establishing shared secrets. Envelope encryption combines X25519 with AES-256-GCM for hybrid encryption of large payloads.
use tenzro_crypto::encryption::{
X25519KeyPair, envelope_encrypt, envelope_decrypt,
};
// Generate ephemeral keypairs for sender and receiver
let sender = X25519KeyPair::generate();
let receiver = X25519KeyPair::generate();
// Sender encrypts to receiver's public key
let message = b"Encrypted inference request payload";
let encrypted = envelope_encrypt(
&receiver.public_key,
message
)?;
// Receiver decrypts with their private key
let decrypted = envelope_decrypt(
&receiver.private_key,
&encrypted
)?;
assert_eq!(message, decrypted.as_slice());
Envelope encryption is used for confidential agent-to-agent communication, encrypted inference requests to TEE providers, and secure channel establishment between nodes.
MPC Threshold Signatures
Multi-party computation (MPC) enables threshold signatures where a transaction requires m-of-n key shares to sign. Tenzro wallets default to 2-of-3 threshold signatures for enhanced security.
use tenzro_crypto::mpc::{
generate_key_shares, sign_partial, combine_signatures,
};
// Generate 3 key shares with 2-of-3 threshold
let master_key = Ed25519KeyPair::generate();
let shares = generate_key_shares(
&master_key.secret_key(),
3, // total shares
2 // threshold
);
// Each party signs with their share
let message = b"Transfer 500 TNZO from wallet";
let partial_sig1 = sign_partial(message, &shares[0])?;
let partial_sig2 = sign_partial(message, &shares[1])?;
// Combine 2-of-3 signatures (any 2 shares work)
let final_signature = combine_signatures(
&[partial_sig1, partial_sig2],
2 // threshold
)?;
// Verify against the master public key
let verifier = Ed25519VerifierImpl::new(master_key.public_key());
assert!(verifier.verify(message, &final_signature).is_ok());
Implementation: The MPC system uses Shamir Secret Sharing over GF(256) with Lagrange interpolation for threshold signatures. Key shares are generated using polynomial-based secret splitting, and signatures are combined using Lagrange coefficients, providing cryptographically secure m-of-n threshold signing.
Despite the current implementation limitations, the API is designed for future integration with proper threshold cryptography. Wallets are provisioned with 2-of-3 key shares stored across different devices or hardware security modules.
BLS12-381 Signature Aggregation
BLS signatures on the BLS12-381 curve enable efficient signature aggregation for consensus. Multiple validators can sign a block, and their signatures can be aggregated into a single compact signature.
use tenzro_crypto::bls::{
BlsKeyPair, BlsSignature,
AggregateSignature, AggregatePublicKey,
};
// Three validators generate BLS keypairs
let validator1 = BlsKeyPair::generate();
let validator2 = BlsKeyPair::generate();
let validator3 = BlsKeyPair::generate();
// All sign the same block hash
let block_hash = b"Block #12345 hash";
let sig1 = validator1.sign(block_hash);
let sig2 = validator2.sign(block_hash);
let sig3 = validator3.sign(block_hash);
// Aggregate signatures
let aggregate_sig = AggregateSignature::from_signatures(
&[sig1, sig2, sig3]
);
// Aggregate public keys
let aggregate_pubkey = AggregatePublicKey::from_public_keys(
&[
validator1.public_key(),
validator2.public_key(),
validator3.public_key(),
]
);
// Verify aggregated signature (single operation)
assert!(aggregate_sig.verify(block_hash, &aggregate_pubkey));
Implementation: The BLS12-381 system uses the blst library for elliptic curve operations. Signatures are generated using BLS12-381 curve operations, and signature aggregation uses native elliptic curve point addition, providing cryptographically secure signature aggregation for consensus.
Once properly implemented, BLS signatures will reduce consensus communication overhead from O(n) to O(1) by replacing n individual signatures with a single aggregated signature. This is critical for scaling to hundreds of validators.
Key Zeroization
Sensitive cryptographic material is zeroized on drop to prevent memory leakage attacks. The wallet crate uses the zeroize crate to ensure key shares are securely cleared from memory.
use zeroize::Zeroize;
// Key material is automatically zeroized when dropped
{
let secret_key = Ed25519KeyPair::generate();
// Use the key for signing...
let signature = secret_key.sign(message);
// Key is automatically zeroized when it goes out of scope
}
// Secret key bytes are now zeroed in memory
The wallet keystore implements key zeroization for all key shares. When a wallet is closed or the application exits, all secret key material is securely erased from memory.
Security Considerations
The cryptography crate is fundamental to network security. Several critical issues must be addressed before production deployment:
Security Status:
✓ MPC: Shamir Secret Sharing over GF(256)
✓ BLS12-381: blst library integration
✓ Hash::combine: SHA-256-based hash combination
Remaining Issues:
1. No constant-time comparison in signature verification
→ Enables timing side-channel attacks
2. SecretKey may remain in memory after drop
→ Implement zeroize-on-drop for all key types
The architecture and API contracts are designed for proper cryptographic primitives. The signature verification in wallet transactions, consensus vote verification, and bridge message verification all use consistent verification flows across the system.
Integration with Other Components
The crypto crate is a foundational dependency for most other crates in the Tenzro stack:
Wallet Module:
- MPC threshold signing (2-of-3 key shares)
- Argon2id + AES-256-GCM keystore encryption
- Transaction signature verification
Consensus Module:
- Vote signature verification
- BLS signature aggregation (future)
Bridge Module:
- Message signing with Ed25519/Secp256k1
- Signature verification via crypto::signatures::verify()
Identity Module:
- DID key derivation
- Credential signing and verification
Payment Module:
- MPP credential signature verification
- Challenge/response signing
All signature operations throughout the stack use the tenzro_crypto::signatures::verify() function for consistent cryptographic verification. This ensures that wallet transactions, consensus votes, bridge messages, payment credentials, and identity proofs all use the same secure verification logic.