From b1db2ba3746d1ccad7e85ed033ca17e1fb4ffdff Mon Sep 17 00:00:00 2001 From: Felix Conway Date: Mon, 4 Aug 2025 11:14:22 +0100 Subject: [PATCH 1/6] Add generated tests for mpi_gcd_invmod_odd Signed-off-by: Felix Conway --- scripts/generate_bignum_tests.py | 138 +++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/scripts/generate_bignum_tests.py b/scripts/generate_bignum_tests.py index 74ad934a8..267fa6c9f 100755 --- a/scripts/generate_bignum_tests.py +++ b/scripts/generate_bignum_tests.py @@ -122,6 +122,71 @@ class BignumOperation(bignum_common.OperationCommon, BignumTarget, return tmp +class BignumGCDInvModOperation(BignumOperation): + #pylint: disable=abstract-method + """Common features for testing GCD and Invmod functions.""" + def __init__(self, val_a: str, val_b: str) -> None: + super().__init__(val_a=val_a, val_b=val_b) + + def description_suffix(self) -> str: + comparison_symbol = '=' + if abs(self.int_a) > abs(self.int_b): + comparison_symbol = '>' + elif abs(self.int_a) < abs(self.int_b): + comparison_symbol = '<' + suffix_parts = [ + f"|A|{comparison_symbol}|N|", + *(["A<0"] if self.int_a < 0 else []), + *(["N<0"] if self.int_b < 0 else []), + "A=0" if self.int_a == 0 else f"A {'even' if self.int_a % 2 == 0 else 'odd'}", + "N=0" if self.int_b == 0 else f"B {'even' if self.int_b % 2 == 0 else 'odd'}" + ] + return ": " + ", ".join(suffix_parts) + + # The default values from BignumOperation are not useful, so overwrite them. + input_values = bignum_common.expand_list_negative([ + "c79e27fc71c69a08b3e85bd48b9cd3be9aa8e2e56df39f4ed8", + "299dd34be98436729eb10f690f8d2bfc5bee21984b775e1e75", + "7da9ec44f42e6311c56a", + "cdbcce3f763819345cfb", + "100000000", "300000000", "500000000", + "50000", "30000", + "1", "2", "3", "", "00", + ]) + input_cases = [ + ("bc7fa9fb389618302e8b", "d49730e586607d42269f"), + ("28bcc01a2d54b174532e", "d1915057d829a934c25d"), + ("d56b50834719280dfa1d", "f007b78f6278ebcccd57"), + ("8c327d1d8743c89d4483", "aa20b0c1f97a428311b5"), + ("e905382f38", "c844b4f9bdaa5ed0002df3dbd2991cd9b9d"), + ("e4623ef13d", "f2a4894ede013e354e481fe8974e67"), + ("9f6afa8bdb", "b50aa03a7066df6f27bd6267b"), + ("95f99b7122", "e8c74031ec75839f7539"), + ("32", "948fbec067"), + ("7445", "948fbec067"), + ("31850e", "948fbec067"), + ("421c2cc8", "948fbec067"), + ("32a69", "71e107"), + ("36d4e9", "3e05d1"), + ("babf01", "1bf699d1"), + ("7", "31"), + ] + + @staticmethod + def get_return_code_gcd(int_a: int, int_b: int) -> str: + code = "0" + if (int_a > int_b) or \ + (int_a < 0) or \ + (int_b % 2 == 0): + code = "MBEDTLS_ERR_MPI_BAD_INPUT_DATA" + return code + + def get_return_code_invmod(self, int_a: int, int_b: int) -> str: + if int_b < 2: + return "MBEDTLS_ERR_MPI_BAD_INPUT_DATA" + return self.get_return_code_gcd(int_a, int_b) + + class BignumCmp(BignumOperation): """Test cases for bignum value comparison.""" count = 0 @@ -260,6 +325,79 @@ class BignumGCD(BignumOperation): return [bignum_common.quote_str("{:x}".format(self._result))] +class BignumGCDModInvOdd(BignumGCDInvModOperation): + """Test cases for both modular inverse and greatest common divisor.""" + count = 0 + symbol = "GCD & ^-1 mod" + test_function = "mpi_gcd_modinv_odd_both" + test_name = "GCD & mod inv" + + def __init__(self, val_a: str, val_b: str) -> None: + super().__init__(val_a, val_b) + self._result_code = self.get_return_code_invmod(self.int_a, self.int_b) + self._result_gcd = math.gcd(self.int_a, self.int_b) + # Only compute the modular inverse if we will get a result - negative + # and zero Ns are also present in the test data so skip them too. + if self._result_gcd == 1 and self.int_b > 1: + self._result_invmod = bignum_common.invmod_positive(self.int_a, self.int_b) + else: + self._result_invmod = -1 # No inverse + + def result(self) -> List[str]: + # The test requires us to tell it if there is no modular inverse. + if self._result_invmod == -1: + result_invmod = "no_inverse" + else: + result_invmod = "{:x}".format(self._result_invmod) + return [ + bignum_common.quote_str("{:x}".format(self._result_gcd)), + bignum_common.quote_str(result_invmod), + self._result_code, + ] + + +class BignumGCDModInvOddOnlyGCD(BignumGCDInvModOperation): + """Test cases for greatest common divisor only.""" + count = 0 + symbol = "GCD" + test_function = "mpi_gcd_modinv_odd_only_gcd" + test_name = "GCD only" + + def __init__(self, val_a: str, val_b: str) -> None: + super().__init__(val_a, val_b) + self._result_code = self.get_return_code_gcd(self.int_a, self.int_b) + # We always expect a positive result as the function should reject + # negative inputs. + self._result_gcd = math.gcd(self.int_a, self.int_b) + + def result(self) -> List[str]: + return [bignum_common.quote_str("{:x}".format(self._result_gcd)), self._result_code] + + +class BignumGCDModInvOddOnlyModInv(BignumGCDInvModOperation): + """Test cases for modular inverse only.""" + count = 0 + symbol = "^-1 mod" + test_function = "mpi_gcd_modinv_odd_only_modinv" + test_name = "Mod inv only" + + def __init__(self, val_a: str, val_b: str) -> None: + super().__init__(val_a, val_b) + self._result_code = self.get_return_code_invmod(self.int_a, self.int_b) + # Only compute the modular inverse if we will get a result - negative + # and zero Ns are also present in the test data so skip them too. + if math.gcd(self.int_a, self.int_b) == 1 and self.int_b > 1: + self._result_invmod = bignum_common.invmod_positive(self.int_a, self.int_b) + else: + self._result_invmod = -1 # No inverse + + def result(self) -> List[str]: + # The test requires us to tell it if there is no modular inverse. + if self._result_invmod == -1: + return [bignum_common.quote_str("no_inverse"), self._result_code] + return [bignum_common.quote_str("{:x}".format(self._result_invmod)), self._result_code] + + class BignumAdd(BignumOperation): """Test cases for bignum value addition.""" count = 0 From 0767f443db43cb2a5cd898e7a4fcf2d07e28766e Mon Sep 17 00:00:00 2001 From: Felix Conway Date: Tue, 5 Aug 2025 09:49:31 +0100 Subject: [PATCH 2/6] Fix code style issue Signed-off-by: Felix Conway --- scripts/generate_bignum_tests.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/generate_bignum_tests.py b/scripts/generate_bignum_tests.py index 267fa6c9f..57cd67ec8 100755 --- a/scripts/generate_bignum_tests.py +++ b/scripts/generate_bignum_tests.py @@ -350,10 +350,10 @@ class BignumGCDModInvOdd(BignumGCDInvModOperation): else: result_invmod = "{:x}".format(self._result_invmod) return [ - bignum_common.quote_str("{:x}".format(self._result_gcd)), - bignum_common.quote_str(result_invmod), - self._result_code, - ] + bignum_common.quote_str("{:x}".format(self._result_gcd)), + bignum_common.quote_str(result_invmod), + self._result_code, + ] class BignumGCDModInvOddOnlyGCD(BignumGCDInvModOperation): From 9c18635291b216d5414f82eccdb43efd56f57dbd Mon Sep 17 00:00:00 2001 From: Felix Conway Date: Tue, 5 Aug 2025 15:12:10 +0100 Subject: [PATCH 3/6] Remove most negative test data in gcd_invmod_odd generated tests Signed-off-by: Felix Conway --- scripts/generate_bignum_tests.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/scripts/generate_bignum_tests.py b/scripts/generate_bignum_tests.py index 57cd67ec8..87fd9c2e4 100755 --- a/scripts/generate_bignum_tests.py +++ b/scripts/generate_bignum_tests.py @@ -144,15 +144,16 @@ class BignumGCDInvModOperation(BignumOperation): return ": " + ", ".join(suffix_parts) # The default values from BignumOperation are not useful, so overwrite them. - input_values = bignum_common.expand_list_negative([ + input_values = [ "c79e27fc71c69a08b3e85bd48b9cd3be9aa8e2e56df39f4ed8", "299dd34be98436729eb10f690f8d2bfc5bee21984b775e1e75", + "-ecbb3a4e986d488172ecd54f7bd71bd18050c4ed", "7da9ec44f42e6311c56a", "cdbcce3f763819345cfb", "100000000", "300000000", "500000000", "50000", "30000", - "1", "2", "3", "", "00", - ]) + "1", "2", "3", "", "00", "-1" + ] input_cases = [ ("bc7fa9fb389618302e8b", "d49730e586607d42269f"), ("28bcc01a2d54b174532e", "d1915057d829a934c25d"), @@ -181,10 +182,11 @@ class BignumGCDInvModOperation(BignumOperation): code = "MBEDTLS_ERR_MPI_BAD_INPUT_DATA" return code - def get_return_code_invmod(self, int_a: int, int_b: int) -> str: + @staticmethod + def get_return_code_invmod(int_a: int, int_b: int) -> str: if int_b < 2: return "MBEDTLS_ERR_MPI_BAD_INPUT_DATA" - return self.get_return_code_gcd(int_a, int_b) + return BignumGCDInvModOperation.get_return_code_gcd(int_a, int_b) class BignumCmp(BignumOperation): From d2a5b7ab339b568767b5d555be1133e5a9e62720 Mon Sep 17 00:00:00 2001 From: Felix Conway Date: Wed, 6 Aug 2025 11:05:26 +0100 Subject: [PATCH 4/6] Improve result code functions in gcd_modinv_odd generated tests Signed-off-by: Felix Conway --- scripts/generate_bignum_tests.py | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/scripts/generate_bignum_tests.py b/scripts/generate_bignum_tests.py index 87fd9c2e4..414c31237 100755 --- a/scripts/generate_bignum_tests.py +++ b/scripts/generate_bignum_tests.py @@ -173,20 +173,18 @@ class BignumGCDInvModOperation(BignumOperation): ("7", "31"), ] - @staticmethod - def get_return_code_gcd(int_a: int, int_b: int) -> str: + def get_return_code_gcd_modinv_odd_gcd_only(self) -> str: code = "0" - if (int_a > int_b) or \ - (int_a < 0) or \ - (int_b % 2 == 0): + if (self.int_a > self.int_b) or \ + (self.int_a < 0) or \ + (self.int_b % 2 == 0): code = "MBEDTLS_ERR_MPI_BAD_INPUT_DATA" return code - @staticmethod - def get_return_code_invmod(int_a: int, int_b: int) -> str: - if int_b < 2: + def get_return_code_gcd_modinv_odd(self) -> str: + if self.int_b < 2: return "MBEDTLS_ERR_MPI_BAD_INPUT_DATA" - return BignumGCDInvModOperation.get_return_code_gcd(int_a, int_b) + return self.get_return_code_gcd_modinv_odd_gcd_only() class BignumCmp(BignumOperation): @@ -336,7 +334,7 @@ class BignumGCDModInvOdd(BignumGCDInvModOperation): def __init__(self, val_a: str, val_b: str) -> None: super().__init__(val_a, val_b) - self._result_code = self.get_return_code_invmod(self.int_a, self.int_b) + self._result_code = self.get_return_code_gcd_modinv_odd() self._result_gcd = math.gcd(self.int_a, self.int_b) # Only compute the modular inverse if we will get a result - negative # and zero Ns are also present in the test data so skip them too. @@ -367,7 +365,7 @@ class BignumGCDModInvOddOnlyGCD(BignumGCDInvModOperation): def __init__(self, val_a: str, val_b: str) -> None: super().__init__(val_a, val_b) - self._result_code = self.get_return_code_gcd(self.int_a, self.int_b) + self._result_code = self.get_return_code_gcd_modinv_odd_gcd_only() # We always expect a positive result as the function should reject # negative inputs. self._result_gcd = math.gcd(self.int_a, self.int_b) @@ -385,7 +383,7 @@ class BignumGCDModInvOddOnlyModInv(BignumGCDInvModOperation): def __init__(self, val_a: str, val_b: str) -> None: super().__init__(val_a, val_b) - self._result_code = self.get_return_code_invmod(self.int_a, self.int_b) + self._result_code = self.get_return_code_gcd_modinv_odd() # Only compute the modular inverse if we will get a result - negative # and zero Ns are also present in the test data so skip them too. if math.gcd(self.int_a, self.int_b) == 1 and self.int_b > 1: From aaac8276696c3ec0608c7bde91a5651dab5a1c75 Mon Sep 17 00:00:00 2001 From: Felix Conway Date: Wed, 6 Aug 2025 11:08:06 +0100 Subject: [PATCH 5/6] Return None instead of -1 when modinv does not exist Signed-off-by: Felix Conway --- scripts/generate_bignum_tests.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/scripts/generate_bignum_tests.py b/scripts/generate_bignum_tests.py index 414c31237..acef85237 100755 --- a/scripts/generate_bignum_tests.py +++ b/scripts/generate_bignum_tests.py @@ -339,13 +339,14 @@ class BignumGCDModInvOdd(BignumGCDInvModOperation): # Only compute the modular inverse if we will get a result - negative # and zero Ns are also present in the test data so skip them too. if self._result_gcd == 1 and self.int_b > 1: - self._result_invmod = bignum_common.invmod_positive(self.int_a, self.int_b) + self._result_invmod = \ + bignum_common.invmod_positive(self.int_a, self.int_b) # type: int | None else: - self._result_invmod = -1 # No inverse + self._result_invmod = None # No inverse def result(self) -> List[str]: # The test requires us to tell it if there is no modular inverse. - if self._result_invmod == -1: + if self._result_invmod is None: result_invmod = "no_inverse" else: result_invmod = "{:x}".format(self._result_invmod) @@ -387,13 +388,14 @@ class BignumGCDModInvOddOnlyModInv(BignumGCDInvModOperation): # Only compute the modular inverse if we will get a result - negative # and zero Ns are also present in the test data so skip them too. if math.gcd(self.int_a, self.int_b) == 1 and self.int_b > 1: - self._result_invmod = bignum_common.invmod_positive(self.int_a, self.int_b) + self._result_invmod = \ + bignum_common.invmod_positive(self.int_a, self.int_b) # type: int | None else: - self._result_invmod = -1 # No inverse + self._result_invmod = None # No inverse def result(self) -> List[str]: # The test requires us to tell it if there is no modular inverse. - if self._result_invmod == -1: + if self._result_invmod is None: return [bignum_common.quote_str("no_inverse"), self._result_code] return [bignum_common.quote_str("{:x}".format(self._result_invmod)), self._result_code] From b83b99a31c711b788694e76c008984b780c63f96 Mon Sep 17 00:00:00 2001 From: Felix Conway Date: Wed, 6 Aug 2025 11:14:48 +0100 Subject: [PATCH 6/6] Use format_result() when applicable Signed-off-by: Felix Conway --- scripts/generate_bignum_tests.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/generate_bignum_tests.py b/scripts/generate_bignum_tests.py index acef85237..cfafc5658 100755 --- a/scripts/generate_bignum_tests.py +++ b/scripts/generate_bignum_tests.py @@ -351,7 +351,7 @@ class BignumGCDModInvOdd(BignumGCDInvModOperation): else: result_invmod = "{:x}".format(self._result_invmod) return [ - bignum_common.quote_str("{:x}".format(self._result_gcd)), + self.format_result(self._result_gcd), bignum_common.quote_str(result_invmod), self._result_code, ] @@ -372,7 +372,7 @@ class BignumGCDModInvOddOnlyGCD(BignumGCDInvModOperation): self._result_gcd = math.gcd(self.int_a, self.int_b) def result(self) -> List[str]: - return [bignum_common.quote_str("{:x}".format(self._result_gcd)), self._result_code] + return [self.format_result(self._result_gcd), self._result_code] class BignumGCDModInvOddOnlyModInv(BignumGCDInvModOperation): @@ -397,7 +397,7 @@ class BignumGCDModInvOddOnlyModInv(BignumGCDInvModOperation): # The test requires us to tell it if there is no modular inverse. if self._result_invmod is None: return [bignum_common.quote_str("no_inverse"), self._result_code] - return [bignum_common.quote_str("{:x}".format(self._result_invmod)), self._result_code] + return [self.format_result(self._result_invmod), self._result_code] class BignumAdd(BignumOperation):