Closed kakashigr closed 2 years ago
Hello, please paste here your js code and php code, with example signature and address, so we can try to reproduce your problem.
In your js code you unnecessary hash the message before signing/verifying. Check the code below, it returns the same address in both js and php:
<?php
// composer require simplito/elliptic-php && composer require kornrunner/keccak
require_once("vendor/autoload.php");
use Elliptic\EC;
use kornrunner\Keccak;
function pubKeyToAddress($pubkey) {
return "0x" . substr(Keccak::hash(substr(hex2bin($pubkey->encode("hex")), 1), 256), 24);
}
function recoverAddress($message, $signature) {
$msglen = strlen($message);
$hash = Keccak::hash("\x19Ethereum Signed Message:\n{$msglen}{$message}", 256);
$sign = ["r" => substr($signature, 2, 64),
"s" => substr($signature, 66, 64)];
$recid = ord(hex2bin(substr($signature, 130, 2))) - 27;
if ($recid != ($recid & 1))
return false;
$ec = new EC('secp256k1');
$pubKey = $ec->recoverPubKey($hash, $sign, $recid);
return pubKeyToAddress($pubKey);
}
$message = "I like signatures";
$signature = "0xacb175089543ac060ed48c3e25ada5ffeed6f008da9eaca3806e4acb707b9481401409ae1f5f9f290f54f29684e7bac1d79b2964e0edcb7f083bacd5fc48882e1b";
$address = recoverAddress($message, $signature);
echo "Address: " . $address . "\n"; // prints 0x5a214a45585b336a776b62a3a61dbafd39f9fa2a
and for js:
// npm i ethers
const ethers = require("ethers");
const message = Buffer.from("I like signatures", "utf8")
const signature = "0xacb175089543ac060ed48c3e25ada5ffeed6f008da9eaca3806e4acb707b9481401409ae1f5f9f290f54f29684e7bac1d79b2964e0edcb7f083bacd5fc48882e1b";
const address = ethers.utils.verifyMessage(message, signature);
console.log("Address: " + address); // prints 0x5a214a45585b336a776b62a3a61dbafd39f9fa2a
Thank you very much, this is indeed working!
The only problem I found is that pubKeytoAddress() function returns all lower-case so I need it to run it through a getChecksumAddress() function:
public static function getChecksumAddress(string $address): string {
$address = substr($address, 2);
$addressHash = Keccak::hash(strtolower($address), 256);
$addressArray = str_split($address);
$addressHashArray = str_split($addressHash);
$ret = '';
for ($i = 0; $i < 40; $i++) {
// the nth letter should be uppercase if the nth digit of casemap is 1
if (intval($addressHashArray[$i], 16) > 7) {
$ret .= strtoupper($addressArray[$i]);
} else /*if (intval($addressHashArray[$i], 16) <= 7)*/ {
$ret .= strtolower($addressArray[$i]);
}
}
return '0x' . $ret;
}
I'm trying to use your example "Verifying Ethereum Signature"
In the client, using ethers.js I used this code:
And then to verify it
which works, it correctly returns the userAddress.
But then, using your example to verify the signature in php, I get a different address. I noticed the messageHash is different than the hash calculated in the php. When I removed the $message prefix (string and length) and just hash the message as is, I get the same hash as in javascript. However, in both cases, the address returned is different.
Can you help me?