Skip to content
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

Big Issue when recovering a signature of a hashed message #42

Open
moda20 opened this issue Aug 25, 2022 · 3 comments
Open

Big Issue when recovering a signature of a hashed message #42

moda20 opened this issue Aug 25, 2022 · 3 comments

Comments

@moda20
Copy link

moda20 commented Aug 25, 2022

I am trying to verify the signature of a hashed message, and the method used in the description doesn't return the right address :

function verifySignature($message, $signature, $address) {
        $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 $address == $this->pubKeyToAddress($pubkey);
    }


$address   = "0xd927a97442c8bce9f18e84de11cac6e54a890ff8";
            $message   = "0xa880c297e04a9a4e1b8856dd4b48c1f6c0b0b82b1da2907b3d16f6ab1357c8b9";
// signature returned by eth.sign(address, message)
            $signature = "0xcd33577b169a3f2a5c835b3ca7dab1d41fa32db4b791c6856319756e7fecc3cb13676706408b019b6dcc3fe28a72f8435390bb0a1572ba241cfd09ae917784511c";

            if ($this->verifySignature($message, $signature, $address)) {
                Log::error("SUCCSS");
            } else {
                Log::error("FAIL");
            }

the address returned by verifySignature (that we try to compare to the original address) is 0xad21644cb255d77dbf4b1ab716cca9797ce3e5bb which is different than the original address.

The problem here is that when not signing the hashed message but the original message it works correctly.

the original message is : "It'sMe MArio". (without the quotes) and the hashing is done by sha3 : web3.utils.sha3(message)

@sumitkumar33
Copy link

sumitkumar33 commented Oct 22, 2022

I am trying to verify the signature of a hashed message, and the method used in the description doesn't return the right address :

function verifySignature($message, $signature, $address) {
        $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 $address == $this->pubKeyToAddress($pubkey);
    }


$address   = "0xd927a97442c8bce9f18e84de11cac6e54a890ff8";
            $message   = "0xa880c297e04a9a4e1b8856dd4b48c1f6c0b0b82b1da2907b3d16f6ab1357c8b9";
// signature returned by eth.sign(address, message)
            $signature = "0xcd33577b169a3f2a5c835b3ca7dab1d41fa32db4b791c6856319756e7fecc3cb13676706408b019b6dcc3fe28a72f8435390bb0a1572ba241cfd09ae917784511c";

            if ($this->verifySignature($message, $signature, $address)) {
                Log::error("SUCCSS");
            } else {
                Log::error("FAIL");
            }

the address returned by verifySignature (that we try to compare to the original address) is 0xad21644cb255d77dbf4b1ab716cca9797ce3e5bb which is different than the original address.

The problem here is that when not signing the hashed message but the original message it works correctly.

the original message is : "It'sMe MArio". (without the quotes) and the hashing is done by sha3 : web3.utils.sha3(message)

Quick fix is in js code and not in php end if finally got it working here is my script:

    //Load account from metamask
    const accounts = await window.ethereum.request({
        method: 'eth_requestAccounts'
    });
    console.log(accounts[0]);
    //prepare message to sign
    const msgtext = "Hello World";
    const hashedMessage = Web3.utils.fromUtf8(msgtext);
    console.log(hashedMessage);
    //get user to sign the message
    const signature = await window.web3.eth.sign(hashedMessage, accounts[0]);

Explaination:
Thing is you have to take a string data and covert it to hex which returns a bytes32 hexcode and then ask user to sign it.

@gniax
Copy link

gniax commented Oct 26, 2022

Explaination: Thing is you have to take a string data and covert it to hex which returns a bytes32 hexcode and then ask user to sign it.

I'm getting the same issue as soon as the provider is not MetaMask (e.g. WalletConnect)

With

        let publicAddress = address;
        let paToLower = publicAddress.toLowerCase();
        const signature = await web3ModalProv.eth.sign(web3ModalProv.utils.fromUtf8(message), paToLower);

or without
```
let message = response.data;
let publicAddress = address;
handleSignMessage(message, publicAddress).then(handleAuthenticate);

  function handleSignMessage(rawMessage, publicAddress) {
    let message = web3ModalProv.utils.utf8ToHex(rawMessage);

    return new Promise((resolve, reject) =>  
    web3ModalProv.eth.personal.sign(
      message,
      publicAddress,
        (err, signature) => {
          if (err || typeof signature === 'undefined') {
            userLoginData.state = "loggedOut";
            showMsg(userLoginData.state);
          }

          return resolve({ publicAddress, signature });
        }
      )
    )
  }
 ```

In all cases the PHP code returns false if it is not a MetaMask address at if ($recid != ($recid & 1))
If I use the same code with MetaMask, it works.

I'm using a fork of this https://github.com/giekaton/php-metamask-user-login to establish a login with WalletConnect

@gniax
Copy link

gniax commented Oct 26, 2022

I managed to fix the issue which was actually on the back-end (verifySignature).

Solution:

Include php-ecrecover in your project

This needs the following version of CryptoCurrencyPHP (I did not tested the HEAD one)

Simply replace function verifySignature() by:

function verifySignature($message, $signature, $address) 
  {
    return $address == personal_ecRecover($message, $signature);
  }

The personal_ecRecover function is based on gmp and looks more efficient than the current public key recovery method which seems to have been developed only for MetaMask.

You might need to allow gmp extension in php.ini if not done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants