From b6d43c60e309bccd681eb1baab502f817ff226b6 Mon Sep 17 00:00:00 2001 From: Alicja Kario Date: Thu, 13 Mar 2025 11:09:08 +0100 Subject: [PATCH] use integer division for canonicalization of signatures fixes #353 --- src/ecdsa/test_pyecdsa.py | 17 +++++++++++++++++ src/ecdsa/util.py | 26 ++++++++++++++++++++------ 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/ecdsa/test_pyecdsa.py b/src/ecdsa/test_pyecdsa.py index 41878959..799e9b74 100644 --- a/src/ecdsa/test_pyecdsa.py +++ b/src/ecdsa/test_pyecdsa.py @@ -520,6 +520,23 @@ def test_sigencode_der_canonize(self): self.assertEqual(r, new_r) self.assertEqual(order - s, new_s) + def test_sigencode_der_canonize_with_close_to_half_order(self): + r = 13 + order = SECP112r1.order + s = order // 2 + 1 + + regular_encode = sigencode_der(r, s, order) + canonical_encode = sigencode_der_canonize(r, s, order) + + self.assertNotEqual(regular_encode, canonical_encode) + + new_r, new_s = sigdecode_der( + sigencode_der_canonize(r, s, order), order + ) + + self.assertEqual(r, new_r) + self.assertEqual(order - s, new_s) + def test_sig_decode_strings_with_invalid_count(self): with self.assertRaises(MalformedSignature): sigdecode_strings([b"one", b"two", b"three"], 0xFF) diff --git a/src/ecdsa/util.py b/src/ecdsa/util.py index 639bc0c2..1aff5bf5 100644 --- a/src/ecdsa/util.py +++ b/src/ecdsa/util.py @@ -304,6 +304,23 @@ def sigencode_der(r, s, order): return der.encode_sequence(der.encode_integer(r), der.encode_integer(s)) +def _canonize(s, order): + """ + Internal function for ensuring that the ``s`` value of a signature is in + the "canonical" format. + + :param int s: the second parameter of ECDSA signature + :param int order: the order of the curve over which the signatures was + computed + + :return: canonical value of s + :rtype: int + """ + if s > order // 2: + s = order - s + return s + + def sigencode_strings_canonize(r, s, order): """ Encode the signature to a pair of strings in a tuple @@ -326,8 +343,7 @@ def sigencode_strings_canonize(r, s, order): :return: raw encoding of ECDSA signature :rtype: tuple(bytes, bytes) """ - if s > order / 2: - s = order - s + s = _canonize(s, order) return sigencode_strings(r, s, order) @@ -350,8 +366,7 @@ def sigencode_string_canonize(r, s, order): :return: raw encoding of ECDSA signature :rtype: bytes """ - if s > order / 2: - s = order - s + s = _canonize(s, order) return sigencode_string(r, s, order) @@ -381,8 +396,7 @@ def sigencode_der_canonize(r, s, order): :return: DER encoding of ECDSA signature :rtype: bytes """ - if s > order / 2: - s = order - s + s = _canonize(s, order) return sigencode_der(r, s, order)