Key Custody
Tenzro provides institutional-grade key custody through MPC threshold wallets, encrypted keystores, and hardware-backed TEE key sealing. Every identity on the network — human or machine — receives an auto-provisioned 2-of-3 MPC wallet with no seed phrases required. Keys are protected at rest with Argon2id KDF and AES-256-GCM encryption, and in computation with TEE enclave sealing.
MPC Wallet Architecture
┌──────────────────────────────────────────────┐
│ MPC Wallet (2-of-3) │
│ │
│ Share 1: User device (encrypted keystore) │
│ Share 2: TEE enclave (hardware-sealed) │
│ Share 3: Recovery backup (offline/custodian) │
│ │
│ Any 2 shares can reconstruct the signing key │
│ No single party ever holds the full key │
└──────────────────────────────────────────────┘
Signing flow:
1. User initiates tx on device (Share 1)
2. TEE enclave co-signs (Share 2)
3. Partial signatures combined → valid signature
4. Share 3 never touched unless recovery neededThe wallet uses Shamir Secret Sharing over GF(256) to split keys into 3 shares. The threshold is configurable but defaults to 2-of-3. Signing produces partial signatures from any 2 shares which are combined into a full Ed25519 or Secp256k1 signature.
Encrypted Keystore
Key shares stored on the user's device are protected with a password-derived encryption key using industry-standard parameters:
| Parameter | Value | Purpose |
|---|---|---|
| KDF | Argon2id | Memory-hard password hashing (resistant to GPU/ASIC attacks) |
| Memory | 64 MB | Memory cost per hash computation |
| Iterations | 3 | Time cost (passes over memory) |
| Parallelism | 4 | Degree of parallelism |
| Cipher | AES-256-GCM | Authenticated encryption of key material |
| Nonce | 12 bytes (random) | Generated from CSPRNG (OsRng) |
use tenzro_wallet::{WalletService, KeystoreConfig};
// Create a new MPC wallet with encrypted keystore
let wallet = WalletService::create_wallet(KeystoreConfig {
password: "user-password",
threshold: 2,
total_shares: 3,
key_type: KeyType::Ed25519,
})?;
println!("Address: {}", wallet.address());
// The keystore file is encrypted at rest:
// password → Argon2id(64MB, 3 iterations, p=4) → 256-bit key
// key + random nonce → AES-256-GCM(key_share) → ciphertext
// Stored as JSON: { version, kdf_params, cipher, ciphertext, nonce, tag }
// Import from encrypted keystore
let imported = WalletService::import_keystore(
"/path/to/keystore.json",
"user-password",
)?;Key Rotation
Key shares can be rotated without changing the wallet address. During rotation, new shares are generated and distributed while the old shares are securely zeroized. The underlying public key and address remain the same.
// Rotate key shares (requires 2-of-3 current shares)
wallet.rotate_shares(
&[current_share_1, current_share_2],
"new-password", // Re-encrypt with new password
)?;
// Old shares are zeroized in memory via the zeroize crate
// New shares distributed to the same locationsSpending Limits
Wallets enforce configurable spending limits through the TDIP delegation scope. Machine identities (AI agents) always have spending limits set by their human controller.
# Set spending limits via delegation scope
curl -X POST https://rpc.tenzro.network \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "tenzro_setDelegationScope",
"params": [{
"did": "did:tenzro:machine:controller123:agent456",
"max_transaction_value": "1000000000000000000",
"max_daily_spend": "10000000000000000000",
"allowed_operations": ["transfer", "inference"],
"time_bound": {
"start": "2026-01-01T00:00:00Z",
"end": "2027-01-01T00:00:00Z"
}
}],
"id": 1
}'Session Keys
Session keys are temporary signing keys with limited permissions and expiration. They allow dApps and agents to sign transactions without accessing the master wallet. Session keys are implemented through the ERC-4337 account abstraction module.
import { TenzroClient } from "@tenzro/sdk";
const client = new TenzroClient("https://rpc.tenzro.network");
// Create a session key with limited permissions
const session = await client.wallet.createSessionKey({
permissions: ["transfer", "inference"],
maxTransactionValue: "1000000000000000000", // 1 TNZO
expiresIn: 3600, // 1 hour
allowedContracts: ["0xmodel_registry..."],
});
console.log("Session key:", session.publicKey);
console.log("Expires:", session.expiresAt);
// Use session key to sign transactions
const tx = await client.wallet.sendWithSessionKey(session.key, {
to: "0xrecipient...",
value: "500000000000000000", // 0.5 TNZO
});TEE Key Sealing
Hardware-sealed keys — On nodes with TEE hardware (Intel TDX, AMD SEV-SNP, AWS Nitro), key shares are sealed using the enclave's hardware key. Sealed data can only be decrypted inside the same enclave with the same measurement. The sealing uses AES-256-GCM with keys derived via HKDF-SHA256(key_id, vendor_tag) with domain separation per TEE vendor. See TEE Attestation for details.
CLI Usage
# Create a new wallet
tenzro-cli wallet create
# Import from keystore
tenzro-cli wallet import --keystore /path/to/keystore.json
# Check balance
tenzro-cli wallet balance
# Send transaction (requires password to decrypt keystore)
tenzro-cli wallet send --to 0xrecipient... --amount 10.0
# List wallets
tenzro-cli wallet listKey Zeroization
All sensitive key material is zeroized on drop using the zeroize crate. This ensures private keys, key shares, and decrypted keystore contents are wiped from memory as soon as they go out of scope, preventing recovery from memory dumps.