無法編碼我的私鑰到公鑰轉換器(Pyhon)
無法為比特幣 (Secp256k1) 編寫我的私鑰到公鑰轉換器腳本
目前正在看《Programming Bitcoin by Jimmy Song》這本書,卡在第 61 頁(第 3 章),但完成了第 3 章的練習 5。您可以在此處查看原始碼,或在Github
儘管這本書非常適合理解密碼學的不同概念,但本書中高度抽象的 OOP 程式碼使得獲得關鍵原理背後的基本低級概念的直覺變得有些困難。這就是為什麼除了完成練習之外,我還喜歡編寫自己的程序函式來解決相同的問題。
我嘗試編寫 ECC Secp256k1 priv-to-pub 密鑰轉換功能,但我的實現……只是不起作用。
它錯誤地轉換數字。
#Secp256k1 Bitcoin private to public key converter script a = 0 b = 7 #Order of the finite field prime = 2**256 - 2**32 - 977 #G coordinates gx = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 #Order of the group G n = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 #n -1 => is the number of all possible private keys privateKey = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140 def addition(currentX, currentY, gx, gy, a, b, prime): if gy == 0: return (None, None) elif currentX is None and currentY is None: return (gx, gy) elif currentX == gx and currentY != gy: return (None, None) elif currentX == gx and currentY == gy and currentY == 0: return (None, None) elif currentX == gx and currentY == gy: s1 = (3 * pow(gx, 2, prime) + a) % prime s2 = (gy * 2) % prime s = (s1 * pow(s2, (prime - 2), prime)) % prime currentX = (s ** 2 - 2 * gx) % prime currentY = (s * (gx - currentX) - gy) % prime elif currentX != gx: s1 = (currentY - gy) s2 = (currentX - gx) s = (s1 * pow(s2, (prime - 2), prime)) % prime currentX = ((s ** 2) - gx - currentX) % prime currentY = ((s * (gx - currentX)) - gy) % prime return (currentX, currentY) def secp256k1BinaryExpansion(privateKey, gx, gy, a, b, prime): if pow(gy, 2, prime) != (pow(gx, 3, prime) + a * gx + b) % prime: return "The point is not on the curve" coef = privateKey currentX, currentY = gx, gy resultX, resultY = None, None while coef: if coef & 1: resultX, resultY = addition(currentX, currentY, gx, gy, a, b, prime) currentX, currentY = addition(currentX, currentY, gx, gy, a, b, prime) coef >>= 1 return (resultX, resultY) #privateKey, gx, gy, a, b, prime #Smaller numbers (Not Secp256k1). Works, but incorrecly. Right output for this is: (49, 71) print(secp256k1BinaryExpansion(8, 47, 71, a, b, 223)) #Test case 2 priv = 0x45300f2b990d332c0ee0efd69f2c21c323d0e2d20e7bfa7b1970bbf169174c82 print(secp256k1BinaryExpansion(priv, gx, gy, a, b, prime)) #Works incorrectly. The right values for test case 2: #x = 40766947848522619068424335498612406856128862642075168802372109289834906557916 #y = 70486353993054234343658342414815626812704078223802622900411169732153437188990
主要功能使用“二進制擴展”技術,但似乎問題在於沒有它的“加法”功能。
為了查看一些結果,我從書中複製了 OOP 程式碼,對其進行了重構,並上傳到了 github,它可以工作:
https://github.com/MaltoonYezi/Python-DSA/blob/main/Cryptography/SECP256K1OOP.py
試圖自己調試第一個程式碼,但失敗了。如果您能提供幫助,我將不勝感激!
您的直接問題是您用單獨的冪和模運算替換了用於模冪運算的 python ‘3-arg pow’ - 即
x ** y % p
而不是pow(x,y,p)
. 雖然這些看起來在數學上是等效的,但它們的實現卻完全不同——pow3 方法需要幾微秒,而根據我非常粗略的預測**/%
,256 位數字的方法大約需要宇宙的生命週期。請參閱交叉https://stackoverflow.com/questions/14133806/why-is-powa-dn-so-much-faster-than-ad-n和更多連結。您的
addition()
函式還處理涉及身份元素的情況,即“無限點”,不一致,有時是錯誤的。加上使用pow(,prime-2,prime)
來獲得模組化逆 - 你顯然是從連結的書/回購中複製的 - 不是最好的方法,但我留下它的理由是你的程式碼不會用於任何重要或材料。有關使用擴展歐幾里得算法進行模逆的幾個範例,包括 python,請參閱如何從私鑰中獲取比特幣公鑰。(添加)或者我剛剛發現您可以簡單地使用pow(,-1,p)
並且 python 為您完成,請參閱https://stackoverflow.com/questions/60019932/what-does-powa-bc-do-when-the-exponent-is-negative .但是,在
addition()
修復之後,您會遇到更大的問題。我不知道你從哪裡得到“二進制擴展”,因為 ECC 中沒有這樣的東西,但從它的結構來看,你似乎正在嘗試進行標量乘法,這確實是從私鑰計算公鑰的正確方法。但是,即使與正確的addition()
. 大多數情況下,您似乎認為傳遞給的第二點addition()
(作為第三個和第四個參數)始終是 G,而實際上它幾乎從來不是,我懷疑將參數命名addition()
為 currentX/Y, gx/y 要麼反映了這一點,要麼對此有所貢獻.因此,我稍微修改了您的添加()以使用 pow3 並處理身份情況,更改測試順序以簡化一些,並更改參數名稱(簡單地為“一”和“二”);並用正確的 scalarmult() 完全替換了你的“二進制擴展”,它現在可以工作了:
#Secp256k1 Bitcoin private to public key converter script a = 0 b = 7 #Order of the finite field prime = 2**256 - 2**32 - 977 #G coordinates gx = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 #Order of the group G n = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 def addition(onex,oney, twox,twoy, a,b,prime): if onex is None and oney is None: return (twox,twoy) elif twox is None and twoy is None: return (onex,oney) elif onex != twox: # normal addition s = (oney-twoy) * pow(onex-twox, prime-2,prime) %prime; newx = (s*s - onex - twox) %prime; newy = (s * (twox-newx) - twoy) %prime; return (newx,newy) elif oney == (prime - twoy)%prime: # cancellation potentially including +-0 # (although +-0 will never actually occur for secp256k1 or any other # X9/SECG curve over Fp because they were chosen to have cofactor 1) return (None,None) else: # doubling (one==two) s = (onex**2*3+a)%prime * pow(oney*2, prime-2, prime) %prime; newx = (s*s - onex*2) %prime; newy = (s * (onex-newx) - oney) %prime; return (newx, newy) def scalarmult(k, inx,iny, a,b,prime): outx,outy = (None,None); while k: if k&1: outx,outy = addition(outx,outy, inx,iny, a,b,prime); k>>=1; inx,iny = addition(inx,iny, inx,iny, a,b,prime); return (outx,outy); # main routine: if run with no arguments use n-1 as private key, # if run with one argument use that hex number as private key, # if run with two arguments use those two hex numbers as a point # and do just a single addition for debugging/demo purposes import sys; if len(sys.argv)==1: x,y = scalarmult(n-1, gx,gy, a,b,prime); elif len(sys.argv)==2: x,y = scalarmult(int(sys.argv[1],16), gx,gy, a,b,prime); else: x,y = addition(int(sys.argv[1],16),int(sys.argv[2],16), gx,gy, a,b,prime); print( hex(x) ); print( hex(y) );
另請注意 b 可以被刪除並且不通過,因為它不用於加法或乘法,因為它對於像 secp256k1 這樣的 Koblitz 曲線為零,但我留下它們是為了保留你的結構。