本文介绍了OpenZeppelin Contracts社区版中用于密码学的多个实用工具,包括验证类型化数据签名(EIP712)、嵌套EIP712签名验证(ERC-7739)以及ERC-7913签名验证器,这些工具旨在简化智能合约中复杂签名的处理并提高账户安全性。
OpenZeppelin Contracts 社区版本中包含的多个库和通用实用工具。这些只是一组实用工具合约。有关完整列表,请查看 API 参考。
有关如何在链上验证签名的预备知识,请查看 OpenZeppelin Contracts 文档
与验证纯文本消息相反,可以让用户签署结构化数据(即类型化值),并在他们的钱包上仍然可读。这可以通过实现 EIP712
来实现,这是一种将结构化数据编码为类型化数据哈希的标准方法。
要开始验证已签名的类型化结构,只需验证 类型化数据哈希:
// contracts/MyContractDomain.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
/// @dev 不安全的合约,用于演示 EIP712 和 ECDSA 的用法。
abstract contract MyContractDomain is EIP712 {
function validateSignature(
address mailTo,
string memory mailContents,
bytes memory signature
) internal view returns (address) {
bytes32 digest = _hashTypedDataV4(
keccak256(abi.encode(keccak256("Mail(address to,string contents)"), mailTo, keccak256(bytes(mailContents))))
);
return ECDSA.recover(digest, signature);
}
}
作为消息的一部分,EIP-712 要求实施者包含一个域名分隔符,这是一个包含当前智能合约地址及其部署链 ID 的哈希。这样,智能合约可以确保结构化消息是为其特定域签名的,从而避免了智能合约中签名的可重放性。
帐户(即智能合约钱包或智能帐户)特别可能由多个签名者控制。因此,重要的是要确保签名:
仅对预期的域和帐户有效。
以最终签名者可读的方式进行验证。
一方面,确保帐户签名仅对特定的智能合约(即应用程序)有效是困难的,因为它需要验证其域是应用程序而且是帐户本身的签名。因此,社区开发了 ERC-7739;一种防御性重新哈希机制,它使用嵌套的 EIP-712 方法(即包装另一个 EIP-712 类型结构的 EIP-712)将签名绑定到单个域。
如果你的智能合约验证签名,则使用 ERC7739Signer
将实现 IERC1271
接口,用于验证智能合约签名,遵循 ERC-7739 建议的方法:
// contracts/ERC7739ECDSA.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import {ERC7739} from "@openzeppelin/community-contracts/utils/cryptography/signers/ERC7739.sol";
contract ERC7739ECDSA is ERC7739 {
address private immutable _signer;
constructor(address signerAddr) EIP712("ERC7739ECDSA", "1") {
_signer = signerAddr;
}
function _rawSignatureValidation(
bytes32 hash,
bytes calldata signature
) internal view virtual override returns (bool) {
(address recovered, ECDSA.RecoverError err, ) = ECDSA.tryRecover(hash, signature);
return _signer == recovered && err == ECDSA.RecoverError.NoError;
}
}
ERC-7913 扩展了签名验证的概念,以支持没有自己的以太坊地址的密钥。这对于将非以太坊加密曲线、硬件设备或其他身份系统集成到智能帐户中特别有用。
该标准定义了一个可以实现的验证器接口,以支持不同类型的密钥。签名者表示为一个 bytes
对象,该对象连接一个验证器地址和一个密钥:verifier || key
。
ERC7913Utils
提供了使用与 ERC-7913 兼容的验证器来验证签名的函数:
using ERC7913Utils for bytes;
function _verify(bytes memory signer, bytes32 hash, bytes memory signature) internal view returns (bool) {
return signer.isValidSignatureNow(hash, signature);
}
验证过程如下:
如果 signer.length < 20
:验证失败
如果 signer.length == 20
:使用 SignatureChecker 完成验证
否则:使用 ERC-7913 验证器完成验证。
这允许与 EOA 和 ERC-1271 合约向后兼容,同时支持新类型的密钥。
- 原文链接: docs.openzeppelin.com/co...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!