Skip to content

Instantly share code, notes, and snippets.

@jaonoctus
Last active July 22, 2025 20:25
Show Gist options
  • Save jaonoctus/dd1556d4dcbf7ac246f877255a92b341 to your computer and use it in GitHub Desktop.
Save jaonoctus/dd1556d4dcbf7ac246f877255a92b341 to your computer and use it in GitHub Desktop.
How can you extract secret keys from ECDSA by doing: d = (((s1 * k) - h1) * r^-1) % n and how RFC6979 fix it
# Import needed libraries
import hashlib
# secp256k1 parameters
p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
a = 0
b = 7
n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
Gx = 55066263022277343669578718895168534326250603453777594175500187360389116729240
Gy = 32670510020758816978083085130507043184471273380659243275938904335757337482424
# Define curve over F_p
F = FiniteField(p)
E = EllipticCurve(F, [a, b])
G = E(F(Gx), F(Gy))
# Generate a private key
d = randint(1, n-1)
Q = d * G # Public key
# Messages
m1 = b"Message One"
m2 = b"Message Two"
# Hash messages (SHA-256)
H1 = Integer(hashlib.sha256(m1).hexdigest(), 16) % n
H2 = Integer(hashlib.sha256(m2).hexdigest(), 16) % n
# Use same nonce (bad practice)
k = randint(1, n-1)
R = k * G
r = Integer(R.xy()[0]) % n
# Signature generation
s1 = (inverse_mod(k, n) * (H1 + d * r)) % n
s2 = (inverse_mod(k, n) * (H2 + d * r)) % n
# Attacker's recovery of private key from reused nonce
numerator = (s1 * H2 - s2 * H1) % n
denominator = (r * (s2 - s1)) % n
d_recovered = (numerator * inverse_mod(denominator, n)) % n
# Output
print("Original private key: ", d)
print("Recovered private key:", d_recovered)
print("Match:", d == d_recovered)
import hashlib
import hmac
# secp256k1 parameters
p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
a = 0
b = 7
n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
Gx = 55066263022277343669578718895168534326250603453777594175500187360389116729240
Gy = 32670510020758816978083085130507043184471273380659243275938904335757337482424
# Define curve
F = FiniteField(p)
E = EllipticCurve(F, [a, b])
G = E(Gx, Gy)
# Helper: RFC 6979 deterministic k generation
def rfc6979_generate_k(privkey, hashmsg, q):
hlen = 32
bx = privkey.to_bytes(32, 'big') + hashmsg.to_bytes(32, 'big')
v = b'\x01' * hlen
k = b'\x00' * hlen
k = hmac.new(k, v + b'\x00' + bx, hashlib.sha256).digest()
v = hmac.new(k, v, hashlib.sha256).digest()
k = hmac.new(k, v + b'\x01' + bx, hashlib.sha256).digest()
v = hmac.new(k, v, hashlib.sha256).digest()
while True:
v = hmac.new(k, v, hashlib.sha256).digest()
k_candidate = Integer(int.from_bytes(v, 'big'))
if 1 <= k_candidate < q:
return k_candidate
k = hmac.new(k, v + b'\x00', hashlib.sha256).digest()
v = hmac.new(k, v, hashlib.sha256).digest()
# Sign a message deterministically
def ecdsa_sign_rfc6979(msg_bytes, privkey):
z = Integer(hashlib.sha256(msg_bytes).hexdigest(), 16) % n
k = rfc6979_generate_k(privkey, z, n)
R = k * G
r = Integer(R.xy()[0]) % n
s = (inverse_mod(k, n) * (z + r * privkey)) % n
return (r, s, k)
# Generate private key
d = randint(1, n - 1)
print("Private key d:", d)
# Sign two messages with RFC6979 (deterministic k)
m1 = b"Message One"
m2 = b"Message Two"
r1, s1, k1 = ecdsa_sign_rfc6979(m1, d)
r2, s2, k2 = ecdsa_sign_rfc6979(m2, d)
print("\nSignature 1:")
print(" r1 =", r1)
print(" s1 =", s1)
print(" k1 =", k1)
print("\nSignature 2:")
print(" r2 =", r2)
print(" s2 =", s2)
print(" k2 =", k2)
print("\nReused nonce?", k1 == k2)
@jaonoctus
Copy link
Author

jaonoctus commented Jul 21, 2025

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment