Merge pull request #322 from rohankl-arm/mldsa-899-framework

Added MLDSA sanity check and supported key sizes
This commit is contained in:
Gilles Peskine
2026-10-15 15:30:18 +02:00
committed by GitHub
7 changed files with 59 additions and 62 deletions
+21 -1
View File
@@ -8,7 +8,7 @@
import os
import inspect
import re
from typing import Optional
from typing import List, Optional
def looks_like_tf_psa_crypto_root(path: str) -> bool:
"""Whether the given directory looks like the root of the PSA Crypto source tree."""
@@ -148,3 +148,23 @@ def is_mbedtls_3_6() -> bool:
return False
with open(os.path.join(root, 'include', 'mbedtls', 'build_info.h'), 'r') as f:
return re.search(r"#define MBEDTLS_VERSION_NUMBER.*0x0306", f.read()) is not None
def public_include_path() -> List[str]:
"""Return the complete header search paths for the current source tree."""
includes = ['include']
if looks_like_tf_psa_crypto_root('.'):
includes.append('drivers/builtin/include')
includes.append('drivers/everest/include')
includes.append('drivers/everest/include/tf-psa-crypto/private/')
includes.append('drivers/pqcp/include')
elif looks_like_mbedtls_root('.'):
if not is_mbedtls_3_6():
includes.append('tf-psa-crypto/include')
includes.append('tf-psa-crypto/drivers/builtin/include')
includes.append('tf-psa-crypto/drivers/everest/include')
includes.append('tf-psa-crypto/drivers/everest/include/tf-psa-crypto/private/')
includes.append('tf-psa-crypto/drivers/pqcp/include')
else:
raise Exception('Failed to find Mbed TLS or TF-PSA-Crypto source tree.')
return includes
+11 -6
View File
@@ -11,6 +11,9 @@ import subprocess
import sys
import tempfile
from . import build_tree
class CompileError(Exception):
"""Exception to represent an error during the compilation."""
@@ -86,14 +89,18 @@ int main(void)
}
''')
def compile_c_file(c_filename, exe_filename, include_dirs):
def compile_c_file(c_filename, exe_filename, include_path=None):
"""Compile a C source file with the host compiler.
* ``c_filename``: the name of the source file to compile.
* ``exe_filename``: the name for the executable to be created.
* ``include_dirs``: a list of paths to include directories to be passed
with the -I switch.
* ``include_path``: an optional list of paths to include directories
to be passed with the -I switch.
"""
if include_path is None:
include_path = build_tree.public_include_path()
# Respect $HOSTCC if it is set
cc = os.getenv('HOSTCC', None)
if cc is None:
@@ -106,7 +113,7 @@ def compile_c_file(c_filename, exe_filename, include_dirs):
universal_newlines=True)
cc_is_msvc = 'Microsoft (R) C/C++' in proc.communicate()[1]
cmd += ['-I' + dir for dir in include_dirs]
cmd += ['-I' + dir for dir in include_path]
if cc_is_msvc:
# MSVC has deprecated using -o to specify the output file,
# and produces an object file in the working directory by default.
@@ -148,8 +155,6 @@ def get_c_expression_values(
Return the list of values of the ``expressions``.
"""
if include_path is None:
include_path = []
c_name = None
exe_name = None
obj_name = None
@@ -160,6 +160,8 @@ class KeyType:
'PSA_KEY_TYPE_RSA_KEY_PAIR': (1024, 1536), # small sample
'PSA_KEY_TYPE_SM4': (128,), # exhaustive
'PSA_KEY_TYPE_XCHACHA20': (256,), # exhaustive
'PSA_KEY_TYPE_ML_DSA_KEY_PAIR': (87,), # only size implemented so far
'PSA_KEY_TYPE_ML_DSA_PUBLIC_KEY': (87,), # only size implemented so far
} # type: Dict[str, Tuple[int, ...]]
def sizes_to_test(self) -> Tuple[int, ...]:
"""Return a tuple of key sizes to test.
+1 -21
View File
@@ -16,7 +16,6 @@ from typing import Dict, List, Optional, Set, Union
import unittest
from . import c_build_helper
from . import build_tree
class Expr:
@@ -41,31 +40,12 @@ class Expr:
def update_cache(self) -> None:
"""Update `value_cache` for expressions registered in `unknown_values`."""
expressions = sorted(self.unknown_values)
# Temporary, while Mbed TLS does not just rely on the TF-PSA-Crypto
# build system to build its crypto library. When it does, the first
# case can just be removed.
if build_tree.looks_like_root('.'):
includes = ['include']
if build_tree.looks_like_tf_psa_crypto_root('.'):
includes.append('drivers/builtin/include')
includes.append('drivers/everest/include')
includes.append('drivers/everest/include/tf-psa-crypto/private/')
includes.append('drivers/pqcp/include')
elif not build_tree.is_mbedtls_3_6():
includes.append('tf-psa-crypto/include')
includes.append('tf-psa-crypto/drivers/builtin/include')
includes.append('tf-psa-crypto/drivers/everest/include')
includes.append('tf-psa-crypto/drivers/everest/include/tf-psa-crypto/private/')
includes.append('tf-psa-crypto/drivers/pqcp/include')
values = c_build_helper.get_c_expression_values(
'unsigned long', '%lu',
expressions,
header="""
#include <psa/crypto.h>
""",
include_path=includes) #type: List[str]
""") #type: List[str]
for e, v in zip(expressions, values):
self.value_cache[e] = int(v, 0)
self.unknown_values.clear()
+10 -15
View File
@@ -164,20 +164,9 @@ else:
def main():
parser = argparse.ArgumentParser(description=globals()['__doc__'])
if build_tree.is_mbedtls_3_6():
parser.add_argument('--include', '-I',
action='append', default=['include'],
help='Directory for header files')
else:
parser.add_argument('--include', '-I',
action='append', default=['tf-psa-crypto/include',
'tf-psa-crypto/drivers/builtin/include',
'tf-psa-crypto/drivers/everest/include',
'tf-psa-crypto/drivers/everest/include/' +
'tf-psa-crypto/private',
'tf-psa-crypto/drivers/pqcp/include',
'include'],
help='Directory for header files')
parser.add_argument('--include', '-I',
action='append', default=build_tree.public_include_path(),
help='Directory for header files')
parser.add_argument('--keep-c',
action='store_true', dest='keep_c', default=False,
help='Keep the intermediate C file')
@@ -199,7 +188,13 @@ def main():
action='store_false', dest='show',
help='Don\'t show tested values (default)')
options = parser.parse_args()
headers = [os.path.join(options.include[0], h) for h in HEADERS]
headers = []
for header in HEADERS:
for directory in options.include:
header_path = os.path.join(directory, header)
if os.path.isfile(header_path):
headers.append(header_path)
break
inputs = gather_inputs(headers, TEST_SUITES)
tests = Tests(options)
tests.run_all(inputs)
+13 -19
View File
@@ -997,6 +997,14 @@ int mbedtls_test_psa_exported_key_sanity_check(
{
TEST_ASSERT(exported_length <= PSA_EXPORT_KEY_OUTPUT_SIZE(type, bits));
if (PSA_KEY_TYPE_IS_PUBLIC_KEY(type)) {
TEST_ASSERT(exported_length <=
PSA_EXPORT_PUBLIC_KEY_OUTPUT_SIZE(type, bits));
TEST_ASSERT(exported_length <= PSA_EXPORT_PUBLIC_KEY_MAX_SIZE);
} else if (PSA_KEY_TYPE_IS_KEY_PAIR(type)) {
TEST_ASSERT(exported_length <= PSA_EXPORT_KEY_PAIR_MAX_SIZE);
}
if (PSA_KEY_TYPE_IS_UNSTRUCTURED(type)) {
TEST_EQUAL(exported_length, PSA_BITS_TO_BYTES(bits));
} else
@@ -1052,16 +1060,12 @@ int mbedtls_test_psa_exported_key_sanity_check(
goto exit;
}
TEST_EQUAL(p - end, 0);
TEST_ASSERT(exported_length <= PSA_EXPORT_KEY_PAIR_MAX_SIZE);
} else
#endif /* MBEDTLS_ASN1_PARSE_C */
if (PSA_KEY_TYPE_IS_ECC_KEY_PAIR(type)) {
/* Just the secret value */
TEST_EQUAL(exported_length, PSA_BITS_TO_BYTES(bits));
TEST_ASSERT(exported_length <= PSA_EXPORT_KEY_PAIR_MAX_SIZE);
} else
#if defined(MBEDTLS_ASN1_PARSE_C)
@@ -1085,22 +1089,10 @@ int mbedtls_test_psa_exported_key_sanity_check(
goto exit;
}
TEST_EQUAL(p - end, 0);
TEST_ASSERT(exported_length <=
PSA_EXPORT_PUBLIC_KEY_OUTPUT_SIZE(type, bits));
TEST_ASSERT(exported_length <=
PSA_EXPORT_PUBLIC_KEY_MAX_SIZE);
} else
#endif /* MBEDTLS_ASN1_PARSE_C */
if (PSA_KEY_TYPE_IS_ECC_PUBLIC_KEY(type)) {
TEST_ASSERT(exported_length <=
PSA_EXPORT_PUBLIC_KEY_OUTPUT_SIZE(type, bits));
TEST_ASSERT(exported_length <=
PSA_EXPORT_PUBLIC_KEY_MAX_SIZE);
if (PSA_KEY_TYPE_ECC_GET_FAMILY(type) == PSA_ECC_FAMILY_MONTGOMERY) {
/* The representation of an ECC Montgomery public key is
* the raw compressed point */
@@ -1120,11 +1112,13 @@ int mbedtls_test_psa_exported_key_sanity_check(
TEST_EQUAL(exported[0], 4);
}
} else
if (PSA_KEY_TYPE_IS_DH_PUBLIC_KEY(type) || PSA_KEY_TYPE_IS_DH_KEY_PAIR(type)) {
if (PSA_KEY_TYPE_IS_DH_PUBLIC_KEY(type) || PSA_KEY_TYPE_IS_DH_KEY_PAIR(type)
#if defined(PSA_KEY_TYPE_IS_ML_DSA)
|| PSA_KEY_TYPE_IS_ML_DSA(type)
#endif
) {
TEST_ASSERT(exported_length ==
PSA_EXPORT_PUBLIC_KEY_OUTPUT_SIZE(type, bits));
TEST_ASSERT(exported_length <=
PSA_EXPORT_PUBLIC_KEY_MAX_SIZE);
} else {
(void) exported;
TEST_FAIL("Sanity check not implemented for this key type");
@@ -413,6 +413,7 @@ class DriverGenerator(Generator):
for kl in sorted(KEYS.keys()):
yield from self.gen_multipart(KEYS[kl][0])
class DispatchGenerator(DriverGenerator):
"""Test the driver dispatch layer."""