Created
October 16, 2015 20:58
-
-
Save JustinTArthur/4d8e101a3f76023b4e5d to your computer and use it in GitHub Desktop.
High-S vs Low-S ECDSA Signatures in Electrum <2.5
This file contains 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
""" | |
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