在分析源碼之前,一定要先講講什么是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的步驟。
參考