diff options
author | slush <info@bitcoin.cz> | 2013-09-10 02:55:09 +0200 |
---|---|---|
committer | slush <info@bitcoin.cz> | 2013-09-10 02:55:09 +0200 |
commit | 8efb52fad5025ae87b649ff78faa9f8076768065 (patch) | |
tree | 5d9b2375c88cea1b707a901ec059af643d921084 | |
parent | 846a6d137f4954e50fce53bc8d8013c1d0139c2c (diff) | |
download | ecdsa-8efb52fad5025ae87b649ff78faa9f8076768065.tar.gz |
Integrates RFC 6979 into SigningKey. sign(), sign_digest() and sign_number accept
new parameter 'k'. Added new method sign_deterministic().
Unit test for sign_deterministic() added, all unit test passed.
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | ecdsa/keys.py | 37 | ||||
-rw-r--r-- | ecdsa/test_pyecdsa.py | 23 |
3 files changed, 52 insertions, 9 deletions
@@ -5,3 +5,4 @@ /ecdsa/_version.py /MANIFEST /dist/ +*.pyc diff --git a/ecdsa/keys.py b/ecdsa/keys.py index 29a1cd7..f462700 100644 --- a/ecdsa/keys.py +++ b/ecdsa/keys.py @@ -2,6 +2,7 @@ import binascii import ecdsa import der +import rfc6979 from curves import NIST192p, find_curve from util import string_to_number, number_to_string, randrange from util import sigencode_string, sigdecode_string @@ -213,7 +214,20 @@ class SigningKey: def get_verifying_key(self): return self.verifying_key - def sign(self, data, entropy=None, hashfunc=None, sigencode=sigencode_string): + def sign_deterministic(self, data, hashfunc=None, sigencode=sigencode_string): + """ + Calculates 'k' from data itself, removing the need for strong + random generator and producing deterministic (reproducible) signatures. + See RFC 6979 for more details. + """ + hashfunc = hashfunc or self.default_hashfunc + secexp = self.privkey.secret_multiplier + h = hashfunc(data).digest() + k = rfc6979.generate_k(self.curve.generator, secexp, hashfunc, h) + + return self.sign(data, hashfunc=hashfunc, sigencode=sigencode, k=k) + + def sign(self, data, entropy=None, hashfunc=None, sigencode=sigencode_string, k=None): """ hashfunc= should behave like hashlib.sha1 . The output length of the hash (in bytes) must not be longer than the length of the curve order @@ -228,25 +242,32 @@ class SigningKey: hashfunc = hashfunc or self.default_hashfunc h = hashfunc(data).digest() - return self.sign_digest(h, entropy, sigencode) + return self.sign_digest(h, entropy, sigencode, k) - def sign_digest(self, digest, entropy=None, sigencode=sigencode_string): + def sign_digest(self, digest, entropy=None, sigencode=sigencode_string, k=None): if len(digest) > self.curve.baselen: raise BadDigestError("this curve (%s) is too short " "for your digest (%d)" % (self.curve.name, 8*len(digest))) number = string_to_number(digest) - r, s = self.sign_number(number, entropy) + r, s = self.sign_number(number, entropy, k) return sigencode(r, s, self.privkey.order) - def sign_number(self, number, entropy=None): + def sign_number(self, number, entropy=None, k=None): # returns a pair of numbers order = self.privkey.order # privkey.sign() may raise RuntimeError in the amazingly unlikely # (2**-192) event that r=0 or s=0, because that would leak the key. # We could re-try with a different 'k', but we couldn't test that # code, so I choose to allow the signature to fail instead. - k = randrange(order, entropy) - assert 1 <= k < order - sig = self.privkey.sign(number, k) + + # If k is set, it is used directly. In other cases + # it is generated using entropy function + if k is not None: + _k = k + else: + _k = randrange(order, entropy) + + assert 1 <= _k < order + sig = self.privkey.sign(number, _k) return sig.r, sig.s diff --git a/ecdsa/test_pyecdsa.py b/ecdsa/test_pyecdsa.py index 2c1e7b1..7a20b70 100644 --- a/ecdsa/test_pyecdsa.py +++ b/ecdsa/test_pyecdsa.py @@ -47,6 +47,27 @@ class ECDSA(unittest.TestCase): pub2 = VerifyingKey.from_string(pub.to_string()) self.failUnless(pub2.verify(sig, data)) + def test_deterministic(self): + data = "blahblah" + secexp = int("9d0219792467d7d37b4d43298a7d0c05", 16) + + priv = SigningKey.from_secret_exponent(secexp, SECP256k1, sha256) + pub = priv.get_verifying_key() + + k = rfc6979.generate_k(SECP256k1.generator, secexp, sha256, sha256(data).digest()) + + sig1 = priv.sign(data, k=k) + self.failUnless(pub.verify(sig1, data)) + + sig2 = priv.sign(data, k=k) + self.failUnless(pub.verify(sig2, data)) + + sig3 = priv.sign_deterministic(data, sha256) + self.failUnless(pub.verify(sig3, data)) + + self.failUnlessEqual(sig1, sig2) + self.failUnlessEqual(sig1, sig3) + def test_bad_usage(self): # sk=SigningKey() is wrong self.failUnlessRaises(TypeError, SigningKey) @@ -488,7 +509,7 @@ class RFC6979(unittest.TestCase): self.failUnlessEqual(expected, actual) def test_SECP256k1(self): - ''' RFC doesn't contain test vectors for SECP256k1 used in bitcoin. + '''RFC doesn't contain test vectors for SECP256k1 used in bitcoin. This vector has been computed by Golang reference implementation instead.''' self._do( generator = SECP256k1.generator, |