Asymmetric encryption, also known as public-key cryptography, is an encryption scheme that enables anyone to encrypt messages, but only the intended receiver can decrypt it. This method works by generating a key pair: a public key can be freely distributed and used by anyone for encrypting data, and a corresponding secret private key that is used for decrypting data.
Asymmetric encryption is a natural fit for sending encrypted messages. The receiver shares their public encryption key, so that any sender can encrypt a message that only the receiver can decrypt.
Unfortunately, there is no single standard for asymmetric encryption in Web3. While there have been attempts to standardise primitives for building asymmetric encryption, they have not been successful:
EIP-1024: This EIP proposed methods for eth_getEncryptionPublicKey
and eth_decrypt
, unfortunately the EIP was never merged. While some wallets have implemented this EIP, most have since deprecated the methods citing a lack of standardisation as well as security concerns.
The security concerns raised around EIP-1024 were that the same secret was used for both signing on the
secp256k1
curve (for signing Ethereum transactions for example) as well as encrypting and decrypting on thex25519
curve (for the EIP-1024 encryption methods). While there are no known attacks on this setup, it has not been properly studied, and there are legitimate concerns in using a cryptographic system for which there is no known soundness or security proof.
ERC-5630: This draft ERC was proposed as an alternative to the deprecated EIP-1024. It implements a similar eth_getEncryptionPublicKey
function, and a slightly different eth_performECDH
, which is more flexible than eth_decrypt
. While this ERC does not have the same security concerns as EIP-1024, it has little to no adoption.
Using these methods for encryption in a decentralised application (dapp) come with significant risks: you would be making use of non-standard wallet RPC methods, which are both called and implemented in slightly different ways:
MetaMask: MetaMask deprecated EIP-1024 support for eth_getEncryptionPublicKey
and eth_decrypt
, but provides a Snap that implements the same methods. The Snap does not have the same security concerns (as it uses a different secret for encryption than the account’s main private key used for signing).
Ledger: Ledger added support for EIP-1024 in their hardware wallets, using the same derived secret for both signing and encrypting.
While Ledger and MetaMask are very popular wallets in Web3, the current state of asymmetric encryption is far from ideal:
Not all wallets implement the required RPC methods; this means dapps would be required to only support a short list of wallets in order to leverage E2EE.
Different implementations derive the secret encryption key differently, meaning that a user’s seed phrase would not be portable across wallets with respect to E2EE, potentially losing access to their encrypted data.
Another portable approach to E2EE Web3, originally pioneered by 3Box and now Ceramic Network, is to use standard wallet RPC methods to deterministically generate some entropy — a source of randomness that is computationally infeasible for an attacker to guess — that is tied to the user’s wallet. This “deterministic randomness” (an oxymoron if I’ve ever heard one!) is used to create an encryption key pair: the public key is shared so anyone can encrypt messages for a wallet, and the private key is only used in the dapp for decrypting those messages. The “randomness” ensures that an attacker cannot brute-force your private encryption key and decrypt the user’s secret data, and the “deterministic” aspect ensures that you will always generate the same encryption key from the same wallet.
The most commonly used method for generating entropy is to request a signature. Because Ethereum wallets use RFC 6979 deterministic signatures, the same account will always generate the exact same signature for the exact same message. A dapp would then chose a specific authorization message, which once signed would deterministically produce an encryption key pair for use by the dapp.
This mechanism has the distinct advantage of being portable across all compliant wallets, as it only relies on standard wallet RPC methods (in particular, eth_sign
or personal_sign
- for more information on the complicated history and relation between the two, I recommend taking a look at A History of eth_sign in MetaMask by Dan Finlay). However, this added portability comes with a serious security downside: the encryption key is materialised in the dapp.
If ever the private key were to be leaked for whatever reason, then all past and future messages that are encrypted for the corresponding public key can be decrypted by the attacker.
This encryption scheme opens the user up to phishing tactics: a malicious app can request the user to sign the same authorization message as the legitimate app in order to phish the user’s encryption key. While the signing domain can be included in the authorisation message (as shown in the above example), it is very easy to overlook the URL in the message and confirm the signature anyway; leaking your encryption key to the malicious dapp. This is further amplified by the previous security downside, where leaked keys allow the attacker to decrypt all past and future messages.
Note that this phishing vector can be mitigated by using Sign-In with Ethereum (SIWE) and specifying a domain tied to your dapp. Many wallets, including MetaMask, have additional support for SIWE and will either warn or prohibit sign-in requests from a domain that doesn’t match the message. This helps prevent malicious websites from phishing SIWE signatures, and therefore helps secure the user’s encryption key.
The pseudo-random function (PRF) extension is a recent addition to the web authentication (WebAuthn) API that underpins passkeys. It is a dedicated and standardised mechanism for providing cryptographic entropy associated with a passkey credential. The PRF extension is based on the client to authenticator protocol (CTAP) widely supported hmac-secret
extension, and is specifically designed with the “generating symmetric keys” use case in mind. In fact, age
— a popular file encryption tool — makes use of this extension for encrypting files with passkeys, and LUKS — the de facto standard for full disk encryption on Linux — makes use of the underlying for filesystem encryption using FIDO2 hardware tokens (like a Yubikey).
That being said, PRF is still not an ideal solution for asymmetric encryption and is not designed with that use case in mind. The encryption key is still materialised in the dapp and leaks of this key are just as problematic as with using signatures as entropy. The notable advantage, however, of using the passkey PRF extension is that the passkey credentials are tied to the domain that they are used in. It is, therefore, not possible for a malicious dapp to have access to the credential and entropy used in by other dapps, which prevents the phishing of encryption keys and removes one of the main attack vectors compared to using eth_sign
as a source of entropy.
Finally, another approach with is seeing increasing use in Web3 as the technology behind it matures is threshold encryption.
Shutter uses it for shielding mempool transactions only to revealing them once they are committed for inclusion in a block.
The Lighthouse storage protocol uses threshold encryption for storing files on Filecoin, only decrypting files for authorised users.
This allows encryption in a secure way that is both portable across wallets, and does not require a secret key to be constructed directly in dapp. This comes, however, at a significant cost of adding the complexity of a decentralised threshold encryption protocol (decentralised nodes in a threshold encryption network, economic incentives for encrypting and decrypting user data, slashing mechanisms for decrypting data when it shouldn’t be, etc.).
E2EE in Web3 is challenging. There is currently no single, ideal solution; implementers must choose between supporting a limited number of wallets each with a slightly different implementation, or embracing schemes that, while portable, introduce potential security issues or significant additional complexity.
That being said, it is still possible to build privacy preserving dapps leveraging E2EE. Specifically, depending on your threat model and the nature of the encrypted data, using wallet or passkey generated entropy for generating encryption keys would enable building a portable and secure dapp. Additionally, some strategies can be used to reduce risks associated with materialising the key directly in the dapp:
SIWE or the passkey PRF extension can be used to mitigate phishing attack vectors by binding encryption keys to a specific dapp domain.
Key separation and rotation can be built directly into the dapp. This would users to generate different keys for different message topics, while allowing for easy rotation of the key in case of suspected compromise. Combined, this greatly reduces the risk of leaking sensitive information.
Using IPFS or similar content-addressable systems for hosting dapps, ensuring authenticity and integrity of the dapp code and mitigating supply-chain attacks.
As the Web3 ecosystem works towards adopting a future wallet standards for encryption, we can still proactively build impactful, privacy-preserving dapps leveraging end-to-end encryption today.
A shout-out to both Joel Thorstensson (@joelthorst) and Sebastian Bürgel (@SCBuergel) for taking the time to talk to us about the subject and providing thoughtful feedback!
Safe Research is the applied R&D arm of Safe, dedicated to advancing the self custody stack. Our work is grounded in the cypherpunk principles of security, censorship resistance, and privacy, and we focus on building trustless, user centric infrastructure for smart accounts and wallets.
Discuss this post in our Forum
Learn more about Safe Research
Read our manifesto
Let’s make Ethereum’s original cypherpunk vision real, one commit at a time.
©2023–2025 Safe Ecosystem Foundation