在智能合约的开发中,验证签名是非常常用的一种身份验证手段,那么该如何进行签名,又该如何进行签名验证呢?本篇文章将会涉及到一些密码学的知识以及 solidity 部分代码。
开始读文章之前需要了解的两个小知识点
Tips:文章结尾有代码
在合约部署完之后开始测试
接下来我们就可以对 hash 签名,运行命令如下:
ethereum.request({method: "personal_sign", params: [account, hash]})
当我们运行这个命令后 metamask 会弹出签名请求。
以上是把一个签名到验证的过程分解,相当于拆解了验证「verify」方法,那么接下来演示一下完整的「verify」是如何运行的。
示例代码
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
/*
1. 如何对一个消息进行签名
2. 对签名结果进行数据恢复
3. 验证签名和签名人是否是同一个人
*/
contract VerifySig {
/*验证函数
_signer: 签名人的地址
_message: 消息的原文
_sig:签名的结果
*/
function verify(address _signer, string memory _message, bytes memory _sig) external pure returns (bool)
{
//给输入消息进行 hash 运算
bytes32 messageHash = getMessageHash(_message);
//再把消息结果进行 eth 消息运算
bytes32 ethSignedMessageHash = getEthSignedMessageHash(messageHash);
//判断 hash 运算之后的结果和 传进来的hash值是否是同一个地址
return recover(ethSignedMessageHash, _sig) == _signer;
}
function getMessageHash(string memory _message) public pure returns (bytes32) {
return keccak256(abi.encodePacked(_message));
}
//进行两次hash 运算是因为在今天的数学界,进行一次hash运算的结果已经有可能被破解
function getEthSignedMessageHash(bytes32 _messageHash) public pure returns (bytes32) {
return keccak256(abi.encodePacked(
"\x19Ethereum Signed Message:\n32",
_messageHash
));
}
function recover(bytes32 _ethSignedMessageHash, bytes memory _sig)
public pure returns (address)
{
//r,s,v 属于非对称加密算法里的知识,文章中有扩展
(bytes32 r, bytes32 s, uint8 v) = _split(_sig);
//ecrecover 是solidity自带函数,会反回还原之后的地址
return ecrecover(_ethSignedMessageHash, v, r, s);
}
//分割 r,s,v变量
function _split(bytes memory _sig) internal pure returns (bytes32 r, bytes32 s, uint8 v)
{
require(_sig.length == 65, "invalid signature length");
//签名bytes是用rsv三个元素拼接的,所以第一个r占前32位,第二个s占后32位,最后一个v占最后一位
assembly {
r :=mload(add(_sig, 32))
s :=mload(add(_sig, 64))
v :=byte(0, mload(add(_sig, 96)))
}
}
}
在成为科学家的道路上一起成长,在赚钱的道路上并肩前行,你好我是Jack_Zhang.