diff options
Diffstat (limited to 'src/ecdsa/keys.py')
-rw-r--r-- | src/ecdsa/keys.py | 113 |
1 files changed, 71 insertions, 42 deletions
diff --git a/src/ecdsa/keys.py b/src/ecdsa/keys.py index c4b3f06..8edd99c 100644 --- a/src/ecdsa/keys.py +++ b/src/ecdsa/keys.py @@ -124,6 +124,38 @@ class MalformedPointError(AssertionError): pass +def _truncate_and_convert_digest(digest, curve, allow_truncate): + """Truncates and converts digest to an integer.""" + if not allow_truncate: + if len(digest) > curve.baselen: + raise BadDigestError( + "this curve ({0}) is too short " + "for the length of your digest ({1})".format( + curve.name, 8 * len(digest) + ) + ) + else: + digest = digest[: curve.baselen] + number = string_to_number(digest) + if allow_truncate: + max_length = bit_length(curve.order) + # we don't use bit_length(number) as that truncates leading zeros + length = len(digest) * 8 + + # See NIST FIPS 186-4: + # + # When the length of the output of the hash function is greater + # than N (i.e., the bit length of q), then the leftmost N bits of + # the hash function output block shall be used in any calculation + # using the hash function output during the generation or + # verification of a digital signature. + # + # as such, we need to shift-out the low-order bits: + number >>= max(0, length - max_length) + + return number + + class VerifyingKey(object): """ Class for handling keys that can verify signatures (public keys). @@ -240,12 +272,12 @@ class VerifyingKey(object): order = curve.order # real assert, from_string() should not call us with different length assert len(string) == curve.verifying_key_length - xs = string[: curve.baselen] - ys = string[curve.baselen :] - if len(xs) != curve.baselen: - raise MalformedPointError("Unexpected length of encoded x") - if len(ys) != curve.baselen: - raise MalformedPointError("Unexpected length of encoded y") + xs = string[: curve.verifying_key_length // 2] + ys = string[curve.verifying_key_length // 2 :] + # real assert, verifying_key_length is calculated by multiplying an + # integer by two so it will always be even + assert len(xs) == curve.verifying_key_length // 2 + assert len(ys) == curve.verifying_key_length // 2 x = string_to_number(xs) y = string_to_number(ys) @@ -339,7 +371,7 @@ class VerifyingKey(object): raise MalformedPointError( "Invalid X9.62 encoding of the public point" ) - elif sig_len == curve.baselen + 1: + elif sig_len == curve.verifying_key_length // 2 + 1: point = cls._from_compressed(string, curve) else: raise MalformedPointError( @@ -435,7 +467,13 @@ class VerifyingKey(object): @classmethod def from_public_key_recovery( - cls, signature, data, curve, hashfunc=sha1, sigdecode=sigdecode_string + cls, + signature, + data, + curve, + hashfunc=sha1, + sigdecode=sigdecode_string, + allow_truncate=True, ): """ Return keys that can be used as verifiers of the provided signature. @@ -458,6 +496,9 @@ class VerifyingKey(object): a tuple with two integers, "r" as the first one and "s" as the second one. See :func:`ecdsa.util.sigdecode_string` and :func:`ecdsa.util.sigdecode_der` for examples. + :param bool allow_truncate: if True, the provided hashfunc can generate + values larger than the bit size of the order of the curve, the + extra bits (at the end of the digest) will be truncated. :type sigdecode: callable :return: Initialised VerifyingKey objects @@ -466,7 +507,12 @@ class VerifyingKey(object): data = normalise_bytes(data) digest = hashfunc(data).digest() return cls.from_public_key_recovery_with_digest( - signature, digest, curve, hashfunc=hashfunc, sigdecode=sigdecode + signature, + digest, + curve, + hashfunc=hashfunc, + sigdecode=sigdecode, + allow_truncate=allow_truncate, ) @classmethod @@ -477,6 +523,7 @@ class VerifyingKey(object): curve, hashfunc=sha1, sigdecode=sigdecode_string, + allow_truncate=False, ): """ Return keys that can be used as verifiers of the provided signature. @@ -500,7 +547,10 @@ class VerifyingKey(object): 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 hashfunc can generate + values larger than the bit size of the order of the curve (and + the length of provided `digest`), the extra bits (at the end of the + digest) will be truncated. :return: Initialised VerifyingKey object :rtype: VerifyingKey @@ -510,7 +560,9 @@ class VerifyingKey(object): sig = ecdsa.Signature(r, s) digest = normalise_bytes(digest) - digest_as_number = string_to_number(digest) + digest_as_number = _truncate_and_convert_digest( + digest, curve, allow_truncate + ) pks = sig.recover_public_keys(digest_as_number, generator) # Transforms the ecdsa.Public_key object into a VerifyingKey @@ -521,14 +573,14 @@ class VerifyingKey(object): def _raw_encode(self): """Convert the public key to the :term:`raw encoding`.""" - order = self.pubkey.order + order = self.curve.curve.p() x_str = number_to_string(self.pubkey.point.x(), order) y_str = number_to_string(self.pubkey.point.y(), order) return x_str + y_str def _compressed_encode(self): """Encode the public point into the compressed form.""" - order = self.pubkey.order + order = self.curve.curve.p() x_str = number_to_string(self.pubkey.point.x(), order) if self.pubkey.point.y() & 1: return b("\x03") + x_str @@ -717,27 +769,9 @@ class VerifyingKey(object): # signature doesn't have to be a bytes-like-object so don't normalise # it, the decoders will do that digest = normalise_bytes(digest) - if not allow_truncate and 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) - if allow_truncate: - max_length = bit_length(self.curve.order) - # we don't use bit_length(number) as that truncates leading zeros - length = len(digest) * 8 - - # See NIST FIPS 186-4: - # - # When the length of the output of the hash function is greater - # than N (i.e., the bit length of q), then the leftmost N bits of - # the hash function output block shall be used in any calculation - # using the hash function output during the generation or - # verification of a digital signature. - # - # as such, we need to shift-out the low-order bits: - number >>= max(0, length - max_length) + number = _truncate_and_convert_digest( + digest, self.curve, allow_truncate, + ) try: r, s = sigdecode(signature, self.pubkey.order) @@ -1409,14 +1443,9 @@ class SigningKey(object): :rtype: bytes or sigencode function dependant type """ digest = normalise_bytes(digest) - if allow_truncate: - digest = digest[: self.curve.baselen] - 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) + number = _truncate_and_convert_digest( + digest, self.curve, allow_truncate, + ) r, s = self.sign_number(number, entropy, k) return sigencode(r, s, self.privkey.order) |