Encryption Mechanisms in Aleo. From ElGamal to Viewing Keys.
Aleo’s privacy model encrypts all record data by default, using an Elliptic-curve ElGamal–style scheme built for zero-knowledge proofs. In Aleo, each record (the analog of a UTXO) includes an owner’s address, a data payload, and a unique nonce. The record’s commitment is computed as a SNARK-friendly Pedersen/PRF commitment over these fields. For example, a record commitment cm is formed as cm = CM.Commit(pp, v‖apk‖d‖ρ; r), where apk is the owner’s public address key, d is the payload, ρ is the one-time nonce, and r is hiding randomness. This commitment binds the owner and data while hiding contents on-chain. (The diagram below illustrates how the address, payload and nonce feed into the record commitment.)
Figure: Aleo record commitment (simplified). Each record r produces a commitment cm = CM.Commit(pp, visibility||owner_address||data||nonce; randomness), binding the owner’s public key (apk), payload, and unique nonce ρ into the committed record【73†】.
With the record structure committed on-chain, Aleo encrypts the actual payload using an ECIES-like scheme. In practice, Aleo’s record encryption works like an elliptic-curve ElGamal: the sender derives a shared secret with the receiver’s public address and then uses it to mask the record data. Concretely, the sender chooses a fresh random nonce (scalar) and publishes the corresponding public “nonce point” in the record. This plays the role of the ephemeral ElGamal public key. Both sender and receiver compute a shared secret S = owner_address * nonce; taking its x-coordinate gives a symmetric record view key. This shared secret is fed into a Poseidon-based PRF to generate one-time masking values that encrypt each private field of the record. The use of Poseidon (a SNARK-friendly hash) and elliptic-curve Diffie-Hellman (ECDH) ensures the scheme is compatible with ZK circuits. In code (SnarkVM/Rust), one finds:
rustКопировать код// Pseudocode from SnarkVM: // Ensure `nonce_point = G * randomizer` matches the record’s nonce assert(nonce == G * randomizer); // Derive shared secret (x-coordinate of EC Diffie-Hellman) let record_view_key = (owner_public_key * randomizer).to_x_coordinate(); // Encrypt data with derived key via Poseidon-based symmetric cipher self.encrypt_symmetric(record_view_key);
This matches the Aleo implementation: the encrypt method computes record_view_key = (*self.owner * randomizer).to_x_coordinate() and then calls a Poseidon-based encrypt_symmetric routine. In other words, Aleo’s symmetric record key = ECDH(owner_pub, nonce), and encryption = Poseidon-PRF masking (rather than e.g. AES) for SNARK efficiency.
Ephemeral Key Generation and Record Encryption
Each Aleo transaction (called a transition) generates fresh ephemeral keys for encrypting its output records. The sender first derives a transition view key by hashing their own account key with randomness. From this seed the sender obtains a one-time EC keypair: a transition secret (view) key and its public key. This “transition public key” is published with the transaction so that owners of the outputs can decrypt. Then for each output record, the sender chooses a random scalar “randomizer” = nonce and computes nonce_point = G * randomizer, embedding it in the record. The shared secret is S = receiver_address * randomizer, whose x-coordinate (a field element) is the record view key. This key is used to mask the record’s private fields via Poseidon.
In summary, the record encryption flow is:
- Key Derivation: Compute
S = P_recipient * r, whereris the randomizer (nonce) andP_recipientis the owner’s address point. - Symmetric Key: Let
K = x(S)be the x-coordinate ofS. This is the shared secret (record view key) for this record. - Encryption: Expand
Kvia Poseidon PRF to produce keystream blocks, XOR-ing/masking each private field of the record.
The Lambdaclass implementation succinctly shows this: it asserts nonce == G * randomizer, then computes record_view_key = (owner.to_group() * randomizer).to_x_coordinate(), and finally encrypts with encrypt_symmetric(record_view_key). Because randomizer (the nonce) is fresh per record, each encryption is one-time padded by a unique ECDH key.
Security note: Although new ECDH secrets are used per record, Aleo’s scheme is known to be non-committing. In other words, the ciphertext alone does not bind to a specific plaintext without the view key. A recent audit flags “Non-Committing Encryption” in Aleo’s input/output encryption as a high-risk issue. In practice this means one should rely on Aleo’s built-in proof system (and cautious contract patterns) to prevent malleability attacks, since an encrypted record could in principle be tampered with and re-encrypted without detection. (This subtlety is common in Turing-complete ZKP systems.)
Viewing Keys and Selective Decryption
Aleo accounts have a private view key and a corresponding public address key. The address apk is essentially an elliptic-curve public key (a combination of the user’s signing and PRF public keys). The view key avk is a private scalar derived from the account’s secret values. Cryptographically, the view key is the secret corresponding to the address: one can think of apk = avk * G in the underlying group.
Only someone with the correct view key can derive the shared secret to decrypt a record. When a user scans the blockchain, they see each record’s ciphertext and nonce. They compute K = avk * nonce_point. If their avk matches the intended owner (so that avk * nonce_point = S), this recovers the same shared secret used by the sender. Feeding K into the Poseidon PRF yields the masking stream, allowing recovery of the plaintext values. If the wrong key is used, decryption fails.
Importantly, Aleo’s design allows selective disclosure: an account holder may share their view key with auditors or services. Anyone holding the private view key avk can decrypt all records owned by that account (and only those records). This enables transparent auditing of encrypted history. But without the view key, encrypted record data remains opaque.
In practice, Aleo provides a CLI for these operations. For example, one may run:
php-templateКопировать кодsnarkos developer decrypt -v <VIEW_KEY> -c <RECORD_CIPHERTEXT>
to decode a ciphertext with a given view key. The Decrypt command outputs the original record fields if the key matches. Conversely, record creation/encryption is handled automatically by Aleo’s SDK or snarkOS when sending a transaction. (The SDK takes care of generating nonces and computing the encrypt_symmetric step internally.)
Security Analysis
The security of Aleo’s record encryption rests on standard ECC assumptions. Assuming the hardness of the elliptic-curve Diffie–Hellman problem, an adversary cannot recover the shared secret from the public address and nonce alone. The Poseidon PRF is cryptographically secure under standard hashing assumptions in the SNARK field. Thus Aleo’s encryption is indistinguishable under chosen-plaintext attack (IND-CPA) for record payloads.
A unique nonce per record provides anti-replay: any attempt to reuse a record ciphertext or its nonce will produce a duplicate serial number (nullifier), which the ledger rejects as a double-spend The nonces ensure each record has a unique serial (sn = PRF(skPRF, ρ)) so that replaying an old transaction is infeasible.
However, Aleo’s encryption does not provide forward secrecy against key compromise: if an account’s view key avk is later leaked, an attacker can decrypt all past records for that account, since all ephemeral nonces are published on-chain. Aleo assumes long-term secrecy of the view key for confidentiality. On the other hand, because new Ephemeral transition keys are used per transaction, compromise of one transition key does not break others.
The correctness of encryption/decryption is integrated into the ZK proofs. In each transition, the prover must demonstrate consistency of encryptions and decryptions within the circuit, ensuring that the committed and revealed values match. This proves both correctness and prevents arbitrary tampering (beyond the non-committing issue noted above) under the Aleo proof system.
Code Examples
Developers can interact with Aleo encryption via the snarkos CLI and SDK. For instance, to decrypt a record’s ciphertext with a view key, one uses:
snarkos developer decrypt -v AViewKey1nKB4qr... -c eyJhbGciOi...<ciphertext>...
This command takes a Base58‐encoded view key and a record ciphertext string, and returns the plaintext contents of the record if the key is correct. (The view key format and address prefixes are defined in the account key docs.)
Records themselves are created within Aleo programs by emitting new record values (e.g. with output r as RecordType.private). The Aleo VM automatically handles encryption when building the transaction. In Rust (SnarkVM), encryption looks like this snippet (from the Arkworks implementation):
// Example from lambdaclass/aleo_lambda_vm // Given owner (account address) and randomizer scalar: let record_view_key = (owner.to_group() * randomizer).to_x_coordinate(); // This value encrypts the record symmetrically: let ciphertext = record.encrypt_symmetric(record_view_key);
This matches how Aleo’s backend enforces nonce == G * randomizer and computes owner_pub * randomizer as the shared key. After this, all private fields of the record are masked by the Poseidon-PRF stream derived from record_view_key.
Comparison with Zcash and Ethereum Stealth Approaches
Aleo’s scheme is conceptually similar to Zcash’s note encryption but differs in implementation and scope. In Zcash (Sapling/Orchard), each note is also encrypted to a recipient using an ephemeral Diffie–Hellman key: the sender computes a shared secret with the recipient’s public spend/view keys, then uses KDF and symmetric ciphers (AES/SHA3) to encrypt the note plaintext. Aleo likewise uses ECDH to derive a shared key and a symmetric cipher (Poseidon instead of AES) to mask data. Both use view keys: Zcash has an “incoming viewing key” (IVK) that allows a recipient to decrypt notes sent to them. Aleo’s view key plays the same role for record data. (Zcash even defines an outgoing viewing key so senders can later recover their own sent notes; Aleo’s model is simpler, treating the sender as always knowing the plaintext they create.) The Zcash Rust library calls this the “in-band secret distribution” scheme.
Ethereum’s stealth addresses are also based on similar ECDH ideas, but with important differences. In a stealth scheme (as proposed in EIP-5564), Alice picks a random r, computes R = G*r and publishes R. She shares funds to Bob by creating an address P = Bob_pub + G*H(S) where S = Bob_priv * R. Bob can compute the corresponding private key b = Bob_priv + H(S) and spend from P. Vitalik describes this process: Alice computes S = M * r and the stealth public key P = M + G*hash(S), while Bob computes the private key m + hash(S). Stealth addresses hide the link between sender and receiver on-chain, but they do not encrypt transaction values (only the destination address is hidden). In contrast, Aleo encrypts the contents of a record (amounts or state) on-chain, not just the address. Ethereum’s approach also typically requires off-chain scanning of transaction nonces (ephemeral public keys) to find funds. Aleo always reveals the encrypted record on-chain, but only holders of the view key can recover it.
In summary, all three use elliptic-curve Diffie–Hellman: Aleo and Zcash use it for full data encryption of private fields, while Ethereum stealth uses it for address generation. Aleo’s innovation is integrating this into a general-purpose ZK VM with SNARK-friendly hashes, whereas Zcash built it into a UTXO-style shielded protocol and Ethereum stealth is an ad-hoc privacy layer atop accounts.
Privacy, Auditability, and Performance Trade-offs
Aleo maximizes privacy by default: all record fields flagged private are encrypted and hidden from peers. This provides strong confidentiality, but comes at computational cost. Every private transaction requires elliptic-curve operations and hash-based symmetric encryption inside the proof. Poseidon is efficient in SNARKs, but still heavier than cleartext operations. Moreover, large payloads (complex state) mean more encrypted data and PRF blocks to compute. Thus privacy comes with throughput and latency trade-offs compared to a transparent chain.
Auditability is enabled by view keys. A user can choose to share their view key with regulators, auditors or dApps, allowing them to see the encrypted record values while keeping on-chain privacy. This granular control is more flexible than Ethereum’s default transparency or Zcash’s model (where a single “full viewing key” reveals all notes). In Aleo, one could even derive a read-only compute key from the private key, granting limited access without exposing signing power.
Compared to alternatives, Aleo’s scheme is relatively lightweight: it encrypts only record payloads, not the entire state or transaction. Zcash’s note encryption also handles large notes (value + memo), but uses AES/SHA which are fast but not SNARK-friendly. Aleo’s use of Poseidon optimizes prover time. Ethereum stealth addresses add privacy for addresses, but to hide values one needs separate tools (e.g. zk rollups or mixers). Each choice has its trade-offs: Aleo favors on-chain data confidentiality at the expense of proof complexity, Zcash balances QAP-friendly ciphers with specialized note structures, and Ethereum stealth addresses focus only on unlinkability of accounts.
In all cases, achieving private transfers means sacrificing some transparency and adding cryptography. Aleo’s design carefully balances these concerns: it delivers full-value encryption and auditing support, while using ZK-friendly primitives (Poseidon, EC groups) to keep proving efficient. The result is a system where professional developers and cryptographers can reason about security (under standard ECC assumptions) yet still enjoy programmable privacy in practice.