-
-
Save hellresistor/e0d27fd40d004dff17dc831c0a527272 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