EIP-5630: Encryption and Decryption

hello @ronaldmannak, apologies, I missed your message when you first posted it. thanks for your contribution and glad to have you on board.

actually, this is an almost exact description of how ECIES works, and is actually the only way to use asymmetric encryption for long messages. indeed, “true” asymmetric encryption only works for short messages—say, on the order of 32 bytes. everything longer than that, you’ll use the “hybrid” style of encryption you described above, which is what both GPG and ECIES use.

the model we propose is proven (and in fact this is true regardless of whether you use the HKDF to go from signing key ⟶ decryption key or not). the only model which is not proven secure is that of EIP-1024, in which the same secret key is used for both signing on the secp curve and simultaneously for decryption on curve25519.

I’d like to mention the " Deterministic Encryption and Storage via Snap/IPFS" project as one of the use cases that this EIP could apply to. It is the ETH Lisbon hackathon winner #1.

It is not messaging. Not sure if it falls into “dApp → user” category or “dApp → dApp”: the private key should be controlled by the same user all the time. In this case, there is no server behind the dApp.

3 Likes

Ahh fascinating! Thank you for this link. It appears to be achieving something quite useful which is like an encrypted personal data store pattern. Nice work @skgbafa! This is effectively what I was trying to describe in this comment but this achieves it in a way that requires less wallet code, so I’m in favor of this pattern. We’ll probably want to standardize a few more things to make it a bit more interoperable, but I’m willing to help out on that. Others want to leave that out of scope for this EIP so probably worth defining it separately and building on this one separately.

That helps me answer a ton of the questions I’ve been trying to understand albeit only for the “user to themselves” use case (maybe “user → user” category although I think people mean different people normally here). Up until this point, I’ve not found any code for a dApp that was using these APIs (including in firn’s dApp and tornado cash - I can see from the UX flow that it is being used in tornado.cash but can’t find the code on GH) so I was genuinely confused how this was achieving anything.

I’m still a bit confused about “user → dApp” category or “dApp → dApp” categories particularly because I think it depends on the deployment model of the dApp as well so they can decrypt the messages they’re receiving. Anyone got a code example of how that might work? Right now it seems like the way the code might work would be:

fetch(url, {
    method: 'POST',
    mode: 'cors', 
    cache: 'no-cache', 
    credentials: 'same-origin', 
    headers: {
      'Content-Type': 'application/json'
      // 'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: "my message to the dApp" 
  });
...

// time to decrypt response

window.ethereum.request({
  method: 'eth_decrypt',
  params: [{
    version: 'secp256k1-sha512kdf-aes256cbc-hmacsha256',
    ciphertext
  }, myEthAccount],
})

window.ethereum.request({
  method: 'eth_getEncryptionPublicKey',
  params: [myEthAccount],
})

window.ethereum.request({
  method: 'eth_decrypt',
  params: [{
    version: 'secp256k1-sha512kdf-aes256cbc-hmacsha256',
    ciphertext
  }, myEthAccount],
})

If we did it the opposite way where it was the dApp that initiated the message I believe the order here would just be flipped around. If we wanted to replace that fetch call with an encrypted message then we’d need to define how to retrieve the dApp public key and how to post the message to it. If we don’t want to define this, then I’m back to my original point of why send messages to the dApp unencrypted by receive messages from the dApp encrypted?

1 Like

it looks like they actually invoke our EIP :slight_smile: (presumably as a mock at this stage.) plus additional logic for storage and retrieval. nice find.

1 Like

This is awesome work. It lends itself nicely to extension as well. One such way would be to use a contract as a public key registry. This effectively enables decentralized private file sharing. I was doing previous work on this here, with the former EIP-1024.

I think this EIP has the potential to open up a bunch of use cases based on work like this. I think the “person → person” or “person → people” categories are easily opened up as well. Again using an extension to this methodology through a contract. The same code shared above has a methodology to share a encrypted file to a group of people. I think the use case for people to communicate over encrypted channels itself is very compelling and important. Not to mention in the current form it fits nicely into permissionless building.

1 Like

For the key discovery or public key registry, I think it is time to summarize:

  1. Smart Contracts could expose a function to return a public key;
  2. EOA: encrypt to the “direct” public key, that is, the one corresponding to the address. Usually this could be recovered from past signatures. We need a utility for this purpose.
  3. EOA: encrypt to the “derived” public key as currently specified. We may need a registry, be it smart contract or ENS or something else. Fee may incur.

There are also security implications to the choice between “direct” or “derived” public key, mostly about key management.

I’d like to link this to my other proposal about private key encapsulation: that is, if we rely on this EIP for file encryption or end2end encrypted messaging, we don’t want to enter our seed to a messaging app. We could safely transport the needed private key (“direct” or derived) to the app without endangering the entire seed.

1 Like

hi @Weiji.

let’s back up—i don’t see why there would ever be a need to input your seed into a messaging app?

let’s take an example. in the case of e2e messaging, it’d work the following way. the “dapp”—which, i emphasize, is just some static javascript loaded into the browser—will perform the following steps to send a message:

  • it would acquire the recipient’s public key, either by doing an ECDSA recovery on an existing signature (“direct”) or retrieving it from a registry (“derived”).
  • it would solicit the desired plaintext message from the user, through some kind of input form.
  • it would run some local javascript code to encrypt this plaintext to the desired recipient’s key.
  • it would prompt the user to post this ciphertext on-chain, and to pay the gas for doing that.

to receive / decrypt a message, the dapp would do as follows:

  • it would retrieve the relevant ciphertext(s) from an on-chain source, e.g., the user’s “inbox” contract.
  • the (static) dapp code would issue an eth_decrypt request to the user’s wallet, with the ciphertext as argument. the user will be prompted to approve the decryption.
  • the plaintext will now be displayed by the dapp web interface.

there is never a need to expose the seed or any private material whatsoever to the dapp, or to any remote server.

hopefully this helps clarify things? thanks for your thoughts.

Consider if the user is keeping his or her seed or private keys in a hardware wallet, yet the dapp runs in a mobile device or browser extension for convenience. If we rely on the hardware wallet to decrypt, then literally user will have to approve the decryption for every incoming message. And if he or she leaves the hardware at home, there is no way to decrypt messages for the day.

so yes, this is true. but usually we accept and even expect user approval for each sensitive action. but if you wanted to bypass this, you could use the following flow instead.

to log in / create an account:

  • the browser client generates a fresh random secret key for the user, say nk. it locally computes the corresponding public key, say mk.
  • the browser posts mk in a registry known to the dapp, so that other users can discover the key mk.
  • the browser separately issues an eth_getEncryptionPublicKey request to the wallet, which the user is prompted for; it receives the resulting stable public encryption key, say ek.
  • it encrypts the secret nk to ek, and posts the resulting ciphertext to the blockchain somewhere belonging to the user, for recovery later.
  • to log back in later, the browser retrieves this ciphertext, and issues an eth_decrypt request on it to the user. after the user accepts the request, the browser obtains nk, which it can use to decrypt every message.

to send a message:

  • after inputting the desired recipient into the dapp, the dapp retrieves the dapp-specific public key mk corresponding to this receipient.
  • the dapp solicits the plaintext from the user, encrypts it to mk, and posts the ciphertext on-chain.

to receive a message:

  • once you’re logged in (see above) the browser has access to nk, using which it can automatically retrieve / decrypt every message.

in this case, you do provide the secret nk to the dapp. but this secret is completely unconnected to your seed phrase hierarchy, and is not used for any purpose other than the dapp. moreover, there is no need for TLS / Diffie–Hellman / key encapsulation / etc., since the dapp runs locally on the same machine as the wallet—nk is never transmitted to any external machine. thoughts?

Hi @firnprotocol Key management is a complicated issue and not a pure technical decision. Sometimes it involves uers. Let’s focus on this EIP here and redirect KEM related discussion to the other thread. Do you mind if quote your comments there?

absolutely, go ahead.

@firnprotocol What do you think about this? Do you see a need to include it in 5630? It’d simplify public key discovery issues in some applications/scenarios.

right, yes, i think it’s a great idea. i just think we need to first decide (i.e., as a community) whether we want to include the HKDF or not (i.e., whether we do 2 or 3 in the message you’re quoting from).

  • benefit of using HKDF: slightly stronger security (i.e., security argument doesn’t rely on the GGM).
  • benefit of not using HKDF: allow key discovery by performing ecrecover on existing signatures.

which benefit is more “worth it” first needs to be decided by us, on technical grounds.

i’m not quite sure what you mean. certainly the decision of whether to use the HKDF or not needs to be made in this EIP.

on the other hand—regardless whether or not we include the HKDF—my sense is that the mechanism for public key discovery is out-of-scope for this EIP. if we don’t use the HKDF, then there’s not much of a problem to solve, since it’s essentially trivial (at least for addresses which have signed before). If we do use the HKDF, then it becomes much less trivial, and we’d need something analogous to ENS. either way though, i think that would be the territory of a separate EIP. thoughts?

I do think it is important for an EIP to be considered for its infrastructure dependencies. Public key discovery is non-trivial and subtle to manage right. And when such is lack we need to jump start this EIP.

Can we support both? Then users and community could choose either or both as they see fit. I do see applications without HKDF, when people can send each other encrypted messages as long as the peer had signed any txn/msgs before.

hmm. perhaps we could support both, and use separate version tags for each. e.g., secp256k1-sha512kdf-aes256cbc-hmacsha256 and secp256k1-sha512kdf-aes256cbc-hmacsha256-nokdf. (these are getting fairly long…). i’d personally prefer that we pick one, just for simplicity, but maybe we can let community members weigh on on this.

while i agree with you that it’s important and subtle, I think I have to respectfully disagree that it should be included in this EIP. i would be happy to help & advise on a separate EIP treating the issue of public key distribution / advertising.

Maybe simply secp256k1-aes256cbc-hmacsha256

This has been going around a few times. I totally respect your decision here. Let’s close it here.

1 Like

Before I submit a new pull request, let’s do a little bit polling here.

Do you think we need to add a new version secp256k1-aes256cbc-hmacsha256?

  • Yes
  • No

0 voters

The purpose of this new version is that applications could choose to encrypt message to the original public key or one derived with sha512kdf. Please checkout earlier discussion for context. Thanks.

in the interest of clarifying the poll…

do you intend that a “yes” vote entails that we do both? or that we only do the version without the KDF? if possible, please clarify that, and then i will vote.

btw, to clarify, the sha512kdf in the original naming suggestion refers to the KDF done within ECIES (to conver the DDH secret into an AES + MAC secret key). it has nothing to do with the KDF relating skdk, which is separate. just so you know.

both, sorry for the confusion

Thanks for the clarification. I assumed the wrong way. Then maybe the suggested additional version could be: secp256k1-none-sha512kdf-aes256cbc-hmacsha256, with none following immediately after secp256k1 to indicate that no KDF is done to derive dk from sk. The original version could also be changed to sth like secp256k1-X963-sha512kdf-aes256cbc-hmacsha256. I’d also suggest that we trim the trailing cbc-hmacsha256 as there is no other option here. How do you think? @firnprotocol