Merge branch 'development' into Remove__CHECK_PARAMS_option
This commit is contained in:
commit
0730cd5d9e
78 changed files with 6402 additions and 1229 deletions
|
@ -189,7 +189,6 @@ EXCLUDE_FROM_FULL = frozenset([
|
|||
'MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER', # incompatible with USE_PSA_CRYPTO
|
||||
'MBEDTLS_PSA_CRYPTO_SPM', # platform dependency (PSA SPM)
|
||||
'MBEDTLS_PSA_INJECT_ENTROPY', # build dependency (hook functions)
|
||||
'MBEDTLS_REMOVE_3DES_CIPHERSUITES', # removes a feature
|
||||
'MBEDTLS_RSA_NO_CRT', # influences the use of RSA in X.509 and TLS
|
||||
'MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN', # build dependency (clang+memsan)
|
||||
'MBEDTLS_TEST_CONSTANT_FLOW_VALGRIND', # build dependency (valgrind headers)
|
||||
|
|
249
scripts/ecp_comb_table.py
Executable file
249
scripts/ecp_comb_table.py
Executable file
|
@ -0,0 +1,249 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Purpose
|
||||
|
||||
This script dumps comb table of ec curve. When you add a new ec curve, you
|
||||
can use this script to generate codes to define `<curve>_T` in ecp_curves.c
|
||||
"""
|
||||
|
||||
# Copyright The Mbed TLS Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
HOW_TO_ADD_NEW_CURVE = """
|
||||
If you are trying to add new curve, you can follow these steps:
|
||||
|
||||
1. Define curve parameters (<curve>_p, <curve>_gx, etc...) in ecp_curves.c.
|
||||
2. Add a macro to define <curve>_T to NULL following these parameters.
|
||||
3. Build mbedcrypto
|
||||
4. Run this script with an argument of new curve
|
||||
5. Copy the output of this script into ecp_curves.c and replace the macro added
|
||||
in Step 2
|
||||
6. Rebuild and test if everything is ok
|
||||
|
||||
Replace the <curve> in the above with the name of the curve you want to add."""
|
||||
|
||||
CC = os.getenv('CC', 'cc')
|
||||
MBEDTLS_LIBRARY_PATH = os.getenv('MBEDTLS_LIBRARY_PATH', "library")
|
||||
|
||||
SRC_DUMP_COMB_TABLE = r'''
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "mbedtls/ecp.h"
|
||||
#include "mbedtls/error.h"
|
||||
|
||||
static void dump_mpi_initialize( const char *name, const mbedtls_mpi *d )
|
||||
{
|
||||
uint8_t buf[128] = {0};
|
||||
size_t olen;
|
||||
uint8_t *p;
|
||||
|
||||
olen = mbedtls_mpi_size( d );
|
||||
mbedtls_mpi_write_binary_le( d, buf, olen );
|
||||
printf("static const mbedtls_mpi_uint %s[] = {\n", name);
|
||||
for (p = buf; p < buf + olen; p += 8) {
|
||||
printf( " BYTES_TO_T_UINT_8( 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X ),\n",
|
||||
p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7] );
|
||||
}
|
||||
printf("};\n");
|
||||
}
|
||||
|
||||
static void dump_T( const mbedtls_ecp_group *grp )
|
||||
{
|
||||
char name[128];
|
||||
|
||||
printf( "#if MBEDTLS_ECP_FIXED_POINT_OPTIM == 1\n" );
|
||||
|
||||
for (size_t i = 0; i < grp->T_size; ++i) {
|
||||
snprintf( name, sizeof(name), "%s_T_%zu_X", CURVE_NAME, i );
|
||||
dump_mpi_initialize( name, &grp->T[i].X );
|
||||
|
||||
snprintf( name, sizeof(name), "%s_T_%zu_Y", CURVE_NAME, i );
|
||||
dump_mpi_initialize( name, &grp->T[i].Y );
|
||||
}
|
||||
printf( "static const mbedtls_ecp_point %s_T[%zu] = {\n", CURVE_NAME, grp->T_size );
|
||||
size_t olen;
|
||||
for (size_t i = 0; i < grp->T_size; ++i) {
|
||||
int z;
|
||||
if ( mbedtls_mpi_cmp_int(&grp->T[i].Z, 0) == 0 ) {
|
||||
z = 0;
|
||||
} else if ( mbedtls_mpi_cmp_int(&grp->T[i].Z, 1) == 0 ) {
|
||||
z = 1;
|
||||
} else {
|
||||
fprintf( stderr, "Unexpected value of Z (i = %d)\n", (int)i );
|
||||
exit( 1 );
|
||||
}
|
||||
printf( " ECP_POINT_INIT_XY_Z%d(%s_T_%zu_X, %s_T_%zu_Y),\n",
|
||||
z,
|
||||
CURVE_NAME, i,
|
||||
CURVE_NAME, i
|
||||
);
|
||||
}
|
||||
printf("};\n#endif\n\n");
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
int rc;
|
||||
mbedtls_mpi m;
|
||||
mbedtls_ecp_point R;
|
||||
mbedtls_ecp_group grp;
|
||||
|
||||
mbedtls_ecp_group_init( &grp );
|
||||
rc = mbedtls_ecp_group_load( &grp, CURVE_ID );
|
||||
if (rc != 0) {
|
||||
char buf[100];
|
||||
mbedtls_strerror( rc, buf, sizeof(buf) );
|
||||
fprintf( stderr, "mbedtls_ecp_group_load: %s (-0x%x)\n", buf, -rc );
|
||||
return 1;
|
||||
}
|
||||
grp.T = NULL;
|
||||
mbedtls_ecp_point_init( &R );
|
||||
mbedtls_mpi_init( &m);
|
||||
mbedtls_mpi_lset( &m, 1 );
|
||||
rc = mbedtls_ecp_mul( &grp, &R, &m, &grp.G, NULL, NULL );
|
||||
if ( rc != 0 ) {
|
||||
char buf[100];
|
||||
mbedtls_strerror( rc, buf, sizeof(buf) );
|
||||
fprintf( stderr, "mbedtls_ecp_mul: %s (-0x%x)\n", buf, -rc );
|
||||
return 1;
|
||||
}
|
||||
if ( grp.T == NULL ) {
|
||||
fprintf( stderr, "grp.T is not generated. Please make sure"
|
||||
"MBEDTLS_ECP_FIXED_POINT_OPTIM is enabled in config.h\n" );
|
||||
return 1;
|
||||
}
|
||||
dump_T( &grp );
|
||||
return 0;
|
||||
}
|
||||
'''
|
||||
|
||||
SRC_DUMP_KNOWN_CURVE = r'''
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "mbedtls/ecp.h"
|
||||
|
||||
int main() {
|
||||
const mbedtls_ecp_curve_info *info = mbedtls_ecp_curve_list();
|
||||
mbedtls_ecp_group grp;
|
||||
|
||||
mbedtls_ecp_group_init( &grp );
|
||||
while ( info->name != NULL ) {
|
||||
mbedtls_ecp_group_load( &grp, info->grp_id );
|
||||
if ( mbedtls_ecp_get_type(&grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS ) {
|
||||
printf( " %s", info->name );
|
||||
}
|
||||
info++;
|
||||
}
|
||||
printf( "\n" );
|
||||
return 0;
|
||||
}
|
||||
'''
|
||||
|
||||
|
||||
def join_src_path(*args):
|
||||
return os.path.normpath(os.path.join(os.path.dirname(__file__), "..", *args))
|
||||
|
||||
|
||||
def run_c_source(src, cflags):
|
||||
"""
|
||||
Compile and run C source code
|
||||
:param src: the c language code to run
|
||||
:param cflags: additional cflags passing to compiler
|
||||
:return:
|
||||
"""
|
||||
binname = tempfile.mktemp(prefix="mbedtls")
|
||||
fd, srcname = tempfile.mkstemp(prefix="mbedtls", suffix=".c")
|
||||
srcfile = os.fdopen(fd, mode="w")
|
||||
srcfile.write(src)
|
||||
srcfile.close()
|
||||
args = [CC,
|
||||
*cflags,
|
||||
'-I' + join_src_path("include"),
|
||||
"-o", binname,
|
||||
'-L' + MBEDTLS_LIBRARY_PATH,
|
||||
srcname,
|
||||
'-lmbedcrypto']
|
||||
|
||||
p = subprocess.run(args=args, check=False)
|
||||
if p.returncode != 0:
|
||||
return False
|
||||
p = subprocess.run(args=[binname], check=False, env={
|
||||
'LD_LIBRARY_PATH': MBEDTLS_LIBRARY_PATH
|
||||
})
|
||||
if p.returncode != 0:
|
||||
return False
|
||||
os.unlink(srcname)
|
||||
os.unlink(binname)
|
||||
return True
|
||||
|
||||
|
||||
def compute_curve(curve):
|
||||
"""compute comb table for curve"""
|
||||
r = run_c_source(
|
||||
SRC_DUMP_COMB_TABLE,
|
||||
[
|
||||
'-g',
|
||||
'-DCURVE_ID=MBEDTLS_ECP_DP_%s' % curve.upper(),
|
||||
'-DCURVE_NAME="%s"' % curve.lower(),
|
||||
])
|
||||
if not r:
|
||||
print("""\
|
||||
Unable to compile and run utility.""", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def usage():
|
||||
print("""
|
||||
Usage: python %s <curve>...
|
||||
|
||||
Arguments:
|
||||
curve Specify one or more curve names (e.g secp256r1)
|
||||
|
||||
All possible curves: """ % sys.argv[0])
|
||||
run_c_source(SRC_DUMP_KNOWN_CURVE, [])
|
||||
print("""
|
||||
Environment Variable:
|
||||
CC Specify which c compile to use to compile utility.
|
||||
MBEDTLS_LIBRARY_PATH
|
||||
Specify the path to mbedcrypto library. (e.g. build/library/)
|
||||
|
||||
How to add a new curve: %s""" % HOW_TO_ADD_NEW_CURVE)
|
||||
|
||||
|
||||
def run_main():
|
||||
shared_lib_path = os.path.normpath(os.path.join(MBEDTLS_LIBRARY_PATH, "libmbedcrypto.so"))
|
||||
static_lib_path = os.path.normpath(os.path.join(MBEDTLS_LIBRARY_PATH, "libmbedcrypto.a"))
|
||||
if not os.path.exists(shared_lib_path) and not os.path.exists(static_lib_path):
|
||||
print("Warning: both '%s' and '%s' are not exists. This script will use "
|
||||
"the library from your system instead of the library compiled by "
|
||||
"this source directory.\n"
|
||||
"You can specify library path using environment variable "
|
||||
"'MBEDTLS_LIBRARY_PATH'." % (shared_lib_path, static_lib_path),
|
||||
file=sys.stderr)
|
||||
|
||||
if len(sys.argv) <= 1:
|
||||
usage()
|
||||
else:
|
||||
for curve in sys.argv[1:]:
|
||||
compute_curve(curve)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run_main()
|
|
@ -33,7 +33,7 @@ class KeyType:
|
|||
`name` is a string 'PSA_KEY_TYPE_xxx' which is the name of a PSA key
|
||||
type macro. For key types that take arguments, the arguments can
|
||||
be passed either through the optional argument `params` or by
|
||||
passing an expression of the form 'PSA_KEY_TYPE_xxx(param1, param2)'
|
||||
passing an expression of the form 'PSA_KEY_TYPE_xxx(param1, ...)'
|
||||
in `name` as a string.
|
||||
"""
|
||||
|
||||
|
@ -48,7 +48,7 @@ class KeyType:
|
|||
m = re.match(r'(\w+)\s*\((.*)\)\Z', self.name)
|
||||
assert m is not None
|
||||
self.name = m.group(1)
|
||||
params = ','.split(m.group(2))
|
||||
params = m.group(2).split(',')
|
||||
self.params = (None if params is None else
|
||||
[param.strip() for param in params])
|
||||
"""The parameters of the key type, if there are any.
|
||||
|
|
|
@ -18,7 +18,55 @@
|
|||
|
||||
import itertools
|
||||
import re
|
||||
from typing import Dict, Iterable, Iterator, List, Set
|
||||
from typing import Dict, Iterable, Iterator, List, Optional, Pattern, Set, Tuple, Union
|
||||
|
||||
|
||||
class ReadFileLineException(Exception):
|
||||
def __init__(self, filename: str, line_number: Union[int, str]) -> None:
|
||||
message = 'in {} at {}'.format(filename, line_number)
|
||||
super(ReadFileLineException, self).__init__(message)
|
||||
self.filename = filename
|
||||
self.line_number = line_number
|
||||
|
||||
|
||||
class read_file_lines:
|
||||
# Dear Pylint, conventionally, a context manager class name is lowercase.
|
||||
# pylint: disable=invalid-name,too-few-public-methods
|
||||
"""Context manager to read a text file line by line.
|
||||
|
||||
```
|
||||
with read_file_lines(filename) as lines:
|
||||
for line in lines:
|
||||
process(line)
|
||||
```
|
||||
is equivalent to
|
||||
```
|
||||
with open(filename, 'r') as input_file:
|
||||
for line in input_file:
|
||||
process(line)
|
||||
```
|
||||
except that if process(line) raises an exception, then the read_file_lines
|
||||
snippet annotates the exception with the file name and line number.
|
||||
"""
|
||||
def __init__(self, filename: str, binary: bool = False) -> None:
|
||||
self.filename = filename
|
||||
self.line_number = 'entry' #type: Union[int, str]
|
||||
self.generator = None #type: Optional[Iterable[Tuple[int, str]]]
|
||||
self.binary = binary
|
||||
def __enter__(self) -> 'read_file_lines':
|
||||
self.generator = enumerate(open(self.filename,
|
||||
'rb' if self.binary else 'r'))
|
||||
return self
|
||||
def __iter__(self) -> Iterator[str]:
|
||||
assert self.generator is not None
|
||||
for line_number, content in self.generator:
|
||||
self.line_number = line_number
|
||||
yield content
|
||||
self.line_number = 'exit'
|
||||
def __exit__(self, exc_type, exc_value, exc_traceback) -> None:
|
||||
if exc_type is not None:
|
||||
raise ReadFileLineException(self.filename, self.line_number) \
|
||||
from exc_value
|
||||
|
||||
|
||||
class PSAMacroEnumerator:
|
||||
|
@ -57,6 +105,20 @@ class PSAMacroEnumerator:
|
|||
'tag_length': [],
|
||||
'min_tag_length': [],
|
||||
} #type: Dict[str, List[str]]
|
||||
# Whether to include intermediate macros in enumerations. Intermediate
|
||||
# macros serve as category headers and are not valid values of their
|
||||
# type. See `is_internal_name`.
|
||||
# Always false in this class, may be set to true in derived classes.
|
||||
self.include_intermediate = False
|
||||
|
||||
def is_internal_name(self, name: str) -> bool:
|
||||
"""Whether this is an internal macro. Internal macros will be skipped."""
|
||||
if not self.include_intermediate:
|
||||
if name.endswith('_BASE') or name.endswith('_NONE'):
|
||||
return True
|
||||
if '_CATEGORY_' in name:
|
||||
return True
|
||||
return name.endswith('_FLAG') or name.endswith('_MASK')
|
||||
|
||||
def gather_arguments(self) -> None:
|
||||
"""Populate the list of values for macro arguments.
|
||||
|
@ -73,7 +135,11 @@ class PSAMacroEnumerator:
|
|||
|
||||
@staticmethod
|
||||
def _format_arguments(name: str, arguments: Iterable[str]) -> str:
|
||||
"""Format a macro call with arguments.."""
|
||||
"""Format a macro call with arguments.
|
||||
|
||||
The resulting format is consistent with
|
||||
`InputsForTest.normalize_argument`.
|
||||
"""
|
||||
return name + '(' + ', '.join(arguments) + ')'
|
||||
|
||||
_argument_split_re = re.compile(r' *, *')
|
||||
|
@ -111,6 +177,15 @@ class PSAMacroEnumerator:
|
|||
except BaseException as e:
|
||||
raise Exception('distribute_arguments({})'.format(name)) from e
|
||||
|
||||
def distribute_arguments_without_duplicates(
|
||||
self, seen: Set[str], name: str
|
||||
) -> Iterator[str]:
|
||||
"""Same as `distribute_arguments`, but don't repeat seen results."""
|
||||
for result in self.distribute_arguments(name):
|
||||
if result not in seen:
|
||||
seen.add(result)
|
||||
yield result
|
||||
|
||||
def generate_expressions(self, names: Iterable[str]) -> Iterator[str]:
|
||||
"""Generate expressions covering values constructed from the given names.
|
||||
|
||||
|
@ -123,7 +198,11 @@ class PSAMacroEnumerator:
|
|||
* ``macros.generate_expressions(macros.key_types)`` generates all
|
||||
key types.
|
||||
"""
|
||||
return itertools.chain(*map(self.distribute_arguments, names))
|
||||
seen = set() #type: Set[str]
|
||||
return itertools.chain(*(
|
||||
self.distribute_arguments_without_duplicates(seen, name)
|
||||
for name in names
|
||||
))
|
||||
|
||||
|
||||
class PSAMacroCollector(PSAMacroEnumerator):
|
||||
|
@ -144,15 +223,6 @@ class PSAMacroCollector(PSAMacroEnumerator):
|
|||
self.key_types_from_group = {} #type: Dict[str, str]
|
||||
self.algorithms_from_hash = {} #type: Dict[str, str]
|
||||
|
||||
def is_internal_name(self, name: str) -> bool:
|
||||
"""Whether this is an internal macro. Internal macros will be skipped."""
|
||||
if not self.include_intermediate:
|
||||
if name.endswith('_BASE') or name.endswith('_NONE'):
|
||||
return True
|
||||
if '_CATEGORY_' in name:
|
||||
return True
|
||||
return name.endswith('_FLAG') or name.endswith('_MASK')
|
||||
|
||||
def record_algorithm_subtype(self, name: str, expansion: str) -> None:
|
||||
"""Record the subtype of an algorithm constructor.
|
||||
|
||||
|
@ -251,3 +321,179 @@ class PSAMacroCollector(PSAMacroEnumerator):
|
|||
m = re.search(self._continued_line_re, line)
|
||||
line = re.sub(self._nonascii_re, rb'', line).decode('ascii')
|
||||
self.read_line(line)
|
||||
|
||||
|
||||
class InputsForTest(PSAMacroEnumerator):
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
"""Accumulate information about macros to test.
|
||||
enumerate
|
||||
This includes macro names as well as information about their arguments
|
||||
when applicable.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self.all_declared = set() #type: Set[str]
|
||||
# Identifier prefixes
|
||||
self.table_by_prefix = {
|
||||
'ERROR': self.statuses,
|
||||
'ALG': self.algorithms,
|
||||
'ECC_CURVE': self.ecc_curves,
|
||||
'DH_GROUP': self.dh_groups,
|
||||
'KEY_TYPE': self.key_types,
|
||||
'KEY_USAGE': self.key_usage_flags,
|
||||
} #type: Dict[str, Set[str]]
|
||||
# Test functions
|
||||
self.table_by_test_function = {
|
||||
# Any function ending in _algorithm also gets added to
|
||||
# self.algorithms.
|
||||
'key_type': [self.key_types],
|
||||
'block_cipher_key_type': [self.key_types],
|
||||
'stream_cipher_key_type': [self.key_types],
|
||||
'ecc_key_family': [self.ecc_curves],
|
||||
'ecc_key_types': [self.ecc_curves],
|
||||
'dh_key_family': [self.dh_groups],
|
||||
'dh_key_types': [self.dh_groups],
|
||||
'hash_algorithm': [self.hash_algorithms],
|
||||
'mac_algorithm': [self.mac_algorithms],
|
||||
'cipher_algorithm': [],
|
||||
'hmac_algorithm': [self.mac_algorithms],
|
||||
'aead_algorithm': [self.aead_algorithms],
|
||||
'key_derivation_algorithm': [self.kdf_algorithms],
|
||||
'key_agreement_algorithm': [self.ka_algorithms],
|
||||
'asymmetric_signature_algorithm': [],
|
||||
'asymmetric_signature_wildcard': [self.algorithms],
|
||||
'asymmetric_encryption_algorithm': [],
|
||||
'other_algorithm': [],
|
||||
} #type: Dict[str, List[Set[str]]]
|
||||
self.arguments_for['mac_length'] += ['1', '63']
|
||||
self.arguments_for['min_mac_length'] += ['1', '63']
|
||||
self.arguments_for['tag_length'] += ['1', '63']
|
||||
self.arguments_for['min_tag_length'] += ['1', '63']
|
||||
|
||||
def add_numerical_values(self) -> None:
|
||||
"""Add numerical values that are not supported to the known identifiers."""
|
||||
# Sets of names per type
|
||||
self.algorithms.add('0xffffffff')
|
||||
self.ecc_curves.add('0xff')
|
||||
self.dh_groups.add('0xff')
|
||||
self.key_types.add('0xffff')
|
||||
self.key_usage_flags.add('0x80000000')
|
||||
|
||||
# Hard-coded values for unknown algorithms
|
||||
#
|
||||
# These have to have values that are correct for their respective
|
||||
# PSA_ALG_IS_xxx macros, but are also not currently assigned and are
|
||||
# not likely to be assigned in the near future.
|
||||
self.hash_algorithms.add('0x020000fe') # 0x020000ff is PSA_ALG_ANY_HASH
|
||||
self.mac_algorithms.add('0x03007fff')
|
||||
self.ka_algorithms.add('0x09fc0000')
|
||||
self.kdf_algorithms.add('0x080000ff')
|
||||
# For AEAD algorithms, the only variability is over the tag length,
|
||||
# and this only applies to known algorithms, so don't test an
|
||||
# unknown algorithm.
|
||||
|
||||
def get_names(self, type_word: str) -> Set[str]:
|
||||
"""Return the set of known names of values of the given type."""
|
||||
return {
|
||||
'status': self.statuses,
|
||||
'algorithm': self.algorithms,
|
||||
'ecc_curve': self.ecc_curves,
|
||||
'dh_group': self.dh_groups,
|
||||
'key_type': self.key_types,
|
||||
'key_usage': self.key_usage_flags,
|
||||
}[type_word]
|
||||
|
||||
# Regex for interesting header lines.
|
||||
# Groups: 1=macro name, 2=type, 3=argument list (optional).
|
||||
_header_line_re = \
|
||||
re.compile(r'#define +' +
|
||||
r'(PSA_((?:(?:DH|ECC|KEY)_)?[A-Z]+)_\w+)' +
|
||||
r'(?:\(([^\n()]*)\))?')
|
||||
# Regex of macro names to exclude.
|
||||
_excluded_name_re = re.compile(r'_(?:GET|IS|OF)_|_(?:BASE|FLAG|MASK)\Z')
|
||||
# Additional excluded macros.
|
||||
_excluded_names = set([
|
||||
# Macros that provide an alternative way to build the same
|
||||
# algorithm as another macro.
|
||||
'PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG',
|
||||
'PSA_ALG_FULL_LENGTH_MAC',
|
||||
# Auxiliary macro whose name doesn't fit the usual patterns for
|
||||
# auxiliary macros.
|
||||
'PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG_CASE',
|
||||
])
|
||||
def parse_header_line(self, line: str) -> None:
|
||||
"""Parse a C header line, looking for "#define PSA_xxx"."""
|
||||
m = re.match(self._header_line_re, line)
|
||||
if not m:
|
||||
return
|
||||
name = m.group(1)
|
||||
self.all_declared.add(name)
|
||||
if re.search(self._excluded_name_re, name) or \
|
||||
name in self._excluded_names or \
|
||||
self.is_internal_name(name):
|
||||
return
|
||||
dest = self.table_by_prefix.get(m.group(2))
|
||||
if dest is None:
|
||||
return
|
||||
dest.add(name)
|
||||
if m.group(3):
|
||||
self.argspecs[name] = self._argument_split(m.group(3))
|
||||
|
||||
_nonascii_re = re.compile(rb'[^\x00-\x7f]+') #type: Pattern
|
||||
def parse_header(self, filename: str) -> None:
|
||||
"""Parse a C header file, looking for "#define PSA_xxx"."""
|
||||
with read_file_lines(filename, binary=True) as lines:
|
||||
for line in lines:
|
||||
line = re.sub(self._nonascii_re, rb'', line).decode('ascii')
|
||||
self.parse_header_line(line)
|
||||
|
||||
_macro_identifier_re = re.compile(r'[A-Z]\w+')
|
||||
def generate_undeclared_names(self, expr: str) -> Iterable[str]:
|
||||
for name in re.findall(self._macro_identifier_re, expr):
|
||||
if name not in self.all_declared:
|
||||
yield name
|
||||
|
||||
def accept_test_case_line(self, function: str, argument: str) -> bool:
|
||||
#pylint: disable=unused-argument
|
||||
undeclared = list(self.generate_undeclared_names(argument))
|
||||
if undeclared:
|
||||
raise Exception('Undeclared names in test case', undeclared)
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def normalize_argument(argument: str) -> str:
|
||||
"""Normalize whitespace in the given C expression.
|
||||
|
||||
The result uses the same whitespace as
|
||||
` PSAMacroEnumerator.distribute_arguments`.
|
||||
"""
|
||||
return re.sub(r',', r', ', re.sub(r' +', r'', argument))
|
||||
|
||||
def add_test_case_line(self, function: str, argument: str) -> None:
|
||||
"""Parse a test case data line, looking for algorithm metadata tests."""
|
||||
sets = []
|
||||
if function.endswith('_algorithm'):
|
||||
sets.append(self.algorithms)
|
||||
if function == 'key_agreement_algorithm' and \
|
||||
argument.startswith('PSA_ALG_KEY_AGREEMENT('):
|
||||
# We only want *raw* key agreement algorithms as such, so
|
||||
# exclude ones that are already chained with a KDF.
|
||||
# Keep the expression as one to test as an algorithm.
|
||||
function = 'other_algorithm'
|
||||
sets += self.table_by_test_function[function]
|
||||
if self.accept_test_case_line(function, argument):
|
||||
for s in sets:
|
||||
s.add(self.normalize_argument(argument))
|
||||
|
||||
# Regex matching a *.data line containing a test function call and
|
||||
# its arguments. The actual definition is partly positional, but this
|
||||
# regex is good enough in practice.
|
||||
_test_case_line_re = re.compile(r'(?!depends_on:)(\w+):([^\n :][^:\n]*)')
|
||||
def parse_test_cases(self, filename: str) -> None:
|
||||
"""Parse a test case file (*.data), looking for algorithm metadata tests."""
|
||||
with read_file_lines(filename) as lines:
|
||||
for line in lines:
|
||||
m = re.match(self._test_case_line_re, line)
|
||||
if m:
|
||||
self.add_test_case_line(m.group(1), m.group(2))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue