Merge remote-tracking branch 'restricted/main-restricted' into framework-common

Signed-off-by: Minos Galanakis <minos.galanakis@arm.com>
This commit is contained in:
Minos Galanakis
2025-10-02 15:25:04 +01:00
4 changed files with 341 additions and 6 deletions
+248
View File
@@ -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
@@ -121,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 = [
"c79e27fc71c69a08b3e85bd48b9cd3be9aa8e2e56df39f4ed8",
"299dd34be98436729eb10f690f8d2bfc5bee21984b775e1e75",
"-ecbb3a4e986d488172ecd54f7bd71bd18050c4ed",
"7da9ec44f42e6311c56a",
"cdbcce3f763819345cfb",
"100000000", "300000000", "500000000",
"50000", "30000",
"1", "2", "3", "", "00", "-1"
]
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"),
]
def get_return_code_gcd_modinv_odd_gcd_only(self) -> str:
code = "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
def get_return_code_gcd_modinv_odd(self) -> str:
if self.int_b < 2:
return "MBEDTLS_ERR_MPI_BAD_INPUT_DATA"
return self.get_return_code_gcd_modinv_odd_gcd_only()
class BignumCmp(BignumOperation):
"""Test cases for bignum value comparison."""
count = 0
@@ -152,6 +218,188 @@ class BignumCmpAbs(BignumCmp):
super().__init__(val_a.strip("-"), val_b.strip("-"))
class BignumInvMod(BignumOperation):
"""Test cases for bignum modular inverse."""
count = 0
symbol = "^-1 mod"
test_function = "mpi_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(
# Input values for A
bignum_common.expand_list_negative([
"aa4df5cb14b4c31237f98bd1faf527c283c2d0f3eec89718664ba33f9762907c",
"f847e7731a2687c837f6b825f2937d997bf66814d3db79b27b",
"2ec0888f",
"22fbdf4c",
"32cf9a75",
]),
# Input values for N - must be positive.
[
"fffbbd660b94412ae61ead9c2906a344116e316a256fd387874c6c675b1d587d",
"2fe72fa5c05bc14c1279e37e2701bd956822999f42c5cbe84",
"2ec0888f",
"22fbdf4c",
"34d0830",
"364b6729",
"14419cd",
],
)
def __init__(self, val_a: str, val_b: str) -> None:
super().__init__(val_a, val_b)
if math.gcd(self.int_a, self.int_b) == 1:
self._result = bignum_common.invmod_positive(self.int_a, self.int_b)
else:
self._result = -1 # No modular inverse.
def description_suffix(self) -> str:
suffix = ": "
# 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|"
if abs(self.int_a) > self.int_b:
suffix += f"{a_str}>N"
elif abs(self.int_a) < self.int_b:
suffix += f"{a_str}<N"
else:
suffix += f"{a_str}=N"
if self.int_a < 0:
suffix += ", A<0"
if self._result == -1:
suffix += ", no 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 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 overwrite them.
input_values = bignum_common.expand_list_negative([
"3c094fd6b36ee4902c8ba84d13a401def90a2130116dad3361",
"b2b06ebe14a185a83d5d2d7bddd1dd0e05e800d6b914fbed4e",
"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)
# We always expect a positive 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 = ": "
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 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_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.
if self._result_gcd == 1 and self.int_b > 1:
self._result_invmod = \
bignum_common.invmod_positive(self.int_a, self.int_b) # type: int | None
else:
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 is None:
result_invmod = "no_inverse"
else:
result_invmod = "{:x}".format(self._result_invmod)
return [
self.format_result(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_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)
def result(self) -> List[str]:
return [self.format_result(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_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:
self._result_invmod = \
bignum_common.invmod_positive(self.int_a, self.int_b) # type: int | None
else:
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 is None:
return [bignum_common.quote_str("no_inverse"), self._result_code]
return [self.format_result(self._result_invmod), self._result_code]
class BignumAdd(BignumOperation):
"""Test cases for bignum value addition."""
count = 0
+13 -3
View File
@@ -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
@@ -66,6 +68,14 @@ 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 expand_list_negative(values: List[str]) -> List[str]:
"""Adds the negative of every element in the list to the 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
a number contained in input bits"""
+79 -3
View File
@@ -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,78 @@ 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"
# - 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),
("2", 2),
("3", 3),
("4", 4),
("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),
("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, 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 = 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}"
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 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:
return f"GCD-modinv, A = {self.a_desc}, N = {self.n_desc}"
@classmethod
def generate_function_tests(cls) -> Iterator[test_case.TestCase]:
for n_desc, n in cls.DATA:
if n % 2 == 0:
continue
for a_desc, a in cls.DATA:
if a > n:
continue
yield cls(a, a_desc, n, n_desc).create_test_case()
+1
View File
@@ -76,6 +76,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;
}