EIP

【EIP】ERC191 簽名數據標準

【EIP】ERC191 簽名數據標準

這個 ERC 提出了關於如何處理以太坊合約中簽名數據的規範。

ECDSA

以太坊使用的數字簽名是橢圓曲線數字簽名算法(ECDSA),基於橢圓曲線的“私鑰-公鑰”對生成數字簽名。
在以太坊中具有以下三個作用:
1.身份認證:通過使用私鑰簽名,驗證方可以證明其為私鑰的持有人,從而進行身份認證。
2.不可否認:發送方使用私鑰對消息進行簽名後,無法否認發送過該消息。接收方可以通過驗證簽名來確認消息的來源。
3.完整性:消息在傳輸過程中無法被篡改或修改。接收方可以通過驗證簽名來確保消息的完整性。

文章目錄

  1. ERC191 簽名 第一種方式
  2. ERC191 簽名 第二種方式(推薦,可以清楚知道簽了甚麼)
  3. 智能合約驗證簽名
  4. web3簽名
  5. web3驗證簽名

1.ERC191 第一種方式

Signed Data Standard

把原始簽名先 Hash 一次,再利用以太坊簽名。
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

contract Sign {

    // Hash簽名
    function getMessageHash(string memory _message) public pure returns(bytes32) {
        return keccak256(abi.encodePacked(_message));
    }

    // 以太坊簽名
    function toEthSignedMessageHash(bytes32 hash) public pure returns(bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }

}
原始簽名: Hello Solidity
Hash簽名: 0x93e78dde9ad5c12ec321bc5263d2a9b5f4896649cbb61e1049549b0a6896226e
以太坊簽名(把 Hash簽名 拿來簽): 0xf55a510beec43cf9765ed67805f86fc1d7eca8c86abf9ed3c70a1f63f664a4fc

私鑰簽名(把 Hash簽名 拿來簽): 0x3539a322c8ed9c4065ec24aeba7a685b5ca281beddf86ea662dcb6aabfe85f5f3b4acbe7ff55e6f3cac7de1391611fb379dbc8bc751827fec5bbb363d012a6de1b

2.ERC191 第二種方式(推薦,可以清楚知道簽了甚麼)

原始簽名直接利用以太坊簽名。
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

contract Sign {

    // 1.將原始簽名丟入以太坊簽名(後面數字看文字長度)
    function toEthSignedMessageHash(string memory _message) public pure returns(bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n11", _message));
    }

    // 1-1.計算文字長度
    function toEthSignedMessageHash2(string memory _message) public pure returns(bytes32) {
        string memory length = toString(bytes(_message).length);
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", length, _message));
    }

    // 整數轉字串
    function toString(uint256 value) internal pure returns (string memory) {
        uint256 temp = value;
        uint256 digits;

        while (temp != 0) {
            digits++;
            temp /= 10;
        }

        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }

        return string(buffer);
    }

}
原始簽名: Hello Solidity
以太坊簽名: 0x07a1b12aeb1be79ae59584445520cf9b2f9662aacbb296fc2940740416f4820e

私鑰簽名(把 Hash簽名 拿來簽): 0xbcce9249b992e82bc4ec6057bafc0b951b1d614542031cd2e5cf568f8bbf3dcd432fabdf78d179aec76cf90ce115c5ff58d177763ee5c5424b34db1380b342b61c

3.智能合約驗證簽名

// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

contract Sign {

    // 判斷是否跟簽名的地址相同
    function verify(bytes32 _msgHash, bytes memory _signature, address _signer) public pure returns (bool) {
        return recoverSigner(_msgHash, _signature) ==== _signer;
    }

    // 驗證簽名返回地址
    function recoverSigner(bytes32 _msgHash, bytes memory _signature) internal pure returns (address) {
        require(_signature.length ==== 65, "invalid signature length");
        bytes32 r;
        bytes32 s;
        uint8 v;

        assembly {
            r := mload(add(_signature, 32))
            s := mload(add(_signature, 64))
            v := and(mload(add(_signature, 65)), 255)
        }

        return ecrecover(_msgHash, v, r, s);
    }

}
msgHash以太坊簽名: 0x07a1b12aeb1be79ae59584445520cf9b2f9662aacbb296fc2940740416f4820e
signature簽名: 0xbcce9249b992e82bc4ec6057bafc0b951b1d614542031cd2e5cf568f8bbf3dcd432fabdf78d179aec76cf90ce115c5ff58d177763ee5c5424b34db1380b342b61c
反解地址: 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4

4.web3簽名

直接把原始簽名利用以太坊簽名
account = "0xd5F2581024b14DCA7B6c1f2156767E4125Cf33fC"
hash = "Hello Solidity"
ethereum.request({method: "personal_sign", params: [account, hash]})
signature簽名: 0xee51c9938d66fa2525d9c643275ac09e53f26d69c0ac1403c2b6a6b68faa3feb75903969f4caa58322e64f96bfa675afdde73729b8d4a76413eb7e65b20897011c

5.web3驗證簽名(web3.js or ethers.js)

web3.js

簽名
const coinbase = await web3.eth.getCoinbase();
const result = await web3.eth.personal.sign("Hello Solidity", coinbase);
驗證簽名
const result = await web3.eth.personal.ecRecover("Hello Solidity", "0xee51c9938d66fa2525d9c643275ac09e53f26d69c0ac1403c2b6a6b68faa3feb75903969f4caa58322e64f96bfa675afdde73729b8d4a76413eb7e65b20897011c")

ethers.js

簽名
const result = await provider.getSigner().signMessage("Hello Solidity")
驗證簽名
const result = await ethers.utils.verifyMessage("Hello Solidity", "0xee51c9938d66fa2525d9c643275ac09e53f26d69c0ac1403c2b6a6b68faa3feb75903969f4caa58322e64f96bfa675afdde73729b8d4a76413eb7e65b20897011c")

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *