Last active
July 22, 2025 20:25
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
run the code1 with SageMath clicking here
run the code2 with SageMath clicking here
libsecp