Skip to content

Instantly share code, notes, and snippets.

@JustinTArthur
Created October 16, 2015 20:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JustinTArthur/4d8e101a3f76023b4e5d to your computer and use it in GitHub Desktop.
Save JustinTArthur/4d8e101a3f76023b4e5d to your computer and use it in GitHub Desktop.
High-S vs Low-S ECDSA Signatures in Electrum <2.5
"""
Uses the same ecdsa library and hashing as Electrum <2.5 and approximates how likely a high-s
signature is to be produced.
Needs same dependencies as electrum, so can be run in the same virtualenv as electrum.
"""
import ecdsa
from random import randint
from os import urandom
from ecdsa.curves import SECP256k1
import hashlib
G = SECP256k1.generator
order = G.order()
def sha256(x):
return hashlib.sha256(x).digest()
def Hash(x):
if type(x) is unicode: x=x.encode('utf-8')
return sha256(sha256(x))
class RecordSSigingKey(ecdsa.SigningKey):
def sign_number(self, *args, **kwargs):
global low_s_results, high_s_results
r, s = ecdsa.SigningKey.sign_number(self, *args, **kwargs)
if s > order/2:
high_s_results += 1
else:
low_s_results += 1
return r, s
def attempt_signing(key):
random_message = urandom(randint(100,2048))
message_digest = Hash(random_message)
private_signing_ley = RecordSSigingKey.from_secret_exponent(key, curve = SECP256k1)
private_signing_ley.sign_digest_deterministic(
message_digest,
hashfunc=hashlib.sha256,
sigencode = ecdsa.util.sigencode_string
)
low_s_results = 0
high_s_results = 0
for _ in xrange(0,100):
random_private_key = randint(0x1, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140)
attempt_signing(random_private_key)
total_attempts = low_s_results+high_s_results
print("With a random key each time, {} ({:.0%}%) attempts yielded low-s, {} ({:.0%}%) yielded high s.".format(
low_s_results,
float(low_s_results) / total_attempts,
high_s_results,
float(high_s_results) / total_attempts
))
low_s_results = 0
high_s_results = 0
random_private_key = randint(0x1, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140)
for _ in xrange(0,100):
attempt_signing(random_private_key)
total_attempts = low_s_results+high_s_results
print("With the same key each time, {} ({:.0%}%) attempts yielded low-s, {} ({:.0%}%) yielded high s.".format(
low_s_results,
float(low_s_results) / total_attempts,
high_s_results,
float(high_s_results) / total_attempts
))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment