-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to sign messages without the "\x19Ethereum Signed Message" prefix? #555
Comments
This is not, in general, possible and is incredibly unsafe. :s Basically, allowing signing raw messages, without a prefix, enables an app to steal all ether, tokens and assets, which is why MetaMask does not permit you to perform this operation, and it will always force prefixing a signed message (even when the message is a hash, it will still prefix it, just with the embedded message length of 32). So, there is no safe way to adjust the signer API to do this, since most Signers do not support this type of unsafe operation anyways. The only Signer that actually allows this, is the Wallet, using the technique you described (accessing the raw signing key), but that is highly unrecommended. Which generic libraries are you using? No modern Ethereum library should allow signing an unprefixed message. I have an article about it here, if you need to implement this in Solidity. In addition to being unsafe, it also presents a UX issue, since any UI popping up and saying "Do you agree to sign this hash: 0x1234567899?" would allow users to sign away anything, and without any meaningful description... |
We are using Even the own examples on the Ethers.js manual assume that Solidity contracts have to add the salt on top, because Prefixing payloads make sense to prevent replay attacks on different chains. But we don't need to deal with transactions or tokens. All we need to sign is JSON payloads between Gateways and clients so they can check integrity and authenticity. These payloads contain an (ephemeral) timestamp, which is much better salt than static text.
How would this be? If so, this means that keccak256 and EC encryption are both broken? Thank you |
Geth may provide the ability to sign digests, but there should be a call to correctly use the message signing API. If not, it is easy enough to add (I have added it to Web3J, and implemented in the Objective-C ethers, and the C implementation for the Firefly hardware wallet). That is completely correct, the Solidity The prefixing has nothing to do with replay protection (EIP-155), and for signed messages, if there are cross-chain concerns, it is important to add replay protection on top of it. Keccak256 and ECC are not broken, but when doing low-level operations, it is important to understand how they operate. When you create a transaction, you create an unsigned transaction, and hash it, which is then signed. When you create a message to sign, you prefix it, hash it and then sign it. The important reason for prefixing is so that a cleverly designed message cannot possibly be a valid transaction. The \x19 prevents this. For example:
If I give this By signing the hash of a string that begins with Also, keep in mind the prefix also has nothing to do with being a salt. It's purpose is entirely to invalidate any payload as a valid RLP encoded transaction. Also, all future protocols (such as eth_signTypedData) specifically ensure backwards and forwards compatibility. Using a custom prefix, it is important that you familiarize yourself with these restrictions, so you don't expose people to future risk and you aren't exposed to risk from the current schemes. A timestamp is definitely not a safe prefix, especially if you do not use a distinguished encoding (i.e. allowing "1234" and "01234" to both represent the same time opens you up much more significant attacks). Even if you are just signing a hash of a JSON payload, it is perfectly safe to use Anyways, hope that helps. There is a lot to keep in mind when exposing these types of data, which is why it is nice to layer them on top of known safe, higher-level operations, like |
Insightful response @ricmoo :) I understand now the concerns you express, but yet this forces everyone to use the ethereum prefix for signing generic messages. As I said, we don't intend to use signatures for transfers or transactions, but the motivations for the current approach make total sense. We'll need to consider other alternatives for signing on a browser in this use case. And by the way, thank you for the huge effort that has been done to make Ethers.js nice and solid :) Closing now |
So, if you don't plan to use it for Ethereum transactions or messages, you could simply not use the Ethereum parts. You could use Another option may be to specify a mnemonic path and use your own custom prefix. For example, Ethereum is just doing what Bitcoin does (which uses a path of Just an idea. :) |
Hey there, I have a similar question. In my contract, I am implementing the EIP-2612 I understand you mention security being the reason for it, but I am also wondering if there's any other method from ethers.js that is compatible to sign messages with the EIP-2612 compliant prefix? |
@preston4896 I’m not familiar with that EIP, but checking it out, it is just using EIP-712, so as long as you format your EIP-712 domain correctly, you should be able to use the |
Was there ever a solution here? Uniswap prefixes with |
@ricmoo Hi, I have some question of your comment
btw, I'have read your article https://blog.ricmoo.com/verifying-messages-in-solidity-50a94f82b2ca, It helped me a lot to understand, but still, I don't get it why '\x19' makes transaction invalid |
@dovigod, The prefix explicitly declares that the hash represents a message, not a transaction. Since the prefix and message length are not part of any transaction structure, the resulting hash will not match the hash of a valid transaction and cannot be interpreted as a transaction, even if someone tries to repurpose the signature. In contrast, if you directly hash the message (without the prefix), there is no guarantee that the hash won't match the hash of a valid transaction under specific circumstances. This introduces the risk of the signature being misused to authorize a transaction. |
We're currently working on a project that needs to interoperate with other platforms and we're using Metamask through a
Signer
. However, when running something likemessage
will always be salted with'\x19Ethereum Signed Message:\n' + <message.length>
:This will produce signatures that will not match other generic libraries.
We could use
myWallet.signingKey.signDigest(hashBytes)
, but unfortunately this method is not available when using aSigner
attached to MetaMask.Would it be possible to pass a flag to
Wallet.prototype.signMessage
to allow bypassing the salt? I can provide a PR if needed.Thank you!
The text was updated successfully, but these errors were encountered: