Skip to content

Instantly share code, notes, and snippets.

@educationofjon
Created March 11, 2019 09:44
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save educationofjon/32898da0079c64ad5de7200d22743437 to your computer and use it in GitHub Desktop.
Save educationofjon/32898da0079c64ad5de7200d22743437 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
# Reproduce Bitcoin ABC's deterministic Schnorr signature near end of key_tests.cpp
from ecdsa.numbertheory import jacobi
from ecdsa import SECP256k1
import hashlib
import hmac
G = SECP256k1.generator
p = SECP256k1.curve.p()
n = SECP256k1.order
def sha(b):
return hashlib.sha256(b).digest()
msg = b"Very deterministic message"
msghash = sha(sha(msg))
assert msghash == bytes.fromhex("5255683da567900bfd3e786ed8836a4e7763c221bf1ac20ece2a5171b9199e8a")
print("msg = "+repr(msg))
print("msghash = "+msghash.hex())
# 5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj
# Kwr371tjA9u2rFSMZjTNun2PXXP3WPZu2afRHTcta6KxEUdm1vEw
privkey = 0x12b004fff7f4b69ef8650e767f18f11ede158148b425660723b9f9a66e61f747
print("privkey = "+hex(privkey))
# pubkey point
P = privkey*G
comppub = (b'\x03' if (P.y()&1) else b'\x02') + P.x().to_bytes(32,'big')
assert comppub == bytes.fromhex("030b4c866585dd868a9d62348a9cd008d6a312937048fff31670e7e920cfc7a744")
### Validate the signature found in the code
sig = bytes.fromhex("2c56731ac2f7a7e7f11518fc7722a166b02438924ca9d8b4d1113"
"47b81d0717571846de67ad3d913a8fdf9d8f3f73161a4c48ae81c"
"b183b214765feb86e255ce")
r = int.from_bytes(sig[:32],'big')
s = int.from_bytes(sig[32:],'big')
print("sig.r = " + hex(r))
print("sig.s = " + hex(s))
e = int.from_bytes(sha(r.to_bytes(32, 'big') + comppub + msghash), 'big') % n
print("e = " + hex(e))
checkR = s*G + (n-e)*P
print("chk.rx = " + hex(checkR.x()))
print("chk.ry = " + hex(checkR.y()))
print("jacobi(chk.y) = %+d"%(jacobi(checkR.y(),p)))
assert checkR.x() == r
### Now try to reconstruct the deterministic nonce
# see secp256k1_schnorr_sig_generate_k() in schnorr_impl.h
# and nonce_function_rfc6979() in secp256k1.c
algo16 = b"Schnorr+SHA256 "
ndata = b''
V = b'\x01'*32
K = b'\x00'*32
buf = privkey.to_bytes(32,'big') + msghash + ndata + algo16
# initialize
K = hmac.HMAC(K, V+b'\x00'+buf, 'sha256').digest()
V = hmac.HMAC(K, V, 'sha256').digest()
K = hmac.HMAC(K, V+b'\x01'+buf, 'sha256').digest()
V = hmac.HMAC(K, V, 'sha256').digest()
# generate once
V = hmac.HMAC(K, V, 'sha256').digest()
T = b''
k = int.from_bytes(V, 'big')
print("nonce k = " + hex(k))
R = k*G
print(" (kG).x = " + hex(R.x()))
print("+(kG).y = " + hex(R.y()))
print("jacobi((kG).y) = %+d"%(jacobi(R.y(),p)))
print("-(kG).y = " + hex(p-R.y()))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment