EIP-191
简单来说EIP-191是为了定义智能合约中签名数据的格式。
EIP191的数据格式为:
0x19 <1 byte version> <version specific data> <data to sign>.
这其中的<1 byte version>用来确定EIP191的版本。
目前一共有三个版本号:0x00,0x01,0x45。其中0x01即为EIP712。
version 0x00数据格式为:
0x19 <0x00> <intended validator address> <data to sign>
version 0x45数据格式为:
0x19 <0x45 (E)> <thereum Signed Message:n" + len(message)> <data to sign>
之所以使用0x19作为前缀:
-
是为了和RLP编码区分。
-
是为了兼容交易。因为一开始并没有EIP191格式,人们常用的是最初由Geth实现的 personal_sign方案,数据格式为:
"x19Ethereum Signed Message:n" + length(message) + message
而人们往往会对message做哈希运算,因此更常见的格式为:
"x19Ethereum Signed Message:n32" + Keccak256(message)
为了兼容这类交易,就以0x19为前缀,再把第一个字母“E”作为版本号。
EIP712
EIP712是EIP191的改良版,用于改良EIP191中存在的问题。
EIP191有几个问题:
-
没有明确防止重访攻击的规定。
-
没有编码规范,造成一些外部组件,比如钱包,无法解析编码,导致用户无法了解签名内容。
EIP712就是为了解决以上两个问题:
-
通过DOMAIN_SEPARATOR设定,防止重放攻击。
-
规范对结构体编码的方式,使签名内容可视化,高签名时的安全性。
-
防止重放攻击
EIP-712结构如下:
encode(domainSeparator,message)=x19x01/domainSeparator/hashStruct(message)
其中的x19x01与hashStruct(message)就不解释了,我们主要看domainSeparator字段,这才是防止重入的关键。
domainSeparator字段结构如下:
domainSeparator = keccak256(typeHash / encodeData(S))
typeHash为一个结构体的编码再进行keccak256,结构体如下:
struct EIP712Domain{
string name,
string version,
uint256 chainId,
address verifyingContract,
bytes32 salt
}
encodeData是上述结构体中的参数的编码。
需要注意的是,结构体中的字段可以省略,但不可以颠倒顺序。
-
签名内容可视化
EIP712通过规范对结构体编码的方式来使签名内容可视化。其主要体现在hashStruct(message)字段中。
hashStruct(message)字段结构如下:
hashStruct(message) = keccak256(typeHash / encodeData(S))
与domainSeparator字段类似,因此就不再赘述了。
可以看到messageHash中,把结构体名称,属性名称都编码进去了,因此钱包等第三方能够知道编码的结构体数据结构。
作者:于文杰
编辑:舒婷
原文始发于微信公众号(ChainSecLabs):EIP191、EIP712解析