-
Notifications
You must be signed in to change notification settings - Fork 98
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
Trouble validating signatures from MetaMask #18
Comments
In case others run into this as well, I'm able to use node.js to do this, which may be a viable workaround for some people. const message = utils.hashPersonalMessage(utils.toBuffer('numa'));
const sgn = "0x641a251cce30223cbcf391ecfe3aa939acc7361bbaf166dd929f940bf6589f8b3a2defc1b19c5db7f5816cc83a07a1be5a452e0387491a52e3b50c5138b7acb81c";
const r = utils.toBuffer(sgn.slice(0, 66));
const s = utils.toBuffer('0x' + sgn.slice(66, 130));
const v = utils.bufferToInt(utils.toBuffer('0x' + sgn.slice(130, 132)));
const pub = utils.ecrecover(message, v, r, s);
const addr = utils.pubToAddress(pub);
console.log(utils.bufferToHex(addr)); |
I'd prefer not to use javascript for this part of my system so this is rather confusing to me. According to @se3000 's comment here we should be able to recover a signature generated by something like metamask (which afaik uses libsecp). Here's the code I'm running. I also happen to be using original_message = 'test'
prefix = "#{Eth::Utils.hex_to_bin('19')}Ethereum Signed Message:\n#{original_message.length}"
personal_message_hash = Eth::Utils.keccak256("#{prefix}#{original_message}")
personal_message_hash_hex = Eth::Utils.bin_to_hex(personal_message_hash)
# -> 4a5c5d454721bbbb25540c3317521e71c373ae36458f960d2ad46ef088110e95
# (which is the same data that metamask signs, checked by breakpoints)
Eth::OpenSsl.recover_compact(
personal_message_hash_hex,
signature
)
# false Any advice/help would be appreciated :) |
@se3000 Here's an example: On Metamask:
Then, on ruby console: >> key = Eth::Key.new(priv: "510c561d3d79ab0b80e60310929bdf9ff96200658b036b1a81ae5852e426e343")
>> key.address
=> "0x101b17d50c01B555CCb7534d46CAaCa70D0B9B55"
>> key.verify_signature("hello", Eth::Utils.hex_to_bin("0x7b706331232925682dfa46695914095243eacd65f75c21e0ee3179c55c6cf9e45465b23716aba9eb1ccc36905732a147bc8d7bdebb78e4978d6558e11d69f0781c"))
=> false This is Chain ID 1 (mainnet) |
Hello, having the same issue. When signing a personal message with metamask, a prefix is applied, which is missing in the example provided by @ngan. The example below should be correct, but still unable to verify the signature from metamask. I can't figure out, why... In a browser with metamask installed:
In ruby console:
Any help would be appreciated... |
Hey guys I'm offering a 1 eth bounty if somebody can help me figure this out |
We recently faced a similar issue and it looks we found a solution that seems to work.
We realized that the V value was being checked against the last signature's byte and for some reason, with this library that value was added in the first byte instead of the last one, so it was just a matter of moving it to the end:
Now, we can sign the message again and pass it back to the geth console:
So, if you want to check it the other way round, to generate the signature from the geth console(and I assume that doing it from Metamask should work too), it's just a matter of moving the signature's V byte to the beginning:
Hope it makes sense and we are not forgetting anything critical @buhrmi if the 1 ETH bounty is still on the table, happy to receive it and share it with the team: |
@dagi3d @buhrmi Awesome, thanks for figuring this out. So far we return and expect signatures in the format V,R,S. This is how they are ordered in serialized Ethereum transactions(see page 4 of the yellow paper). V is that first byte that you move to the end. You can see in the EIP 191 spec that for some reason the signature is expected as R,S,V. Looking into the discussion around 191, it seems like this signature ordering was to fit with geth, but it's unclear why geth made that choice to begin with. I'm a bit swamped at the moment, and won't be able to get to this anytime soon. Anyone interested in turning this into a PR? |
@buhrmi Thanks, that's very generous of you. You can send donations to Anyway, I'll try to get more details on the bounty together in the future. Thanks again! |
Does it make sense to update the How about adding |
@buhrmi yes, I think |
There is now Eth::Key#personal_sign and Eth::Key.personal_recover. Thanks again @buhrmi! |
@se3000 what does personal_recover do? |
It returns the public key that signed the message. # signer information
signer_address = "0xe611a720778a5f6723d6b4866F84828504657181"
# message and signatre (from javascript/metamask)
message = "Hello from Rails! 07b54ba3-fe2b-4932-8723-52e6c1d109c1"
signature = "0x03a45db0ec6cada40f0227bb67cfd99e69a92c5c6809450d4db884c83b41401c062c039f1465012965ec2426c0dd2d4d3737cf08009866f9a3bccfa3f312bda81c"
# recover the public key from the signature
recovered_public_key = Eth::Key.personal_recover message, signature
# recover the address from the public key
recovered_address = Eth::Utils.public_key_to_address recovered_public_key
# are they the same? huge, if true ;)
p signer_address
p recovered_address
p signer_address.eql? recovered_address |
Hey there, thanks for the project. It's been super helpful.
What I'm trying to do is validate a signed message that I can get from MetaMask. Getting the signature is easy. Let's say I've signed the message "numa", and I get the signature
0x641a251cce30223cbcf391ecfe3aa939acc7361bbaf166dd929f940bf6589f8b3a2defc1b19c5db7f5816cc83a07a1be5a452e0387491a52e3b50c5138b7acb81c
. Here is the code that I think would work:But
public_hex
isnil
. After debugging, I'm finding that theBN_cmp(x, field) >= 0
flag is caught, which is why it returns nil. Unfortunately I don't really understand what this aspect of the code is doing. I'd really appreciate any help, if possible.Thanks!
The text was updated successfully, but these errors were encountered: