# ERC-? Encrypt To Account
## Abstract
This standard allows contract wallets to receive encrypted messages.
The sender calls `getEncryptionPublicKeys()`, a view function, and uses the result to perform key agreement. Messages are encrypted to each of the returned keys.
## Motivation
Contract accounts have many great advantages. When implementing just ERC-4337, however, they lack two important primitives: encryption and signing. An EOA, by contrast, consists of an `secp256k1` keypair, and so has those capabilities automatically.
ERC-1271 generalizes signing to contract accounts.
This proposal generalizes encryption.
There are many ways in which encrypt-to-account is a useful primitive. One of the main ones is end-to-end encrypted messaging between Ethereum accounts. (Such systems should generally use a ratchet protocol for forward secrecy, but encrypt-to-account is useful for the initial key exchange.) Another important use case is private payments, where encrypt-to-account can either directly be part of the payment protocol or useful for notifying a recipient of payment.
## Specification
```solidity
pragma solidity ^0.8.0;
struct PubKey {
/** @dev Key algorithm: bytes4(keccak256(name)) */
bytes4 keyType;
/** @dev Public key: raw encoding. */
bytes keyData;
}
interface ERCXXX {
/**
* @dev Returns the current public key(s) associated with this account.
*
* - SHOULD NOT read call context (msg.sender, etc) or any state other than
* the current owner(s) of this account.
* - MUST NOT modify state. See the view modifier.
*/
function getEncryptionPublicKeys() external view returns (PubKey[] memory);
}
```
For example, for P-256 keys, the type is `bytes4(keccak256("P256-ECDH")) = 0x4da09a0f`.
Senders SHOULD support the following types.
- `P256-ECDH` for NIST P-256 (secp256r1) ECDH. The key is 64 bytes, (x, y) curve point, each component encoded as uint256.
- `secp256k1-ECDH` for secp256k1 keys, the same algorithm used to encrypt to an EOA. The key is similarly 64 bytes, (x, y).
Additional types may be added in the future by rough consensus. Account implementers should avoid introducing new key types unless for good reason, since excessive algorithm choice has historically made cryptosystems more complex and less secure.
## Rationale
We considered a simpler, more general encryption facility based on an offchain view function:
``` solidity
interface ERCXXX {
function encryptToAccount(bytes32 message, bytes32 nonce)
external view returns (bytes ciphertext);
}
```
The issue with this approach is that it requires encryption keys to generated in-memory in local EVM execution. Hardware enclaves (including Ledger for secp256k1; Yubikey, Secure Enclave, and others for r1) achieve better security by keeping secrets out of application memory.
Furthermore, this interface would be easy to misuse: most Ethereum applications today use centralized RPC APIs for chain operations. An `encryptToAccount` view function requires local execution to avoid revealing your plaintext to a third party.
Shared secret derivation based on `getEncryptionPublicKeys` avoids both of those issues.
## Security considerations
Shared key derivation is only one part of a larger cryptosystem. Future standards can specify surrounding protocols for Ethereum account-based end-to-end encrypted messaging, private payments, and other use cases.
In general, applications should encrypt-then-sign using using a derived shared key for encryption, then signing the ciphertext with a ERC-1271-compatible signature.
One key feature of contract accounts is key rotation. Encryption is less protected by key rotation than signing or transacting, in the sense that if a old (no longer valid) signing key is compromised, an attacker is not able to sign ERC-1271 messages or send userops, but they *would* be able to retroactively decrypt messages sent to that account while the key was valid.
This can be mitigated with forward secrecy in a encrypted messaging context, and in general by not logging ciphertexts in transit or storing them longer than necessary.