Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose allow_truncate option in SigningKey.sign() and VerifyingKey.verify() #205

Merged
merged 5 commits into from
Aug 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions src/ecdsa/keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -602,7 +602,12 @@ def to_der(self, point_encoding="uncompressed"):
)

def verify(
self, signature, data, hashfunc=None, sigdecode=sigdecode_string
self,
signature,
data,
hashfunc=None,
sigdecode=sigdecode_string,
allow_truncate=True,
):
"""
Verify a signature made over provided data.
Expand All @@ -629,6 +634,11 @@ def verify(
second one. See :func:`ecdsa.util.sigdecode_string` and
:func:`ecdsa.util.sigdecode_der` for examples.
:type sigdecode: callable
:param bool allow_truncate: if True, the provided digest can have
bigger bit-size than the order of the curve, the extra bits (at
the end of the digest) will be truncated. Use it when verifying
SHA-384 output using NIST256p or in similar situations. Defaults to
True.

:raises BadSignatureError: if the signature is invalid or malformed

Expand All @@ -641,7 +651,7 @@ def verify(

hashfunc = hashfunc or self.default_hashfunc
digest = hashfunc(data).digest()
return self.verify_digest(signature, digest, sigdecode, True)
return self.verify_digest(signature, digest, sigdecode, allow_truncate)

def verify_digest(
self,
Expand Down Expand Up @@ -1262,6 +1272,7 @@ def sign(
hashfunc=None,
sigencode=sigencode_string,
k=None,
allow_truncate=True,
):
"""
Create signature over data using the probabilistic ECDSA algorithm.
Expand Down Expand Up @@ -1298,6 +1309,11 @@ def sign(
:param int k: a pre-selected nonce for calculating the signature.
In typical use cases, it should be set to None (the default) to
allow its generation from an entropy source.
:param bool allow_truncate: if True, the provided digest can have
bigger bit-size than the order of the curve, the extra bits (at
the end of the digest) will be truncated. Use it when signing
SHA-384 output using NIST256p or in similar situations. True by
default.

:raises RSZeroError: in the unlikely event when "r" parameter or
"s" parameter is equal 0 as that would leak the key. Calee should
Expand All @@ -1309,7 +1325,7 @@ def sign(
hashfunc = hashfunc or self.default_hashfunc
data = normalise_bytes(data)
h = hashfunc(data).digest()
return self.sign_digest(h, entropy, sigencode, k, allow_truncate=True)
return self.sign_digest(h, entropy, sigencode, k, allow_truncate)

def sign_digest(
self,
Expand Down
46 changes: 46 additions & 0 deletions src/ecdsa/test_pyecdsa.py
Original file line number Diff line number Diff line change
Expand Up @@ -1203,6 +1203,52 @@ def do_test_to_openssl(self, curve, hash_name="SHA1"):
)


class TooSmallCurve(unittest.TestCase):
OPENSSL_SUPPORTED_CURVES = set(
c.split(":")[0].strip()
for c in run_openssl("ecparam -list_curves").split("\n")
)

@pytest.mark.skipif(
"prime192v1" not in OPENSSL_SUPPORTED_CURVES,
reason="system openssl does not support prime192v1",
)
def test_sign_too_small_curve_dont_allow_truncate_raises(self):
sk = SigningKey.generate(curve=NIST192p)
vk = sk.get_verifying_key()
data = b("data")
with self.assertRaises(BadDigestError):
sk.sign(
data,
hashfunc=partial(hashlib.new, "SHA256"),
sigencode=sigencode_der,
allow_truncate=False,
)

@pytest.mark.skipif(
"prime192v1" not in OPENSSL_SUPPORTED_CURVES,
reason="system openssl does not support prime192v1",
)
def test_verify_too_small_curve_dont_allow_truncate_raises(self):
sk = SigningKey.generate(curve=NIST192p)
vk = sk.get_verifying_key()
data = b("data")
sig_der = sk.sign(
data,
hashfunc=partial(hashlib.new, "SHA256"),
sigencode=sigencode_der,
allow_truncate=True,
)
with self.assertRaises(BadDigestError):
vk.verify(
sig_der,
data,
hashfunc=partial(hashlib.new, "SHA256"),
sigdecode=sigdecode_der,
allow_truncate=False,
)


class DER(unittest.TestCase):
def test_integer(self):
self.assertEqual(der.encode_integer(0), b("\x02\x01\x00"))
Expand Down