Private-Key

無法編碼我的私鑰到公鑰轉換器(Pyhon)

  • August 9, 2022

無法為比特幣 (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 曲線為零,但我留下它們是為了保留你的結構。

引用自:https://bitcoin.stackexchange.com/questions/114766