在分析源码之前,一定要先讲讲什么是RFC6979。
比特币的签名机制是基于椭圆曲线算法。在椭圆曲线里面k值(用于签名)是要严格保密的,暴露k值就相当于暴露私钥。k值要保证两点:
- 保密
- 唯一
有人提出一种方式来产生k值,类似下面这样的公式:
k = SHA256(d + HASH(m));
其中,d是私钥,m是消息,我们一般会对消息的HASH进行签名,因此这里是HASH(m)。
有私钥d,就保证了“保密”,再加上消息m,保证了“唯一”,这也是“确定性”的算法,只要SHA256是安全的,此算法就是安全的。
当然真正的RFC6979比这个要复杂的多。
k在使用椭圆曲线签名的参与过程
关于椭圆曲线算法的详细信息请自行查阅
这里只简要说明k在使用椭圆曲线签名的参与过程。
签名的步骤:
- 使用bits2int将H(m)变换成模q的整数
h = bits2int(H(m))mod q
- 产生一个随机值q,称为k。值不得为0;它在[1,q-1]范围内。大多数
在传统的ECDSA中,通过在q-1范围内选择一个随机值作为k。
- k和其它关键参数计算值r(模q):
对于ECDSA(椭圆曲线):计算点kG;其X坐标(a被定义为E的字段的成员)被转换为一个整数,其被减数为q,产生r。
如果r为零,则应选择一个新的k再次计算(这是一个完全不可能发生的事情)。
计算值s(模q):
s =(h + x * r)/ k mod q
(r,s)即使是签名。
RFC6979(确定性签名算法)生产k的流程
首先我们定义:
HMAC_K(V)
使用密钥(key)K对数据V进行HMAC算法。
给定输入消息m,应用以下过程:
- 通过哈希函数H处理m,产生:
h1 = H(m)
V = 0x01 0x01 0x01 ... 0x01
V(以比特)的长度等于8 * ceil(hlen / 8)。例如,如果H是SHA-256,则V被设置为值为1的32个八位字节的序列。
K = 0x00 0x00 0x00 ... 0x00
K的长度(以比特)等于8 * ceil(hlen / 8)。
K = HMAC_K(V || 0x00 || int2octets(x)|| bits2octets(h1))
‘||’表示连接。x是私钥。
V = HMAC_K(V)
K = HMAC_K(V || 0x01 || int2octets(x)|| bits2octets(h1))
V = HMAC_K(V)
- 执行以下流程,直到找到适当的值k:
将T设置为空序列。 T的长度(以比特为单位)表示为tlen, 因此tlen = 0。
当tlen <qlen时,请执行以下操作:
V = HMAC_K(V)
T = T || V
- 计算
k = bits2int(T)
如果k的值在[1,q-1]范围内,那么k的生成就完了。否则,计算:
K = HMAC_K(V || 0x00)
V = HMAC_K(V)
并循环(尝试生成一个新的T,等等)。
源码分析
有了上面的理论支撑再来分析源码就比较容易了。
i = 1
result_k = deterministic_generate_k(bin_sha256(str(i)), encode(i, 256, 32))
print result_k
bin_sha256()返回输入数据的hash256的结果,不过是python的byte格式的(也就是字符串在计算机的真正样子)。比如这里的
bin_sha256('1')
#结果是:
b'\x6b\x86\xb2\x73\xff\x34\xfc\xe1\x9d\x6b\x80\x4e\xff\x5a\x3f\x57\x47\xad\xa4\xea\xa2\x2f\x1d\x49\xc0\x1e\x52\xdd\xb7\x87\x5b\x4b'
这里的结果作为消息的hash结果,也就是上面提到的h1。
encode(i, 256, 32)得到一个私钥。
下面进入deterministic_generate_k里面看看,
def deterministic_generate_k(msghash, priv):
v = b'\x01' * 32
k = b'\x00' * 32
priv = encode_privkey(priv, 'bin')
msghash = encode(hash_to_int(msghash), 256, 32)
k = hmac.new(k, v+b'\x00'+priv+msghash, hashlib.sha256).digest()
v = hmac.new(k, v, hashlib.sha256).digest()
k = hmac.new(k, v+b'\x01'+priv+msghash, hashlib.sha256).digest()
v = hmac.new(k, v, hashlib.sha256).digest()
return decode(hmac.new(k, v, hashlib.sha256).digest(), 256)
v = b'\x01' * 32
k = b'\x00' * 32
分别代表上面流程中的
V = 0x01 0x01 0x01 ... 0x01
K = 0x00 0x00 0x00 ... 0x00
encode(hash_to_int(msghash), 256, 32)对应bits2octets(h1)。
接下来的5行,就是进行上面的4~8的步骤。
参考