summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorslush <info@bitcoin.cz>2013-09-10 02:55:09 +0200
committerslush <info@bitcoin.cz>2013-09-10 02:55:09 +0200
commit8efb52fad5025ae87b649ff78faa9f8076768065 (patch)
tree5d9b2375c88cea1b707a901ec059af643d921084
parent846a6d137f4954e50fce53bc8d8013c1d0139c2c (diff)
downloadecdsa-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--.gitignore1
-rw-r--r--ecdsa/keys.py37
-rw-r--r--ecdsa/test_pyecdsa.py23
3 files changed, 52 insertions, 9 deletions
diff --git a/.gitignore b/.gitignore
index 57b3ce1..c686b2a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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,