乘以椭圆曲线的常量 G ,来计算椭圆曲线上的点(x₁, y₁)。
计算 r = x₁ mod n 。如果 r 等于 0,请返回步骤 2 。
计算 s = k⁻¹(e + rdₐ) mod n 。如果 s 等于 0,请返回步骤 2。
在以太坊上,通常使用 Keccak256("Ethereum Signed Message: 32" + Keccak256(message)) 来计算哈希值。这样可以确保该签名不能在以太坊之外使用。
由于 k 是随机值,我们每次得到的签名都不一样。如果 k 的随机程度不够高,或者随机值被泄漏,就有可能使用两个不同的签名计算出私钥【「fault attack」】。但是,如果你在 MyCrypto 内签署同一条消息,每次得到的输出值都相同,那么如何确保其安全性?这些确定性签名均采用 RFC 6979 标准。该标准描述了如何基于私钥和消息(或哈希值)来生成安全的 k 值。
{r, s, v} 签名可以组成一个长达 65 字节的序列:r 有 32 个字节,s 有 32 个字节,v 有一个字节。如果我们将该签名编码成一个十六进制的字符串,我们最后会得到一个 130 个字符长的字符串。大多数钱包和界面都会使用这个字符串。以 MyCrypto 为例,一个完整的签名如下图所示:
{ "address": "0x76e01859d6cf4a8637350bdb81e3cef71e29b7c2", "msg": "Hello world!", "sig": "0x21fbf0696d5e0aa2ef41a2b4ffb623bcaf070461d61cf7251c74161f82fec3a4370854bc0a34b3ab487c1bc021cd318c734c51ae29374f2beb0e6f2dd49b4bf41c", "version": "2" }
在 MyCrypto 的 「验证消息(Verify Message)」 一页中,我们可以使用该签名,并看到该消息是由 0x76e01859d6cf4a8637350bdb81e3cef71e29b7c2 签署的。
MyCrypto 上的签名验证通过。点击此处,即可体验
你可能会问:为什么要将 address 、msg 和 version 等其它信息也包括在内?不能只验证签名本身吗?好吧,不能。如果不保留其它信息,就好像签了一个合同,然后删除了合同里的所有信息,只留下当事人的签名。不同于交易签名(我们之后会作更深入解释),消息签名就只是签名而已(译者注:因此只有签名是没法验证的)。
为了验证消息,我们需要掌握原始消息、使用私钥签署消息的地址,以及 {r, s, v} 签名本身。版本号就是 MyCrypto 使用的某个版本号。旧版本的 MyCrypto 通常会加上消息的当前日期和时间,计算其哈希值,然后按照上述步骤签署该消息。后来又进行了更改,以符合 JSON-RPC 方法 personal_sign 方法,因此需要指明版本号(「2」)。
简化后的公钥恢复流程如下:
-
计算消息的哈希值(
e )。
-
计算椭圆曲线上的点
R = (x₁, y₁) ,其中 x₁ 是 r (v = 27 ),或 r + n (v = 28 )。
-
计算
u₁ = -zr⁻¹ mod n 和 u₂ = sr⁻¹ mod n 。
-
计算点
Qₐ = (xₐ, yₐ) = u₁ × G + u₂ × R 。
Qₐ 是地址用来签名的私钥所对应的公钥。我们可以通过公钥计算出一个地址,并检查该地址是否与已提供地址相符。如果相符,则签名有效。
恢复标识符(「v」)
v 是签名的最后一个字节,而且不是 27 (0x1b ) 就是 28 (0x1c )。恢复标识符非常重要,因为我们使用的是椭圆曲线算法,仅凭 r 和 s 可计算出曲线上的多个点,因此会恢复出两个不同的公钥(及其对应地址)。v 会告诉我们应该使用这些点中的哪一个。
在大多数实现中,v 在内部只是 0 或 1,而 27 是在签署比特币消息时加上的任意数。以太坊也接受了这一点。
从 EIP-155 开始,我们还使用链 ID 来计算 v 值。这可以防止跨链重放攻击:以太坊上签署的交易无法在以太坊经典上使用,反之亦然。目前,恢复标识符只用来签署交易而非消息。
签署交易
目前为止,我们主要讨论了针对消息的签名。就像消息一样,交易在发送前也需要签名。如果你使用 Ledger 和 Trezor 之类的硬件钱包,签名过程会在硬件内部发生。如果使用私钥(或 keysotre 文件、助记词),可以直接在 MyCrypto 上完成签名。签署交易所使用的方法与签署消息非常相似,只不过交易的编码方式略有不同。
要签署的交易先用 RLP 编码方式编码,包含了所有交易参数(nonce、gas price、gas limit、to、value、data)和签名(v, r, s)。签过名的交易如下所示:
0xf86c0a8502540be400825208944bbeeb066ed09b7aed07bf39eee0460dfa261520880de0b6b3a7640000801ca0f3ae52c1ef3300f44df0bcfd1341c232ed6134672b16e35699ae3f5fe2493379a023d23d2955a239dd6f61c4e8b2678d174356ff424eac53da53e17706c43ef871
如果我们在 MyCrypto 的已签名交易广播页面上输入该交易,我们就会看到所有交易参数:
MyCrypto 的已签名交易广播页面上的交易参数概览
签过名的交易的第一组字节包含 RLP 编码后的交易参数,最后一组字节包含签名 {r, s, v} 。我们可以通过以下方式对签名交易进行编码:
-
交易参数:
RLP(nonce, gasPrice, gasLimit, to, value, data, chainId, 0, 0)
(责任编辑:admin1) |