背景图

关于以太坊的签名

在之前的博文当中我们已经提到过关于区块链技术的基础密码学之椭圆曲线的知识.

前言

我们在正常的ecdsa算法中发现签名都是(r,s),但是以太坊中的签名返回好像还有一个v,而且在RFC6979中也没有提到这个v,我实在不知道这个v到底是一个什么的东西,今天特地来深入地研究一番。

这个v其实是用于椭圆曲线点恢复使用的,在椭圆曲线中(ecdsa)点的y轴是经过mod p的,这样你获得的点可能是经过转换的,你可以查看ECDSA: (v, r, s), what is v?。另外比特币中是直接使用v=0|1,但是以太坊中v=27|28,这又是为啥呢?这主要是为了防止被攻击,可以查看What does v, r, s in eth_getTransactionByHash mean?中的回答,主要的说明在EIP 155当中。

签名

实施签名需要两个部分:待签名的数据+实施签名的账户。签名过程可以使用web3.eth.sign()来实现,具体代码为:

1
2
3
> let msg = web3.sha3('today is 20171026')
> let signature = web3.eth.sign(address, msg)
0x125a275046b65a96f11fdb7cd1072054e67526a76f54b1622fde4e4592d6fe2d5bf664ace77da52c6f94f08a56077e5d7a80048f70c38a92169205df3c9c43ea1b

该返回值总共132字节(去掉前面的’0x’的话是130字节)。因为以太坊采用的ECDSA签名算法,根据ECDSA: (v, r, s), what is v?的介绍, 返回值可以分为三个部分:r, s, v。其中前0~66个字节为r, 66~130之间的字节为s, 130~132的字节为v。代码实现如下:

1
2
3
4
let r = signature.slice(0, 66)
let s = '0x' + signature.slice(66, 130)
let v = '0x' + signature.slice(130, 132)
v = web3.toDecimal(v)

接下来我们可以将它打印出来,在接下来验证签名的部分会用到。

验证

签名完成了,我们如何验证某些签名后的数据是哪个账户签名的呢?在web3.js 发布1.0版本以前,验证签名只能通过智能合约的ecrecover函数来实现。新版的web3.js提供了web3.eth.accounts.recover函数用于验证签名。这里我们仍然使用传统的智能合约ecrecover方式。

ecrecover接收数据的哈希值以及r/s/v等参数作为输入,返回实施该签名的账户地址。因此我们只需要通过合约拿到实施签名的地址,和我们真正的地址进行对比,如果地址一致,就说明验证通过了。

智能合约代码如下:

1
2
3
4
5
6
7
8
9
10
pragma solidity ^0.4.15;
// 专门写了一个用于验证签名的合约呀
contract Auth {
function verify( bytes32 hash, uint8 v, bytes32 r, bytes32 s) constant returns(address retAddr) {
bytes memory prefix = "\x19Ethereum Signed Message:\n32";
bytes32 prefixedHash = sha3(prefix, hash);
// 验证过程(这里就可以看出,通过v返回address)
return ecrecover(prefixedHash, v, r, s);
}
}

接下来我们调用合约进行交互:

1
2
3
const contract=web3.eth.contract(abi).at('0x2e2A4cD2869862492C744307310847466c008257');
console.log(contract.verify(msg, v, r, s));
console.log(address)

本地执行结果为:

1
2
3
$ node sign.js
0xe0803904cbfce8e07745e1b404de43ce6f1e43bc
0xe0803904cbfce8e07745e1b404de43ce6f1e43bc

可以看到实施签名的地址和验证后返回的地址一致,签名通过验证。在以太坊中完成对数据的签名和验证还是比较简单的。并且账户不仅可以对交易进行签名,还可以对任意数据进行签名并验证。

还有就是我们可以通过发合约来利用solidity帮我们处理一些自己写代码比较复杂的事情,比如说这里的验证签名,这个是一个很好的思路,记下了!

总结

签名的算法比较奇特,这里我在网上搜索资料的时候发现了一篇DFINITY区块链:密码学技术介绍,我们先不理会这里面关于密码学深入的问题,里面的图其实和椭圆曲线大概是一致的,因为椭圆曲线中基点不停地加之后就是将所有的点联系起来,也就形成了这篇文章中的那些点线。感觉之前在看椭圆曲线的时候,还停留在代数基础上,其实去掉理论就会发现现在变成图了。然后思维就被打开了。

关于验签是如何成功返回公钥的,这个可能是去找所有的公钥进行匹配,哪个公钥成功了就是这个人前的吧!不过确实合约中好像没有公钥,那么这个公钥是如何生成的呢?这个需要进一步地学习,我想生成是可以的因为有私钥,基点等数据,关键就是k这个值。因为P=k*G,只要从公式中导出这个就可以了。

参考和引用

基于以太坊(Ethereum)完成对数据的签名及验证
区块链语言Solidity校验椭圆曲线加密数字签名(附实例)

0%