MetaMask/eth-sig-util

message data with non-ASCII string signed by MetaMask can not pass the verification on-chain

yuwiggin opened this issue · 4 comments

I found that message data with non-ASCII string signed by MetaMask can not pass the verification on-chain. Here is an example: it will pass the verification on-chain if title is set to a ASCII string, i.e. English, but failed in the following case.

            const msgParams = JSON.stringify(
                {
                    types:
                        {
                            EIP712Domain: [
                                {name: "name", type: "string"},
                                {name: "version", type: "string"},
                                {name: "chainId", type: "uint256"},
                                {name: "verifyingContract", type: "address"}
                            ],
                            State: [
                                {name:"title",type:"string"}
                            ]
                        },
                    //make sure to replace verifyingContract with address of deployed contract
                    primaryType: "State",
                    domain: {
                        name: "StringTest",
                        version: "1",
                        chainId: 1337,
                        verifyingContract: "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
                    },

                    message: {
                         title: "にほんご"
                    }
                })

Any suggestion?

Thanks for the report! How are you verifying the signature on-chain?


  function setString(
      uint8 v,
      bytes32 r,
      bytes32 s,
      string calldata x
  ) external {
        hashStruct = keccak256(
          abi.encode(
              keccak256("StringTest(string title)"),
              keccak256(bytes(title))
          )
      );

      hash = keccak256(abi.encodePacked("\x19\x01", eip712DomainHash, hashStruct));
      signer = ecrecover(hash, v, r, s);
      // require(signer == sender, "setInteger: invalid signature");
      require(signer != address(0), "ECDSA: invalid signature");
}

I tried comparing the Solidity output of keccak256(bytes('\xe3\x81\xab\xe3\x81\xbb\xe3\x82\x93\xe3\x81\x94')) (that string is the escaped unicode string にほんご) with the encoding this library uses, which is:

const { keccak } = require('ethereumjs-util');

console.log(keccak(Buffer.from('にほんご')).toString('hex'))

Both gave identical answers: 0x28520ffd6b58b7c6e707f2193e3149cc3153158364203cd746e0140d91eb2114

Maybe there is some problem with your contract, or with how the string is encoded? Without a more complete reproduction I'm not sure how to proceed with investigating this.

I'll close this for now, but please let us know if you find a way to reproduce this!