From a30d72519bd081bda8a861854eff0252f52b2022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Mon, 28 Jul 2025 23:42:14 +0200 Subject: [PATCH 01/28] Add generated tests for gcd_modinv_odd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This generates 211 test cases, of which 81 have a non-trivial GCD, and the other 130 have a modular inverse. Signed-off-by: Manuel Pégourié-Gonnard --- scripts/mbedtls_framework/bignum_core.py | 68 ++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 3 deletions(-) diff --git a/scripts/mbedtls_framework/bignum_core.py b/scripts/mbedtls_framework/bignum_core.py index 164da0562..93de3e030 100644 --- a/scripts/mbedtls_framework/bignum_core.py +++ b/scripts/mbedtls_framework/bignum_core.py @@ -3,6 +3,7 @@ # SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later # +import math import random from typing import Dict, Iterator, List, Tuple @@ -10,7 +11,7 @@ from typing import Dict, Iterator, List, Tuple from . import test_case from . import test_data_generation from . import bignum_common -from .bignum_data import ADD_SUB_DATA +from . import bignum_data class BignumCoreTarget(test_data_generation.BaseTarget): #pylint: disable=abstract-method, too-few-public-methods @@ -166,7 +167,7 @@ class BignumCoreAddAndAddIf(BignumCoreTarget, bignum_common.OperationCommon): test_function = "mpi_core_add_and_add_if" test_name = "mpi_core_add_and_add_if" input_style = "arch_split" - input_values = ADD_SUB_DATA + input_values = bignum_data.ADD_SUB_DATA unique_combinations_only = True def result(self) -> List[str]: @@ -187,7 +188,7 @@ class BignumCoreSub(BignumCoreTarget, bignum_common.OperationCommon): symbol = "-" test_function = "mpi_core_sub" test_name = "mbedtls_mpi_core_sub" - input_values = ADD_SUB_DATA + input_values = bignum_data.ADD_SUB_DATA def result(self) -> List[str]: if self.int_a >= self.int_b: @@ -894,3 +895,64 @@ class BignumCoreZeroCheckCT(BignumCoreTarget, bignum_common.OperationCommon): def result(self) -> List[str]: result = 1 if self.int_a == 0 else 0 return [str(result)] + +class BignumCoreGcdModinvOdd(BignumCoreTarget, test_data_generation.BaseTest): + """Test cases for bignum core GCD+modinv (odd modulus)""" + + test_function = "mpi_core_gcd_modinv_odd" + test_name = "mpi_core_gcd_modinv_odd" + + # X * 2, X * 3 is so that we get GCD(X*2, X*3) = X where the GCD has a + # the same order of magnitude as the inputs (all other cases give a + # single-limb GCD). + DATA = ( + 0, 1, 2, 3, 4, 5, 6, 7, + int(bignum_data.SAFE_PRIME_192_BIT_SEED_1, 16), + int(bignum_data.SAFE_PRIME_192_BIT_SEED_1, 16) * 2, + int(bignum_data.SAFE_PRIME_192_BIT_SEED_1, 16) * 3, + int(bignum_data.RANDOM_192_BIT_SEED_2_NO1, 16), + int(bignum_data.RANDOM_192_BIT_SEED_2_NO2, 16), + int(bignum_data.RANDOM_192_BIT_SEED_2_NO3, 16), + int(bignum_data.RANDOM_192_BIT_SEED_2_NO4, 16), + int(bignum_data.RANDOM_192_BIT_SEED_2_NO9, 16), + int(bignum_data.SAFE_PRIME_1024_BIT_SEED_3, 16), + int(bignum_data.RANDOM_1024_BIT_SEED_4_NO1, 16), + int(bignum_data.RANDOM_1024_BIT_SEED_4_NO2, 16), + int(bignum_data.RANDOM_1024_BIT_SEED_4_NO3, 16), + int(bignum_data.RANDOM_1024_BIT_SEED_4_NO4, 16), + int(bignum_data.RANDOM_1024_BIT_SEED_4_NO5, 16), + int(bignum_data.RANDOM_1024_BIT_SEED_4_NO5, 16) * 2, + int(bignum_data.RANDOM_1024_BIT_SEED_4_NO5, 16) * 3, + ) + + def __init__(self, a: int, n: int, g: int, i: int) -> None: + self.a_val = a + self.n_val = n + self.g_val = g + self.i_val = i + + def arguments(self) -> List[str]: + a_str = bignum_common.quote_str(f"{self.a_val:x}") + n_str = bignum_common.quote_str(f"{self.n_val:x}") + n_len = len(n_str) - 2 # quotes + g_str = bignum_common.quote_str(f"{self.g_val:x}".zfill(n_len)) + if self.i_val != 0: + i_str = bignum_common.quote_str(f"{self.i_val:x}".zfill(n_len)) + else: + i_str = bignum_common.quote_str("") + return [a_str, n_str, g_str, i_str] + + @classmethod + def generate_function_tests(cls) -> Iterator[test_case.TestCase]: + for n in cls.DATA: + if n % 2 == 0: + continue + for a in cls.DATA: + if a > n: + continue + g = math.gcd(a, n) + if g == 1: + i = bignum_common.invmod_positive(a, n) + else: + i = 0 + yield cls(a, n, g, i).create_test_case() From b3575b30f26699ef8ce39490aa53f623865b7478 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Mon, 28 Jul 2025 23:42:14 +0200 Subject: [PATCH 02/28] Fix documentation of bignum_common.invmod() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Python's built-in pow() always returns a positive result. Signed-off-by: Manuel Pégourié-Gonnard --- scripts/mbedtls_framework/bignum_common.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/mbedtls_framework/bignum_common.py b/scripts/mbedtls_framework/bignum_common.py index eebc858b2..28143ce0f 100644 --- a/scripts/mbedtls_framework/bignum_common.py +++ b/scripts/mbedtls_framework/bignum_common.py @@ -19,8 +19,7 @@ T = TypeVar('T') #pylint: disable=invalid-name def invmod(a: int, n: int) -> int: """Return inverse of a to modulo n. - Equivalent to pow(a, -1, n) in Python 3.8+. Implementation is equivalent - to long_invmod() in CPython. + Warning: the output might be negative! See invmod_positive(). """ b, c = 1, 0 while n: @@ -32,7 +31,10 @@ def invmod(a: int, n: int) -> int: raise ValueError("Not invertible") def invmod_positive(a: int, n: int) -> int: - """Return a non-negative inverse of a to modulo n.""" + """Return a non-negative inverse of a to modulo n. + + Equivalent to pow(a, -1, n) in Python 3.8+. + """ inv = invmod(a, n) return inv if inv >= 0 else inv + n From 319641a7ee8b99536b245e1486792a26ea67e8b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Mon, 28 Jul 2025 23:42:14 +0200 Subject: [PATCH 03/28] Improve safety of a test helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In case parsing fails and we free the pointer, we should also set it to NULL. Otherwise, we get a double-free when the caller tries to free it as well. This can only happen with bad test data, but it tripped me up during development. (I got bad test data because bignum_common.invmod() sometimes returned a negative value, which was a surprise to me, see previous commit.) Signed-off-by: Manuel Pégourié-Gonnard --- tests/src/bignum_helpers.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/src/bignum_helpers.c b/tests/src/bignum_helpers.c index 913f5e387..f6d9f4dea 100644 --- a/tests/src/bignum_helpers.c +++ b/tests/src/bignum_helpers.c @@ -71,6 +71,7 @@ int mbedtls_test_read_mpi_core(mbedtls_mpi_uint **pX, size_t *plimbs, exit: mbedtls_free(*pX); + *pX = NULL; return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; } From a93d353b1eb32c91caf3b50c2f2f41de99b3c0a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Mon, 28 Jul 2025 23:42:15 +0200 Subject: [PATCH 04/28] GCD-modinv tests: improve descriptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Manuel Pégourié-Gonnard --- scripts/mbedtls_framework/bignum_core.py | 56 ++++++++++++++---------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/scripts/mbedtls_framework/bignum_core.py b/scripts/mbedtls_framework/bignum_core.py index 93de3e030..1ecc5f3b1 100644 --- a/scripts/mbedtls_framework/bignum_core.py +++ b/scripts/mbedtls_framework/bignum_core.py @@ -904,30 +904,39 @@ class BignumCoreGcdModinvOdd(BignumCoreTarget, test_data_generation.BaseTest): # X * 2, X * 3 is so that we get GCD(X*2, X*3) = X where the GCD has a # the same order of magnitude as the inputs (all other cases give a - # single-limb GCD). + # single-limb GCD, except when A == N). DATA = ( - 0, 1, 2, 3, 4, 5, 6, 7, - int(bignum_data.SAFE_PRIME_192_BIT_SEED_1, 16), - int(bignum_data.SAFE_PRIME_192_BIT_SEED_1, 16) * 2, - int(bignum_data.SAFE_PRIME_192_BIT_SEED_1, 16) * 3, - int(bignum_data.RANDOM_192_BIT_SEED_2_NO1, 16), - int(bignum_data.RANDOM_192_BIT_SEED_2_NO2, 16), - int(bignum_data.RANDOM_192_BIT_SEED_2_NO3, 16), - int(bignum_data.RANDOM_192_BIT_SEED_2_NO4, 16), - int(bignum_data.RANDOM_192_BIT_SEED_2_NO9, 16), - int(bignum_data.SAFE_PRIME_1024_BIT_SEED_3, 16), - int(bignum_data.RANDOM_1024_BIT_SEED_4_NO1, 16), - int(bignum_data.RANDOM_1024_BIT_SEED_4_NO2, 16), - int(bignum_data.RANDOM_1024_BIT_SEED_4_NO3, 16), - int(bignum_data.RANDOM_1024_BIT_SEED_4_NO4, 16), - int(bignum_data.RANDOM_1024_BIT_SEED_4_NO5, 16), - int(bignum_data.RANDOM_1024_BIT_SEED_4_NO5, 16) * 2, - int(bignum_data.RANDOM_1024_BIT_SEED_4_NO5, 16) * 3, + ("0", 0), + ("1", 1), + ("2", 2), + ("3", 3), + ("4", 4), + ("5", 5), + ("6", 6), + ("7", 7), + ("prime192[1]", int(bignum_data.SAFE_PRIME_192_BIT_SEED_1, 16)), + ("prime192[1] * 2", int(bignum_data.SAFE_PRIME_192_BIT_SEED_1, 16) * 2), + ("prime192[1] * 3", int(bignum_data.SAFE_PRIME_192_BIT_SEED_1, 16) * 3), + ("rand192[2.1]", int(bignum_data.RANDOM_192_BIT_SEED_2_NO1, 16)), + ("rand192[2.2]", int(bignum_data.RANDOM_192_BIT_SEED_2_NO2, 16)), + ("rand192[2.3]", int(bignum_data.RANDOM_192_BIT_SEED_2_NO3, 16)), + ("rand192[2.4]", int(bignum_data.RANDOM_192_BIT_SEED_2_NO4, 16)), + ("rand192[2.9]", int(bignum_data.RANDOM_192_BIT_SEED_2_NO9, 16)), + ("prime1024[3]", int(bignum_data.SAFE_PRIME_1024_BIT_SEED_3, 16)), + ("rand1024[4.1]", int(bignum_data.RANDOM_1024_BIT_SEED_4_NO1, 16)), + ("rand1024[4.2]", int(bignum_data.RANDOM_1024_BIT_SEED_4_NO2, 16)), + ("rand1024[4.3]", int(bignum_data.RANDOM_1024_BIT_SEED_4_NO3, 16)), + ("rand1024[4.4]", int(bignum_data.RANDOM_1024_BIT_SEED_4_NO4, 16)), + ("rand1024[4.5]", int(bignum_data.RANDOM_1024_BIT_SEED_4_NO5, 16)), + ("rand1024[4.5] * 2", int(bignum_data.RANDOM_1024_BIT_SEED_4_NO5, 16) * 2), + ("rand1024[4.5] * 3", int(bignum_data.RANDOM_1024_BIT_SEED_4_NO5, 16) * 3), ) - def __init__(self, a: int, n: int, g: int, i: int) -> None: + def __init__(self, a: int, a_desc: str, n: int, n_desc: str, g: int, i: int) -> None: self.a_val = a + self.a_desc = a_desc self.n_val = n + self.n_desc = n_desc self.g_val = g self.i_val = i @@ -942,12 +951,15 @@ class BignumCoreGcdModinvOdd(BignumCoreTarget, test_data_generation.BaseTest): i_str = bignum_common.quote_str("") return [a_str, n_str, g_str, i_str] + def description(self) -> str: + return f"GCD-modinv, A = {self.a_desc}, N = {self.n_desc}" + @classmethod def generate_function_tests(cls) -> Iterator[test_case.TestCase]: - for n in cls.DATA: + for n_desc, n in cls.DATA: if n % 2 == 0: continue - for a in cls.DATA: + for a_desc, a in cls.DATA: if a > n: continue g = math.gcd(a, n) @@ -955,4 +967,4 @@ class BignumCoreGcdModinvOdd(BignumCoreTarget, test_data_generation.BaseTest): i = bignum_common.invmod_positive(a, n) else: i = 0 - yield cls(a, n, g, i).create_test_case() + yield cls(a, a_desc, n, n_desc, g, i).create_test_case() From 44a653c3c87a14a80fbd9e05baa177211b13f8cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Mon, 28 Jul 2025 23:42:15 +0200 Subject: [PATCH 05/28] GCD-modinv tests: include (close to) 2^n values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Manuel Pégourié-Gonnard --- scripts/mbedtls_framework/bignum_core.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/scripts/mbedtls_framework/bignum_core.py b/scripts/mbedtls_framework/bignum_core.py index 1ecc5f3b1..40427b781 100644 --- a/scripts/mbedtls_framework/bignum_core.py +++ b/scripts/mbedtls_framework/bignum_core.py @@ -902,9 +902,13 @@ class BignumCoreGcdModinvOdd(BignumCoreTarget, test_data_generation.BaseTest): test_function = "mpi_core_gcd_modinv_odd" test_name = "mpi_core_gcd_modinv_odd" - # X * 2, X * 3 is so that we get GCD(X*2, X*3) = X where the GCD has a - # the same order of magnitude as the inputs (all other cases give a - # single-limb GCD, except when A == N). + # - All small integers because that naturally covers a lot of cases. + # - (Close to) powers of 2 because that looks like interesting values. + # Also covers the cases where N, N+1 or N-1 is a multiple of A (with + # multiple limbs). + # - X * 2, X * 3 is so that we get GCD(X*2, X*3) = X where the GCD has a + # the same order of magnitude as the inputs. + # - Random values of cryptographic size for good measure. DATA = ( ("0", 0), ("1", 1), @@ -914,6 +918,12 @@ class BignumCoreGcdModinvOdd(BignumCoreTarget, test_data_generation.BaseTest): ("5", 5), ("6", 6), ("7", 7), + ("2^64 - 1", 2**64 - 1), + ("2^64", 2**64), + ("2^64 + 1", 2**64 + 1), + ("2^128 - 1", 2**128 - 1), + ("2^128", 2**128), + ("2^128 + 1", 2**128 + 1), ("prime192[1]", int(bignum_data.SAFE_PRIME_192_BIT_SEED_1, 16)), ("prime192[1] * 2", int(bignum_data.SAFE_PRIME_192_BIT_SEED_1, 16) * 2), ("prime192[1] * 3", int(bignum_data.SAFE_PRIME_192_BIT_SEED_1, 16) * 3), From 09489a1a0a0018a7ad99e77b3d93c7389831c59d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Mon, 28 Jul 2025 23:42:15 +0200 Subject: [PATCH 06/28] GCD-modinv tests: simplify arguments() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We no longer need zfill() as the test function has become less picky. Signed-off-by: Manuel Pégourié-Gonnard --- scripts/mbedtls_framework/bignum_core.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/scripts/mbedtls_framework/bignum_core.py b/scripts/mbedtls_framework/bignum_core.py index 40427b781..4254427a5 100644 --- a/scripts/mbedtls_framework/bignum_core.py +++ b/scripts/mbedtls_framework/bignum_core.py @@ -951,15 +951,11 @@ class BignumCoreGcdModinvOdd(BignumCoreTarget, test_data_generation.BaseTest): self.i_val = i def arguments(self) -> List[str]: - a_str = bignum_common.quote_str(f"{self.a_val:x}") - n_str = bignum_common.quote_str(f"{self.n_val:x}") - n_len = len(n_str) - 2 # quotes - g_str = bignum_common.quote_str(f"{self.g_val:x}".zfill(n_len)) - if self.i_val != 0: - i_str = bignum_common.quote_str(f"{self.i_val:x}".zfill(n_len)) - else: - i_str = bignum_common.quote_str("") - return [a_str, n_str, g_str, i_str] + a_str = f"{self.a_val:x}" + n_str = f"{self.n_val:x}" + g_str = f"{self.g_val:x}" + i_str = f"{self.i_val:x}" if self.i_val != 0 else "" + return [bignum_common.quote_str(s) for s in (a_str, n_str, g_str, i_str)] def description(self) -> str: return f"GCD-modinv, A = {self.a_desc}, N = {self.n_desc}" From af84b28c9ec8299863452ba5ecd0ee550621e18f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Mon, 28 Jul 2025 23:42:15 +0200 Subject: [PATCH 07/28] GCD-modinv tests: centralize things in __init__() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit That way generate_function_tests() only needs to filter test data. Signed-off-by: Manuel Pégourié-Gonnard --- scripts/mbedtls_framework/bignum_core.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/scripts/mbedtls_framework/bignum_core.py b/scripts/mbedtls_framework/bignum_core.py index 4254427a5..0481d7579 100644 --- a/scripts/mbedtls_framework/bignum_core.py +++ b/scripts/mbedtls_framework/bignum_core.py @@ -942,19 +942,19 @@ class BignumCoreGcdModinvOdd(BignumCoreTarget, test_data_generation.BaseTest): ("rand1024[4.5] * 3", int(bignum_data.RANDOM_1024_BIT_SEED_4_NO5, 16) * 3), ) - def __init__(self, a: int, a_desc: str, n: int, n_desc: str, g: int, i: int) -> None: + def __init__(self, a: int, a_desc: str, n: int, n_desc: str) -> None: self.a_val = a self.a_desc = a_desc self.n_val = n self.n_desc = n_desc - self.g_val = g - self.i_val = i + self.g_val = g = math.gcd(a, n) + self.i_val = bignum_common.invmod_positive(a, n) if g == 1 else None def arguments(self) -> List[str]: a_str = f"{self.a_val:x}" n_str = f"{self.n_val:x}" g_str = f"{self.g_val:x}" - i_str = f"{self.i_val:x}" if self.i_val != 0 else "" + i_str = f"{self.i_val:x}" if self.i_val is not None else "" return [bignum_common.quote_str(s) for s in (a_str, n_str, g_str, i_str)] def description(self) -> str: @@ -968,9 +968,4 @@ class BignumCoreGcdModinvOdd(BignumCoreTarget, test_data_generation.BaseTest): for a_desc, a in cls.DATA: if a > n: continue - g = math.gcd(a, n) - if g == 1: - i = bignum_common.invmod_positive(a, n) - else: - i = 0 - yield cls(a, a_desc, n, n_desc, g, i).create_test_case() + yield cls(a, a_desc, n, n_desc).create_test_case() From ead52d56b21f23e44b74bff67f6be11d5a71ad88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Mon, 28 Jul 2025 23:42:15 +0200 Subject: [PATCH 08/28] GCD-modinv tests: adapt to new pre-condition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function does not compute inverses mod 1 Signed-off-by: Manuel Pégourié-Gonnard --- scripts/mbedtls_framework/bignum_core.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/mbedtls_framework/bignum_core.py b/scripts/mbedtls_framework/bignum_core.py index 0481d7579..3f798fc3c 100644 --- a/scripts/mbedtls_framework/bignum_core.py +++ b/scripts/mbedtls_framework/bignum_core.py @@ -947,8 +947,9 @@ class BignumCoreGcdModinvOdd(BignumCoreTarget, test_data_generation.BaseTest): self.a_desc = a_desc self.n_val = n self.n_desc = n_desc - self.g_val = g = math.gcd(a, n) - self.i_val = bignum_common.invmod_positive(a, n) if g == 1 else None + self.g_val = math.gcd(a, n) + test_i = self.g_val == 1 and self.n_val != 1 + self.i_val = bignum_common.invmod_positive(a, n) if test_i else None def arguments(self) -> List[str]: a_str = f"{self.a_val:x}" From f29e0a5846c36b413d1d1ac95a83a6cf7ed6f444 Mon Sep 17 00:00:00 2001 From: Felix Conway Date: Thu, 24 Jul 2025 15:22:40 +0100 Subject: [PATCH 09/28] Add generated tests for mbedtls_mpi_inv_mod() Signed-off-by: Felix Conway --- scripts/generate_bignum_tests.py | 59 +++++++++++++++++++++- scripts/mbedtls_framework/bignum_common.py | 4 ++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/scripts/generate_bignum_tests.py b/scripts/generate_bignum_tests.py index 68ad42f48..1ace79ce7 100755 --- a/scripts/generate_bignum_tests.py +++ b/scripts/generate_bignum_tests.py @@ -45,7 +45,7 @@ of BaseTarget in test_data_generation.py. import sys from abc import ABCMeta -from typing import List +from typing import List, Any from mbedtls_framework import test_data_generation from mbedtls_framework import bignum_common @@ -152,6 +152,63 @@ class BignumCmpAbs(BignumCmp): super().__init__(val_a.strip("-"), val_b.strip("-")) +class BignumInvMod(BignumOperation): + """Test cases for bignum modular inverse.""" + count = 0 + symbol = "inversed, modulo" + test_function = "mpi_inv_mod" + test_name = "MPI test inv_mod" + # The default values are not very useful here, so clear them. + input_values = [] # type: List[Any] + input_cases = bignum_common.combination_two_lists( + # Input values for A + [ + "aa4df5cb14b4c31237f98bd1faf527c283c2d0f3eec89718664ba33f9762907c", + "2ec0888f", + "22fbdf4c", + "32cf9a75", + "-aa4df5cb14b4c31237f98bd1faf527c283c2d0f3eec89718664ba33f9762907c", + "-2ec0888f", + "-22fbdf4c", + "-32cf9a75", + ], + # Input values for N + [ + "fffbbd660b94412ae61ead9c2906a344116e316a256fd387874c6c675b1d587d", + "34d0830", + "364b6729", + "14419cd", + "2ec0888f", + ], + ) + + def __init__(self, val_a: str, val_b: str) -> None: + super().__init__(val_a, val_b) + try: + self._result = bignum_common.invmod_positive(self.int_a, self.int_b) + except ValueError: + self._result = -1 # No modular inverse. + + def description_suffix(self) -> str: + suffix = ": " + if (self.int_a > self.int_b): + suffix += "A > N" + elif (self.int_a < self.int_b): + suffix += "A < N" + else: + suffix += "A == N" + if (self.int_a < 0): + suffix += ", A < 0" + if (self._result == -1): + suffix += ", No modular inverse" + return suffix + + def result(self) -> List[str]: + if (self._result == -1): # No modular inverse. + return [bignum_common.quote_str("0"), "MBEDTLS_ERR_MPI_NOT_ACCEPTABLE"] + return [bignum_common.quote_str("{:x}".format(self._result)), "0"] + + class BignumAdd(BignumOperation): """Test cases for bignum value addition.""" count = 0 diff --git a/scripts/mbedtls_framework/bignum_common.py b/scripts/mbedtls_framework/bignum_common.py index 28143ce0f..58a76a171 100644 --- a/scripts/mbedtls_framework/bignum_common.py +++ b/scripts/mbedtls_framework/bignum_common.py @@ -68,6 +68,10 @@ def combination_pairs(values: List[T]) -> List[Tuple[T, T]]: """Return all pair combinations from input values.""" return [(x, y) for x in values for y in values] +def combination_two_lists(first_vals: List[T], second_vals: List[T]) -> List[Tuple[T, T]]: + """Return all pair combinations from two input lists""" + return [(x, y) for x in first_vals for y in second_vals] + def bits_to_limbs(bits: int, bits_in_limb: int) -> int: """ Return the appropriate ammount of limbs needed to store a number contained in input bits""" From 05953f3a1bdc89d292f88895891b4b6450f1aa0e Mon Sep 17 00:00:00 2001 From: Felix Conway Date: Thu, 24 Jul 2025 16:18:48 +0100 Subject: [PATCH 10/28] Fix pylint complaints Signed-off-by: Felix Conway --- scripts/generate_bignum_tests.py | 36 ++++++++++++++++---------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/scripts/generate_bignum_tests.py b/scripts/generate_bignum_tests.py index 1ace79ce7..e9fcfaec8 100755 --- a/scripts/generate_bignum_tests.py +++ b/scripts/generate_bignum_tests.py @@ -163,22 +163,22 @@ class BignumInvMod(BignumOperation): input_cases = bignum_common.combination_two_lists( # Input values for A [ - "aa4df5cb14b4c31237f98bd1faf527c283c2d0f3eec89718664ba33f9762907c", - "2ec0888f", - "22fbdf4c", - "32cf9a75", - "-aa4df5cb14b4c31237f98bd1faf527c283c2d0f3eec89718664ba33f9762907c", - "-2ec0888f", - "-22fbdf4c", - "-32cf9a75", + "aa4df5cb14b4c31237f98bd1faf527c283c2d0f3eec89718664ba33f9762907c", + "2ec0888f", + "22fbdf4c", + "32cf9a75", + "-aa4df5cb14b4c31237f98bd1faf527c283c2d0f3eec89718664ba33f9762907c", + "-2ec0888f", + "-22fbdf4c", + "-32cf9a75", ], # Input values for N [ - "fffbbd660b94412ae61ead9c2906a344116e316a256fd387874c6c675b1d587d", - "34d0830", - "364b6729", - "14419cd", - "2ec0888f", + "fffbbd660b94412ae61ead9c2906a344116e316a256fd387874c6c675b1d587d", + "34d0830", + "364b6729", + "14419cd", + "2ec0888f", ], ) @@ -191,20 +191,20 @@ class BignumInvMod(BignumOperation): def description_suffix(self) -> str: suffix = ": " - if (self.int_a > self.int_b): + if self.int_a > self.int_b: suffix += "A > N" - elif (self.int_a < self.int_b): + elif self.int_a < self.int_b: suffix += "A < N" else: suffix += "A == N" - if (self.int_a < 0): + if self.int_a < 0: suffix += ", A < 0" - if (self._result == -1): + if self._result == -1: suffix += ", No modular inverse" return suffix def result(self) -> List[str]: - if (self._result == -1): # No modular inverse. + if self._result == -1: # No modular inverse. return [bignum_common.quote_str("0"), "MBEDTLS_ERR_MPI_NOT_ACCEPTABLE"] return [bignum_common.quote_str("{:x}".format(self._result)), "0"] From 4110e6580a777bd98571c95282433df62247ea57 Mon Sep 17 00:00:00 2001 From: Felix Conway Date: Mon, 28 Jul 2025 10:31:09 +0100 Subject: [PATCH 11/28] Fix python types Signed-off-by: Felix Conway --- scripts/generate_bignum_tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/generate_bignum_tests.py b/scripts/generate_bignum_tests.py index e9fcfaec8..46e14fa8f 100755 --- a/scripts/generate_bignum_tests.py +++ b/scripts/generate_bignum_tests.py @@ -45,7 +45,7 @@ of BaseTarget in test_data_generation.py. import sys from abc import ABCMeta -from typing import List, Any +from typing import List from mbedtls_framework import test_data_generation from mbedtls_framework import bignum_common @@ -159,7 +159,7 @@ class BignumInvMod(BignumOperation): test_function = "mpi_inv_mod" test_name = "MPI test inv_mod" # The default values are not very useful here, so clear them. - input_values = [] # type: List[Any] + input_values = [] # type: List[str] input_cases = bignum_common.combination_two_lists( # Input values for A [ From 7a0db96337ee982ff40367808533dd07befe28e9 Mon Sep 17 00:00:00 2001 From: Felix Conway Date: Mon, 28 Jul 2025 10:45:30 +0100 Subject: [PATCH 12/28] Condense and improve test reporting comments Signed-off-by: Felix Conway --- scripts/generate_bignum_tests.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/scripts/generate_bignum_tests.py b/scripts/generate_bignum_tests.py index 46e14fa8f..f65125a69 100755 --- a/scripts/generate_bignum_tests.py +++ b/scripts/generate_bignum_tests.py @@ -155,9 +155,9 @@ class BignumCmpAbs(BignumCmp): class BignumInvMod(BignumOperation): """Test cases for bignum modular inverse.""" count = 0 - symbol = "inversed, modulo" + symbol = "^-1 mod" test_function = "mpi_inv_mod" - test_name = "MPI test inv_mod" + test_name = "MPI inv_mod" # The default values are not very useful here, so clear them. input_values = [] # type: List[str] input_cases = bignum_common.combination_two_lists( @@ -172,7 +172,7 @@ class BignumInvMod(BignumOperation): "-22fbdf4c", "-32cf9a75", ], - # Input values for N + # Input values for N - must be positive. [ "fffbbd660b94412ae61ead9c2906a344116e316a256fd387874c6c675b1d587d", "34d0830", @@ -191,16 +191,20 @@ class BignumInvMod(BignumOperation): def description_suffix(self) -> str: suffix = ": " - if self.int_a > self.int_b: - suffix += "A > N" - elif self.int_a < self.int_b: - suffix += "A < N" + # Assuming N (int_b) is always positive, compare absolute values, + # but only print the absolute value bars when A is negative. + a_str = "A" if (self.int_a >= 0) else "|A|" + n_str = "N" if (self.int_a >= 0) else "|N|" + if abs(self.int_a) > self.int_b: + suffix += f"{a_str}>{n_str}" + elif abs(self.int_a) < self.int_b: + suffix += f"{a_str}<{n_str}" else: - suffix += "A == N" + suffix += f"{a_str}=={n_str}" if self.int_a < 0: - suffix += ", A < 0" + suffix += ", A<0" if self._result == -1: - suffix += ", No modular inverse" + suffix += ", no inverse" return suffix def result(self) -> List[str]: From cd3f8dad0416d7f7640cbaa0c6f8c7db9a64c10f Mon Sep 17 00:00:00 2001 From: Felix Conway Date: Mon, 28 Jul 2025 13:50:23 +0100 Subject: [PATCH 13/28] Adjust test comments Signed-off-by: Felix Conway --- scripts/generate_bignum_tests.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/scripts/generate_bignum_tests.py b/scripts/generate_bignum_tests.py index f65125a69..44782b5f5 100755 --- a/scripts/generate_bignum_tests.py +++ b/scripts/generate_bignum_tests.py @@ -194,13 +194,12 @@ class BignumInvMod(BignumOperation): # Assuming N (int_b) is always positive, compare absolute values, # but only print the absolute value bars when A is negative. a_str = "A" if (self.int_a >= 0) else "|A|" - n_str = "N" if (self.int_a >= 0) else "|N|" if abs(self.int_a) > self.int_b: - suffix += f"{a_str}>{n_str}" + suffix += f"{a_str}>N" elif abs(self.int_a) < self.int_b: - suffix += f"{a_str}<{n_str}" + suffix += f"{a_str} Date: Mon, 28 Jul 2025 16:25:06 +0100 Subject: [PATCH 14/28] Add generated tests for mpi_gcd() Signed-off-by: Felix Conway --- scripts/generate_bignum_tests.py | 66 ++++++++++++++++++++++ scripts/mbedtls_framework/bignum_common.py | 7 +++ 2 files changed, 73 insertions(+) diff --git a/scripts/generate_bignum_tests.py b/scripts/generate_bignum_tests.py index 44782b5f5..c84794f91 100755 --- a/scripts/generate_bignum_tests.py +++ b/scripts/generate_bignum_tests.py @@ -212,6 +212,72 @@ class BignumInvMod(BignumOperation): return [bignum_common.quote_str("{:x}".format(self._result)), "0"] +class BignumGCD(BignumOperation): + """Test cases for greatest common divisor.""" + count = 0 + symbol = "GCD" + test_function = "mpi_gcd" + test_name = "GCD" + # The default values are not very useful here, so clear them. + input_values = [] # type: List[str] + input_cases = bignum_common.combination_two_lists( + # Input values for A + [ + "3c094fd6b36ee4902c8ba84d13a401def90a2130116dad3361", + "-3c094fd6b36ee4902c8ba84d13a401def90a2130116dad3361", + "b2b06ebe14a185a83d5d2d7bddd1dd0e05e800d6b914fbed4e", + "-b2b06ebe14a185a83d5d2d7bddd1dd0e05e800d6b914fbed4e", + "203265b387", + "-203265b387", + "9bc8e63852", + "-9bc8e63852", + "123456789abcdef", + "-123456789abcdef", + "50000", + "-50000", + ], + # Input values for B + [ + "63522ef00b3fa4c6773f1116dca45c121ee51b722644d96b63", + "-63522ef00b3fa4c6773f1116dca45c121ee51b722644d96b63", + "20902ee9da48664a3b9d5d68542405ac0ad0dd90ce79d633c0", + "-20902ee9da48664a3b9d5d68542405ac0ad0dd90ce79d633c0", + "7e9de225bb", + "-7e9de225bb", + "4796a42828", + "-4796a42828", + "123456789abcdef", + "-123456789abcdef", + "50000", + "-50000", + ], + ) + + def __init__(self, val_a: str, val_b: str) -> None: + super().__init__(val_a, val_b) + # We always expect a positive result. + self._result = abs(bignum_common.gcd(self.int_a, self.int_b)) + + def description_suffix(self) -> str: + suffix = ": " + if abs(self.int_a) > abs(self.int_b): + suffix += "|A|>|B|" + elif abs(self.int_a) < abs(self.int_b): + suffix += "|A|<|B|" + else: + suffix += "|A|=|B|" + if self.int_a < 0: + suffix += ", A<0" + if self.int_b < 0: + suffix += ", B<0" + suffix += ", A even" if (self.int_a % 2 == 0) else ", A odd" + suffix += ", B even" if (self.int_b % 2 == 0) else ", B odd" + return suffix + + def result(self) -> List[str]: + return [bignum_common.quote_str("{:x}".format(self._result))] + + class BignumAdd(BignumOperation): """Test cases for bignum value addition.""" count = 0 diff --git a/scripts/mbedtls_framework/bignum_common.py b/scripts/mbedtls_framework/bignum_common.py index 58a76a171..8bc6a9dd3 100644 --- a/scripts/mbedtls_framework/bignum_common.py +++ b/scripts/mbedtls_framework/bignum_common.py @@ -30,6 +30,13 @@ def invmod(a: int, n: int) -> int: return b raise ValueError("Not invertible") +def gcd(a: int, b: int) -> int: + """Return the greatest common divisor of two numbers. + The result will match the sign of b.""" + while b: + a, b = b, a % b + return a + def invmod_positive(a: int, n: int) -> int: """Return a non-negative inverse of a to modulo n. From 2b4e41d37f983ba4f4553804f7216cebd3c04d33 Mon Sep 17 00:00:00 2001 From: Felix Conway Date: Tue, 29 Jul 2025 12:02:42 +0100 Subject: [PATCH 15/28] Improve invmod & gcd generated test data Signed-off-by: Felix Conway --- scripts/generate_bignum_tests.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/scripts/generate_bignum_tests.py b/scripts/generate_bignum_tests.py index c84794f91..17ee1bfde 100755 --- a/scripts/generate_bignum_tests.py +++ b/scripts/generate_bignum_tests.py @@ -164,10 +164,12 @@ class BignumInvMod(BignumOperation): # Input values for A [ "aa4df5cb14b4c31237f98bd1faf527c283c2d0f3eec89718664ba33f9762907c", + "f847e7731a2687c837f6b825f2937d997bf66814d3db79b27b", "2ec0888f", "22fbdf4c", "32cf9a75", "-aa4df5cb14b4c31237f98bd1faf527c283c2d0f3eec89718664ba33f9762907c", + "-f847e7731a2687c837f6b825f2937d997bf66814d3db79b27b", "-2ec0888f", "-22fbdf4c", "-32cf9a75", @@ -175,10 +177,12 @@ class BignumInvMod(BignumOperation): # Input values for N - must be positive. [ "fffbbd660b94412ae61ead9c2906a344116e316a256fd387874c6c675b1d587d", + "2fe72fa5c05bc14c1279e37e2701bd956822999f42c5cbe84", + "2ec0888f", + "22fbdf4c", "34d0830", "364b6729", "14419cd", - "2ec0888f", ], ) @@ -235,6 +239,8 @@ class BignumGCD(BignumOperation): "-123456789abcdef", "50000", "-50000", + "30000", + "-30000", ], # Input values for B [ @@ -250,6 +256,8 @@ class BignumGCD(BignumOperation): "-123456789abcdef", "50000", "-50000", + "30000", + "-30000", ], ) From ed5b14ee233de198ddd7e0c1952a7869e1ddbb9f Mon Sep 17 00:00:00 2001 From: Felix Conway Date: Tue, 29 Jul 2025 12:09:16 +0100 Subject: [PATCH 16/28] Use math.gcd() instead of custom function Signed-off-by: Felix Conway --- scripts/generate_bignum_tests.py | 7 ++++--- scripts/mbedtls_framework/bignum_common.py | 7 ------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/scripts/generate_bignum_tests.py b/scripts/generate_bignum_tests.py index 17ee1bfde..e363c2968 100755 --- a/scripts/generate_bignum_tests.py +++ b/scripts/generate_bignum_tests.py @@ -43,6 +43,7 @@ of BaseTarget in test_data_generation.py. # SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later import sys +import math from abc import ABCMeta from typing import List @@ -188,9 +189,9 @@ class BignumInvMod(BignumOperation): def __init__(self, val_a: str, val_b: str) -> None: super().__init__(val_a, val_b) - try: + if math.gcd(self.int_a, self.int_b) == 1: self._result = bignum_common.invmod_positive(self.int_a, self.int_b) - except ValueError: + else: self._result = -1 # No modular inverse. def description_suffix(self) -> str: @@ -264,7 +265,7 @@ class BignumGCD(BignumOperation): def __init__(self, val_a: str, val_b: str) -> None: super().__init__(val_a, val_b) # We always expect a positive result. - self._result = abs(bignum_common.gcd(self.int_a, self.int_b)) + self._result = math.gcd(self.int_a, self.int_b) def description_suffix(self) -> str: suffix = ": " diff --git a/scripts/mbedtls_framework/bignum_common.py b/scripts/mbedtls_framework/bignum_common.py index 8bc6a9dd3..58a76a171 100644 --- a/scripts/mbedtls_framework/bignum_common.py +++ b/scripts/mbedtls_framework/bignum_common.py @@ -30,13 +30,6 @@ def invmod(a: int, n: int) -> int: return b raise ValueError("Not invertible") -def gcd(a: int, b: int) -> int: - """Return the greatest common divisor of two numbers. - The result will match the sign of b.""" - while b: - a, b = b, a % b - return a - def invmod_positive(a: int, n: int) -> int: """Return a non-negative inverse of a to modulo n. From b812f2658b8b63a95d94d2a09c1e08d511838edd Mon Sep 17 00:00:00 2001 From: Felix Conway Date: Tue, 29 Jul 2025 15:37:24 +0100 Subject: [PATCH 17/28] Add more test data for GCD generated tests Signed-off-by: Felix Conway --- scripts/generate_bignum_tests.py | 67 +++++++++++++------------------- 1 file changed, 27 insertions(+), 40 deletions(-) diff --git a/scripts/generate_bignum_tests.py b/scripts/generate_bignum_tests.py index e363c2968..56ee8675f 100755 --- a/scripts/generate_bignum_tests.py +++ b/scripts/generate_bignum_tests.py @@ -223,49 +223,36 @@ class BignumGCD(BignumOperation): symbol = "GCD" test_function = "mpi_gcd" test_name = "GCD" - # The default values are not very useful here, so clear them. - input_values = [] # type: List[str] - input_cases = bignum_common.combination_two_lists( - # Input values for A - [ - "3c094fd6b36ee4902c8ba84d13a401def90a2130116dad3361", - "-3c094fd6b36ee4902c8ba84d13a401def90a2130116dad3361", - "b2b06ebe14a185a83d5d2d7bddd1dd0e05e800d6b914fbed4e", - "-b2b06ebe14a185a83d5d2d7bddd1dd0e05e800d6b914fbed4e", - "203265b387", - "-203265b387", - "9bc8e63852", - "-9bc8e63852", - "123456789abcdef", - "-123456789abcdef", - "50000", - "-50000", - "30000", - "-30000", - ], - # Input values for B - [ - "63522ef00b3fa4c6773f1116dca45c121ee51b722644d96b63", - "-63522ef00b3fa4c6773f1116dca45c121ee51b722644d96b63", - "20902ee9da48664a3b9d5d68542405ac0ad0dd90ce79d633c0", - "-20902ee9da48664a3b9d5d68542405ac0ad0dd90ce79d633c0", - "7e9de225bb", - "-7e9de225bb", - "4796a42828", - "-4796a42828", - "123456789abcdef", - "-123456789abcdef", - "50000", - "-50000", - "30000", - "-30000", - ], - ) + # The default values are not very useful here, so overwrite them. + input_values = [ + "3c094fd6b36ee4902c8ba84d13a401def90a2130116dad3361", + "-3c094fd6b36ee4902c8ba84d13a401def90a2130116dad3361", + "b2b06ebe14a185a83d5d2d7bddd1dd0e05e800d6b914fbed4e", + "-b2b06ebe14a185a83d5d2d7bddd1dd0e05e800d6b914fbed4e", + "203265b387", "-203265b387", + "9bc8e63852", "-9bc8e63852", + "100000000", "-100000000", + "300000000", "-300000000", + "500000000", "-500000000", + "50000", "-50000", + "30000", "-30000", + "1", "-1", + "2", "-2", + "3", "-3", + "00", "-00", + "", + ] def __init__(self, val_a: str, val_b: str) -> None: super().__init__(val_a, val_b) - # We always expect a positive result. - self._result = math.gcd(self.int_a, self.int_b) + # If the value of B is 0 (null or 0 with 1 limb), then the value + # returned by mbedtls_mpi_gcd() will be A, which may be negative, + # however the Python function will always return a positive value, + # so this needs to be accounted for. + if self.int_b == 0: + self._result = self.int_a + else: + self._result = math.gcd(self.int_a, self.int_b) def description_suffix(self) -> str: suffix = ": " From 89ca5ffe748177e545bcc516d784be8f635e0aef Mon Sep 17 00:00:00 2001 From: Felix Conway Date: Wed, 30 Jul 2025 09:51:17 +0100 Subject: [PATCH 18/28] Remove 0 from generated GCD test data Signed-off-by: Felix Conway --- scripts/generate_bignum_tests.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/generate_bignum_tests.py b/scripts/generate_bignum_tests.py index 56ee8675f..d2d5438aa 100755 --- a/scripts/generate_bignum_tests.py +++ b/scripts/generate_bignum_tests.py @@ -239,8 +239,6 @@ class BignumGCD(BignumOperation): "1", "-1", "2", "-2", "3", "-3", - "00", "-00", - "", ] def __init__(self, val_a: str, val_b: str) -> None: From 6341ceac6b09a4f6665849e10a0e78b43c948529 Mon Sep 17 00:00:00 2001 From: Felix Conway Date: Wed, 30 Jul 2025 09:55:05 +0100 Subject: [PATCH 19/28] Generate negative values in bignum tests Signed-off-by: Felix Conway --- scripts/generate_bignum_tests.py | 35 +++++++++------------- scripts/mbedtls_framework/bignum_common.py | 7 +++++ 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/scripts/generate_bignum_tests.py b/scripts/generate_bignum_tests.py index d2d5438aa..8b948665c 100755 --- a/scripts/generate_bignum_tests.py +++ b/scripts/generate_bignum_tests.py @@ -163,18 +163,13 @@ class BignumInvMod(BignumOperation): input_values = [] # type: List[str] input_cases = bignum_common.combination_two_lists( # Input values for A - [ + bignum_common.expand_list_negative([ "aa4df5cb14b4c31237f98bd1faf527c283c2d0f3eec89718664ba33f9762907c", "f847e7731a2687c837f6b825f2937d997bf66814d3db79b27b", "2ec0888f", "22fbdf4c", "32cf9a75", - "-aa4df5cb14b4c31237f98bd1faf527c283c2d0f3eec89718664ba33f9762907c", - "-f847e7731a2687c837f6b825f2937d997bf66814d3db79b27b", - "-2ec0888f", - "-22fbdf4c", - "-32cf9a75", - ], + ]), # Input values for N - must be positive. [ "fffbbd660b94412ae61ead9c2906a344116e316a256fd387874c6c675b1d587d", @@ -224,22 +219,20 @@ class BignumGCD(BignumOperation): test_function = "mpi_gcd" test_name = "GCD" # The default values are not very useful here, so overwrite them. - input_values = [ + input_values = bignum_common.expand_list_negative([ "3c094fd6b36ee4902c8ba84d13a401def90a2130116dad3361", - "-3c094fd6b36ee4902c8ba84d13a401def90a2130116dad3361", "b2b06ebe14a185a83d5d2d7bddd1dd0e05e800d6b914fbed4e", - "-b2b06ebe14a185a83d5d2d7bddd1dd0e05e800d6b914fbed4e", - "203265b387", "-203265b387", - "9bc8e63852", "-9bc8e63852", - "100000000", "-100000000", - "300000000", "-300000000", - "500000000", "-500000000", - "50000", "-50000", - "30000", "-30000", - "1", "-1", - "2", "-2", - "3", "-3", - ] + "203265b387", + "9bc8e63852", + "100000000", + "300000000", + "500000000", + "50000", + "30000", + "1", + "2", + "3", + ]) def __init__(self, val_a: str, val_b: str) -> None: super().__init__(val_a, val_b) diff --git a/scripts/mbedtls_framework/bignum_common.py b/scripts/mbedtls_framework/bignum_common.py index 58a76a171..7079cc780 100644 --- a/scripts/mbedtls_framework/bignum_common.py +++ b/scripts/mbedtls_framework/bignum_common.py @@ -72,6 +72,13 @@ def combination_two_lists(first_vals: List[T], second_vals: List[T]) -> List[Tup """Return all pair combinations from two input lists""" return [(x, y) for x in first_vals for y in second_vals] +def expand_list_negative(values: List[str]) -> List[str]: + """Adds the negative of every element in the list to the list""" + new_list = [] + for value in values: + new_list.extend([value, f"-{value}"]) + return new_list + def bits_to_limbs(bits: int, bits_in_limb: int) -> int: """ Return the appropriate ammount of limbs needed to store a number contained in input bits""" From e34d06dfbfd2cc90462dd80b666d5277f841b60d Mon Sep 17 00:00:00 2001 From: Felix Conway Date: Wed, 30 Jul 2025 10:32:58 +0100 Subject: [PATCH 20/28] Remove conditional now that 0 not in generated test data Signed-off-by: Felix Conway --- scripts/generate_bignum_tests.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/scripts/generate_bignum_tests.py b/scripts/generate_bignum_tests.py index 8b948665c..6a5a96afd 100755 --- a/scripts/generate_bignum_tests.py +++ b/scripts/generate_bignum_tests.py @@ -236,14 +236,9 @@ class BignumGCD(BignumOperation): def __init__(self, val_a: str, val_b: str) -> None: super().__init__(val_a, val_b) - # If the value of B is 0 (null or 0 with 1 limb), then the value - # returned by mbedtls_mpi_gcd() will be A, which may be negative, - # however the Python function will always return a positive value, - # so this needs to be accounted for. - if self.int_b == 0: - self._result = self.int_a - else: - self._result = math.gcd(self.int_a, self.int_b) + # We always expect as postivite result as the test data + # does not contain zero. + self._result = math.gcd(self.int_a, self.int_b) def description_suffix(self) -> str: suffix = ": " From 8c81b9f8dde641686a6d6cdf8b97735f14a18ef6 Mon Sep 17 00:00:00 2001 From: Felix Conway Date: Wed, 30 Jul 2025 11:27:12 +0100 Subject: [PATCH 21/28] Simplify negative list expansion Signed-off-by: Felix Conway --- scripts/mbedtls_framework/bignum_common.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/scripts/mbedtls_framework/bignum_common.py b/scripts/mbedtls_framework/bignum_common.py index 7079cc780..3cec3fd4d 100644 --- a/scripts/mbedtls_framework/bignum_common.py +++ b/scripts/mbedtls_framework/bignum_common.py @@ -74,10 +74,7 @@ def combination_two_lists(first_vals: List[T], second_vals: List[T]) -> List[Tup def expand_list_negative(values: List[str]) -> List[str]: """Adds the negative of every element in the list to the list""" - new_list = [] - for value in values: - new_list.extend([value, f"-{value}"]) - return new_list + return values + [f"-{value}" for value in values] def bits_to_limbs(bits: int, bits_in_limb: int) -> int: """ Return the appropriate ammount of limbs needed to store From 1e21fc7dc443a845794430ed0bdfbd788eb76472 Mon Sep 17 00:00:00 2001 From: Felix Conway Date: Wed, 30 Jul 2025 11:27:59 +0100 Subject: [PATCH 22/28] Fix spelling Signed-off-by: Felix Conway --- scripts/generate_bignum_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/generate_bignum_tests.py b/scripts/generate_bignum_tests.py index 6a5a96afd..74ad934a8 100755 --- a/scripts/generate_bignum_tests.py +++ b/scripts/generate_bignum_tests.py @@ -236,7 +236,7 @@ class BignumGCD(BignumOperation): def __init__(self, val_a: str, val_b: str) -> None: super().__init__(val_a, val_b) - # We always expect as postivite result as the test data + # We always expect a positive result as the test data # does not contain zero. self._result = math.gcd(self.int_a, self.int_b) From b1db2ba3746d1ccad7e85ed033ca17e1fb4ffdff Mon Sep 17 00:00:00 2001 From: Felix Conway Date: Mon, 4 Aug 2025 11:14:22 +0100 Subject: [PATCH 23/28] 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 24/28] 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 25/28] 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 26/28] 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 27/28] 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 28/28] 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):