EIP-5630: Encryption and Decryption

EIP-5630 + EIP-5437 sounds cost prohibitive for a generic inbox style messaging system. It also sounds like it would discourage reporting bugs. If this is done it’s going to be limited in scope of usage to only high value messages. Gas fees already can touch $5+ for a transaction on L1 at peek capacity and the incentives don’t align for security researchers to report vulnerabilities if there’s an additional cost to submit a bug report.

This seems like it would be recreating the economics of email spam protection mechanisms that were never widely used. I don’t think this would be useful. Can someone explain the usefulness of this from the security researcher’s perspective that would justify why this is better than a H1 bug bounty program?

I am interested in this EIP and curious to help push it forward in whatever form. I think there are a large variety of use cases.

I am most interested in building an encrypted filesystem primitive. The file is a well established pattern and will help bring the EVM up a level of abstraction to more users. This primitive alone enables many new use cases.

A filesystem/namespace could generally looks like Upspin to start. In terms of cryptography and access control Upspin chooses to implement something very similar to what is suggested by @SamWilsn. That is: using asymmetric keys for encrypting a symmetric passcode which then encrypts a file. This method could reasonably be used to share files and do access control.

I’ve built a proto-version of this using the deprecated eth_encrypt and eth_decrypt provided by MetaMask, and it would be nice to have an EIP which is well supported across many wallet implementations. I have no preference for curves, so whatever seems most cryptographically secure seems to make sense. I think it seems like this would be curve25519 instead of secp256k1.

On top of such a filesystem primitive many applications could be built. Messaging. Encrypted File Sharing. Encrypted Group Photo Albums. Encrypted GPS data. Helping to give people better ownership and control over their data using ETH address as an identifier. This may not ultimately be enough to solve UX problems, but may get us closer to revealing what is possible through mainstream adoption of asymmetric public key cryptography by the way of a decentralized system at the user level.

@rekmarks in terms of supporting a new standard from MetaMask’s perspective, what would be the key functionality or difference from the original EIP-1024’s specification you’d be looking for?

Also curious from @kdenhartog, what would be required for adoption of a fairly generic encryption/decryption EIP? What is left for something like this to have approval? Choice of curve? More specific methodology? Anything else?

1 Like

hi @cjpais, thanks for the message, and glad to have you on board! i absolutely agree with the applications you proposed.

regarding the matter of curves, i have already weighed in above: see the message beginning

as well as the subsequent one. as I’ve already “made my case” on this front, I will defer from further comment at this time, and let the community come to an agreement on this one (though if further input is wanted from me, I’m happy to give it).

my best stab at answering this question would be, “avoid the reuse of the same secret key in two different curves”. thus, something like our use of a KDF is perhaps what’s wanted. (others have taken issue with the fact that we additionally use secp256k1 for the encryption curve, though, as I’ve made clear above, I emphatically maintain that this is not a security concern.) happy to advocate for this with you, as well as discuss any questions you have.

1 Like

I’m very excited to see a follow up to EIP-1024. I’m one of the developers of Moonfish, basically a GPG-like solution for email using Ethereum keys.

If the decision to use a single curve was made to make the life of wallet developers easy, I have to agree with @kdenhartog. Using two curves shouldn’t be an issue. I’d suggest to use proven models, unless there is a good reason not to (there might be one that I missed, feel free to correct me).

As I understand the proposal, the intention is to encrypt the message itself asymmetrically. While this works, I do see two possible issues:

  1. I am not sure if it’s still an issue on modern devices, but asymmetric encryption is slower than symmetric encryption. If you want to scale the solution for use with large attachments or files (as mentioned in one comment), you might run into performance issues on low-end devices. Then again, maybe it’s not an issue, but I would certainly benchmark it.

  2. The current proposal doesn’t seem to support multiple recipients, a common scenario in messaging and email.

Have you considered using the GPG model where the message itself is encrypted with a one-time symmetrical session key, and the session key itself is encrypted with the recipient’s public key? This would scale for multiple recipients.

Another things worth considering is adding metadata to the standard, such as chainID and the public keys/addresses of the sender and all recipients to simplify replying to an encrypted message.

Have you considered extending Age?

I’d like to point out the implications of this to a MPC/TSS controlled wallet. It seems to me very difficult if not infeasible to derive a new key pair if the underlying secret key is controlled by multiple parties through some TSS scheme, like GG18/GG20 or some other similar ones.

Also, if we keep using the same key pair, then the public key is available if the account had signed any messages on-chain: ECDSA allows public key recovery from signature. This will save lots of troubles of key discovery.

Also as Ethereum accounts are migrating to Account Abstraction / ERC-4337 contracts, maybe it makes sense to allow contracts to have an API to expose its public key for encryption purposes.

this is an interesting point about key discovery. i am actually perfectly open to using literally the same secp256k1 keypair for both signing and decryption. as remarked above, this scheme is proven secure in the generic group model, and is probably the best from a simplicitly standpoint. cc. @kdenhartog.

you’re absolutely right. FWIW, it is possible to do HKDF through MPC, or, at least, 2PC, and even to do it relatively efficiently (say, using garbled circuits), but it is very difficult implementation-wise. so I agree that this would be essentially prohibitive.

Also curious from @kdenhartog, what would be required for adoption of a fairly generic encryption/decryption EIP? What is left for something like this to have approval? Choice of curve? More specific methodology? Anything else?

First off, support should be added for both encryption and decryption in a bilateral direction. It seems everyone here wants to focus on the bikeshed of which cryptographic primitives to use, without considering there’s no widely deployed solution for a dApp to have a public key so we’ll still be stuck with every request to the dApp being sent over TLS, but all the responses are encrypted to the ethereum account.

This is effectively like building TLS where the browser only sends HTTP requests and the server only responds with HTTPS responses.

let’s keep the tone respectful, please.

Happy to edit my comment since it’s tone has come off in a way other than intended. Which parts would you like changed?

Could you please elaborate why this is so important or even a prerequisite? What are the use cases besides a TLS connection?

Sure, the user wishes to send a confidential transaction to the mixer dApp in order to submit a private transaction where the only party they’re willing to reveal the amount to is the dApp.

Today, these confidentiality guarantees to send a request to a dApp is handled by the TLS session which is good enough for most people. If we believe TLS is good enough for the request then I don’t see the reason it’s not good enough for the response.

If we don’t believe TLS is good enough for the response from the dApp and therefore believe we need an additional message layer security in order to provide better confidentiality then we should also provide the same guarantees for the request.

Put another way as a question, which API defined in this EIP do I as a wallet developer call in order to enable my user to send an encrypted message to firn protocol’s dApp?

Thanks. This is a fair point. Maybe we need to clarify what exactly is the “user → dApp” use case and why TLS not enough for some cases.

1 Like

Finally, someone else sees what I’m try to convey. This is what I’ve been trying to convince others of for awhile and I must just not be conveying the problem clearly enough if it’s only now just clicking for people.

to be honest, i still don’t see the problem. it’s easy to securely associate a public key to your Ethereum account:

  • if you’re an EOA, post it as an entry in some contract, exactly like how ENS does.
  • if you’re a contract, just expose a public pure function returning the key.

this completely bypasses the need for “TLS”, since we’re assuming that the user/client already knows your ethereum address, and has reliable access to a node. so relevant the TLS happens between the user and the node, not between the user and the dapp. meanwhile, the authenticity of the public key comes from the fact that it was posted by someone with control over that ethereum account.

1 Like

i’m sorry, but this doesn’t compute. you would call some “get public key” function exposed by the dapp. the dapp would deliver the public key. you would locally encrypt under that key. you would then send the ciphertext to the dapp. what’s wrong with this?

actually this is a much less difficult case, since the public key depends only on the dapp, not the user. so it could even be hardcoded in the static front-end. if you wanted to authenticate the public key, you would use the technique in the post above.

i think there may be some serious misunderstandings here, so i want to step back and address these. in the cases relevant to this EIP, the “dapp” is a purely static front-end, containing some javascript UI / recipes which allow a user to more easily interact with some on-chain contracts, together with these contracts themselves. so there is no meaningful “TLS” engaged in between the user and the dapp. (rather, the TLS happens between the user and whoever serves the static content, say the IPFS gateway.) in fact, that’s arguably the whole point of this EIP: if you want to send something confidentially to the “dapp”, where do you send it? the “dapp” doesn’t exist as something that can live on the other end of a TLS channel; it consists entirely of contracts and static front-end code, which you can’t “send” things to. so what do you do?

essentially the only thing you can do is to obtain a public key from the front-end—which you can certifiably attest is controlled by the dapp—encrypt under that key, and then post the ciphertext somewhere the dapp can see it, say, on-chain. the arguably more interesting case is where a user wants to encrypt to another user. here, you can imagine a sort of “on-chain encrypted email” Dapp. here, the encryption doesn’t go to the dapp, but from one user to another. the dapp would retrieve the recipient’s public key from a registry (something like ENS), obtain the message, facilitate its encryption by user to the recipient key, and then post the ciphertext for the user somewhere the recipient can find it (say, an inbox contract).

in this example, the mixer would require a non-static backend server. in this case, you probably don’t need this EIP; you can just use TLS.

let me know if this clarifies things; apologies for the misunderstandings.

To be honest, i still don’t see the problem. it’s easy to securely associate a public key to your Ethereum account:

  • if you’re an EOA, post it as an entry in some contract, exactly like how ENS does.
  • if you’re a contract, just expose a public pure function returning the key.

Where is this defined in the EIP? Sure, there’s numerous ways to do this but our wallet team can’t implement against any of this as none of this is defined.

i’m sorry, but this doesn’t compute. you would call some “get public key” function exposed by the dapp. the dapp would deliver the public key. you would locally encrypt under that key. you would then send the ciphertext to the dapp. what’s wrong with this?

actually this is a much less difficult case, since the public key depends only on the dapp, not the user. so it could even be hardcoded in the static front-end. if you wanted to authenticate the public key, you would use the technique in the post above.

If the dApp is a SPA running locally in my browser with only static front end code, how is the private key loaded in? Also, why do I need to encrypt a message to a dApp in that case? Who’s the adversarially actor I’m trying to maintain confidentiality from in that case? This architecture you’ve described with a completely stateless front end and a public backend (the ETH blockchain) doesn’t need confidentiality via encryption. I guess this is why I’m confused is I never saw this as a usecase where this was necessary.

and then post the ciphertext somewhere the dapp can see it, say, on-chain

If the dApp is not ran in a server and is ran entirely via smart contracts for backend logic and static code for front end logic where is the dApp’s private key kept?

the arguably more interesting case is where a user wants to encrypt to another user . here, you can imagine a sort of “on-chain encrypted email” Dapp.

I’ve eliminated this use case from practicality because of the inherent cost of gas fees. storing messages on chain is impractical. Assuming only SMS style messages were used with a 160 character limit and 4.1 trillion messages sent (numbers pulled from this link MMS vs SMS - Difference and Comparison | Diffen) this equates to a max bound of 656 terabytes of storage required (per year) if we were to entirely replace SMS with this. Now, I can presume the counter argument to this would be “we don’t want to replace SMS it would just be used for limited use cases of X,Y,Z”. So let’s downscale this to say that 1 billion messages are sent per year at 160 character limit. Practically speaking this means 1/8th of the world gets to send only 1 message per year of 160 characters. That still comes out to 160GB of storage (per year) and doesn’t even account for the additional demand for the mempool. Putting all this together just makes this an infeasible architecture in my eyes. I’d rather handle this off chain (there’s additional security/privacy benefits to this I’ve highlighted above) and make the chain a mechanism for discovery on how to find another persons off chain inbox server.

Hopefully, this illuminates my perspective a bit more clearly on this. This is why I was trying to get us to detail out the use cases more clearly above. I knew we’d end up talking past each other if we didn’t align on that first. This is just inherently how standards conversations go by the very nature of the way standards get built from what I’ve learned. If I knew a faster, more fluid way to do this I’d certainly jump to use it, but I don’t have one so I resort to the method I’ve found to build consensus and ship running code.

I have to ask you to please stop replying in this thread. I find your input to be unconstructive, and best suited for a different setting. Thank you.