Move some files to framework repository

Signed-off-by: Gilles Peskine <[email protected]>
This commit is contained in:
Gilles Peskine
2024-10-09 13:49:38 +02:00
parent 2c17ec74be
commit ce00cfe798
1011 changed files with 0 additions and 472803 deletions
-677
View File
@@ -1,677 +0,0 @@
#!/usr/bin/env python3
"""This script compares the interfaces of two versions of Mbed TLS, looking
for backward incompatibilities between two different Git revisions within
an Mbed TLS repository. It must be run from the root of a Git working tree.
### How the script works ###
For the source (API) and runtime (ABI) interface compatibility, this script
is a small wrapper around the abi-compliance-checker and abi-dumper tools,
applying them to compare the header and library files.
For the storage format, this script compares the automatically generated
storage tests and the manual read tests, and complains if there is a
reduction in coverage. A change in test data will be signaled as a
coverage reduction since the old test data is no longer present. A change in
how test data is presented will be signaled as well; this would be a false
positive.
The results of the API/ABI comparison are either formatted as HTML and stored
at a configurable location, or are given as a brief list of problems.
Returns 0 on success, 1 on non-compliance, and 2 if there is an error
while running the script.
### How to interpret non-compliance ###
This script has relatively common false positives. In many scenarios, it only
reports a pass if there is a strict textual match between the old version and
the new version, and it reports problems where there is a sufficient semantic
match but not a textual match. This section lists some common false positives.
This is not an exhaustive list: in the end what matters is whether we are
breaking a backward compatibility goal.
**API**: the goal is that if an application works with the old version of the
library, it can be recompiled against the new version and will still work.
This is normally validated by comparing the declarations in `include/*/*.h`.
A failure is a declaration that has disappeared or that now has a different
type.
* It's ok to change or remove macros and functions that are documented as
for internal use only or as experimental.
* It's ok to rename function or macro parameters as long as the semantics
has not changed.
* It's ok to change or remove structure fields that are documented as
private.
* It's ok to add fields to a structure that already had private fields
or was documented as extensible.
**ABI**: the goal is that if an application was built against the old version
of the library, the same binary will work when linked against the new version.
This is normally validated by comparing the symbols exported by `libmbed*.so`.
A failure is a symbol that is no longer exported by the same library or that
now has a different type.
* All ABI changes are acceptable if the library version is bumped
(see `scripts/bump_version.sh`).
* ABI changes that concern functions which are declared only inside the
library directory, and not in `include/*/*.h`, are acceptable only if
the function was only ever used inside the same library (libmbedcrypto,
libmbedx509, libmbedtls). As a counter example, if the old version
of libmbedtls calls mbedtls_foo() from libmbedcrypto, and the new version
of libmbedcrypto no longer has a compatible mbedtls_foo(), this does
require a version bump for libmbedcrypto.
**Storage format**: the goal is to check that persistent keys stored by the
old version can be read by the new version. This is normally validated by
comparing the `*read*` test cases in `test_suite*storage_format*.data`.
A failure is a storage read test case that is no longer present with the same
function name and parameter list.
* It's ok if the same test data is present, but its presentation has changed,
for example if a test function is renamed or has different parameters.
* It's ok if redundant tests are removed.
**Generated test coverage**: the goal is to check that automatically
generated tests have as much coverage as before. This is normally validated
by comparing the test cases that are automatically generated by a script.
A failure is a generated test case that is no longer present with the same
function name and parameter list.
* It's ok if the same test data is present, but its presentation has changed,
for example if a test function is renamed or has different parameters.
* It's ok if redundant tests are removed.
"""
# Copyright The Mbed TLS Contributors
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
import glob
import os
import re
import sys
import traceback
import shutil
import subprocess
import argparse
import logging
import tempfile
import fnmatch
from types import SimpleNamespace
import xml.etree.ElementTree as ET
import framework_scripts_path # pylint: disable=unused-import
from mbedtls_framework import build_tree
class AbiChecker:
"""API and ABI checker."""
def __init__(self, old_version, new_version, configuration):
"""Instantiate the API/ABI checker.
old_version: RepoVersion containing details to compare against
new_version: RepoVersion containing details to check
configuration.report_dir: directory for output files
configuration.keep_all_reports: if false, delete old reports
configuration.brief: if true, output shorter report to stdout
configuration.check_abi: if true, compare ABIs
configuration.check_api: if true, compare APIs
configuration.check_storage: if true, compare storage format tests
configuration.skip_file: path to file containing symbols and types to skip
"""
self.repo_path = "."
self.log = None
self.verbose = configuration.verbose
self._setup_logger()
self.report_dir = os.path.abspath(configuration.report_dir)
self.keep_all_reports = configuration.keep_all_reports
self.can_remove_report_dir = not (os.path.exists(self.report_dir) or
self.keep_all_reports)
self.old_version = old_version
self.new_version = new_version
self.skip_file = configuration.skip_file
self.check_abi = configuration.check_abi
self.check_api = configuration.check_api
if self.check_abi != self.check_api:
raise Exception('Checking API without ABI or vice versa is not supported')
self.check_storage_tests = configuration.check_storage
self.brief = configuration.brief
self.git_command = "git"
self.make_command = "make"
def _setup_logger(self):
self.log = logging.getLogger()
if self.verbose:
self.log.setLevel(logging.DEBUG)
else:
self.log.setLevel(logging.INFO)
self.log.addHandler(logging.StreamHandler())
@staticmethod
def check_abi_tools_are_installed():
for command in ["abi-dumper", "abi-compliance-checker"]:
if not shutil.which(command):
raise Exception("{} not installed, aborting".format(command))
def _get_clean_worktree_for_git_revision(self, version):
"""Make a separate worktree with version.revision checked out.
Do not modify the current worktree."""
git_worktree_path = tempfile.mkdtemp()
if version.repository:
self.log.debug(
"Checking out git worktree for revision {} from {}".format(
version.revision, version.repository
)
)
fetch_output = subprocess.check_output(
[self.git_command, "fetch",
version.repository, version.revision],
cwd=self.repo_path,
stderr=subprocess.STDOUT
)
self.log.debug(fetch_output.decode("utf-8"))
worktree_rev = "FETCH_HEAD"
else:
self.log.debug("Checking out git worktree for revision {}".format(
version.revision
))
worktree_rev = version.revision
worktree_output = subprocess.check_output(
[self.git_command, "worktree", "add", "--detach",
git_worktree_path, worktree_rev],
cwd=self.repo_path,
stderr=subprocess.STDOUT
)
self.log.debug(worktree_output.decode("utf-8"))
version.commit = subprocess.check_output(
[self.git_command, "rev-parse", "HEAD"],
cwd=git_worktree_path,
stderr=subprocess.STDOUT
).decode("ascii").rstrip()
self.log.debug("Commit is {}".format(version.commit))
return git_worktree_path
def _update_git_submodules(self, git_worktree_path, version):
"""If the crypto submodule is present, initialize it.
if version.crypto_revision exists, update it to that revision,
otherwise update it to the default revision"""
update_output = subprocess.check_output(
[self.git_command, "submodule", "update", "--init", '--recursive'],
cwd=git_worktree_path,
stderr=subprocess.STDOUT
)
self.log.debug(update_output.decode("utf-8"))
if not (os.path.exists(os.path.join(git_worktree_path, "crypto"))
and version.crypto_revision):
return
if version.crypto_repository:
fetch_output = subprocess.check_output(
[self.git_command, "fetch", version.crypto_repository,
version.crypto_revision],
cwd=os.path.join(git_worktree_path, "crypto"),
stderr=subprocess.STDOUT
)
self.log.debug(fetch_output.decode("utf-8"))
crypto_rev = "FETCH_HEAD"
else:
crypto_rev = version.crypto_revision
checkout_output = subprocess.check_output(
[self.git_command, "checkout", crypto_rev],
cwd=os.path.join(git_worktree_path, "crypto"),
stderr=subprocess.STDOUT
)
self.log.debug(checkout_output.decode("utf-8"))
def _build_shared_libraries(self, git_worktree_path, version):
"""Build the shared libraries in the specified worktree."""
my_environment = os.environ.copy()
my_environment["CFLAGS"] = "-g -Og"
my_environment["SHARED"] = "1"
if os.path.exists(os.path.join(git_worktree_path, "crypto")):
my_environment["USE_CRYPTO_SUBMODULE"] = "1"
make_output = subprocess.check_output(
[self.make_command, "lib"],
env=my_environment,
cwd=git_worktree_path,
stderr=subprocess.STDOUT
)
self.log.debug(make_output.decode("utf-8"))
for root, _dirs, files in os.walk(git_worktree_path):
for file in fnmatch.filter(files, "*.so"):
version.modules[os.path.splitext(file)[0]] = (
os.path.join(root, file)
)
@staticmethod
def _pretty_revision(version):
if version.revision == version.commit:
return version.revision
else:
return "{} ({})".format(version.revision, version.commit)
def _get_abi_dumps_from_shared_libraries(self, version):
"""Generate the ABI dumps for the specified git revision.
The shared libraries must have been built and the module paths
present in version.modules."""
for mbed_module, module_path in version.modules.items():
output_path = os.path.join(
self.report_dir, "{}-{}-{}.dump".format(
mbed_module, version.revision, version.version
)
)
abi_dump_command = [
"abi-dumper",
module_path,
"-o", output_path,
"-lver", self._pretty_revision(version),
]
abi_dump_output = subprocess.check_output(
abi_dump_command,
stderr=subprocess.STDOUT
)
self.log.debug(abi_dump_output.decode("utf-8"))
version.abi_dumps[mbed_module] = output_path
@staticmethod
def _normalize_storage_test_case_data(line):
"""Eliminate cosmetic or irrelevant details in storage format test cases."""
line = re.sub(r'\s+', r'', line)
return line
def _read_storage_tests(self,
directory,
filename,
is_generated,
storage_tests):
"""Record storage tests from the given file.
Populate the storage_tests dictionary with test cases read from
filename under directory.
"""
at_paragraph_start = True
description = None
full_path = os.path.join(directory, filename)
with open(full_path) as fd:
for line_number, line in enumerate(fd, 1):
line = line.strip()
if not line:
at_paragraph_start = True
continue
if line.startswith('#'):
continue
if at_paragraph_start:
description = line.strip()
at_paragraph_start = False
continue
if line.startswith('depends_on:'):
continue
# We've reached a test case data line
test_case_data = self._normalize_storage_test_case_data(line)
if not is_generated:
# In manual test data, only look at read tests.
function_name = test_case_data.split(':', 1)[0]
if 'read' not in function_name.split('_'):
continue
metadata = SimpleNamespace(
filename=filename,
line_number=line_number,
description=description
)
storage_tests[test_case_data] = metadata
@staticmethod
def _list_generated_test_data_files(git_worktree_path):
"""List the generated test data files."""
generate_psa_tests = 'framework/scripts/generate_psa_tests.py'
if not os.path.isfile(git_worktree_path + '/' + generate_psa_tests):
# The checked-out revision is from before generate_psa_tests.py
# was moved to the framework submodule. Use the old location.
generate_psa_tests = 'tests/scripts/generate_psa_tests.py'
output = subprocess.check_output(
[generate_psa_tests, '--list'],
cwd=git_worktree_path,
).decode('ascii')
return [line for line in output.split('\n') if line]
def _get_storage_format_tests(self, version, git_worktree_path):
"""Record the storage format tests for the specified git version.
The storage format tests are the test suite data files whose name
contains "storage_format".
The version must be checked out at git_worktree_path.
This function creates or updates the generated data files.
"""
# Existing test data files. This may be missing some automatically
# generated files if they haven't been generated yet.
if os.path.isdir(os.path.join(git_worktree_path, 'tf-psa-crypto',
'tests', 'suites')):
storage_data_files = set(glob.glob(
'tf-psa-crypto/tests/suites/test_suite_*storage_format*.data'
))
else:
storage_data_files = set(glob.glob(
'tests/suites/test_suite_*storage_format*.data'
))
# Discover and (re)generate automatically generated data files.
to_be_generated = set()
for filename in self._list_generated_test_data_files(git_worktree_path):
if 'storage_format' in filename:
storage_data_files.add(filename)
to_be_generated.add(filename)
generate_psa_tests = 'framework/scripts/generate_psa_tests.py'
if not os.path.isfile(git_worktree_path + '/' + generate_psa_tests):
# The checked-out revision is from before generate_psa_tests.py
# was moved to the framework submodule. Use the old location.
generate_psa_tests = 'tests/scripts/generate_psa_tests.py'
subprocess.check_call(
[generate_psa_tests] + sorted(to_be_generated),
cwd=git_worktree_path,
)
for test_file in sorted(storage_data_files):
self._read_storage_tests(git_worktree_path,
test_file,
test_file in to_be_generated,
version.storage_tests)
def _cleanup_worktree(self, git_worktree_path):
"""Remove the specified git worktree."""
shutil.rmtree(git_worktree_path)
worktree_output = subprocess.check_output(
[self.git_command, "worktree", "prune"],
cwd=self.repo_path,
stderr=subprocess.STDOUT
)
self.log.debug(worktree_output.decode("utf-8"))
def _get_abi_dump_for_ref(self, version):
"""Generate the interface information for the specified git revision."""
git_worktree_path = self._get_clean_worktree_for_git_revision(version)
self._update_git_submodules(git_worktree_path, version)
if self.check_abi:
self._build_shared_libraries(git_worktree_path, version)
self._get_abi_dumps_from_shared_libraries(version)
if self.check_storage_tests:
self._get_storage_format_tests(version, git_worktree_path)
self._cleanup_worktree(git_worktree_path)
def _remove_children_with_tag(self, parent, tag):
children = parent.getchildren()
for child in children:
if child.tag == tag:
parent.remove(child)
else:
self._remove_children_with_tag(child, tag)
def _remove_extra_detail_from_report(self, report_root):
for tag in ['test_info', 'test_results', 'problem_summary',
'added_symbols', 'affected']:
self._remove_children_with_tag(report_root, tag)
for report in report_root:
for problems in report.getchildren()[:]:
if not problems.getchildren():
report.remove(problems)
def _abi_compliance_command(self, mbed_module, output_path):
"""Build the command to run to analyze the library mbed_module.
The report will be placed in output_path."""
abi_compliance_command = [
"abi-compliance-checker",
"-l", mbed_module,
"-old", self.old_version.abi_dumps[mbed_module],
"-new", self.new_version.abi_dumps[mbed_module],
"-strict",
"-report-path", output_path,
]
if self.skip_file:
abi_compliance_command += ["-skip-symbols", self.skip_file,
"-skip-types", self.skip_file]
if self.brief:
abi_compliance_command += ["-report-format", "xml",
"-stdout"]
return abi_compliance_command
def _is_library_compatible(self, mbed_module, compatibility_report):
"""Test if the library mbed_module has remained compatible.
Append a message regarding compatibility to compatibility_report."""
output_path = os.path.join(
self.report_dir, "{}-{}-{}.html".format(
mbed_module, self.old_version.revision,
self.new_version.revision
)
)
try:
subprocess.check_output(
self._abi_compliance_command(mbed_module, output_path),
stderr=subprocess.STDOUT
)
except subprocess.CalledProcessError as err:
if err.returncode != 1:
raise err
if self.brief:
self.log.info(
"Compatibility issues found for {}".format(mbed_module)
)
report_root = ET.fromstring(err.output.decode("utf-8"))
self._remove_extra_detail_from_report(report_root)
self.log.info(ET.tostring(report_root).decode("utf-8"))
else:
self.can_remove_report_dir = False
compatibility_report.append(
"Compatibility issues found for {}, "
"for details see {}".format(mbed_module, output_path)
)
return False
compatibility_report.append(
"No compatibility issues for {}".format(mbed_module)
)
if not (self.keep_all_reports or self.brief):
os.remove(output_path)
return True
@staticmethod
def _is_storage_format_compatible(old_tests, new_tests,
compatibility_report):
"""Check whether all tests present in old_tests are also in new_tests.
Append a message regarding compatibility to compatibility_report.
"""
missing = frozenset(old_tests.keys()).difference(new_tests.keys())
for test_data in sorted(missing):
metadata = old_tests[test_data]
compatibility_report.append(
'Test case from {} line {} "{}" has disappeared: {}'.format(
metadata.filename, metadata.line_number,
metadata.description, test_data
)
)
compatibility_report.append(
'FAIL: {}/{} storage format test cases have changed or disappeared.'.format(
len(missing), len(old_tests)
) if missing else
'PASS: All {} storage format test cases are preserved.'.format(
len(old_tests)
)
)
compatibility_report.append(
'Info: number of storage format tests cases: {} -> {}.'.format(
len(old_tests), len(new_tests)
)
)
return not missing
def get_abi_compatibility_report(self):
"""Generate a report of the differences between the reference ABI
and the new ABI. ABI dumps from self.old_version and self.new_version
must be available."""
compatibility_report = ["Checking evolution from {} to {}".format(
self._pretty_revision(self.old_version),
self._pretty_revision(self.new_version)
)]
compliance_return_code = 0
if self.check_abi:
shared_modules = list(set(self.old_version.modules.keys()) &
set(self.new_version.modules.keys()))
for mbed_module in shared_modules:
if not self._is_library_compatible(mbed_module,
compatibility_report):
compliance_return_code = 1
if self.check_storage_tests:
if not self._is_storage_format_compatible(
self.old_version.storage_tests,
self.new_version.storage_tests,
compatibility_report):
compliance_return_code = 1
for version in [self.old_version, self.new_version]:
for mbed_module, mbed_module_dump in version.abi_dumps.items():
os.remove(mbed_module_dump)
if self.can_remove_report_dir:
os.rmdir(self.report_dir)
self.log.info("\n".join(compatibility_report))
return compliance_return_code
def check_for_abi_changes(self):
"""Generate a report of ABI differences
between self.old_rev and self.new_rev."""
build_tree.check_repo_path()
if self.check_api or self.check_abi:
self.check_abi_tools_are_installed()
self._get_abi_dump_for_ref(self.old_version)
self._get_abi_dump_for_ref(self.new_version)
return self.get_abi_compatibility_report()
def run_main():
try:
parser = argparse.ArgumentParser(
description=__doc__
)
parser.add_argument(
"-v", "--verbose", action="store_true",
help="set verbosity level",
)
parser.add_argument(
"-r", "--report-dir", type=str, default="reports",
help="directory where reports are stored, default is reports",
)
parser.add_argument(
"-k", "--keep-all-reports", action="store_true",
help="keep all reports, even if there are no compatibility issues",
)
parser.add_argument(
"-o", "--old-rev", type=str, help="revision for old version.",
required=True,
)
parser.add_argument(
"-or", "--old-repo", type=str, help="repository for old version."
)
parser.add_argument(
"-oc", "--old-crypto-rev", type=str,
help="revision for old crypto submodule."
)
parser.add_argument(
"-ocr", "--old-crypto-repo", type=str,
help="repository for old crypto submodule."
)
parser.add_argument(
"-n", "--new-rev", type=str, help="revision for new version",
required=True,
)
parser.add_argument(
"-nr", "--new-repo", type=str, help="repository for new version."
)
parser.add_argument(
"-nc", "--new-crypto-rev", type=str,
help="revision for new crypto version"
)
parser.add_argument(
"-ncr", "--new-crypto-repo", type=str,
help="repository for new crypto submodule."
)
parser.add_argument(
"-s", "--skip-file", type=str,
help=("path to file containing symbols and types to skip "
"(typically \"-s identifiers\" after running "
"\"tests/scripts/list-identifiers.sh --internal\")")
)
parser.add_argument(
"--check-abi",
action='store_true', default=True,
help="Perform ABI comparison (default: yes)"
)
parser.add_argument("--no-check-abi", action='store_false', dest='check_abi')
parser.add_argument(
"--check-api",
action='store_true', default=True,
help="Perform API comparison (default: yes)"
)
parser.add_argument("--no-check-api", action='store_false', dest='check_api')
parser.add_argument(
"--check-storage",
action='store_true', default=True,
help="Perform storage tests comparison (default: yes)"
)
parser.add_argument("--no-check-storage", action='store_false', dest='check_storage')
parser.add_argument(
"-b", "--brief", action="store_true",
help="output only the list of issues to stdout, instead of a full report",
)
abi_args = parser.parse_args()
if os.path.isfile(abi_args.report_dir):
print("Error: {} is not a directory".format(abi_args.report_dir))
parser.exit()
old_version = SimpleNamespace(
version="old",
repository=abi_args.old_repo,
revision=abi_args.old_rev,
commit=None,
crypto_repository=abi_args.old_crypto_repo,
crypto_revision=abi_args.old_crypto_rev,
abi_dumps={},
storage_tests={},
modules={}
)
new_version = SimpleNamespace(
version="new",
repository=abi_args.new_repo,
revision=abi_args.new_rev,
commit=None,
crypto_repository=abi_args.new_crypto_repo,
crypto_revision=abi_args.new_crypto_rev,
abi_dumps={},
storage_tests={},
modules={}
)
configuration = SimpleNamespace(
verbose=abi_args.verbose,
report_dir=abi_args.report_dir,
keep_all_reports=abi_args.keep_all_reports,
brief=abi_args.brief,
check_abi=abi_args.check_abi,
check_api=abi_args.check_api,
check_storage=abi_args.check_storage,
skip_file=abi_args.skip_file
)
abi_check = AbiChecker(old_version, new_version, configuration)
return_code = abi_check.check_for_abi_changes()
sys.exit(return_code)
except Exception: # pylint: disable=broad-except
# Print the backtrace and exit explicitly so as to exit with
# status 2, not 1.
traceback.print_exc()
sys.exit(2)
if __name__ == "__main__":
run_main()
-28
View File
@@ -1,28 +0,0 @@
#!/bin/sh
# Generate doxygen documentation with a full mbedtls_config.h (this ensures that every
# available flag is documented, and avoids warnings about documentation
# without a corresponding #define).
#
# /!\ This must not be a Makefile target, as it would create a race condition
# when multiple targets are invoked in the same parallel build.
#
# Copyright The Mbed TLS Contributors
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
set -eu
CONFIG_H='include/mbedtls/mbedtls_config.h'
if [ -r $CONFIG_H ]; then :; else
echo "$CONFIG_H not found" >&2
exit 1
fi
CONFIG_BAK=${CONFIG_H}.bak
cp -p $CONFIG_H $CONFIG_BAK
scripts/config.py realfull
make apidoc
mv $CONFIG_BAK $CONFIG_H
-534
View File
@@ -1,534 +0,0 @@
#!/usr/bin/env python3
"""Assemble Mbed TLS change log entries into the change log file.
Add changelog entries to the first level-2 section.
Create a new level-2 section for unreleased changes if needed.
Remove the input files unless --keep-entries is specified.
In each level-3 section, entries are sorted in chronological order
(oldest first). From oldest to newest:
* Merged entry files are sorted according to their merge date (date of
the merge commit that brought the commit that created the file into
the target branch).
* Committed but unmerged entry files are sorted according to the date
of the commit that adds them.
* Uncommitted entry files are sorted according to their modification time.
You must run this program from within a git working directory.
"""
# Copyright The Mbed TLS Contributors
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
import argparse
from collections import OrderedDict, namedtuple
import datetime
import functools
import glob
import os
import re
import subprocess
import sys
class InputFormatError(Exception):
def __init__(self, filename, line_number, message, *args, **kwargs):
message = '{}:{}: {}'.format(filename, line_number,
message.format(*args, **kwargs))
super().__init__(message)
class CategoryParseError(Exception):
def __init__(self, line_offset, error_message):
self.line_offset = line_offset
self.error_message = error_message
super().__init__('{}: {}'.format(line_offset, error_message))
class LostContent(Exception):
def __init__(self, filename, line):
message = ('Lost content from {}: "{}"'.format(filename, line))
super().__init__(message)
class FilePathError(Exception):
def __init__(self, filenames):
message = ('Changelog filenames do not end with .txt: {}'.format(", ".join(filenames)))
super().__init__(message)
# The category names we use in the changelog.
# If you edit this, update ChangeLog.d/README.md.
STANDARD_CATEGORIES = (
'API changes',
'Default behavior changes',
'Requirement changes',
'New deprecations',
'Removals',
'Features',
'Security',
'Bugfix',
'Changes',
)
# The maximum line length for an entry
MAX_LINE_LENGTH = 80
CategoryContent = namedtuple('CategoryContent', [
'name', 'title_line', # Title text and line number of the title
'body', 'body_line', # Body text and starting line number of the body
])
class ChangelogFormat:
"""Virtual class documenting how to write a changelog format class."""
@classmethod
def extract_top_version(cls, changelog_file_content):
"""Split out the top version section.
If the top version is already released, create a new top
version section for an unreleased version.
Return ``(header, top_version_title, top_version_body, trailer)``
where the "top version" is the existing top version section if it's
for unreleased changes, and a newly created section otherwise.
To assemble the changelog after modifying top_version_body,
concatenate the four pieces.
"""
raise NotImplementedError
@classmethod
def version_title_text(cls, version_title):
"""Return the text of a formatted version section title."""
raise NotImplementedError
@classmethod
def split_categories(cls, version_body):
"""Split a changelog version section body into categories.
Return a list of `CategoryContent` the name is category title
without any formatting.
"""
raise NotImplementedError
@classmethod
def format_category(cls, title, body):
"""Construct the text of a category section from its title and body."""
raise NotImplementedError
class TextChangelogFormat(ChangelogFormat):
"""The traditional Mbed TLS changelog format."""
_unreleased_version_text = '= {} x.x.x branch released xxxx-xx-xx'
@classmethod
def is_released_version(cls, title):
# Look for an incomplete release date
return not re.search(r'[0-9x]{4}-[0-9x]{2}-[0-9x]?x', title)
_top_version_re = re.compile(r'(?:\A|\n)(=[^\n]*\n+)(.*?\n)(?:=|$)',
re.DOTALL)
_name_re = re.compile(r'=\s(.*)\s[0-9x]+\.', re.DOTALL)
@classmethod
def extract_top_version(cls, changelog_file_content):
"""A version section starts with a line starting with '='."""
m = re.search(cls._top_version_re, changelog_file_content)
top_version_start = m.start(1)
top_version_end = m.end(2)
top_version_title = m.group(1)
top_version_body = m.group(2)
name = re.match(cls._name_re, top_version_title).group(1)
if cls.is_released_version(top_version_title):
top_version_end = top_version_start
top_version_title = cls._unreleased_version_text.format(name) + '\n\n'
top_version_body = ''
return (changelog_file_content[:top_version_start],
top_version_title, top_version_body,
changelog_file_content[top_version_end:])
@classmethod
def version_title_text(cls, version_title):
return re.sub(r'\n.*', version_title, re.DOTALL)
_category_title_re = re.compile(r'(^\w.*)\n+', re.MULTILINE)
@classmethod
def split_categories(cls, version_body):
"""A category title is a line with the title in column 0."""
if not version_body:
return []
title_matches = list(re.finditer(cls._category_title_re, version_body))
if not title_matches or title_matches[0].start() != 0:
# There is junk before the first category.
raise CategoryParseError(0, 'Junk found where category expected')
title_starts = [m.start(1) for m in title_matches]
body_starts = [m.end(0) for m in title_matches]
body_ends = title_starts[1:] + [len(version_body)]
bodies = [version_body[body_start:body_end].rstrip('\n') + '\n'
for (body_start, body_end) in zip(body_starts, body_ends)]
title_lines = [version_body[:pos].count('\n') for pos in title_starts]
body_lines = [version_body[:pos].count('\n') for pos in body_starts]
return [CategoryContent(title_match.group(1), title_line,
body, body_line)
for title_match, title_line, body, body_line
in zip(title_matches, title_lines, bodies, body_lines)]
@classmethod
def format_category(cls, title, body):
# `split_categories` ensures that each body ends with a newline.
# Make sure that there is additionally a blank line between categories.
if not body.endswith('\n\n'):
body += '\n'
return title + '\n' + body
class ChangeLog:
"""An Mbed TLS changelog.
A changelog file consists of some header text followed by one or
more version sections. The version sections are in reverse
chronological order. Each version section consists of a title and a body.
The body of a version section consists of zero or more category
subsections. Each category subsection consists of a title and a body.
A changelog entry file has the same format as the body of a version section.
A `ChangelogFormat` object defines the concrete syntax of the changelog.
Entry files must have the same format as the changelog file.
"""
# Only accept dotted version numbers (e.g. "3.1", not "3").
# Refuse ".x" in a version number where x is a letter: this indicates
# a version that is not yet released. Something like "3.1a" is accepted.
_version_number_re = re.compile(r'[0-9]+\.[0-9A-Za-z.]+')
_incomplete_version_number_re = re.compile(r'.*\.[A-Za-z]')
_only_url_re = re.compile(r'^\s*\w+://\S+\s*$')
_has_url_re = re.compile(r'.*://.*')
def add_categories_from_text(self, filename, line_offset,
text, allow_unknown_category):
"""Parse a version section or entry file."""
try:
categories = self.format.split_categories(text)
except CategoryParseError as e:
raise InputFormatError(filename, line_offset + e.line_offset,
e.error_message)
for category in categories:
if not allow_unknown_category and \
category.name not in self.categories:
raise InputFormatError(filename,
line_offset + category.title_line,
'Unknown category: "{}"',
category.name)
body_split = category.body.splitlines()
for line_number, line in enumerate(body_split, 1):
if not self._only_url_re.match(line) and \
len(line) > MAX_LINE_LENGTH:
long_url_msg = '. URL exceeding length limit must be alone in its line.' \
if self._has_url_re.match(line) else ""
raise InputFormatError(filename,
category.body_line + line_number,
'Line is longer than allowed: '
'Length {} (Max {}){}',
len(line), MAX_LINE_LENGTH,
long_url_msg)
self.categories[category.name] += category.body
def __init__(self, input_stream, changelog_format):
"""Create a changelog object.
Populate the changelog object from the content of the file
input_stream.
"""
self.format = changelog_format
whole_file = input_stream.read()
(self.header,
self.top_version_title, top_version_body,
self.trailer) = self.format.extract_top_version(whole_file)
# Split the top version section into categories.
self.categories = OrderedDict()
for category in STANDARD_CATEGORIES:
self.categories[category] = ''
offset = (self.header + self.top_version_title).count('\n') + 1
self.add_categories_from_text(input_stream.name, offset,
top_version_body, True)
def add_file(self, input_stream):
"""Add changelog entries from a file.
"""
self.add_categories_from_text(input_stream.name, 1,
input_stream.read(), False)
def write(self, filename):
"""Write the changelog to the specified file.
"""
with open(filename, 'w', encoding='utf-8') as out:
out.write(self.header)
out.write(self.top_version_title)
for title, body in self.categories.items():
if not body:
continue
out.write(self.format.format_category(title, body))
out.write(self.trailer)
@functools.total_ordering
class EntryFileSortKey:
"""This classes defines an ordering on changelog entry files: older < newer.
* Merged entry files are sorted according to their merge date (date of
the merge commit that brought the commit that created the file into
the target branch).
* Committed but unmerged entry files are sorted according to the date
of the commit that adds them.
* Uncommitted entry files are sorted according to their modification time.
This class assumes that the file is in a git working directory with
the target branch checked out.
"""
# Categories of files. A lower number is considered older.
MERGED = 0
COMMITTED = 1
LOCAL = 2
@staticmethod
def creation_hash(filename):
"""Return the git commit id at which the given file was created.
Return None if the file was never checked into git.
"""
hashes = subprocess.check_output(['git', 'log', '--format=%H',
'--follow',
'--', filename])
m = re.search('(.+)$', hashes.decode('ascii'))
if not m:
# The git output is empty. This means that the file was
# never checked in.
return None
# The last commit in the log is the oldest one, which is when the
# file was created.
return m.group(0)
@staticmethod
def list_merges(some_hash, target, *options):
"""List merge commits from some_hash to target.
Pass options to git to select which commits are included.
"""
text = subprocess.check_output(['git', 'rev-list',
'--merges', *options,
'..'.join([some_hash, target])])
return text.decode('ascii').rstrip('\n').split('\n')
@classmethod
def merge_hash(cls, some_hash):
"""Return the git commit id at which the given commit was merged.
Return None if the given commit was never merged.
"""
target = 'HEAD'
# List the merges from some_hash to the target in two ways.
# The ancestry list is the ones that are both descendants of
# some_hash and ancestors of the target.
ancestry = frozenset(cls.list_merges(some_hash, target,
'--ancestry-path'))
# The first_parents list only contains merges that are directly
# on the target branch. We want it in reverse order (oldest first).
first_parents = cls.list_merges(some_hash, target,
'--first-parent', '--reverse')
# Look for the oldest merge commit that's both on the direct path
# and directly on the target branch. That's the place where some_hash
# was merged on the target branch. See
# https://stackoverflow.com/questions/8475448/find-merge-commit-which-include-a-specific-commit
for commit in first_parents:
if commit in ancestry:
return commit
return None
@staticmethod
def commit_timestamp(commit_id):
"""Return the timestamp of the given commit."""
text = subprocess.check_output(['git', 'show', '-s',
'--format=%ct',
commit_id])
return datetime.datetime.utcfromtimestamp(int(text))
@staticmethod
def file_timestamp(filename):
"""Return the modification timestamp of the given file."""
mtime = os.stat(filename).st_mtime
return datetime.datetime.fromtimestamp(mtime)
def __init__(self, filename):
"""Determine position of the file in the changelog entry order.
This constructor returns an object that can be used with comparison
operators, with `sort` and `sorted`, etc. Older entries are sorted
before newer entries.
"""
self.filename = filename
creation_hash = self.creation_hash(filename)
if not creation_hash:
self.category = self.LOCAL
self.datetime = self.file_timestamp(filename)
return
merge_hash = self.merge_hash(creation_hash)
if not merge_hash:
self.category = self.COMMITTED
self.datetime = self.commit_timestamp(creation_hash)
return
self.category = self.MERGED
self.datetime = self.commit_timestamp(merge_hash)
def sort_key(self):
""""Return a concrete sort key for this entry file sort key object.
``ts1 < ts2`` is implemented as ``ts1.sort_key() < ts2.sort_key()``.
"""
return (self.category, self.datetime, self.filename)
def __eq__(self, other):
return self.sort_key() == other.sort_key()
def __lt__(self, other):
return self.sort_key() < other.sort_key()
def check_output(generated_output_file, main_input_file, merged_files):
"""Make sanity checks on the generated output.
The intent of these sanity checks is to have reasonable confidence
that no content has been lost.
The sanity check is that every line that is present in an input file
is also present in an output file. This is not perfect but good enough
for now.
"""
with open(generated_output_file, 'r', encoding='utf-8') as fd:
generated_output = set(fd)
for line in open(main_input_file, 'r', encoding='utf-8'):
if line not in generated_output:
raise LostContent('original file', line)
for merged_file in merged_files:
for line in open(merged_file, 'r', encoding='utf-8'):
if line not in generated_output:
raise LostContent(merged_file, line)
def finish_output(changelog, output_file, input_file, merged_files):
"""Write the changelog to the output file.
The input file and the list of merged files are used only for sanity
checks on the output.
"""
if os.path.exists(output_file) and not os.path.isfile(output_file):
# The output is a non-regular file (e.g. pipe). Write to it directly.
output_temp = output_file
else:
# The output is a regular file. Write to a temporary file,
# then move it into place atomically.
output_temp = output_file + '.tmp'
changelog.write(output_temp)
check_output(output_temp, input_file, merged_files)
if output_temp != output_file:
os.rename(output_temp, output_file)
def remove_merged_entries(files_to_remove):
for filename in files_to_remove:
os.remove(filename)
def list_files_to_merge(options):
"""List the entry files to merge, oldest first.
"Oldest" is defined by `EntryFileSortKey`.
Also check for required .txt extension
"""
files_to_merge = glob.glob(os.path.join(options.dir, '*'))
# Ignore 00README.md
readme = os.path.join(options.dir, "00README.md")
if readme in files_to_merge:
files_to_merge.remove(readme)
# Identify files without the required .txt extension
bad_files = [x for x in files_to_merge if not x.endswith(".txt")]
if bad_files:
raise FilePathError(bad_files)
files_to_merge.sort(key=EntryFileSortKey)
return files_to_merge
def merge_entries(options):
"""Merge changelog entries into the changelog file.
Read the changelog file from options.input.
Check that all entries have a .txt extension
Read entries to merge from the directory options.dir.
Write the new changelog to options.output.
Remove the merged entries if options.keep_entries is false.
"""
with open(options.input, 'r', encoding='utf-8') as input_file:
changelog = ChangeLog(input_file, TextChangelogFormat)
files_to_merge = list_files_to_merge(options)
if not files_to_merge:
sys.stderr.write('There are no pending changelog entries.\n')
return
for filename in files_to_merge:
with open(filename, 'r', encoding='utf-8') as input_file:
changelog.add_file(input_file)
finish_output(changelog, options.output, options.input, files_to_merge)
if not options.keep_entries:
remove_merged_entries(files_to_merge)
def show_file_timestamps(options):
"""List the files to merge and their timestamp.
This is only intended for debugging purposes.
"""
files = list_files_to_merge(options)
for filename in files:
ts = EntryFileSortKey(filename)
print(ts.category, ts.datetime, filename)
def set_defaults(options):
"""Add default values for missing options."""
output_file = getattr(options, 'output', None)
if output_file is None:
options.output = options.input
if getattr(options, 'keep_entries', None) is None:
options.keep_entries = (output_file is not None)
def main():
"""Command line entry point."""
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--dir', '-d', metavar='DIR',
default='ChangeLog.d',
help='Directory to read entries from'
' (default: ChangeLog.d)')
parser.add_argument('--input', '-i', metavar='FILE',
default='ChangeLog',
help='Existing changelog file to read from and augment'
' (default: ChangeLog)')
parser.add_argument('--keep-entries',
action='store_true', dest='keep_entries', default=None,
help='Keep the files containing entries'
' (default: remove them if --output/-o is not specified)')
parser.add_argument('--no-keep-entries',
action='store_false', dest='keep_entries',
help='Remove the files containing entries after they are merged'
' (default: remove them if --output/-o is not specified)')
parser.add_argument('--output', '-o', metavar='FILE',
help='Output changelog file'
' (default: overwrite the input)')
parser.add_argument('--list-files-only',
action='store_true',
help=('Only list the files that would be processed '
'(with some debugging information)'))
options = parser.parse_args()
set_defaults(options)
if options.list_files_only:
show_file_timestamps(options)
return
merge_entries(options)
if __name__ == '__main__':
main()
-5
View File
@@ -1,5 +0,0 @@
# Python modules required to build Mbed TLS in ordinary conditions.
# Required to (re-)generate source files. Not needed if the generated source
# files are already present and up-to-date.
-r driver.requirements.txt
-148
View File
@@ -1,148 +0,0 @@
#!/bin/bash
#
# Copyright The Mbed TLS Contributors
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
#
# Purpose
#
# Sets the version numbers in the source code to those given.
#
# Usage: bump_version.sh [ --version <version> ] [ --so-crypto <version>]
# [ --so-x509 <version> ] [ --so-tls <version> ]
# [ -v | --verbose ] [ -h | --help ]
#
set -e
VERSION=""
SOVERSION=""
# Parse arguments
#
until [ -z "$1" ]
do
case "$1" in
--version)
# Version to use
shift
VERSION=$1
;;
--so-crypto)
shift
SO_CRYPTO=$1
;;
--so-x509)
shift
SO_X509=$1
;;
--so-tls)
shift
SO_TLS=$1
;;
-v|--verbose)
# Be verbose
VERBOSE="1"
;;
-h|--help)
# print help
echo "Usage: $0"
echo -e " -h|--help\t\tPrint this help."
echo -e " --version <version>\tVersion to bump to."
echo -e " --so-crypto <version>\tSO version to bump libmbedcrypto to."
echo -e " --so-x509 <version>\tSO version to bump libmbedx509 to."
echo -e " --so-tls <version>\tSO version to bump libmbedtls to."
echo -e " -v|--verbose\t\tVerbose."
exit 1
;;
*)
# print error
echo "Unknown argument: '$1'"
exit 1
;;
esac
shift
done
if [ "X" = "X$VERSION" ];
then
echo "No version specified. Unable to continue."
exit 1
fi
[ $VERBOSE ] && echo "Bumping VERSION in CMakeLists.txt"
sed -e "s/ VERSION [0-9.]\{1,\}/ VERSION $VERSION/g" < CMakeLists.txt > tmp
mv tmp CMakeLists.txt
[ $VERBOSE ] && echo "Bumping VERSION in library/CMakeLists.txt"
sed -e "s/ VERSION [0-9.]\{1,\}/ VERSION $VERSION/g" < library/CMakeLists.txt > tmp
mv tmp library/CMakeLists.txt
if [ "X" != "X$SO_CRYPTO" ];
then
[ $VERBOSE ] && echo "Bumping SOVERSION for libmbedcrypto in library/CMakeLists.txt"
sed -e "/mbedcrypto/ s/ SOVERSION [0-9]\{1,\}/ SOVERSION $SO_CRYPTO/g" < library/CMakeLists.txt > tmp
mv tmp library/CMakeLists.txt
[ $VERBOSE ] && echo "Bumping SOVERSION for libmbedcrypto in library/Makefile"
sed -e "s/SOEXT_CRYPTO?=so.[0-9]\{1,\}/SOEXT_CRYPTO?=so.$SO_CRYPTO/g" < library/Makefile > tmp
mv tmp library/Makefile
fi
if [ "X" != "X$SO_X509" ];
then
[ $VERBOSE ] && echo "Bumping SOVERSION for libmbedx509 in library/CMakeLists.txt"
sed -e "/mbedx509/ s/ SOVERSION [0-9]\{1,\}/ SOVERSION $SO_X509/g" < library/CMakeLists.txt > tmp
mv tmp library/CMakeLists.txt
[ $VERBOSE ] && echo "Bumping SOVERSION for libmbedx509 in library/Makefile"
sed -e "s/SOEXT_X509?=so.[0-9]\{1,\}/SOEXT_X509?=so.$SO_X509/g" < library/Makefile > tmp
mv tmp library/Makefile
fi
if [ "X" != "X$SO_TLS" ];
then
[ $VERBOSE ] && echo "Bumping SOVERSION for libmbedtls in library/CMakeLists.txt"
sed -e "/mbedtls/ s/ SOVERSION [0-9]\{1,\}/ SOVERSION $SO_TLS/g" < library/CMakeLists.txt > tmp
mv tmp library/CMakeLists.txt
[ $VERBOSE ] && echo "Bumping SOVERSION for libmbedtls in library/Makefile"
sed -e "s/SOEXT_TLS?=so.[0-9]\{1,\}/SOEXT_TLS?=so.$SO_TLS/g" < library/Makefile > tmp
mv tmp library/Makefile
fi
[ $VERBOSE ] && echo "Bumping VERSION in include/mbedtls/build_info.h"
read MAJOR MINOR PATCH <<<$(IFS="."; echo $VERSION)
VERSION_NR="$( printf "0x%02X%02X%02X00" $MAJOR $MINOR $PATCH )"
cat include/mbedtls/build_info.h | \
sed -e "s/\(# *define *[A-Z]*_VERSION\)_MAJOR .\{1,\}/\1_MAJOR $MAJOR/" | \
sed -e "s/\(# *define *[A-Z]*_VERSION\)_MINOR .\{1,\}/\1_MINOR $MINOR/" | \
sed -e "s/\(# *define *[A-Z]*_VERSION\)_PATCH .\{1,\}/\1_PATCH $PATCH/" | \
sed -e "s/\(# *define *[A-Z]*_VERSION\)_NUMBER .\{1,\}/\1_NUMBER $VERSION_NR/" | \
sed -e "s/\(# *define *[A-Z]*_VERSION\)_STRING .\{1,\}/\1_STRING \"$VERSION\"/" | \
sed -e "s/\(# *define *[A-Z]*_VERSION\)_STRING_FULL .\{1,\}/\1_STRING_FULL \"Mbed TLS $VERSION\"/" \
> tmp
mv tmp include/mbedtls/build_info.h
[ $VERBOSE ] && echo "Bumping version in tests/suites/test_suite_version.data"
sed -e "s/version:\".\{1,\}/version:\"$VERSION\"/g" < tf-psa-crypto/tests/suites/test_suite_version.data > tmp
mv tmp tf-psa-crypto/tests/suites/test_suite_version.data
[ $VERBOSE ] && echo "Bumping PROJECT_NAME in doxygen/mbedtls.doxyfile and doxygen/input/doc_mainpage.h"
for i in doxygen/mbedtls.doxyfile doxygen/input/doc_mainpage.h;
do
sed -e "s/\\([Mm]bed TLS v\\)[0-9][0-9.]*/\\1$VERSION/g" < $i > tmp
mv tmp $i
done
[ $VERBOSE ] && echo "Re-generating library/error.c"
scripts/generate_errors.pl
[ $VERBOSE ] && echo "Re-generating programs/test/query_config.c"
scripts/generate_query_config.pl
[ $VERBOSE ] && echo "Re-generating library/version_features.c"
scripts/generate_features.pl
[ $VERBOSE ] && echo "Re-generating visualc files"
scripts/generate_visualc_files.pl
+87
View File
@@ -0,0 +1,87 @@
#!/usr/bin/env python3
"""Sanity checks for test data.
This program contains a class for traversing test cases that can be used
independently of the checks.
"""
# Copyright The Mbed TLS Contributors
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
import argparse
import re
import sys
import scripts_path # pylint: disable=unused-import
from mbedtls_framework import collect_test_cases
class DescriptionChecker(collect_test_cases.TestDescriptionExplorer):
"""Check all test case descriptions.
* Check that each description is valid (length, allowed character set, etc.).
* Check that there is no duplicated description inside of one test suite.
"""
def __init__(self, results):
self.results = results
def new_per_file_state(self):
"""Dictionary mapping descriptions to their line number."""
return {}
def process_test_case(self, per_file_state,
file_name, line_number, description):
"""Check test case descriptions for errors."""
results = self.results
seen = per_file_state
if description in seen:
results.error(file_name, line_number,
'Duplicate description (also line {})',
seen[description])
return
if re.search(br'[\t;]', description):
results.error(file_name, line_number,
'Forbidden character \'{}\' in description',
re.search(br'[\t;]', description).group(0).decode('ascii'))
if re.search(br'[^ -~]', description):
results.error(file_name, line_number,
'Non-ASCII character in description')
if len(description) > 66:
results.warning(file_name, line_number,
'Test description too long ({} > 66)',
len(description))
seen[description] = line_number
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--list-all',
action='store_true',
help='List all test cases, without doing checks')
parser.add_argument('--quiet', '-q',
action='store_true',
help='Hide warnings')
parser.add_argument('--verbose', '-v',
action='store_false', dest='quiet',
help='Show warnings (default: on; undoes --quiet)')
options = parser.parse_args()
if options.list_all:
descriptions = collect_test_cases.collect_available_test_cases()
sys.stdout.write('\n'.join(descriptions + ['']))
return
results = collect_test_cases.Results(options)
checker = DescriptionChecker(results)
try:
checker.walk_all()
except collect_test_cases.ScriptOutputError as e:
results.error(e.script_name, e.idx,
'"{}" should be listed as "<suite_name>;<description>"',
e.line)
if (results.warnings or results.errors) and not options.quiet:
sys.stderr.write('{}: {} errors, {} warnings\n'
.format(sys.argv[0], results.errors, results.warnings))
sys.exit(1 if results.errors else 0)
if __name__ == '__main__':
main()
-28
View File
@@ -1,28 +0,0 @@
# Python package requirements for Mbed TLS testing.
-r driver.requirements.txt
# Use a known version of Pylint, because new versions tend to add warnings
# that could start rejecting our code.
# 2.4.4 is the version in Ubuntu 20.04. It supports Python >=3.5.
pylint == 2.4.4
# Use a version of mypy that is compatible with our code base.
# mypy <0.940 is known not to work: see commit
# :/Upgrade mypy to the last version supporting Python 3.6
# mypy >=0.960 is known not to work:
# https://github.com/Mbed-TLS/mbedtls-framework/issues/50
# mypy 0.942 is the version in Ubuntu 22.04.
mypy == 0.942
# At the time of writing, only needed for tests/scripts/audit-validity-dates.py.
# It needs >=35.0.0 for correct operation, and that requires Python >=3.6,
# but our CI has Python 3.5. So let pip install the newest version that's
# compatible with the running Python: this way we get something good enough
# for mypy and pylint under Python 3.5, and we also get something good enough
# to run audit-validity-dates.py on Python >=3.6.
cryptography # >= 35.0.0
# For building `framework/data_files/server9-bad-saltlen.crt` and check python
# files.
asn1crypto
-953
View File
@@ -1,953 +0,0 @@
#!/usr/bin/env python3
"""
This script is for comparing the size of the library files from two
different Git revisions within an Mbed TLS repository.
The results of the comparison is formatted as csv and stored at a
configurable location.
Note: must be run from Mbed TLS root.
"""
# Copyright The Mbed TLS Contributors
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
import argparse
import logging
import os
import re
import shutil
import subprocess
import sys
import typing
from enum import Enum
import framework_scripts_path # pylint: disable=unused-import
from mbedtls_framework import build_tree
from mbedtls_framework import logging_util
from mbedtls_framework import typing_util
class SupportedArch(Enum):
"""Supported architecture for code size measurement."""
AARCH64 = 'aarch64'
AARCH32 = 'aarch32'
ARMV8_M = 'armv8-m'
X86_64 = 'x86_64'
X86 = 'x86'
class SupportedConfig(Enum):
"""Supported configuration for code size measurement."""
DEFAULT = 'default'
TFM_MEDIUM = 'tfm-medium'
# Static library
MBEDTLS_STATIC_LIB = {
'CRYPTO': 'library/libmbedcrypto.a',
'X509': 'library/libmbedx509.a',
'TLS': 'library/libmbedtls.a',
}
class CodeSizeDistinctInfo: # pylint: disable=too-few-public-methods
"""Data structure to store possibly distinct information for code size
comparison."""
def __init__( #pylint: disable=too-many-arguments
self,
version: str,
git_rev: str,
arch: str,
config: str,
compiler: str,
opt_level: str,
) -> None:
"""
:param: version: which version to compare with for code size.
:param: git_rev: Git revision to calculate code size.
:param: arch: architecture to measure code size on.
:param: config: Configuration type to calculate code size.
(See SupportedConfig)
:param: compiler: compiler used to build library/*.o.
:param: opt_level: Options that control optimization. (E.g. -Os)
"""
self.version = version
self.git_rev = git_rev
self.arch = arch
self.config = config
self.compiler = compiler
self.opt_level = opt_level
# Note: Variables below are not initialized by class instantiation.
self.pre_make_cmd = [] #type: typing.List[str]
self.make_cmd = ''
def get_info_indication(self):
"""Return a unique string to indicate Code Size Distinct Information."""
return '{git_rev}-{arch}-{config}-{compiler}'.format(**self.__dict__)
class CodeSizeCommonInfo: # pylint: disable=too-few-public-methods
"""Data structure to store common information for code size comparison."""
def __init__(
self,
host_arch: str,
measure_cmd: str,
) -> None:
"""
:param host_arch: host architecture.
:param measure_cmd: command to measure code size for library/*.o.
"""
self.host_arch = host_arch
self.measure_cmd = measure_cmd
def get_info_indication(self):
"""Return a unique string to indicate Code Size Common Information."""
return '{measure_tool}'\
.format(measure_tool=self.measure_cmd.strip().split(' ')[0])
class CodeSizeResultInfo: # pylint: disable=too-few-public-methods
"""Data structure to store result options for code size comparison."""
def __init__( #pylint: disable=too-many-arguments
self,
record_dir: str,
comp_dir: str,
with_markdown=False,
stdout=False,
show_all=False,
) -> None:
"""
:param record_dir: directory to store code size record.
:param comp_dir: directory to store results of code size comparision.
:param with_markdown: write comparision result into a markdown table.
(Default: False)
:param stdout: direct comparison result into sys.stdout.
(Default False)
:param show_all: show all objects in comparison result. (Default False)
"""
self.record_dir = record_dir
self.comp_dir = comp_dir
self.with_markdown = with_markdown
self.stdout = stdout
self.show_all = show_all
DETECT_ARCH_CMD = "cc -dM -E - < /dev/null"
def detect_arch() -> str:
"""Auto-detect host architecture."""
cc_output = subprocess.check_output(DETECT_ARCH_CMD, shell=True).decode()
if '__aarch64__' in cc_output:
return SupportedArch.AARCH64.value
if '__arm__' in cc_output:
return SupportedArch.AARCH32.value
if '__x86_64__' in cc_output:
return SupportedArch.X86_64.value
if '__i386__' in cc_output:
return SupportedArch.X86.value
else:
print("Unknown host architecture, cannot auto-detect arch.")
sys.exit(1)
TFM_MEDIUM_CONFIG_H = 'configs/ext/tfm_mbedcrypto_config_profile_medium.h'
TFM_MEDIUM_CRYPTO_CONFIG_H = 'configs/ext/crypto_config_profile_medium.h'
CONFIG_H = 'include/mbedtls/mbedtls_config.h'
CRYPTO_CONFIG_H = 'tf-psa-crypto/include/psa/crypto_config.h'
BACKUP_SUFFIX = '.code_size.bak'
class CodeSizeBuildInfo: # pylint: disable=too-few-public-methods
"""Gather information used to measure code size.
It collects information about architecture, configuration in order to
infer build command for code size measurement.
"""
SupportedArchConfig = [
'-a ' + SupportedArch.AARCH64.value + ' -c ' + SupportedConfig.DEFAULT.value,
'-a ' + SupportedArch.AARCH32.value + ' -c ' + SupportedConfig.DEFAULT.value,
'-a ' + SupportedArch.X86_64.value + ' -c ' + SupportedConfig.DEFAULT.value,
'-a ' + SupportedArch.X86.value + ' -c ' + SupportedConfig.DEFAULT.value,
'-a ' + SupportedArch.ARMV8_M.value + ' -c ' + SupportedConfig.TFM_MEDIUM.value,
]
def __init__(
self,
size_dist_info: CodeSizeDistinctInfo,
host_arch: str,
logger: logging.Logger,
) -> None:
"""
:param size_dist_info:
CodeSizeDistinctInfo containing info for code size measurement.
- size_dist_info.arch: architecture to measure code size on.
- size_dist_info.config: configuration type to measure
code size with.
- size_dist_info.compiler: compiler used to build library/*.o.
- size_dist_info.opt_level: Options that control optimization.
(E.g. -Os)
:param host_arch: host architecture.
:param logger: logging module
"""
self.arch = size_dist_info.arch
self.config = size_dist_info.config
self.compiler = size_dist_info.compiler
self.opt_level = size_dist_info.opt_level
self.make_cmd = ['make', '-j', 'lib']
self.host_arch = host_arch
self.logger = logger
def check_correctness(self) -> bool:
"""Check whether we are using proper / supported combination
of information to build library/*.o."""
# default config
if self.config == SupportedConfig.DEFAULT.value and \
self.arch == self.host_arch:
return True
# TF-M
elif self.arch == SupportedArch.ARMV8_M.value and \
self.config == SupportedConfig.TFM_MEDIUM.value:
return True
return False
def infer_pre_make_command(self) -> typing.List[str]:
"""Infer command to set up proper configuration before running make."""
pre_make_cmd = [] #type: typing.List[str]
if self.config == SupportedConfig.TFM_MEDIUM.value:
pre_make_cmd.append('cp {src} {dest}'
.format(src=TFM_MEDIUM_CONFIG_H, dest=CONFIG_H))
pre_make_cmd.append('cp {src} {dest}'
.format(src=TFM_MEDIUM_CRYPTO_CONFIG_H,
dest=CRYPTO_CONFIG_H))
return pre_make_cmd
def infer_make_cflags(self) -> str:
"""Infer CFLAGS by instance attributes in CodeSizeDistinctInfo."""
cflags = [] #type: typing.List[str]
# set optimization level
cflags.append(self.opt_level)
# set compiler by config
if self.config == SupportedConfig.TFM_MEDIUM.value:
self.compiler = 'armclang'
cflags.append('-mcpu=cortex-m33')
# set target
if self.compiler == 'armclang':
cflags.append('--target=arm-arm-none-eabi')
return ' '.join(cflags)
def infer_make_command(self) -> str:
"""Infer make command by CFLAGS and CC."""
if self.check_correctness():
# set CFLAGS=
self.make_cmd.append('CFLAGS=\'{}\''.format(self.infer_make_cflags()))
# set CC=
self.make_cmd.append('CC={}'.format(self.compiler))
return ' '.join(self.make_cmd)
else:
self.logger.error("Unsupported combination of architecture: {} " \
"and configuration: {}.\n"
.format(self.arch,
self.config))
self.logger.error("Please use supported combination of " \
"architecture and configuration:")
for comb in CodeSizeBuildInfo.SupportedArchConfig:
self.logger.error(comb)
self.logger.error("")
self.logger.error("For your system, please use:")
for comb in CodeSizeBuildInfo.SupportedArchConfig:
if "default" in comb and self.host_arch not in comb:
continue
self.logger.error(comb)
sys.exit(1)
class CodeSizeCalculator:
""" A calculator to calculate code size of library/*.o based on
Git revision and code size measurement tool.
"""
def __init__( #pylint: disable=too-many-arguments
self,
git_rev: str,
pre_make_cmd: typing.List[str],
make_cmd: str,
measure_cmd: str,
logger: logging.Logger,
) -> None:
"""
:param git_rev: Git revision. (E.g: commit)
:param pre_make_cmd: command to set up proper config before running make.
:param make_cmd: command to build library/*.o.
:param measure_cmd: command to measure code size for library/*.o.
:param logger: logging module
"""
self.repo_path = "."
self.git_command = "git"
self.make_clean = 'make clean'
self.git_rev = git_rev
self.pre_make_cmd = pre_make_cmd
self.make_cmd = make_cmd
self.measure_cmd = measure_cmd
self.logger = logger
@staticmethod
def validate_git_revision(git_rev: str) -> str:
result = subprocess.check_output(["git", "rev-parse", "--verify",
git_rev + "^{commit}"],
shell=False, universal_newlines=True)
return result[:7]
def _create_git_worktree(self) -> str:
"""Create a separate worktree for Git revision.
If Git revision is current, use current worktree instead."""
if self.git_rev == 'current':
self.logger.debug("Using current work directory.")
git_worktree_path = self.repo_path
else:
self.logger.debug("Creating git worktree for {}."
.format(self.git_rev))
git_worktree_path = os.path.join(self.repo_path,
"temp-" + self.git_rev)
subprocess.check_output(
[self.git_command, "worktree", "add", "--detach",
git_worktree_path, self.git_rev], cwd=self.repo_path,
stderr=subprocess.STDOUT
)
return git_worktree_path
@staticmethod
def backup_config_files(restore: bool) -> None:
"""Backup / Restore config files."""
if restore:
shutil.move(CONFIG_H + BACKUP_SUFFIX, CONFIG_H)
shutil.move(CRYPTO_CONFIG_H + BACKUP_SUFFIX, CRYPTO_CONFIG_H)
else:
shutil.copy(CONFIG_H, CONFIG_H + BACKUP_SUFFIX)
shutil.copy(CRYPTO_CONFIG_H, CRYPTO_CONFIG_H + BACKUP_SUFFIX)
def _build_libraries(self, git_worktree_path: str) -> None:
"""Build library/*.o in the specified worktree."""
self.logger.debug("Building library/*.o for {}."
.format(self.git_rev))
my_environment = os.environ.copy()
try:
if self.git_rev == 'current':
self.backup_config_files(restore=False)
for pre_cmd in self.pre_make_cmd:
subprocess.check_output(
pre_cmd, env=my_environment, shell=True,
cwd=git_worktree_path, stderr=subprocess.STDOUT,
universal_newlines=True
)
subprocess.check_output(
self.make_clean, env=my_environment, shell=True,
cwd=git_worktree_path, stderr=subprocess.STDOUT,
universal_newlines=True
)
subprocess.check_output(
self.make_cmd, env=my_environment, shell=True,
cwd=git_worktree_path, stderr=subprocess.STDOUT,
universal_newlines=True
)
if self.git_rev == 'current':
self.backup_config_files(restore=True)
except subprocess.CalledProcessError as e:
self._handle_called_process_error(e, git_worktree_path)
def _gen_raw_code_size(self, git_worktree_path: str) -> typing.Dict[str, str]:
"""Measure code size by a tool and return in UTF-8 encoding."""
self.logger.debug("Measuring code size for {} by `{}`."
.format(self.git_rev,
self.measure_cmd.strip().split(' ')[0]))
res = {}
for mod, st_lib in MBEDTLS_STATIC_LIB.items():
try:
result = subprocess.check_output(
[self.measure_cmd + ' ' + st_lib], cwd=git_worktree_path,
shell=True, universal_newlines=True
)
res[mod] = result
except subprocess.CalledProcessError as e:
self._handle_called_process_error(e, git_worktree_path)
return res
def _remove_worktree(self, git_worktree_path: str) -> None:
"""Remove temporary worktree."""
if git_worktree_path != self.repo_path:
self.logger.debug("Removing temporary worktree {}."
.format(git_worktree_path))
subprocess.check_output(
[self.git_command, "worktree", "remove", "--force",
git_worktree_path], cwd=self.repo_path,
stderr=subprocess.STDOUT
)
def _handle_called_process_error(self, e: subprocess.CalledProcessError,
git_worktree_path: str) -> None:
"""Handle a CalledProcessError and quit the program gracefully.
Remove any extra worktrees so that the script may be called again."""
# Tell the user what went wrong
self.logger.error(e, exc_info=True)
self.logger.error("Process output:\n {}".format(e.output))
# Quit gracefully by removing the existing worktree
self._remove_worktree(git_worktree_path)
sys.exit(-1)
def cal_libraries_code_size(self) -> typing.Dict[str, str]:
"""Do a complete round to calculate code size of library/*.o
by measurement tool.
:return A dictionary of measured code size
- typing.Dict[mod: str]
"""
git_worktree_path = self._create_git_worktree()
try:
self._build_libraries(git_worktree_path)
res = self._gen_raw_code_size(git_worktree_path)
finally:
self._remove_worktree(git_worktree_path)
return res
class CodeSizeGenerator:
""" A generator based on size measurement tool for library/*.o.
This is an abstract class. To use it, derive a class that implements
write_record and write_comparison methods, then call both of them with
proper arguments.
"""
def __init__(self, logger: logging.Logger) -> None:
"""
:param logger: logging module
"""
self.logger = logger
def write_record(
self,
git_rev: str,
code_size_text: typing.Dict[str, str],
output: typing_util.Writable
) -> None:
"""Write size record into a file.
:param git_rev: Git revision. (E.g: commit)
:param code_size_text:
string output (utf-8) from measurement tool of code size.
- typing.Dict[mod: str]
:param output: output stream which the code size record is written to.
(Note: Normally write code size record into File)
"""
raise NotImplementedError
def write_comparison( #pylint: disable=too-many-arguments
self,
old_rev: str,
new_rev: str,
output: typing_util.Writable,
with_markdown=False,
show_all=False
) -> None:
"""Write a comparision result into a stream between two Git revisions.
:param old_rev: old Git revision to compared with.
:param new_rev: new Git revision to compared with.
:param output: output stream which the code size record is written to.
(File / sys.stdout)
:param with_markdown: write comparision result in a markdown table.
(Default: False)
:param show_all: show all objects in comparison result. (Default False)
"""
raise NotImplementedError
class CodeSizeGeneratorWithSize(CodeSizeGenerator):
"""Code Size Base Class for size record saving and writing."""
class SizeEntry: # pylint: disable=too-few-public-methods
"""Data Structure to only store information of code size."""
def __init__(self, text: int, data: int, bss: int, dec: int):
self.text = text
self.data = data
self.bss = bss
self.total = dec # total <=> dec
def __init__(self, logger: logging.Logger) -> None:
""" Variable code_size is used to store size info for any Git revisions.
:param code_size:
Data Format as following:
code_size = {
git_rev: {
module: {
file_name: SizeEntry,
...
},
...
},
...
}
"""
super().__init__(logger)
self.code_size = {} #type: typing.Dict[str, typing.Dict]
self.mod_total_suffix = '-' + 'TOTALS'
def _set_size_record(self, git_rev: str, mod: str, size_text: str) -> None:
"""Store size information for target Git revision and high-level module.
size_text Format: text data bss dec hex filename
"""
size_record = {}
for line in size_text.splitlines()[1:]:
data = line.split()
if re.match(r'\s*\(TOTALS\)', data[5]):
data[5] = mod + self.mod_total_suffix
# file_name: SizeEntry(text, data, bss, dec)
size_record[data[5]] = CodeSizeGeneratorWithSize.SizeEntry(
int(data[0]), int(data[1]), int(data[2]), int(data[3]))
self.code_size.setdefault(git_rev, {}).update({mod: size_record})
def read_size_record(self, git_rev: str, fname: str) -> None:
"""Read size information from csv file and write it into code_size.
fname Format: filename text data bss dec
"""
mod = ""
size_record = {}
with open(fname, 'r') as csv_file:
for line in csv_file:
data = line.strip().split()
# check if we find the beginning of a module
if data and data[0] in MBEDTLS_STATIC_LIB:
mod = data[0]
continue
if mod:
# file_name: SizeEntry(text, data, bss, dec)
size_record[data[0]] = CodeSizeGeneratorWithSize.SizeEntry(
int(data[1]), int(data[2]), int(data[3]), int(data[4]))
# check if we hit record for the end of a module
m = re.match(r'\w+' + self.mod_total_suffix, line)
if m:
if git_rev in self.code_size:
self.code_size[git_rev].update({mod: size_record})
else:
self.code_size[git_rev] = {mod: size_record}
mod = ""
size_record = {}
def write_record(
self,
git_rev: str,
code_size_text: typing.Dict[str, str],
output: typing_util.Writable
) -> None:
"""Write size information to a file.
Writing Format: filename text data bss total(dec)
"""
for mod, size_text in code_size_text.items():
self._set_size_record(git_rev, mod, size_text)
format_string = "{:<30} {:>7} {:>7} {:>7} {:>7}\n"
output.write(format_string.format("filename",
"text", "data", "bss", "total"))
for mod, f_size in self.code_size[git_rev].items():
output.write("\n" + mod + "\n")
for fname, size_entry in f_size.items():
output.write(format_string
.format(fname,
size_entry.text, size_entry.data,
size_entry.bss, size_entry.total))
def write_comparison( #pylint: disable=too-many-arguments
self,
old_rev: str,
new_rev: str,
output: typing_util.Writable,
with_markdown=False,
show_all=False
) -> None:
# pylint: disable=too-many-locals
"""Write comparison result into a file.
Writing Format:
Markdown Output:
filename new(text) new(data) change(text) change(data)
CSV Output:
filename new(text) new(data) old(text) old(data) change(text) change(data)
"""
header_line = ["filename", "new(text)", "old(text)", "change(text)",
"new(data)", "old(data)", "change(data)"]
if with_markdown:
dash_line = [":----", "----:", "----:", "----:",
"----:", "----:", "----:"]
# | filename | new(text) | new(data) | change(text) | change(data) |
line_format = "| {0:<30} | {1:>9} | {4:>9} | {3:>12} | {6:>12} |\n"
bold_text = lambda x: '**' + str(x) + '**'
else:
# filename new(text) new(data) old(text) old(data) change(text) change(data)
line_format = "{0:<30} {1:>9} {4:>9} {2:>10} {5:>10} {3:>12} {6:>12}\n"
def cal_sect_change(
old_size: typing.Optional[CodeSizeGeneratorWithSize.SizeEntry],
new_size: typing.Optional[CodeSizeGeneratorWithSize.SizeEntry],
sect: str
) -> typing.List:
"""Inner helper function to calculate size change for a section.
Convention for special cases:
- If the object has been removed in new Git revision,
the size is minus code size of old Git revision;
the size change is marked as `Removed`,
- If the object only exists in new Git revision,
the size is code size of new Git revision;
the size change is marked as `None`,
:param: old_size: code size for objects in old Git revision.
:param: new_size: code size for objects in new Git revision.
:param: sect: section to calculate from `size` tool. This could be
any instance variable in SizeEntry.
:return: List of [section size of objects for new Git revision,
section size of objects for old Git revision,
section size change of objects between two Git revisions]
"""
if old_size and new_size:
new_attr = new_size.__dict__[sect]
old_attr = old_size.__dict__[sect]
delta = new_attr - old_attr
change_attr = '{0:{1}}'.format(delta, '+' if delta else '')
elif old_size:
new_attr = 'Removed'
old_attr = old_size.__dict__[sect]
delta = - old_attr
change_attr = '{0:{1}}'.format(delta, '+' if delta else '')
elif new_size:
new_attr = new_size.__dict__[sect]
old_attr = 'NotCreated'
delta = new_attr
change_attr = '{0:{1}}'.format(delta, '+' if delta else '')
else:
# Should never happen
new_attr = 'Error'
old_attr = 'Error'
change_attr = 'Error'
return [new_attr, old_attr, change_attr]
# sort dictionary by key
sort_by_k = lambda item: item[0].lower()
def get_results(
f_rev_size:
typing.Dict[str,
typing.Dict[str,
CodeSizeGeneratorWithSize.SizeEntry]]
) -> typing.List:
"""Return List of results in the format of:
[filename, new(text), old(text), change(text),
new(data), old(data), change(data)]
"""
res = []
for fname, revs_size in sorted(f_rev_size.items(), key=sort_by_k):
old_size = revs_size.get(old_rev)
new_size = revs_size.get(new_rev)
text_sect = cal_sect_change(old_size, new_size, 'text')
data_sect = cal_sect_change(old_size, new_size, 'data')
# skip the files that haven't changed in code size
if not show_all and text_sect[-1] == '0' and data_sect[-1] == '0':
continue
res.append([fname, *text_sect, *data_sect])
return res
# write header
output.write(line_format.format(*header_line))
if with_markdown:
output.write(line_format.format(*dash_line))
for mod in MBEDTLS_STATIC_LIB:
# convert self.code_size to:
# {
# file_name: {
# old_rev: SizeEntry,
# new_rev: SizeEntry
# },
# ...
# }
f_rev_size = {} #type: typing.Dict[str, typing.Dict]
for fname, size_entry in self.code_size[old_rev][mod].items():
f_rev_size.setdefault(fname, {}).update({old_rev: size_entry})
for fname, size_entry in self.code_size[new_rev][mod].items():
f_rev_size.setdefault(fname, {}).update({new_rev: size_entry})
mod_total_sz = f_rev_size.pop(mod + self.mod_total_suffix)
res = get_results(f_rev_size)
total_clm = get_results({mod + self.mod_total_suffix: mod_total_sz})
if with_markdown:
# bold row of mod-TOTALS in markdown table
total_clm = [[bold_text(j) for j in i] for i in total_clm]
res += total_clm
# write comparison result
for line in res:
output.write(line_format.format(*line))
class CodeSizeComparison:
"""Compare code size between two Git revisions."""
def __init__( #pylint: disable=too-many-arguments
self,
old_size_dist_info: CodeSizeDistinctInfo,
new_size_dist_info: CodeSizeDistinctInfo,
size_common_info: CodeSizeCommonInfo,
result_options: CodeSizeResultInfo,
logger: logging.Logger,
) -> None:
"""
:param old_size_dist_info: CodeSizeDistinctInfo containing old distinct
info to compare code size with.
:param new_size_dist_info: CodeSizeDistinctInfo containing new distinct
info to take as comparision base.
:param size_common_info: CodeSizeCommonInfo containing common info for
both old and new size distinct info and
measurement tool.
:param result_options: CodeSizeResultInfo containing results options for
code size record and comparision.
:param logger: logging module
"""
self.logger = logger
self.old_size_dist_info = old_size_dist_info
self.new_size_dist_info = new_size_dist_info
self.size_common_info = size_common_info
# infer pre make command
self.old_size_dist_info.pre_make_cmd = CodeSizeBuildInfo(
self.old_size_dist_info, self.size_common_info.host_arch,
self.logger).infer_pre_make_command()
self.new_size_dist_info.pre_make_cmd = CodeSizeBuildInfo(
self.new_size_dist_info, self.size_common_info.host_arch,
self.logger).infer_pre_make_command()
# infer make command
self.old_size_dist_info.make_cmd = CodeSizeBuildInfo(
self.old_size_dist_info, self.size_common_info.host_arch,
self.logger).infer_make_command()
self.new_size_dist_info.make_cmd = CodeSizeBuildInfo(
self.new_size_dist_info, self.size_common_info.host_arch,
self.logger).infer_make_command()
# initialize size parser with corresponding measurement tool
self.code_size_generator = self.__generate_size_parser()
self.result_options = result_options
self.csv_dir = os.path.abspath(self.result_options.record_dir)
os.makedirs(self.csv_dir, exist_ok=True)
self.comp_dir = os.path.abspath(self.result_options.comp_dir)
os.makedirs(self.comp_dir, exist_ok=True)
def __generate_size_parser(self):
"""Generate a parser for the corresponding measurement tool."""
if re.match(r'size', self.size_common_info.measure_cmd.strip()):
return CodeSizeGeneratorWithSize(self.logger)
else:
self.logger.error("Unsupported measurement tool: `{}`."
.format(self.size_common_info.measure_cmd
.strip().split(' ')[0]))
sys.exit(1)
def cal_code_size(
self,
size_dist_info: CodeSizeDistinctInfo
) -> typing.Dict[str, str]:
"""Calculate code size of library/*.o in a UTF-8 encoding"""
return CodeSizeCalculator(size_dist_info.git_rev,
size_dist_info.pre_make_cmd,
size_dist_info.make_cmd,
self.size_common_info.measure_cmd,
self.logger).cal_libraries_code_size()
def gen_code_size_report(self, size_dist_info: CodeSizeDistinctInfo) -> None:
"""Generate code size record and write it into a file."""
self.logger.info("Start to generate code size record for {}."
.format(size_dist_info.git_rev))
output_file = os.path.join(
self.csv_dir,
'{}-{}.csv'
.format(size_dist_info.get_info_indication(),
self.size_common_info.get_info_indication()))
# Check if the corresponding record exists
if size_dist_info.git_rev != "current" and \
os.path.exists(output_file):
self.logger.debug("Code size csv file for {} already exists."
.format(size_dist_info.git_rev))
self.code_size_generator.read_size_record(
size_dist_info.git_rev, output_file)
else:
# measure code size
code_size_text = self.cal_code_size(size_dist_info)
self.logger.debug("Generating code size csv for {}."
.format(size_dist_info.git_rev))
output = open(output_file, "w")
self.code_size_generator.write_record(
size_dist_info.git_rev, code_size_text, output)
def gen_code_size_comparison(self) -> None:
"""Generate results of code size changes between two Git revisions,
old and new.
- Measured code size result of these two Git revisions must be available.
- The result is directed into either file / stdout depending on
the option, size_common_info.result_options.stdout. (Default: file)
"""
self.logger.info("Start to generate comparision result between "\
"{} and {}."
.format(self.old_size_dist_info.git_rev,
self.new_size_dist_info.git_rev))
if self.result_options.stdout:
output = sys.stdout
else:
output_file = os.path.join(
self.comp_dir,
'{}-{}-{}.{}'
.format(self.old_size_dist_info.get_info_indication(),
self.new_size_dist_info.get_info_indication(),
self.size_common_info.get_info_indication(),
'md' if self.result_options.with_markdown else 'csv'))
output = open(output_file, "w")
self.logger.debug("Generating comparison results between {} and {}."
.format(self.old_size_dist_info.git_rev,
self.new_size_dist_info.git_rev))
if self.result_options.with_markdown or self.result_options.stdout:
print("Measure code size between {} and {} by `{}`."
.format(self.old_size_dist_info.get_info_indication(),
self.new_size_dist_info.get_info_indication(),
self.size_common_info.get_info_indication()),
file=output)
self.code_size_generator.write_comparison(
self.old_size_dist_info.git_rev,
self.new_size_dist_info.git_rev,
output, self.result_options.with_markdown,
self.result_options.show_all)
def get_comparision_results(self) -> None:
"""Compare size of library/*.o between self.old_size_dist_info and
self.old_size_dist_info and generate the result file."""
build_tree.check_repo_path()
self.gen_code_size_report(self.old_size_dist_info)
self.gen_code_size_report(self.new_size_dist_info)
self.gen_code_size_comparison()
def main():
parser = argparse.ArgumentParser(description=(__doc__))
group_required = parser.add_argument_group(
'required arguments',
'required arguments to parse for running ' + os.path.basename(__file__))
group_required.add_argument(
'-o', '--old-rev', type=str, required=True,
help='old Git revision for comparison.')
group_optional = parser.add_argument_group(
'optional arguments',
'optional arguments to parse for running ' + os.path.basename(__file__))
group_optional.add_argument(
'--record-dir', type=str, default='code_size_records',
help='directory where code size record is stored. '
'(Default: code_size_records)')
group_optional.add_argument(
'--comp-dir', type=str, default='comparison',
help='directory where comparison result is stored. '
'(Default: comparison)')
group_optional.add_argument(
'-n', '--new-rev', type=str, default='current',
help='new Git revision as comparison base. '
'(Default is the current work directory, including uncommitted '
'changes.)')
group_optional.add_argument(
'-a', '--arch', type=str, default=detect_arch(),
choices=list(map(lambda s: s.value, SupportedArch)),
help='Specify architecture for code size comparison. '
'(Default is the host architecture.)')
group_optional.add_argument(
'-c', '--config', type=str, default=SupportedConfig.DEFAULT.value,
choices=list(map(lambda s: s.value, SupportedConfig)),
help='Specify configuration type for code size comparison. '
'(Default is the current Mbed TLS configuration.)')
group_optional.add_argument(
'--markdown', action='store_true', dest='markdown',
help='Show comparision of code size in a markdown table. '
'(Only show the files that have changed).')
group_optional.add_argument(
'--stdout', action='store_true', dest='stdout',
help='Set this option to direct comparison result into sys.stdout. '
'(Default: file)')
group_optional.add_argument(
'--show-all', action='store_true', dest='show_all',
help='Show all the objects in comparison result, including the ones '
'that haven\'t changed in code size. (Default: False)')
group_optional.add_argument(
'--verbose', action='store_true', dest='verbose',
help='Show logs in detail for code size measurement. '
'(Default: False)')
comp_args = parser.parse_args()
logger = logging.getLogger()
logging_util.configure_logger(logger, split_level=logging.NOTSET)
logger.setLevel(logging.DEBUG if comp_args.verbose else logging.INFO)
if os.path.isfile(comp_args.record_dir):
logger.error("record directory: {} is not a directory"
.format(comp_args.record_dir))
sys.exit(1)
if os.path.isfile(comp_args.comp_dir):
logger.error("comparison directory: {} is not a directory"
.format(comp_args.comp_dir))
sys.exit(1)
comp_args.old_rev = CodeSizeCalculator.validate_git_revision(
comp_args.old_rev)
if comp_args.new_rev != 'current':
comp_args.new_rev = CodeSizeCalculator.validate_git_revision(
comp_args.new_rev)
# version, git_rev, arch, config, compiler, opt_level
old_size_dist_info = CodeSizeDistinctInfo(
'old', comp_args.old_rev, comp_args.arch, comp_args.config, 'cc', '-Os')
new_size_dist_info = CodeSizeDistinctInfo(
'new', comp_args.new_rev, comp_args.arch, comp_args.config, 'cc', '-Os')
# host_arch, measure_cmd
size_common_info = CodeSizeCommonInfo(
detect_arch(), 'size -t')
# record_dir, comp_dir, with_markdown, stdout, show_all
result_options = CodeSizeResultInfo(
comp_args.record_dir, comp_args.comp_dir,
comp_args.markdown, comp_args.stdout, comp_args.show_all)
logger.info("Measure code size between {} and {} by `{}`."
.format(old_size_dist_info.get_info_indication(),
new_size_dist_info.get_info_indication(),
size_common_info.get_info_indication()))
CodeSizeComparison(old_size_dist_info, new_size_dist_info,
size_common_info, result_options,
logger).get_comparision_results()
if __name__ == "__main__":
main()
-271
View File
@@ -1,271 +0,0 @@
#!/usr/bin/env python3
"""Check or fix the code style by running Uncrustify.
This script must be run from the root of a Git work tree containing Mbed TLS.
"""
# Copyright The Mbed TLS Contributors
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
import argparse
import os
import re
import subprocess
import sys
from typing import FrozenSet, List, Optional
UNCRUSTIFY_SUPPORTED_VERSION = "0.75.1"
CONFIG_FILE = ".uncrustify.cfg"
UNCRUSTIFY_EXE = "uncrustify"
UNCRUSTIFY_ARGS = ["-c", CONFIG_FILE]
CHECK_GENERATED_FILES = "tests/scripts/check-generated-files.sh"
def print_err(*args):
print("Error: ", *args, file=sys.stderr)
# Print the file names that will be skipped and the help message
def print_skip(files_to_skip):
print()
print(*files_to_skip, sep=", SKIP\n", end=", SKIP\n")
print("Warning: The listed files will be skipped because\n"
"they are not known to git.")
print()
# Match FILENAME(s) in "check SCRIPT (FILENAME...)"
CHECK_CALL_RE = re.compile(r"\n\s*check\s+[^\s#$&*?;|]+([^\n#$&*?;|]+)",
re.ASCII)
def list_generated_files() -> FrozenSet[str]:
"""Return the names of generated files.
We don't reformat generated files, since the result might be different
from the output of the generator. Ideally the result of the generator
would conform to the code style, but this would be difficult, especially
with respect to the placement of line breaks in long logical lines.
"""
# Parse check-generated-files.sh to get an up-to-date list of
# generated files. Read the file rather than calling it so that
# this script only depends on Git, Python and uncrustify, and not other
# tools such as sh or grep which might not be available on Windows.
# This introduces a limitation: check-generated-files.sh must have
# the expected format and must list the files explicitly, not through
# wildcards or command substitution.
content = open(CHECK_GENERATED_FILES, encoding="utf-8").read()
checks = re.findall(CHECK_CALL_RE, content)
return frozenset(word for s in checks for word in s.split())
# Check for comment string indicating an auto-generated file
AUTOGEN_RE = re.compile(r"Warning[ :-]+This file is (now )?auto[ -]?generated",
re.ASCII | re.IGNORECASE)
def is_file_autogenerated(filename):
content = open(filename, encoding="utf-8").read()
return AUTOGEN_RE.search(content) is not None
def get_src_files(since: Optional[str]) -> List[str]:
"""
Use git to get a list of the source files.
The optional argument since is a commit, indicating to only list files
that have changed since that commit. Without this argument, list all
files known to git.
Only C files are included, and certain files (generated, or third party)
are excluded.
"""
file_patterns = ["*.[hc]",
"tests/suites/*.function",
"tf-psa-crypto/tests/suites/*.function",
"scripts/data_files/*.fmt"]
output = subprocess.check_output(["git", "ls-files"] + file_patterns,
universal_newlines=True)
src_files = output.split()
# When this script is called from a git hook, some environment variables
# are set by default which force all git commands to use the main repository
# (i.e. prevent us from performing commands on the framework repo).
# Create an environment without these variables for running commands on the
# framework repo.
framework_env = os.environ.copy()
# Get a list of environment vars that git sets
git_env_vars = subprocess.check_output(["git", "rev-parse", "--local-env-vars"],
universal_newlines=True)
# Remove the vars from the environment
for var in git_env_vars.split():
framework_env.pop(var, None)
output = subprocess.check_output(["git", "-C", "framework", "ls-files"]
+ file_patterns,
universal_newlines=True,
env=framework_env)
framework_src_files = output.split()
if since:
# get all files changed in commits since the starting point in ...
# ... the main repository
cmd = ["git", "log", since + "..HEAD", "--ignore-submodules",
"--name-only", "--pretty=", "--"] + src_files
output = subprocess.check_output(cmd, universal_newlines=True)
committed_changed_files = output.split()
# ... the framework submodule
cmd = ["git", "-C", "framework", "log", since + "..HEAD",
"--name-only", "--pretty=", "--"] + framework_src_files
output = subprocess.check_output(cmd, universal_newlines=True,
env=framework_env)
committed_changed_files += ["framework/" + s for s in output.split()]
# and also get all files with uncommitted changes in ...
# ... the main repository
cmd = ["git", "diff", "--name-only", "--"] + src_files
output = subprocess.check_output(cmd, universal_newlines=True)
uncommitted_changed_files = output.split()
# ... the framework submodule
cmd = ["git", "-C", "framework", "diff", "--name-only", "--"] + \
framework_src_files
output = subprocess.check_output(cmd, universal_newlines=True,
env=framework_env)
uncommitted_changed_files += ["framework/" + s for s in output.split()]
src_files = committed_changed_files + uncommitted_changed_files
else:
src_files += ["framework/" + s for s in framework_src_files]
generated_files = list_generated_files()
# Don't correct style for third-party files (and, for simplicity,
# companion files in the same subtree), or for automatically
# generated files (we're correcting the templates instead).
src_files = [filename for filename in src_files
if not (filename.startswith("tf-psa-crypto/drivers/everest/") or
filename.startswith("tf-psa-crypto/drivers/p256-m/") or
filename in generated_files or
is_file_autogenerated(filename))]
return src_files
def get_uncrustify_version() -> str:
"""
Get the version string from Uncrustify
"""
result = subprocess.run([UNCRUSTIFY_EXE, "--version"],
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
check=False)
if result.returncode != 0:
print_err("Could not get Uncrustify version:", str(result.stderr, "utf-8"))
return ""
else:
return str(result.stdout, "utf-8")
def check_style_is_correct(src_file_list: List[str]) -> bool:
"""
Check the code style and output a diff for each file whose style is
incorrect.
"""
style_correct = True
for src_file in src_file_list:
uncrustify_cmd = [UNCRUSTIFY_EXE] + UNCRUSTIFY_ARGS + [src_file]
result = subprocess.run(uncrustify_cmd, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, check=False)
if result.returncode != 0:
print_err("Uncrustify returned " + str(result.returncode) +
" correcting file " + src_file)
return False
# Uncrustify makes changes to the code and places the result in a new
# file with the extension ".uncrustify". To get the changes (if any)
# simply diff the 2 files.
diff_cmd = ["diff", "-u", src_file, src_file + ".uncrustify"]
cp = subprocess.run(diff_cmd, check=False)
if cp.returncode == 1:
print(src_file + " changed - code style is incorrect.")
style_correct = False
elif cp.returncode != 0:
raise subprocess.CalledProcessError(cp.returncode, cp.args,
cp.stdout, cp.stderr)
# Tidy up artifact
os.remove(src_file + ".uncrustify")
return style_correct
def fix_style_single_pass(src_file_list: List[str]) -> bool:
"""
Run Uncrustify once over the source files.
"""
code_change_args = UNCRUSTIFY_ARGS + ["--no-backup"]
for src_file in src_file_list:
uncrustify_cmd = [UNCRUSTIFY_EXE] + code_change_args + [src_file]
result = subprocess.run(uncrustify_cmd, check=False)
if result.returncode != 0:
print_err("Uncrustify with file returned: " +
str(result.returncode) + " correcting file " +
src_file)
return False
return True
def fix_style(src_file_list: List[str]) -> int:
"""
Fix the code style. This takes 2 passes of Uncrustify.
"""
if not fix_style_single_pass(src_file_list):
return 1
if not fix_style_single_pass(src_file_list):
return 1
# Guard against future changes that cause the codebase to require
# more passes.
if not check_style_is_correct(src_file_list):
print_err("Code style still incorrect after second run of Uncrustify.")
return 1
else:
return 0
def main() -> int:
"""
Main with command line arguments.
"""
uncrustify_version = get_uncrustify_version().strip()
if UNCRUSTIFY_SUPPORTED_VERSION not in uncrustify_version:
print("Warning: Using unsupported Uncrustify version '" +
uncrustify_version + "'")
print("Note: The only supported version is " +
UNCRUSTIFY_SUPPORTED_VERSION)
parser = argparse.ArgumentParser()
parser.add_argument('-f', '--fix', action='store_true',
help=('modify source files to fix the code style '
'(default: print diff, do not modify files)'))
parser.add_argument('-s', '--since', metavar='COMMIT', const='development', nargs='?',
help=('only check files modified since the specified commit'
' (e.g. --since=HEAD~3 or --since=development). If no'
' commit is specified, default to development.'))
# --subset is almost useless: it only matters if there are no files
# ('code_style.py' without arguments checks all files known to Git,
# 'code_style.py --subset' does nothing). In particular,
# 'code_style.py --fix --subset ...' is intended as a stable ("porcelain")
# way to restyle a possibly empty set of files.
parser.add_argument('--subset', action='store_true',
help='only check the specified files (default with non-option arguments)')
parser.add_argument('operands', nargs='*', metavar='FILE',
help='files to check (files MUST be known to git, if none: check all)')
args = parser.parse_args()
covered = frozenset(get_src_files(args.since))
# We only check files that are known to git
if args.subset or args.operands:
src_files = [f for f in args.operands if f in covered]
skip_src_files = [f for f in args.operands if f not in covered]
if skip_src_files:
print_skip(skip_src_files)
else:
src_files = list(covered)
if args.fix:
# Fix mode
return fix_style(src_files)
else:
# Check mode
if check_style_is_correct(src_files):
print("Checked {} files, style ok.".format(len(src_files)))
return 0
else:
return 1
if __name__ == '__main__':
sys.exit(main())
-168
View File
@@ -1,168 +0,0 @@
# To compile on SunOS: add "-lsocket -lnsl" to LDFLAGS
ifndef MBEDTLS_PATH
MBEDTLS_PATH := ..
endif
PSASIM_PATH=$(MBEDTLS_PATH)/tests/psa-client-server/psasim
ifeq (,$(wildcard $(MBEDTLS_PATH)/framework/exported.make))
# Use the define keyword to get a multi-line message.
# GNU make appends ". Stop.", so tweak the ending of our message accordingly.
define error_message
$(MBEDTLS_PATH)/framework/exported.make not found.
Run `git submodule update --init` to fetch the submodule contents.
This is a fatal error
endef
$(error $(error_message))
endif
include $(MBEDTLS_PATH)/framework/exported.make
CFLAGS ?= -O2
WARNING_CFLAGS ?= -Wall -Wextra -Wformat=2 -Wno-format-nonliteral
WARNING_CXXFLAGS ?= -Wall -Wextra -Wformat=2 -Wno-format-nonliteral -std=c++11 -pedantic
LDFLAGS ?=
LOCAL_CFLAGS = $(WARNING_CFLAGS) -I$(MBEDTLS_TEST_PATH)/include \
-I$(MBEDTLS_PATH)/include -I$(MBEDTLS_PATH)/tf-psa-crypto/include \
-I$(MBEDTLS_PATH)/tf-psa-crypto/drivers/builtin/include \
-D_FILE_OFFSET_BITS=64
LOCAL_CXXFLAGS = $(WARNING_CXXFLAGS) $(LOCAL_CFLAGS)
ifdef PSASIM
LOCAL_LDFLAGS = ${MBEDTLS_TEST_OBJS} \
-L$(PSASIM_PATH)/client_libs \
-lpsaclient \
-lmbedtls$(SHARED_SUFFIX) \
-lmbedx509$(SHARED_SUFFIX) \
-lmbedcrypto$(SHARED_SUFFIX)
else
LOCAL_LDFLAGS = ${MBEDTLS_TEST_OBJS} \
-L$(MBEDTLS_PATH)/library \
-lmbedtls$(SHARED_SUFFIX) \
-lmbedx509$(SHARED_SUFFIX) \
-lmbedcrypto$(SHARED_SUFFIX)
endif
THIRDPARTY_DIR = $(MBEDTLS_PATH)/tf-psa-crypto/drivers
include $(THIRDPARTY_DIR)/everest/Makefile.inc
include $(THIRDPARTY_DIR)/p256-m/Makefile.inc
LOCAL_CFLAGS+=$(THIRDPARTY_INCLUDES)
ifdef PSASIM
MBEDLIBS=$(PSASIM_PATH)/client_libs/libmbedcrypto.a \
$(PSASIM_PATH)/client_libs/libmbedx509.a \
$(PSASIM_PATH)/client_libs/libmbedtls.a \
$(PSASIM_PATH)/client_libs/libpsaclient.a
else ifndef SHARED
MBEDLIBS=$(MBEDTLS_PATH)/library/libmbedcrypto.a \
$(MBEDTLS_PATH)/library/libmbedx509.a \
$(MBEDTLS_PATH)/library/libmbedtls.a
else
MBEDLIBS=$(MBEDTLS_PATH)/library/libmbedcrypto.$(DLEXT) \
$(MBEDTLS_PATH)/library/libmbedx509.$(DLEXT) \
$(MBEDTLS_PATH)/library/libmbedtls.$(DLEXT)
endif
ifdef DEBUG
LOCAL_CFLAGS += -g3
endif
# if we're running on Windows, build for Windows
ifdef WINDOWS
WINDOWS_BUILD=1
endif
## Usage: $(call remove_enabled_options,PREPROCESSOR_INPUT)
## Remove the preprocessor symbols that are set in the current configuration
## from PREPROCESSOR_INPUT. Also normalize whitespace.
## Example:
## $(call remove_enabled_options,MBEDTLS_FOO MBEDTLS_BAR)
## This expands to an empty string "" if MBEDTLS_FOO and MBEDTLS_BAR are both
## enabled, to "MBEDTLS_FOO" if MBEDTLS_BAR is enabled but MBEDTLS_FOO is
## disabled, etc.
##
## This only works with a Unix-like shell environment (Bourne/POSIX-style shell
## and standard commands) and a Unix-like compiler (supporting -E). In
## other environments, the output is likely to be empty.
define remove_enabled_options
$(strip $(shell
exec 2>/dev/null;
{ echo '#include <mbedtls/build_info.h>'; echo $(1); } |
$(CC) $(LOCAL_CFLAGS) $(CFLAGS) -E - |
tail -n 1
))
endef
ifdef WINDOWS_BUILD
DLEXT=dll
EXEXT=.exe
LOCAL_LDFLAGS += -lws2_32 -lbcrypt
ifdef SHARED
SHARED_SUFFIX=.$(DLEXT)
endif
else # Not building for Windows
DLEXT ?= so
EXEXT=
SHARED_SUFFIX=
ifndef THREADING
# Auto-detect configurations with pthread.
# If the call to remove_enabled_options returns "control", the symbols
# are confirmed set and we link with pthread.
# If the auto-detection fails, the result of the call is empty and
# we keep THREADING undefined.
ifeq (control,$(call remove_enabled_options,control MBEDTLS_THREADING_C MBEDTLS_THREADING_PTHREAD))
THREADING := pthread
endif
endif
ifeq ($(THREADING),pthread)
LOCAL_LDFLAGS += -lpthread
endif
endif
ifdef WINDOWS
PYTHON ?= python
else
PYTHON ?= $(shell if type python3 >/dev/null 2>/dev/null; then echo python3; else echo python; fi)
endif
# See root Makefile
GEN_FILES ?= yes
ifdef GEN_FILES
gen_file_dep =
else
gen_file_dep = |
endif
default: all
$(MBEDLIBS):
$(MAKE) -C $(MBEDTLS_PATH)/library
neat: clean
ifndef WINDOWS
rm -f $(GENERATED_FILES)
else
for %f in ($(subst /,\,$(GENERATED_FILES))) if exist %f del /Q /F %f
endif
# Auxiliary modules used by tests and some sample programs
MBEDTLS_CORE_TEST_OBJS := $(patsubst %.c,%.o,$(wildcard \
${MBEDTLS_TEST_PATH}/src/*.c \
${MBEDTLS_TEST_PATH}/src/drivers/*.c \
))
# Ignore PSA stubs when building for the client side of PSASIM (i.e.
# CRYPTO_CLIENT && !CRYPTO_C) otherwise there will be functions duplicates.
ifdef PSASIM
MBEDTLS_CORE_TEST_OBJS := $(filter-out \
${MBEDTLS_TEST_PATH}/src/psa_crypto_stubs.o, $(MBEDTLS_CORE_TEST_OBJS)\
)
endif
# Additional auxiliary modules for TLS testing
MBEDTLS_TLS_TEST_OBJS = $(patsubst %.c,%.o,$(wildcard \
${MBEDTLS_TEST_PATH}/src/test_helpers/*.c \
))
MBEDTLS_TEST_OBJS = $(MBEDTLS_CORE_TEST_OBJS) $(MBEDTLS_TLS_TEST_OBJS)
-14
View File
@@ -1,14 +0,0 @@
#!/usr/bin/env perl
# Backward compatibility redirection
## Copyright The Mbed TLS Contributors
## SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
##
my $py = $0;
$py =~ s/\.pl$/.py/ or die "Unable to determine the name of the Python script";
exec 'python3', $py, @ARGV;
print STDERR "$0: python3: $!. Trying python instead.\n";
exec 'python', $py, @ARGV;
print STDERR "$0: python: $!\n";
exit 127;
-509
View File
@@ -1,509 +0,0 @@
#!/usr/bin/env python3
"""Mbed TLS and PSA configuration file manipulation library and tool
Basic usage, to read the Mbed TLS configuration:
config = CombinedConfigFile()
if 'MBEDTLS_RSA_C' in config: print('RSA is enabled')
"""
## Copyright The Mbed TLS Contributors
## SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
##
import os
import re
import sys
import framework_scripts_path # pylint: disable=unused-import
from mbedtls_framework import config_common
def is_boolean_setting(name, value):
"""Is this a boolean setting?
Mbed TLS boolean settings are enabled if the preprocessor macro is
defined, and disabled if the preprocessor macro is not defined. The
macro definition line in the configuration file has an empty expansion.
PSA_WANT_xxx settings are also boolean, but when they are enabled,
they expand to a nonzero value. We leave them undefined when they
are disabled. (Setting them to 0 currently means to enable them, but
this might change to mean disabling them. Currently we just never set
them to 0.)
"""
if name.startswith('PSA_WANT_'):
return True
if not value:
return True
return False
def realfull_adapter(_name, _value, _active):
"""Activate all symbols.
This is intended for building the documentation, including the
documentation of settings that are activated by defining an optional
preprocessor macro. There is no expectation that the resulting
configuration can be built.
"""
return True
PSA_UNSUPPORTED_FEATURE = frozenset([
'PSA_WANT_ALG_CBC_MAC',
'PSA_WANT_ALG_XTS',
'PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_DERIVE',
'PSA_WANT_KEY_TYPE_DH_KEY_PAIR_DERIVE'
])
PSA_DEPRECATED_FEATURE = frozenset([
'PSA_WANT_KEY_TYPE_ECC_KEY_PAIR',
'PSA_WANT_KEY_TYPE_RSA_KEY_PAIR'
])
PSA_UNSTABLE_FEATURE = frozenset([
'PSA_WANT_ECC_SECP_K1_224'
])
EXCLUDE_FROM_CRYPTO = PSA_UNSUPPORTED_FEATURE | \
PSA_DEPRECATED_FEATURE | \
PSA_UNSTABLE_FEATURE
# The goal of the full configuration is to have everything that can be tested
# together. This includes deprecated or insecure options. It excludes:
# * Options that require additional build dependencies or unusual hardware.
# * Options that make testing less effective.
# * Options that are incompatible with other options, or more generally that
# interact with other parts of the code in such a way that a bulk enabling
# is not a good way to test them.
# * Options that remove features.
EXCLUDE_FROM_FULL = frozenset([
#pylint: disable=line-too-long
'MBEDTLS_AES_ONLY_128_BIT_KEY_LENGTH', # interacts with CTR_DRBG_128_BIT_KEY
'MBEDTLS_AES_USE_HARDWARE_ONLY', # hardware dependency
'MBEDTLS_BLOCK_CIPHER_NO_DECRYPT', # incompatible with ECB in PSA, CBC/XTS/NIST_KW/DES
'MBEDTLS_CTR_DRBG_USE_128_BIT_KEY', # interacts with ENTROPY_FORCE_SHA256
'MBEDTLS_DEPRECATED_REMOVED', # conflicts with deprecated options
'MBEDTLS_DEPRECATED_WARNING', # conflicts with deprecated options
'MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED', # influences the use of ECDH in TLS
'MBEDTLS_ECP_WITH_MPI_UINT', # disables the default ECP and is experimental
'MBEDTLS_ENTROPY_FORCE_SHA256', # interacts with CTR_DRBG_128_BIT_KEY
'MBEDTLS_HAVE_SSE2', # hardware dependency
'MBEDTLS_MEMORY_BACKTRACE', # depends on MEMORY_BUFFER_ALLOC_C
'MBEDTLS_MEMORY_BUFFER_ALLOC_C', # makes sanitizers (e.g. ASan) less effective
'MBEDTLS_MEMORY_DEBUG', # depends on MEMORY_BUFFER_ALLOC_C
'MBEDTLS_NO_64BIT_MULTIPLICATION', # influences anything that uses bignum
'MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES', # removes a feature
'MBEDTLS_NO_PLATFORM_ENTROPY', # removes a feature
'MBEDTLS_NO_UDBL_DIVISION', # influences anything that uses bignum
'MBEDTLS_PSA_P256M_DRIVER_ENABLED', # influences SECP256R1 KeyGen/ECDH/ECDSA
'MBEDTLS_PLATFORM_NO_STD_FUNCTIONS', # removes a feature
'MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS', # removes a feature
'MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG', # behavior change + build dependency
'MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER', # interface and behavior change
'MBEDTLS_PSA_CRYPTO_SPM', # platform dependency (PSA SPM)
'MBEDTLS_PSA_INJECT_ENTROPY', # conflicts with platform entropy sources
'MBEDTLS_RSA_NO_CRT', # influences the use of RSA in X.509 and TLS
'MBEDTLS_SHA256_USE_A64_CRYPTO_ONLY', # interacts with *_USE_A64_CRYPTO_IF_PRESENT
'MBEDTLS_SHA256_USE_ARMV8_A_CRYPTO_ONLY', # interacts with *_USE_ARMV8_A_CRYPTO_IF_PRESENT
'MBEDTLS_SHA512_USE_A64_CRYPTO_ONLY', # interacts with *_USE_A64_CRYPTO_IF_PRESENT
'MBEDTLS_SHA256_USE_A64_CRYPTO_IF_PRESENT', # setting *_USE_ARMV8_A_CRYPTO is sufficient
'MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN', # build dependency (clang+memsan)
'MBEDTLS_TEST_CONSTANT_FLOW_VALGRIND', # build dependency (valgrind headers)
'MBEDTLS_X509_REMOVE_INFO', # removes a feature
*PSA_UNSUPPORTED_FEATURE,
*PSA_DEPRECATED_FEATURE,
*PSA_UNSTABLE_FEATURE
])
def is_seamless_alt(name):
"""Whether the xxx_ALT symbol should be included in the full configuration.
Include alternative implementations of platform functions, which are
configurable function pointers that default to the built-in function.
This way we test that the function pointers exist and build correctly
without changing the behavior, and tests can verify that the function
pointers are used by modifying those pointers.
Exclude alternative implementations of library functions since they require
an implementation of the relevant functions and an xxx_alt.h header.
"""
if name in (
'MBEDTLS_PLATFORM_GMTIME_R_ALT',
'MBEDTLS_PLATFORM_SETUP_TEARDOWN_ALT',
'MBEDTLS_PLATFORM_MS_TIME_ALT',
'MBEDTLS_PLATFORM_ZEROIZE_ALT',
):
# Similar to non-platform xxx_ALT, requires platform_alt.h
return False
return name.startswith('MBEDTLS_PLATFORM_')
def include_in_full(name):
"""Rules for symbols in the "full" configuration."""
if name in EXCLUDE_FROM_FULL:
return False
if name.endswith('_ALT'):
return is_seamless_alt(name)
return True
def full_adapter(name, value, active):
"""Config adapter for "full"."""
if not is_boolean_setting(name, value):
return active
return include_in_full(name)
# The baremetal configuration excludes options that require a library or
# operating system feature that is typically not present on bare metal
# systems. Features that are excluded from "full" won't be in "baremetal"
# either (unless explicitly turned on in baremetal_adapter) so they don't
# need to be repeated here.
EXCLUDE_FROM_BAREMETAL = frozenset([
#pylint: disable=line-too-long
'MBEDTLS_ENTROPY_NV_SEED', # requires a filesystem and FS_IO or alternate NV seed hooks
'MBEDTLS_FS_IO', # requires a filesystem
'MBEDTLS_HAVE_TIME', # requires a clock
'MBEDTLS_HAVE_TIME_DATE', # requires a clock
'MBEDTLS_NET_C', # requires POSIX-like networking
'MBEDTLS_PLATFORM_FPRINTF_ALT', # requires FILE* from stdio.h
'MBEDTLS_PLATFORM_NV_SEED_ALT', # requires a filesystem and ENTROPY_NV_SEED
'MBEDTLS_PLATFORM_TIME_ALT', # requires a clock and HAVE_TIME
'MBEDTLS_PSA_CRYPTO_SE_C', # requires a filesystem and PSA_CRYPTO_STORAGE_C
'MBEDTLS_PSA_CRYPTO_STORAGE_C', # requires a filesystem
'MBEDTLS_PSA_ITS_FILE_C', # requires a filesystem
'MBEDTLS_THREADING_C', # requires a threading interface
'MBEDTLS_THREADING_PTHREAD', # requires pthread
'MBEDTLS_TIMING_C', # requires a clock
'MBEDTLS_SHA256_USE_A64_CRYPTO_IF_PRESENT', # requires an OS for runtime-detection
'MBEDTLS_SHA256_USE_ARMV8_A_CRYPTO_IF_PRESENT', # requires an OS for runtime-detection
'MBEDTLS_SHA512_USE_A64_CRYPTO_IF_PRESENT', # requires an OS for runtime-detection
])
def keep_in_baremetal(name):
"""Rules for symbols in the "baremetal" configuration."""
if name in EXCLUDE_FROM_BAREMETAL:
return False
return True
def baremetal_adapter(name, value, active):
"""Config adapter for "baremetal"."""
if not is_boolean_setting(name, value):
return active
if name == 'MBEDTLS_NO_PLATFORM_ENTROPY':
# No OS-provided entropy source
return True
return include_in_full(name) and keep_in_baremetal(name)
# This set contains options that are mostly for debugging or test purposes,
# and therefore should be excluded when doing code size measurements.
# Options that are their own module (such as MBEDTLS_ERROR_C) are not listed
# and therefore will be included when doing code size measurements.
EXCLUDE_FOR_SIZE = frozenset([
'MBEDTLS_DEBUG_C', # large code size increase in TLS
'MBEDTLS_SELF_TEST', # increases the size of many modules
'MBEDTLS_TEST_HOOKS', # only useful with the hosted test framework, increases code size
])
def baremetal_size_adapter(name, value, active):
if name in EXCLUDE_FOR_SIZE:
return False
return baremetal_adapter(name, value, active)
def include_in_crypto(name):
"""Rules for symbols in a crypto configuration."""
if name.startswith('MBEDTLS_X509_') or \
name.startswith('MBEDTLS_SSL_') or \
name.startswith('MBEDTLS_KEY_EXCHANGE_'):
return False
if name in [
'MBEDTLS_DEBUG_C', # part of libmbedtls
'MBEDTLS_NET_C', # part of libmbedtls
'MBEDTLS_PKCS7_C', # part of libmbedx509
]:
return False
if name in EXCLUDE_FROM_CRYPTO:
return False
return True
def crypto_adapter(adapter):
"""Modify an adapter to disable non-crypto symbols.
``crypto_adapter(adapter)(name, value, active)`` is like
``adapter(name, value, active)``, but unsets all X.509 and TLS symbols.
"""
def continuation(name, value, active):
if not include_in_crypto(name):
return False
if adapter is None:
return active
return adapter(name, value, active)
return continuation
DEPRECATED = frozenset([
'MBEDTLS_PSA_CRYPTO_SE_C',
*PSA_DEPRECATED_FEATURE
])
def no_deprecated_adapter(adapter):
"""Modify an adapter to disable deprecated symbols.
``no_deprecated_adapter(adapter)(name, value, active)`` is like
``adapter(name, value, active)``, but unsets all deprecated symbols
and sets ``MBEDTLS_DEPRECATED_REMOVED``.
"""
def continuation(name, value, active):
if name == 'MBEDTLS_DEPRECATED_REMOVED':
return True
if name in DEPRECATED:
return False
if adapter is None:
return active
return adapter(name, value, active)
return continuation
def no_platform_adapter(adapter):
"""Modify an adapter to disable platform symbols.
``no_platform_adapter(adapter)(name, value, active)`` is like
``adapter(name, value, active)``, but unsets all platform symbols other
``than MBEDTLS_PLATFORM_C.
"""
def continuation(name, value, active):
# Allow MBEDTLS_PLATFORM_C but remove all other platform symbols.
if name.startswith('MBEDTLS_PLATFORM_') and name != 'MBEDTLS_PLATFORM_C':
return False
if adapter is None:
return active
return adapter(name, value, active)
return continuation
class MbedTLSConfigFile(config_common.ConfigFile):
"""Representation of an MbedTLS configuration file."""
_path_in_tree = 'include/mbedtls/mbedtls_config.h'
default_path = [_path_in_tree,
os.path.join(os.path.dirname(__file__),
os.pardir,
_path_in_tree),
os.path.join(os.path.dirname(os.path.abspath(os.path.dirname(__file__))),
_path_in_tree)]
def __init__(self, filename=None):
super().__init__(self.default_path, 'Mbed TLS', filename)
self.current_section = 'header'
class CryptoConfigFile(config_common.ConfigFile):
"""Representation of a Crypto configuration file."""
# Temporary, while Mbed TLS does not just rely on the TF-PSA-Crypto
# build system to build its crypto library. When it does, the
# condition can just be removed.
_path_in_tree = ('include/psa/crypto_config.h'
if not os.path.isdir(os.path.join(os.path.dirname(__file__),
os.pardir,
'tf-psa-crypto')) else
'tf-psa-crypto/include/psa/crypto_config.h')
default_path = [_path_in_tree,
os.path.join(os.path.dirname(__file__),
os.pardir,
_path_in_tree),
os.path.join(os.path.dirname(os.path.abspath(os.path.dirname(__file__))),
_path_in_tree)]
def __init__(self, filename=None):
super().__init__(self.default_path, 'Crypto', filename)
class MbedTLSConfig(config_common.Config):
"""Representation of the Mbed TLS configuration.
See the documentation of the `Config` class for methods to query
and modify the configuration.
"""
def __init__(self, filename=None):
"""Read the Mbed TLS configuration file."""
super().__init__()
configfile = MbedTLSConfigFile(filename)
self.configfiles.append(configfile)
self.settings.update({name: config_common.Setting(configfile, active, name, value, section)
for (active, name, value, section)
in configfile.parse_file()})
def set(self, name, value=None):
"""Set name to the given value and make it active."""
if name not in self.settings:
self._get_configfile().templates.append((name, '', '#define ' + name + ' '))
super().set(name, value)
class CryptoConfig(config_common.Config):
"""Representation of the PSA crypto configuration.
See the documentation of the `Config` class for methods to query
and modify the configuration.
"""
def __init__(self, filename=None):
"""Read the PSA crypto configuration file."""
super().__init__()
configfile = CryptoConfigFile(filename)
self.configfiles.append(configfile)
self.settings.update({name: config_common.Setting(configfile, active, name, value, section)
for (active, name, value, section)
in configfile.parse_file()})
def set(self, name, value='1'):
"""Set name to the given value and make it active."""
if name in PSA_UNSUPPORTED_FEATURE:
raise ValueError(f'Feature is unsupported: \'{name}\'')
if name in PSA_UNSTABLE_FEATURE:
raise ValueError(f'Feature is unstable: \'{name}\'')
if name not in self.settings:
self._get_configfile().templates.append((name, '', '#define ' + name + ' '))
super().set(name, value)
class CombinedConfig(config_common.Config):
"""Representation of MbedTLS and PSA crypto configuration
See the documentation of the `Config` class for methods to query
and modify the configuration.
"""
def __init__(self, *configs):
super().__init__()
for config in configs:
if isinstance(config, MbedTLSConfigFile):
self.mbedtls_configfile = config
elif isinstance(config, CryptoConfigFile):
self.crypto_configfile = config
else:
raise ValueError(f'Invalid configfile: {config}')
self.configfiles.append(config)
self.settings.update({name: config_common.Setting(configfile, active, name, value, section)
for configfile in [self.mbedtls_configfile, self.crypto_configfile]
for (active, name, value, section) in configfile.parse_file()})
_crypto_regexp = re.compile(r'$PSA_.*')
def _get_configfile(self, name=None):
"""Find a config type for a setting name"""
if name in self.settings:
return self.settings[name].configfile
elif re.match(self._crypto_regexp, name):
return self.crypto_configfile
else:
return self.mbedtls_configfile
def set(self, name, value=None):
"""Set name to the given value and make it active."""
configfile = self._get_configfile(name)
if configfile == self.crypto_configfile:
if name in PSA_UNSUPPORTED_FEATURE:
raise ValueError(f'Feature is unsupported: \'{name}\'')
if name in PSA_UNSTABLE_FEATURE:
raise ValueError(f'Feature is unstable: \'{name}\'')
# The default value in the crypto config is '1'
if not value:
value = '1'
if name not in self.settings:
configfile.templates.append((name, '', '#define ' + name + ' '))
super().set(name, value)
#pylint: disable=arguments-differ
def write(self, mbedtls_file=None, crypto_file=None):
"""Write the whole configuration to the file it was read from.
If mbedtls_file or crypto_file is specified, write the specific configuration
to the corresponding file instead.
Two file name parameters and not only one as in the super class as we handle
two configuration files in this class.
"""
self.mbedtls_configfile.write(self.settings, mbedtls_file)
self.crypto_configfile.write(self.settings, crypto_file)
def filename(self, name=None):
"""Get the name of the config files.
If 'name' is specified return the name of the config file where it is defined.
"""
if not name:
return [config.filename for config in [self.mbedtls_configfile, self.crypto_configfile]]
return self._get_configfile(name).filename
class MbedTLSConfigTool(config_common.ConfigTool):
"""Command line mbedtls_config.h and crypto_config.h manipulation tool."""
def __init__(self):
super().__init__(MbedTLSConfigFile.default_path)
self.config = CombinedConfig(MbedTLSConfigFile(self.args.file),
CryptoConfigFile(self.args.cryptofile))
def custom_parser_options(self):
"""Adds MbedTLS specific options for the parser."""
self.parser.add_argument(
'--cryptofile', '-c',
help="""Crypto file to read (and modify if requested). Default: {}."""
.format(CryptoConfigFile.default_path))
self.add_adapter(
'baremetal', baremetal_adapter,
"""Like full, but exclude features that require platform features
such as file input-output.
""")
self.add_adapter(
'baremetal_size', baremetal_size_adapter,
"""Like baremetal, but exclude debugging features. Useful for code size measurements.
""")
self.add_adapter(
'full', full_adapter,
"""Uncomment most features.
Exclude alternative implementations and platform support options, as well as
some options that are awkward to test.
""")
self.add_adapter(
'full_no_deprecated', no_deprecated_adapter(full_adapter),
"""Uncomment most non-deprecated features.
Like "full", but without deprecated features.
""")
self.add_adapter(
'full_no_platform', no_platform_adapter(full_adapter),
"""Uncomment most non-platform features. Like "full", but without platform features.
""")
self.add_adapter(
'realfull', realfull_adapter,
"""Uncomment all boolean #defines.
Suitable for generating documentation, but not for building.
""")
self.add_adapter(
'crypto', crypto_adapter(None),
"""Only include crypto features. Exclude X.509 and TLS.""")
self.add_adapter(
'crypto_baremetal', crypto_adapter(baremetal_adapter),
"""Like baremetal, but with only crypto features, excluding X.509 and TLS.""")
self.add_adapter(
'crypto_full', crypto_adapter(full_adapter),
"""Like full, but with only crypto features, excluding X.509 and TLS.""")
if __name__ == '__main__':
sys.exit(MbedTLSConfigTool().main())
@@ -1,71 +0,0 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"_comment": {
"type": "string"
},
"prefix": {
"type": "string",
"pattern": "^[A-Z_a-z][0-9A-Z_a-z]*$"
},
"type": {
"type": "string",
"const": ["opaque"]
},
"location": {
"type": ["integer","string"],
"pattern": "^(0x|0X)?[a-fA-F0-9]+$"
},
"mbedtls/h_condition": {
"type": "string"
},
"headers": {
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"capabilities": {
"type": "array",
"items": [
{
"type": "object",
"properties": {
"_comment": {
"type": "string"
},
"mbedtls/c_condition": {
"type": "string"
},
"entry_points": {
"type": "array",
"items": {
"type": "string"
}
},
"names": {
"type": "object",
"patternProperties": {
"^[A-Z_a-z][0-9A-Z_a-z]*$": {
"type": "string",
"pattern": "^[A-Z_a-z][0-9A-Z_a-z]*$"
}
}
}
},
"required": [
"entry_points"
]
}
]
}
},
"required": [
"prefix",
"type",
"location",
"capabilities"
]
}
@@ -1,70 +0,0 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"_comment": {
"type": "string"
},
"prefix": {
"type": "string",
"pattern": "^[A-Z_a-z][0-9A-Z_a-z]*$"
},
"type": {
"type": "string",
"const": ["transparent"]
},
"mbedtls/h_condition": {
"type": "string"
},
"headers": {
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"capabilities": {
"type": "array",
"items": [
{
"type": "object",
"properties": {
"_comment": {
"type": "string"
},
"mbedtls/c_condition": {
"type": "string"
},
"entry_points": {
"type": "array",
"items": {
"type": "string"
}
},
"names": {
"type": "object",
"patternProperties": {
"^[A-Z_a-z][0-9A-Z_a-z]*$": {
"type": "string",
"pattern": "^[A-Z_a-z][0-9A-Z_a-z]*$"
}
}
},
"fallback": {
"type": "boolean",
"default": "false"
}
},
"required": [
"entry_points"
]
}
]
}
},
"required": [
"prefix",
"type",
"capabilities"
]
}
@@ -1 +0,0 @@
["mbedtls_test_opaque_driver.json","mbedtls_test_transparent_driver.json","p256_transparent_driver.json"]
@@ -1,20 +0,0 @@
{
"prefix": "mbedtls_test",
"type": "opaque",
"location": "0x7fffff",
"mbedtls/h_condition": "defined(PSA_CRYPTO_DRIVER_TEST)",
"headers": ["test/drivers/test_driver.h"],
"capabilities": [
{
"_comment": "The Mbed TLS opaque driver supports import key/export key/export_public key",
"mbedtls/c_condition": "defined(PSA_CRYPTO_DRIVER_TEST)",
"entry_points": ["import_key", "export_key", "export_public_key"]
},
{
"_comment": "The Mbed TLS opaque driver supports copy key/ get builtin key",
"mbedtls/c_condition": "defined(PSA_CRYPTO_DRIVER_TEST)",
"entry_points": ["copy_key", "get_builtin_key"],
"names": {"copy_key":"mbedtls_test_opaque_copy_key", "get_builtin_key":"mbedtls_test_opaque_get_builtin_key"}
}
]
}
@@ -1,22 +0,0 @@
{
"prefix": "mbedtls_test",
"type": "transparent",
"mbedtls/h_condition": "defined(PSA_CRYPTO_DRIVER_TEST)",
"headers": ["test/drivers/test_driver.h"],
"capabilities": [
{
"_comment": "The Mbed TLS transparent driver supports import key/export key",
"mbedtls/c_condition": "defined(PSA_CRYPTO_DRIVER_TEST)",
"entry_points": ["import_key"],
"fallback": true
},
{
"_comment": "The Mbed TLS transparent driver supports export_public key",
"mbedtls/c_condition": "defined(PSA_CRYPTO_DRIVER_TEST)",
"entry_points": ["export_public_key"],
"fallback": true,
"names": {"export_public_key":"mbedtls_test_transparent_export_public_key"}
}
]
}
@@ -1,20 +0,0 @@
{
"prefix": "p256",
"type": "transparent",
"mbedtls/h_condition": "defined(MBEDTLS_PSA_P256M_DRIVER_ENABLED)",
"headers": ["../tf-psa-crypto/drivers/p256-m/p256-m_driver_entrypoints.h"],
"capabilities": [
{
"mbedtls/c_condition": "defined(MBEDTLS_PSA_P256M_DRIVER_ENABLED)",
"_comment_entry_points": "This is not the complete list of entry points supported by this driver, only those that are currently supported in JSON. See docs/psa-driver-example-and-guide.md",
"entry_points": ["import_key", "export_public_key"],
"algorithms": ["PSA_ALG_ECDH", "PSA_ALG_ECDSA(PSA_ALG_ANY_HASH)"],
"key_types": [
"PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)",
"PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1)"
],
"key_sizes": [256],
"fallback": false
}
]
}
@@ -1,17 +0,0 @@
{# One Shot function's dispatch code for opaque drivers.
Expected inputs:
* drivers: the list of driver descriptions.
* entry_point: the name of the entry point that this function dispatches to.
* entry_point_param(driver): the parameters to pass to the entry point.
* nest_indent: number of extra spaces to indent the code to.
-#}
{% for driver in drivers if driver.type == "opaque" -%}
{% for capability in driver.capabilities if entry_point in capability.entry_points -%}
#if ({% if capability['mbedtls/c_condition'] is defined -%}{{ capability['mbedtls/c_condition'] }} {% else -%} {{ 1 }} {% endif %})
{%- filter indent(width = nest_indent) %}
case {{ driver.location }}:
return( {{ entry_point_name(capability, entry_point, driver) }}({{entry_point_param(driver) | indent(20)}}));
{% endfilter -%}
#endif
{% endfor %}
{% endfor %}
@@ -1,19 +0,0 @@
{# One Shot function's dispatch code for transparent drivers.
Expected inputs:
* drivers: the list of driver descriptions.
* entry_point: the name of the entry point that this function dispatches to.
* entry_point_param(driver): the parameters to pass to the entry point.
* nest_indent: number of extra spaces to indent the code to.
-#}
{% for driver in drivers if driver.type == "transparent" -%}
{% for capability in driver.capabilities if entry_point in capability.entry_points -%}
#if ({% if capability['mbedtls/c_condition'] is defined -%}{{ capability['mbedtls/c_condition'] }} {% else -%} {{ 1 }} {% endif %})
{%- filter indent(width = nest_indent) %}
status = {{ entry_point_name(capability, entry_point, driver) }}({{entry_point_param(driver) | indent(20)}});
if( status != PSA_ERROR_NOT_SUPPORTED )
return( status );
{% endfilter -%}
#endif
{% endfor %}
{% endfor %}
File diff suppressed because it is too large Load Diff
@@ -1,224 +0,0 @@
/*
* Functions to delegate cryptographic operations to an available
* and appropriate accelerator.
* Warning: This file is now auto-generated.
*/
/* Copyright The Mbed TLS Contributors
* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
*/
/* BEGIN-common headers */
#include "common.h"
#include "psa_crypto_aead.h"
#include "psa_crypto_cipher.h"
#include "psa_crypto_core.h"
#include "psa_crypto_driver_wrappers_no_static.h"
#include "psa_crypto_hash.h"
#include "psa_crypto_mac.h"
#include "psa_crypto_pake.h"
#include "psa_crypto_rsa.h"
#include "mbedtls/platform.h"
/* END-common headers */
#if defined(MBEDTLS_PSA_CRYPTO_C)
/* BEGIN-driver headers */
{% for driver in drivers -%}
/* Headers for {{driver.prefix}} {{driver.type}} driver */
{% if driver['mbedtls/h_condition'] is defined -%}
#if {{ driver['mbedtls/h_condition'] }}
{% endif -%}
{% for header in driver.headers -%}
#include "{{ header }}"
{% endfor %}
{% if driver['mbedtls/h_condition'] is defined -%}
#endif
{% endif -%}
{% endfor %}
/* END-driver headers */
/* Auto-generated values depending on which drivers are registered.
* ID 0 is reserved for unallocated operations.
* ID 1 is reserved for the Mbed TLS software driver. */
/* BEGIN-driver id definition */
#define PSA_CRYPTO_MBED_TLS_DRIVER_ID (1)
{% for driver in drivers -%}
#define {{(driver.prefix + "_" + driver.type + "_driver_id").upper()}} ({{ loop.index + 1 }})
{% endfor %}
/* END-driver id */
/* BEGIN-Common Macro definitions */
{% macro entry_point_name(capability, entry_point, driver) -%}
{% if capability.name is defined and entry_point in capability.names.keys() -%}
{{ capability.names[entry_point]}}
{% else -%}
{{driver.prefix}}_{{driver.type}}_{{entry_point}}
{% endif -%}
{% endmacro %}
/* END-Common Macro definitions */
/* Support the 'old' SE interface when asked to */
#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
/* PSA_CRYPTO_DRIVER_PRESENT is defined when either a new-style or old-style
* SE driver is present, to avoid unused argument errors at compile time. */
#ifndef PSA_CRYPTO_DRIVER_PRESENT
#define PSA_CRYPTO_DRIVER_PRESENT
#endif
#include "psa_crypto_se.h"
#endif
/** Get the key buffer size required to store the key material of a key
* associated with an opaque driver.
*
* \param[in] attributes The key attributes.
* \param[out] key_buffer_size Minimum buffer size to contain the key material
*
* \retval #PSA_SUCCESS
* The minimum size for a buffer to contain the key material has been
* returned successfully.
* \retval #PSA_ERROR_NOT_SUPPORTED
* The type and/or the size in bits of the key or the combination of
* the two is not supported.
* \retval #PSA_ERROR_INVALID_ARGUMENT
* The key is declared with a lifetime not known to us.
*/
psa_status_t psa_driver_wrapper_get_key_buffer_size(
const psa_key_attributes_t *attributes,
size_t *key_buffer_size )
{
psa_key_location_t location = PSA_KEY_LIFETIME_GET_LOCATION( psa_get_key_lifetime(attributes) );
psa_key_type_t key_type = psa_get_key_type(attributes);
size_t key_bits = psa_get_key_bits(attributes);
*key_buffer_size = 0;
switch( location )
{
#if defined(PSA_CRYPTO_DRIVER_TEST)
case PSA_CRYPTO_TEST_DRIVER_LOCATION:
#if defined(MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS)
/* Emulate property 'builtin_key_size' */
if( psa_key_id_is_builtin(
MBEDTLS_SVC_KEY_ID_GET_KEY_ID(
psa_get_key_id( attributes ) ) ) )
{
*key_buffer_size = sizeof( psa_drv_slot_number_t );
return( PSA_SUCCESS );
}
#endif /* MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS */
*key_buffer_size = mbedtls_test_opaque_size_function( key_type,
key_bits );
return( ( *key_buffer_size != 0 ) ?
PSA_SUCCESS : PSA_ERROR_NOT_SUPPORTED );
#endif /* PSA_CRYPTO_DRIVER_TEST */
default:
(void)key_type;
(void)key_bits;
return( PSA_ERROR_INVALID_ARGUMENT );
}
}
psa_status_t psa_driver_wrapper_export_public_key(
const psa_key_attributes_t *attributes,
const uint8_t *key_buffer, size_t key_buffer_size,
uint8_t *data, size_t data_size, size_t *data_length )
{
{% with entry_point = "export_public_key" -%}
{% macro entry_point_param(driver) -%}
attributes,
key_buffer,
key_buffer_size,
data,
data_size,
data_length
{% endmacro %}
psa_status_t status = PSA_ERROR_INVALID_ARGUMENT;
psa_key_location_t location = PSA_KEY_LIFETIME_GET_LOCATION(
psa_get_key_lifetime( attributes ) );
/* Try dynamically-registered SE interface first */
#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
const psa_drv_se_t *drv;
psa_drv_se_context_t *drv_context;
if( psa_get_se_driver( psa_get_key_lifetime(attributes), &drv, &drv_context ) )
{
if( ( drv->key_management == NULL ) ||
( drv->key_management->p_export_public == NULL ) )
{
return( PSA_ERROR_NOT_SUPPORTED );
}
return( drv->key_management->p_export_public(
drv_context,
*( (psa_key_slot_number_t *)key_buffer ),
data, data_size, data_length ) );
}
#endif /* MBEDTLS_PSA_CRYPTO_SE_C */
switch( location )
{
case PSA_KEY_LOCATION_LOCAL_STORAGE:
/* Key is stored in the slot in export representation, so
* cycle through all known transparent accelerators */
#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT)
{% with nest_indent=12 %}
{% include "OS-template-transparent.jinja" -%}
{% endwith -%}
#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */
/* Fell through, meaning no accelerator supports this operation */
return( psa_export_public_key_internal( attributes,
key_buffer,
key_buffer_size,
data,
data_size,
data_length ) );
/* Add cases for opaque driver here */
#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT)
{% with nest_indent=8 %}
{% include "OS-template-opaque.jinja" -%}
{% endwith -%}
#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */
default:
/* Key is declared with a lifetime not known to us */
return( status );
}
{% endwith %}
}
psa_status_t psa_driver_wrapper_get_builtin_key(
psa_drv_slot_number_t slot_number,
psa_key_attributes_t *attributes,
uint8_t *key_buffer, size_t key_buffer_size, size_t *key_buffer_length )
{
{% with entry_point = "get_builtin_key" -%}
{% macro entry_point_param(driver) -%}
slot_number,
attributes,
key_buffer,
key_buffer_size,
key_buffer_length
{% endmacro %}
psa_key_location_t location = PSA_KEY_LIFETIME_GET_LOCATION( psa_get_key_lifetime(attributes) );
switch( location )
{
#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT)
{% with nest_indent=8 %}
{% include "OS-template-opaque.jinja" -%}
{% endwith -%}
#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */
default:
(void) slot_number;
(void) key_buffer;
(void) key_buffer_size;
(void) key_buffer_length;
return( PSA_ERROR_DOES_NOT_EXIST );
}
{% endwith %}
}
#endif /* MBEDTLS_PSA_CRYPTO_C */
-159
View File
@@ -1,159 +0,0 @@
/*
* Error message information
*
* Copyright The Mbed TLS Contributors
* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
*/
#include "common.h"
#include "mbedtls/error.h"
#if defined(MBEDTLS_ERROR_C) || defined(MBEDTLS_ERROR_STRERROR_DUMMY)
#if defined(MBEDTLS_ERROR_C)
#include "mbedtls/platform.h"
#include <stdio.h>
#include <string.h>
HEADER_INCLUDED
const char *mbedtls_high_level_strerr(int error_code)
{
int high_level_error_code;
if (error_code < 0) {
error_code = -error_code;
}
/* Extract the high-level part from the error code. */
high_level_error_code = error_code & 0xFF80;
switch (high_level_error_code) {
/* Begin Auto-Generated Code. */
HIGH_LEVEL_CODE_CHECKS
/* End Auto-Generated Code. */
default:
break;
}
return NULL;
}
const char *mbedtls_low_level_strerr(int error_code)
{
int low_level_error_code;
if (error_code < 0) {
error_code = -error_code;
}
/* Extract the low-level part from the error code. */
low_level_error_code = error_code & ~0xFF80;
switch (low_level_error_code) {
/* Begin Auto-Generated Code. */
LOW_LEVEL_CODE_CHECKS
/* End Auto-Generated Code. */
default:
break;
}
return NULL;
}
void mbedtls_strerror(int ret, char *buf, size_t buflen)
{
size_t len;
int use_ret;
const char *high_level_error_description = NULL;
const char *low_level_error_description = NULL;
if (buflen == 0) {
return;
}
memset(buf, 0x00, buflen);
if (ret < 0) {
ret = -ret;
}
if (ret & 0xFF80) {
use_ret = ret & 0xFF80;
// Translate high level error code.
high_level_error_description = mbedtls_high_level_strerr(ret);
if (high_level_error_description == NULL) {
mbedtls_snprintf(buf, buflen, "UNKNOWN ERROR CODE (%04X)", (unsigned int) use_ret);
} else {
mbedtls_snprintf(buf, buflen, "%s", high_level_error_description);
}
#if defined(MBEDTLS_SSL_TLS_C)
// Early return in case of a fatal error - do not try to translate low
// level code.
if (use_ret == -(MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE)) {
return;
}
#endif /* MBEDTLS_SSL_TLS_C */
}
use_ret = ret & ~0xFF80;
if (use_ret == 0) {
return;
}
// If high level code is present, make a concatenation between both
// error strings.
//
len = strlen(buf);
if (len > 0) {
if (buflen - len < 5) {
return;
}
mbedtls_snprintf(buf + len, buflen - len, " : ");
buf += len + 3;
buflen -= len + 3;
}
// Translate low level error code.
low_level_error_description = mbedtls_low_level_strerr(ret);
if (low_level_error_description == NULL) {
mbedtls_snprintf(buf, buflen, "UNKNOWN ERROR CODE (%04X)", (unsigned int) use_ret);
} else {
mbedtls_snprintf(buf, buflen, "%s", low_level_error_description);
}
}
#else /* MBEDTLS_ERROR_C */
/*
* Provide a dummy implementation when MBEDTLS_ERROR_C is not defined
*/
void mbedtls_strerror(int ret, char *buf, size_t buflen)
{
((void) ret);
if (buflen > 0) {
buf[0] = '\0';
}
}
#endif /* MBEDTLS_ERROR_C */
#if defined(MBEDTLS_TEST_HOOKS)
void (*mbedtls_test_hook_error_add)(int, int, const char *, int);
#endif
#endif /* MBEDTLS_ERROR_C || MBEDTLS_ERROR_STRERROR_DUMMY */
-121
View File
@@ -1,121 +0,0 @@
/*
* Query Mbed TLS compile time configurations from mbedtls_config.h
*
* Copyright The Mbed TLS Contributors
* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
*/
#include "mbedtls/build_info.h"
#include "query_config.h"
#include "mbedtls/platform.h"
/*
* Include all the headers with public APIs in case they define a macro to its
* default value when that configuration is not set in mbedtls_config.h, or
* for PSA_WANT macros, in case they're auto-defined based on mbedtls_config.h
* rather than defined directly in crypto_config.h.
*/
#include "psa/crypto.h"
#include "mbedtls/aes.h"
#include "mbedtls/aria.h"
#include "mbedtls/asn1.h"
#include "mbedtls/asn1write.h"
#include "mbedtls/base64.h"
#include "mbedtls/bignum.h"
#include "mbedtls/camellia.h"
#include "mbedtls/ccm.h"
#include "mbedtls/chacha20.h"
#include "mbedtls/chachapoly.h"
#include "mbedtls/cipher.h"
#include "mbedtls/cmac.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/debug.h"
#include "mbedtls/des.h"
#include "mbedtls/dhm.h"
#include "mbedtls/ecdh.h"
#include "mbedtls/ecdsa.h"
#include "mbedtls/ecjpake.h"
#include "mbedtls/ecp.h"
#include "mbedtls/entropy.h"
#include "mbedtls/error.h"
#include "mbedtls/gcm.h"
#include "mbedtls/hkdf.h"
#include "mbedtls/hmac_drbg.h"
#include "mbedtls/md.h"
#include "mbedtls/md5.h"
#include "mbedtls/memory_buffer_alloc.h"
#include "mbedtls/net_sockets.h"
#include "mbedtls/nist_kw.h"
#include "mbedtls/oid.h"
#include "mbedtls/pem.h"
#include "mbedtls/pk.h"
#include "mbedtls/pkcs12.h"
#include "mbedtls/pkcs5.h"
#if defined(MBEDTLS_HAVE_TIME)
#include "mbedtls/platform_time.h"
#endif
#include "mbedtls/platform_util.h"
#include "mbedtls/poly1305.h"
#include "mbedtls/ripemd160.h"
#include "mbedtls/rsa.h"
#include "mbedtls/sha1.h"
#include "mbedtls/sha256.h"
#include "mbedtls/sha512.h"
#include "mbedtls/ssl.h"
#include "mbedtls/ssl_cache.h"
#include "mbedtls/ssl_ciphersuites.h"
#include "mbedtls/ssl_cookie.h"
#include "mbedtls/ssl_ticket.h"
#include "mbedtls/threading.h"
#include "mbedtls/timing.h"
#include "mbedtls/version.h"
#include "mbedtls/x509.h"
#include "mbedtls/x509_crl.h"
#include "mbedtls/x509_crt.h"
#include "mbedtls/x509_csr.h"
#include <string.h>
/*
* Helper macros to convert a macro or its expansion into a string
* WARNING: This does not work for expanding function-like macros. However,
* Mbed TLS does not currently have configuration options used in this fashion.
*/
#define MACRO_EXPANSION_TO_STR(macro) MACRO_NAME_TO_STR(macro)
#define MACRO_NAME_TO_STR(macro) \
mbedtls_printf("%s", strlen( #macro "") > 0 ? #macro "\n" : "")
#define STRINGIFY(macro) #macro
#define OUTPUT_MACRO_NAME_VALUE(macro) mbedtls_printf( #macro "%s\n", \
(STRINGIFY(macro) "")[0] != 0 ? "=" STRINGIFY( \
macro) : "")
#if defined(_MSC_VER)
/*
* Visual Studio throws the warning 4003 because many Mbed TLS feature macros
* are defined empty. This means that from the preprocessor's point of view
* the macro MBEDTLS_EXPANSION_TO_STR is being invoked without arguments as
* some macros expand to nothing. We suppress that specific warning to get a
* clean build and to ensure that tests treating warnings as errors do not
* fail.
*/
#pragma warning(push)
#pragma warning(disable:4003)
#endif /* _MSC_VER */
int query_config(const char *config)
{
CHECK_CONFIG /* If the symbol is not found, return an error */
return 1;
}
void list_config(void)
{
LIST_CONFIG
}
#if defined(_MSC_VER)
#pragma warning(pop)
#endif /* _MSC_VER */
-50
View File
@@ -1,50 +0,0 @@
/*
* Version feature information
*
* Copyright The Mbed TLS Contributors
* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
*/
#include "common.h"
#if defined(MBEDTLS_VERSION_C)
#include "mbedtls/version.h"
#include <string.h>
static const char * const features[] = {
#if defined(MBEDTLS_VERSION_FEATURES)
FEATURE_DEFINES
#endif /* MBEDTLS_VERSION_FEATURES */
NULL
};
int mbedtls_version_check_feature(const char *feature)
{
const char * const *idx = features;
if (*idx == NULL) {
return -2;
}
if (feature == NULL) {
return -1;
}
if (strncmp(feature, "MBEDTLS_", 8)) {
return -1;
}
feature += 8;
while (*idx != NULL) {
if (!strcmp(*idx, feature)) {
return 0;
}
idx++;
}
return -1;
}
#endif /* MBEDTLS_VERSION_C */
@@ -1,175 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<SOURCES>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="mbedTLS.vcxproj">
<Project>{46cf2d25-6a36-4189-b59c-e4815388e554}</Project>
<LinkLibraryDependencies>true</LinkLibraryDependencies>
</ProjectReference>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid><GUID></ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace><APPNAME></RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<IntDir>$(Configuration)\$(TargetName)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<IntDir>$(Configuration)\$(TargetName)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<IntDir>$(Configuration)\$(TargetName)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<IntDir>$(Configuration)\$(TargetName)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>
INCLUDE_DIRECTORIES
</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>bcrypt.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>Debug</AdditionalLibraryDirectories>
</Link>
<ProjectReference>
<LinkLibraryDependencies>false</LinkLibraryDependencies>
</ProjectReference>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>
INCLUDE_DIRECTORIES
</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>bcrypt.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>Debug</AdditionalLibraryDirectories>
</Link>
<ProjectReference>
<LinkLibraryDependencies>false</LinkLibraryDependencies>
</ProjectReference>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>
INCLUDE_DIRECTORIES
</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalLibraryDirectories>Release</AdditionalLibraryDirectories>
<AdditionalDependencies>bcrypt.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>
INCLUDE_DIRECTORIES
</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalLibraryDirectories>Release</AdditionalLibraryDirectories>
<AdditionalDependencies>bcrypt.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
@@ -1,163 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{46CF2D25-6A36-4189-B59C-E4815388E554}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>mbedTLS</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<IntDir>$(Configuration)\$(TargetName)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<IntDir>$(Configuration)\$(TargetName)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<IntDir>$(Configuration)\$(TargetName)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<IntDir>$(Configuration)\$(TargetName)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_USRDLL;MBEDTLS_EXPORTS;KRML_VERIFIED_UINT128;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>
INCLUDE_DIRECTORIES
</AdditionalIncludeDirectories>
<CompileAs>CompileAsC</CompileAs>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>bcrypt.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_USRDLL;MBEDTLS_EXPORTS;KRML_VERIFIED_UINT128;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>
INCLUDE_DIRECTORIES
</AdditionalIncludeDirectories>
<CompileAs>CompileAsC</CompileAs>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>bcrypt.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_USRDLL;MBEDTLS_EXPORTS;KRML_VERIFIED_UINT128;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>
INCLUDE_DIRECTORIES
</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>bcrypt.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN64;NDEBUG;_WINDOWS;_USRDLL;MBEDTLS_EXPORTS;KRML_VERIFIED_UINT128;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>
INCLUDE_DIRECTORIES
</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
HEADER_ENTRIES
</ItemGroup>
<ItemGroup>
SOURCE_ENTRIES
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
@@ -1,30 +0,0 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2017
VisualStudioVersion = 15.0.26228.4
MinimumVisualStudioVersion = 15.0.26228.4
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mbedTLS", "mbedTLS.vcxproj", "{46CF2D25-6A36-4189-B59C-E4815388E554}"
EndProject
APP_ENTRIES
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64
Release|Win32 = Release|Win32
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{46CF2D25-6A36-4189-B59C-E4815388E554}.Debug|Win32.ActiveCfg = Debug|Win32
{46CF2D25-6A36-4189-B59C-E4815388E554}.Debug|Win32.Build.0 = Debug|Win32
{46CF2D25-6A36-4189-B59C-E4815388E554}.Debug|x64.ActiveCfg = Debug|x64
{46CF2D25-6A36-4189-B59C-E4815388E554}.Debug|x64.Build.0 = Debug|x64
{46CF2D25-6A36-4189-B59C-E4815388E554}.Release|Win32.ActiveCfg = Release|Win32
{46CF2D25-6A36-4189-B59C-E4815388E554}.Release|Win32.Build.0 = Release|Win32
{46CF2D25-6A36-4189-B59C-E4815388E554}.Release|x64.ActiveCfg = Release|x64
{46CF2D25-6A36-4189-B59C-E4815388E554}.Release|x64.Build.0 = Release|x64
CONF_ENTRIES
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
-19
View File
@@ -1,19 +0,0 @@
# Python package requirements for driver implementers.
# Jinja2 <3.0 needs an older version of markupsafe, but does not
# declare it.
# https://github.com/pallets/markupsafe/issues/282
# https://github.com/pallets/jinja/issues/1585
markupsafe < 2.1
# Use the version of Jinja that's in Ubuntu 20.04.
# See https://github.com/Mbed-TLS/mbedtls/pull/5067#discussion_r738794607 .
# Note that Jinja 3.0 drops support for Python 3.5, so we need to support
# Jinja 2.x as long as we're still using Python 3.5 anywhere.
# Jinja 2.10.1 doesn't support Python 3.10+
Jinja2 >= 2.10.1; python_version < '3.10'
Jinja2 >= 2.10.3; python_version >= '3.10'
# Jinja2 >=2.10, <3.0 needs a separate package for type annotations
types-Jinja2 >= 2.11.9
jsonschema >= 3.2.0
types-jsonschema >= 3.2.0
-87
View File
@@ -1,87 +0,0 @@
#!/bin/sh
# Measure heap usage (and performance) of ECC operations with various values of
# the relevant tunable compile-time parameters.
#
# Usage (preferably on a 32-bit platform):
# cmake -D CMAKE_BUILD_TYPE=Release .
# scripts/ecc-heap.sh | tee ecc-heap.log
#
# Copyright The Mbed TLS Contributors
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
set -eu
CONFIG_H='include/mbedtls/mbedtls_config.h'
if [ -r $CONFIG_H ]; then :; else
echo "$CONFIG_H not found" >&2
exit 1
fi
if grep -i cmake Makefile >/dev/null; then :; else
echo "Needs Cmake" >&2
exit 1
fi
if git status | grep -F $CONFIG_H >/dev/null 2>&1; then
echo "mbedtls_config.h not clean" >&2
exit 1
fi
CONFIG_BAK=${CONFIG_H}.bak
cp $CONFIG_H $CONFIG_BAK
cat << EOF >$CONFIG_H
#define MBEDTLS_PLATFORM_C
#define MBEDTLS_PLATFORM_MEMORY
#define MBEDTLS_MEMORY_BUFFER_ALLOC_C
#define MBEDTLS_MEMORY_DEBUG
#define MBEDTLS_TIMING_C
#define MBEDTLS_BIGNUM_C
#define MBEDTLS_ECP_C
#define MBEDTLS_ASN1_PARSE_C
#define MBEDTLS_ASN1_WRITE_C
#define MBEDTLS_ECDSA_C
#define MBEDTLS_SHA256_C // ECDSA benchmark needs it
#define MBEDTLS_SHA224_C // SHA256 requires this for now
#define MBEDTLS_ECDH_C
// NIST curves >= 256 bits
#define MBEDTLS_ECP_DP_SECP256R1_ENABLED
#define MBEDTLS_ECP_DP_SECP384R1_ENABLED
#define MBEDTLS_ECP_DP_SECP521R1_ENABLED
// SECP "koblitz-like" curve >= 256 bits
#define MBEDTLS_ECP_DP_SECP256K1_ENABLED
// Brainpool curves (no specialised "mod p" routine)
#define MBEDTLS_ECP_DP_BP256R1_ENABLED
#define MBEDTLS_ECP_DP_BP384R1_ENABLED
#define MBEDTLS_ECP_DP_BP512R1_ENABLED
// Montgomery curves
#define MBEDTLS_ECP_DP_CURVE25519_ENABLED
#define MBEDTLS_ECP_DP_CURVE448_ENABLED
#define MBEDTLS_HAVE_ASM // just make things a bit faster
#define MBEDTLS_ECP_NIST_OPTIM // faster and less allocations
//#define MBEDTLS_ECP_WINDOW_SIZE 4
//#define MBEDTLS_ECP_FIXED_POINT_OPTIM 1
EOF
for F in 0 1; do
for W in 2 3 4; do
scripts/config.py set MBEDTLS_ECP_WINDOW_SIZE $W
scripts/config.py set MBEDTLS_ECP_FIXED_POINT_OPTIM $F
make benchmark >/dev/null 2>&1
echo "fixed point optim = $F, max window size = $W"
echo "--------------------------------------------"
programs/test/benchmark ecdh ecdsa
done
done
# cleanup
mv $CONFIG_BAK $CONFIG_H
make clean
-237
View File
@@ -1,237 +0,0 @@
#!/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 OR GPL-2.0-or-later
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 mbedtls_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()
-108
View File
@@ -1,108 +0,0 @@
#!/bin/sh
#
# Copyright The Mbed TLS Contributors
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
#
# Purpose
#
# This script determines ROM size (or code size) for the standard Mbed TLS
# configurations, when built for a Cortex M3/M4 target.
#
# Configurations included:
# default include/mbedtls/mbedtls_config.h
# thread configs/config-thread.h
# suite-b configs/config-suite-b.h
# psk configs/config-ccm-psk-tls1_2.h
#
# Usage: footprint.sh
#
set -eu
CONFIG_H='include/mbedtls/mbedtls_config.h'
if [ -r $CONFIG_H ]; then :; else
echo "$CONFIG_H not found" >&2
echo "This script needs to be run from the root of" >&2
echo "a git checkout or uncompressed tarball" >&2
exit 1
fi
if grep -i cmake Makefile >/dev/null; then
echo "Not compatible with CMake" >&2
exit 1
fi
if which arm-none-eabi-gcc >/dev/null 2>&1; then :; else
echo "You need the ARM-GCC toolchain in your path" >&2
echo "See https://launchpad.net/gcc-arm-embedded/" >&2
exit 1
fi
ARMGCC_FLAGS='-Os -march=armv7-m -mthumb'
OUTFILE='00-footprint-summary.txt'
log()
{
echo "$@"
echo "$@" >> "$OUTFILE"
}
doit()
{
NAME="$1"
FILE="$2"
log ""
log "$NAME ($FILE):"
cp $CONFIG_H ${CONFIG_H}.bak
if [ "$FILE" != $CONFIG_H ]; then
cp "$FILE" $CONFIG_H
fi
{
scripts/config.py unset MBEDTLS_NET_C || true
scripts/config.py unset MBEDTLS_TIMING_C || true
scripts/config.py unset MBEDTLS_FS_IO || true
scripts/config.py --force set MBEDTLS_NO_PLATFORM_ENTROPY || true
} >/dev/null 2>&1
make clean >/dev/null
CC=arm-none-eabi-gcc AR=arm-none-eabi-ar LD=arm-none-eabi-ld \
CFLAGS="$ARMGCC_FLAGS" make lib >/dev/null
OUT="size-${NAME}.txt"
arm-none-eabi-size -t library/libmbed*.a > "$OUT"
log "$( head -n1 "$OUT" )"
log "$( tail -n1 "$OUT" )"
cp ${CONFIG_H}.bak $CONFIG_H
}
# truncate the file just this time
echo "(generated by $0)" > "$OUTFILE"
echo "" >> "$OUTFILE"
log "Footprint of standard configurations (minus net_sockets.c, timing.c, fs_io)"
log "for bare-metal ARM Cortex-M3/M4 microcontrollers."
VERSION_H="include/mbedtls/version.h"
MBEDTLS_VERSION=$( sed -n 's/.*VERSION_STRING *"\(.*\)"/\1/p' $VERSION_H )
if git rev-parse HEAD >/dev/null; then
GIT_HEAD=$( git rev-parse HEAD | head -c 10 )
GIT_VERSION=" (git head: $GIT_HEAD)"
else
GIT_VERSION=""
fi
log ""
log "Mbed TLS $MBEDTLS_VERSION$GIT_VERSION"
log "$( arm-none-eabi-gcc --version | head -n1 )"
log "CFLAGS=$ARMGCC_FLAGS"
doit default include/mbedtls/mbedtls_config.h
doit thread configs/config-thread.h
doit suite-b configs/config-suite-b.h
doit psk configs/config-ccm-psk-tls1_2.h
zip mbedtls-footprint.zip "$OUTFILE" size-*.txt >/dev/null
-17
View File
@@ -1,17 +0,0 @@
"""Add our Python library directory to the module search path.
Usage:
import framework_scripts_path # pylint: disable=unused-import
"""
# Copyright The Mbed TLS Contributors
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
#
import os
import sys
sys.path.append(os.path.join(os.path.dirname(__file__),
os.path.pardir,
'framework', 'scripts'))
-214
View File
@@ -1,214 +0,0 @@
#!/usr/bin/env python3
"""Generate psa_crypto_driver_wrappers.h
psa_crypto_driver_wrappers_no_static.c
This module is invoked by the build scripts to auto generate the
psa_crypto_driver_wrappers.h and psa_crypto_driver_wrappers_no_static
based on template files in script/data_files/driver_templates/.
"""
# Copyright The Mbed TLS Contributors
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
import sys
import os
import json
from typing import NewType, Dict, Any
from traceback import format_tb
import argparse
import jsonschema
import jinja2
import framework_scripts_path # pylint: disable=unused-import
from mbedtls_framework import build_tree
JSONSchema = NewType('JSONSchema', object)
# The Driver is an Object, but practically it's indexable and can called a dictionary to
# keep MyPy happy till MyPy comes with a more composite type for JsonObjects.
Driver = NewType('Driver', dict)
class JsonValidationException(Exception):
def __init__(self, message="Json Validation Failed"):
self.message = message
super().__init__(self.message)
class DriverReaderException(Exception):
def __init__(self, message="Driver Reader Failed"):
self.message = message
super().__init__(self.message)
def render(template_path: str, driver_jsoncontext: list) -> str:
"""
Render template from the input file and driver JSON.
"""
environment = jinja2.Environment(
loader=jinja2.FileSystemLoader(os.path.dirname(template_path)),
keep_trailing_newline=True)
template = environment.get_template(os.path.basename(template_path))
return template.render(drivers=driver_jsoncontext)
def generate_driver_wrapper_file(template_dir: str,
output_dir: str,
template_file_name: str,
driver_jsoncontext: list) -> None:
"""
Generate the file psa_crypto_driver_wrapper.c.
"""
driver_wrapper_template_filename = \
os.path.join(template_dir, template_file_name)
result = render(driver_wrapper_template_filename, driver_jsoncontext)
with open(file=os.path.join(output_dir, os.path.splitext(template_file_name)[0]),
mode='w',
encoding='UTF-8') as out_file:
out_file.write(result)
def validate_json(driverjson_data: Driver, driverschema_list: dict) -> None:
"""
Validate the Driver JSON against an appropriate schema
the schema passed could be that matching an opaque/ transparent driver.
"""
driver_type = driverjson_data["type"]
driver_prefix = driverjson_data["prefix"]
try:
_schema = driverschema_list[driver_type]
jsonschema.validate(instance=driverjson_data, schema=_schema)
except KeyError as err:
# This could happen if the driverjson_data.type does not exist in the provided schema list
# schemas = {'transparent': transparent_driver_schema, 'opaque': opaque_driver_schema}
# Print onto stdout and stderr.
print("Unknown Driver type " + driver_type +
" for driver " + driver_prefix, str(err))
print("Unknown Driver type " + driver_type +
" for driver " + driver_prefix, str(err), file=sys.stderr)
raise JsonValidationException() from err
except jsonschema.exceptions.ValidationError as err:
# Print onto stdout and stderr.
print("Error: Failed to validate data file: {} using schema: {}."
"\n Exception Message: \"{}\""
" ".format(driverjson_data, _schema, str(err)))
print("Error: Failed to validate data file: {} using schema: {}."
"\n Exception Message: \"{}\""
" ".format(driverjson_data, _schema, str(err)), file=sys.stderr)
raise JsonValidationException() from err
def load_driver(schemas: Dict[str, Any], driver_file: str) -> Any:
"""loads validated json driver"""
with open(file=driver_file, mode='r', encoding='UTF-8') as f:
json_data = json.load(f)
try:
validate_json(json_data, schemas)
except JsonValidationException as e:
raise DriverReaderException from e
return json_data
def load_schemas(project_root: str) -> Dict[str, Any]:
"""
Load schemas map
"""
schema_file_paths = {
'transparent': os.path.join(project_root,
'scripts',
'data_files',
'driver_jsons',
'driver_transparent_schema.json'),
'opaque': os.path.join(project_root,
'scripts',
'data_files',
'driver_jsons',
'driver_opaque_schema.json')
}
driver_schema = {}
for key, file_path in schema_file_paths.items():
with open(file=file_path, mode='r', encoding='UTF-8') as file:
driver_schema[key] = json.load(file)
return driver_schema
def read_driver_descriptions(project_root: str,
json_directory: str,
jsondriver_list: str) -> list:
"""
Merge driver JSON files into a single ordered JSON after validation.
"""
driver_schema = load_schemas(project_root)
with open(file=os.path.join(json_directory, jsondriver_list),
mode='r',
encoding='UTF-8') as driver_list_file:
driver_list = json.load(driver_list_file)
return [load_driver(schemas=driver_schema,
driver_file=os.path.join(json_directory, driver_file_name))
for driver_file_name in driver_list]
def trace_exception(e: Exception, file=sys.stderr) -> None:
"""Prints exception trace to the given TextIO handle"""
print("Exception: type: %s, message: %s, trace: %s" % (
e.__class__, str(e), format_tb(e.__traceback__)
), file)
TEMPLATE_FILENAMES = ["psa_crypto_driver_wrappers.h.jinja",
"psa_crypto_driver_wrappers_no_static.c.jinja"]
def main() -> int:
"""
Main with command line arguments.
"""
def_arg_project_root = build_tree.guess_project_root()
parser = argparse.ArgumentParser()
parser.add_argument('--project-root', default=def_arg_project_root,
help='root directory of repo source code')
parser.add_argument('--template-dir',
help='directory holding the driver templates')
parser.add_argument('--json-dir',
help='directory holding the driver JSONs')
parser.add_argument('output_directory', nargs='?',
help='output file\'s location')
args = parser.parse_args()
project_root = os.path.abspath(args.project_root)
crypto_core_directory = build_tree.crypto_core_directory(project_root)
output_directory = args.output_directory if args.output_directory is not None else \
crypto_core_directory
template_directory = args.template_dir if args.template_dir is not None else \
os.path.join(project_root,
'scripts',
'data_files',
'driver_templates')
json_directory = args.json_dir if args.json_dir is not None else \
os.path.join(project_root,
'scripts',
'data_files',
'driver_jsons')
try:
# Read and validate list of driver jsons from driverlist.json
merged_driver_json = read_driver_descriptions(project_root,
json_directory,
'driverlist.json')
except DriverReaderException as e:
trace_exception(e)
return 1
for template_filename in TEMPLATE_FILENAMES:
generate_driver_wrapper_file(template_directory, output_directory,
template_filename, merged_driver_json)
return 0
if __name__ == '__main__':
sys.exit(main())
-243
View File
@@ -1,243 +0,0 @@
#!/usr/bin/env perl
# Generate error.c
#
# Usage: ./generate_errors.pl or scripts/generate_errors.pl without arguments,
# or generate_errors.pl crypto_include_dir tls_include_dir data_dir error_file
#
# Copyright The Mbed TLS Contributors
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
use strict;
use warnings;
my ($crypto_include_dir, $tls_include_dir, $data_dir, $error_file);
if( @ARGV ) {
die "Invalid number of arguments" if scalar @ARGV != 4;
($crypto_include_dir, $tls_include_dir, $data_dir, $error_file) = @ARGV;
-d $crypto_include_dir or die "No such directory: $crypto_include_dir\n";
-d $tls_include_dir or die "No such directory: $tls_include_dir\n";
-d $data_dir or die "No such directory: $data_dir\n";
} else {
$crypto_include_dir = 'tf-psa-crypto/drivers/builtin/include/mbedtls';
$tls_include_dir = 'include/mbedtls';
$data_dir = 'scripts/data_files';
$error_file = 'tf-psa-crypto/drivers/builtin/src/error.c';
unless( -d $crypto_include_dir && -d $tls_include_dir && -d $data_dir ) {
chdir '..' or die;
-d $crypto_include_dir && -d $tls_include_dir && -d $data_dir
or die "Without arguments, must be run from root or scripts\n"
}
}
my $error_format_file = $data_dir.'/error.fmt';
my @low_level_modules = qw( AES ARIA ASN1 BASE64 BIGNUM
CAMELLIA CCM CHACHA20 CHACHAPOLY CMAC CTR_DRBG DES
ENTROPY ERROR GCM HKDF HMAC_DRBG LMS MD5
NET OID PBKDF2 PLATFORM POLY1305 RIPEMD160
SHA1 SHA256 SHA512 SHA3 THREADING );
my @high_level_modules = qw( CIPHER DHM ECP MD
PEM PK PKCS12 PKCS5
RSA SSL X509 PKCS7 );
undef $/;
open(FORMAT_FILE, '<:crlf', "$error_format_file") or die "Opening error format file '$error_format_file': $!";
my $error_format = <FORMAT_FILE>;
close(FORMAT_FILE);
my @files = glob qq("$crypto_include_dir/*.h");
push(@files, glob qq("$tls_include_dir/*.h"));
my @necessary_include_files;
my @matches;
foreach my $file (@files) {
open(FILE, '<:crlf', $file) or die("$0: $file: $!");
my $content = <FILE>;
close FILE;
my $found = 0;
while ($content =~ m[
# Both the before-comment and the after-comment are optional.
# Only the comment content is a regex capture group. The comment
# start and end parts are outside the capture group.
(?:/\*[*!](?!<) # Doxygen before-comment start
((?:[^*]|\*+[^*/])*) # $1: Comment content (no */ inside)
\*/)? # Comment end
\s*\#\s*define\s+(MBEDTLS_ERR_\w+) # $2: name
\s+\-(0[Xx][0-9A-Fa-f]+)\s* # $3: value (without the sign)
(?:/\*[*!]< # Doxygen after-comment start
((?:[^*]|\*+[^*/])*) # $4: Comment content (no */ inside)
\*/)? # Comment end
]gsx) {
my ($before, $name, $value, $after) = ($1, $2, $3, $4);
# Discard Doxygen comments that are coincidentally present before
# an error definition but not attached to it. This is ad hoc, based
# on what actually matters (or mattered at some point).
undef $before if defined($before) && $before =~ /\s*\\name\s/s;
die "Description neither before nor after $name in $file\n"
if !defined($before) && !defined($after);
die "Description both before and after $name in $file\n"
if defined($before) && defined($after);
my $description = (defined($before) ? $before : $after);
$description =~ s/^\s+//;
$description =~ s/\n( *\*)? */ /g;
$description =~ s/\.?\s+$//;
push @matches, [$name, $value, $description];
++$found;
}
if ($found) {
my $include_name = $file;
$include_name =~ s!.*/!!;
push @necessary_include_files, $include_name;
}
}
my $ll_old_define = "";
my $hl_old_define = "";
my $ll_code_check = "";
my $hl_code_check = "";
my $headers = "";
my %included_headers;
my %error_codes_seen;
foreach my $match (@matches)
{
my ($error_name, $error_code, $description) = @$match;
die "Duplicated error code: $error_code ($error_name)\n"
if( $error_codes_seen{$error_code}++ );
$description =~ s/\\/\\\\/g;
my ($module_name) = $error_name =~ /^MBEDTLS_ERR_([^_]+)/;
# Fix faulty ones
$module_name = "BIGNUM" if ($module_name eq "MPI");
$module_name = "CTR_DRBG" if ($module_name eq "CTR");
$module_name = "HMAC_DRBG" if ($module_name eq "HMAC");
my $define_name = $module_name;
$define_name = "X509_USE,X509_CREATE" if ($define_name eq "X509");
$define_name = "ASN1_PARSE" if ($define_name eq "ASN1");
$define_name = "SSL_TLS" if ($define_name eq "SSL");
$define_name = "PEM_PARSE,PEM_WRITE" if ($define_name eq "PEM");
$define_name = "PKCS7" if ($define_name eq "PKCS7");
my $include_name = $module_name;
$include_name =~ tr/A-Z/a-z/;
# Fix faulty ones
$include_name = "net_sockets" if ($module_name eq "NET");
$included_headers{"${include_name}.h"} = $module_name;
my $found_ll = grep $_ eq $module_name, @low_level_modules;
my $found_hl = grep $_ eq $module_name, @high_level_modules;
if (!$found_ll && !$found_hl)
{
printf("Error: Do not know how to handle: $module_name\n");
exit 1;
}
my $code_check;
my $old_define;
my $white_space;
my $first;
if ($found_ll)
{
$code_check = \$ll_code_check;
$old_define = \$ll_old_define;
$white_space = ' ';
}
else
{
$code_check = \$hl_code_check;
$old_define = \$hl_old_define;
$white_space = ' ';
}
if ($define_name ne ${$old_define})
{
if (${$old_define} ne "")
{
${$code_check} .= "#endif /* ";
$first = 0;
foreach my $dep (split(/,/, ${$old_define}))
{
${$code_check} .= " || " if ($first++);
${$code_check} .= "MBEDTLS_${dep}_C";
}
${$code_check} .= " */\n\n";
}
${$code_check} .= "#if ";
$headers .= "#if " if ($include_name ne "");
$first = 0;
foreach my $dep (split(/,/, ${define_name}))
{
${$code_check} .= " || " if ($first);
$headers .= " || " if ($first++);
${$code_check} .= "defined(MBEDTLS_${dep}_C)";
$headers .= "defined(MBEDTLS_${dep}_C)" if
($include_name ne "");
}
${$code_check} .= "\n";
$headers .= "\n#include \"mbedtls/${include_name}.h\"\n".
"#endif\n\n" if ($include_name ne "");
${$old_define} = $define_name;
}
${$code_check} .= "${white_space}case -($error_name):\n".
"${white_space} return( \"$module_name - $description\" );\n"
};
if ($ll_old_define ne "")
{
$ll_code_check .= "#endif /* ";
my $first = 0;
foreach my $dep (split(/,/, $ll_old_define))
{
$ll_code_check .= " || " if ($first++);
$ll_code_check .= "MBEDTLS_${dep}_C";
}
$ll_code_check .= " */\n";
}
if ($hl_old_define ne "")
{
$hl_code_check .= "#endif /* ";
my $first = 0;
foreach my $dep (split(/,/, $hl_old_define))
{
$hl_code_check .= " || " if ($first++);
$hl_code_check .= "MBEDTLS_${dep}_C";
}
$hl_code_check .= " */\n";
}
$error_format =~ s/HEADER_INCLUDED\n/$headers/g;
$error_format =~ s/LOW_LEVEL_CODE_CHECKS\n/$ll_code_check/g;
$error_format =~ s/HIGH_LEVEL_CODE_CHECKS\n/$hl_code_check/g;
open(ERROR_FILE, ">$error_file") or die "Opening destination file '$error_file': $!";
print ERROR_FILE $error_format;
close(ERROR_FILE);
my $errors = 0;
for my $include_name (@necessary_include_files)
{
if (not $included_headers{$include_name})
{
print STDERR "The header file \"$include_name\" defines error codes but has not been included!\n";
++$errors;
}
}
exit !!$errors;
-79
View File
@@ -1,79 +0,0 @@
#!/usr/bin/env perl
#
# Copyright The Mbed TLS Contributors
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
use strict;
my ($include_dir, $data_dir, $feature_file);
if( @ARGV ) {
die "Invalid number of arguments" if scalar @ARGV != 3;
($include_dir, $data_dir, $feature_file) = @ARGV;
-d $include_dir or die "No such directory: $include_dir\n";
-d $data_dir or die "No such directory: $data_dir\n";
} else {
$include_dir = 'include/mbedtls';
$data_dir = 'scripts/data_files';
$feature_file = 'tf-psa-crypto/drivers/builtin/src/version_features.c';
unless( -d $include_dir && -d $data_dir ) {
chdir '..' or die;
-d $include_dir && -d $data_dir
or die "Without arguments, must be run from root or scripts\n"
}
}
my $feature_format_file = $data_dir.'/version_features.fmt';
my @sections = ( "System support", "Mbed TLS modules",
"Mbed TLS feature support" );
my $line_separator = $/;
undef $/;
open(FORMAT_FILE, '<:crlf', "$feature_format_file") or die "Opening feature format file '$feature_format_file': $!";
my $feature_format = <FORMAT_FILE>;
close(FORMAT_FILE);
$/ = $line_separator;
open(CONFIG_H, '<:crlf', "$include_dir/mbedtls_config.h") || die("Failure when opening mbedtls_config.h: $!");
my $feature_defines = "";
my $in_section = 0;
while (my $line = <CONFIG_H>)
{
next if ($in_section && $line !~ /#define/ && $line !~ /SECTION/);
next if (!$in_section && $line !~ /SECTION/);
if ($in_section) {
if ($line =~ /SECTION/) {
$in_section = 0;
next;
}
# Strip leading MBEDTLS_ to save binary size
my ($mbedtls_prefix, $define) = $line =~ /#define (MBEDTLS_)?(\w+)/;
if (!$mbedtls_prefix) {
die "Feature does not start with 'MBEDTLS_': $line\n";
}
$feature_defines .= "#if defined(MBEDTLS_${define})\n";
$feature_defines .= " \"${define}\", //no-check-names\n";
$feature_defines .= "#endif /* MBEDTLS_${define} */\n";
}
if (!$in_section) {
my ($section_name) = $line =~ /SECTION: ([\w ]+)/;
my $found_section = grep $_ eq $section_name, @sections;
$in_section = 1 if ($found_section);
}
};
$feature_format =~ s/FEATURE_DEFINES\n/$feature_defines/g;
open(ERROR_FILE, ">$feature_file") or die "Opening destination file '$feature_file': $!";
print ERROR_FILE $feature_format;
close(ERROR_FILE);
-334
View File
@@ -1,334 +0,0 @@
#!/usr/bin/env python3
"""Generate psa_constant_names_generated.c
which is included by programs/psa/psa_constant_names.c.
The code generated by this module is only meant to be used in the context
of that program.
An argument passed to this script will modify the output directory where the
file is written:
* by default (no arguments passed): writes to programs/psa/
* OUTPUT_FILE_DIR passed: writes to OUTPUT_FILE_DIR/
"""
# Copyright The Mbed TLS Contributors
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
import os
import sys
import framework_scripts_path # pylint: disable=unused-import
from mbedtls_framework import build_tree
from mbedtls_framework import macro_collector
OUTPUT_TEMPLATE = '''\
/* Automatically generated by generate_psa_constant.py. DO NOT EDIT. */
static const char *psa_strerror(psa_status_t status)
{
switch (status) {
%(status_cases)s
default: return NULL;
}
}
static const char *psa_ecc_family_name(psa_ecc_family_t curve)
{
switch (curve) {
%(ecc_curve_cases)s
default: return NULL;
}
}
static const char *psa_dh_family_name(psa_dh_family_t group)
{
switch (group) {
%(dh_group_cases)s
default: return NULL;
}
}
static const char *psa_hash_algorithm_name(psa_algorithm_t hash_alg)
{
switch (hash_alg) {
%(hash_algorithm_cases)s
default: return NULL;
}
}
static const char *psa_ka_algorithm_name(psa_algorithm_t ka_alg)
{
switch (ka_alg) {
%(ka_algorithm_cases)s
default: return NULL;
}
}
static int psa_snprint_key_type(char *buffer, size_t buffer_size,
psa_key_type_t type)
{
size_t required_size = 0;
switch (type) {
%(key_type_cases)s
default:
%(key_type_code)s{
return snprintf(buffer, buffer_size,
"0x%%04x", (unsigned) type);
}
break;
}
buffer[0] = 0;
return (int) required_size;
}
#define NO_LENGTH_MODIFIER 0xfffffffflu
static int psa_snprint_algorithm(char *buffer, size_t buffer_size,
psa_algorithm_t alg)
{
size_t required_size = 0;
psa_algorithm_t core_alg = alg;
unsigned long length_modifier = NO_LENGTH_MODIFIER;
if (PSA_ALG_IS_MAC(alg)) {
core_alg = PSA_ALG_TRUNCATED_MAC(alg, 0);
if (alg & PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG) {
append(&buffer, buffer_size, &required_size,
"PSA_ALG_AT_LEAST_THIS_LENGTH_MAC(", 33);
length_modifier = PSA_MAC_TRUNCATED_LENGTH(alg);
} else if (core_alg != alg) {
append(&buffer, buffer_size, &required_size,
"PSA_ALG_TRUNCATED_MAC(", 22);
length_modifier = PSA_MAC_TRUNCATED_LENGTH(alg);
}
} else if (PSA_ALG_IS_AEAD(alg)) {
core_alg = PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG(alg);
if (core_alg == 0) {
/* For unknown AEAD algorithms, there is no "default tag length". */
core_alg = alg;
} else if (alg & PSA_ALG_AEAD_AT_LEAST_THIS_LENGTH_FLAG) {
append(&buffer, buffer_size, &required_size,
"PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG(", 43);
length_modifier = PSA_ALG_AEAD_GET_TAG_LENGTH(alg);
} else if (core_alg != alg) {
append(&buffer, buffer_size, &required_size,
"PSA_ALG_AEAD_WITH_SHORTENED_TAG(", 32);
length_modifier = PSA_ALG_AEAD_GET_TAG_LENGTH(alg);
}
} else if (PSA_ALG_IS_KEY_AGREEMENT(alg) &&
!PSA_ALG_IS_RAW_KEY_AGREEMENT(alg)) {
core_alg = PSA_ALG_KEY_AGREEMENT_GET_KDF(alg);
append(&buffer, buffer_size, &required_size,
"PSA_ALG_KEY_AGREEMENT(", 22);
append_with_alg(&buffer, buffer_size, &required_size,
psa_ka_algorithm_name,
PSA_ALG_KEY_AGREEMENT_GET_BASE(alg));
append(&buffer, buffer_size, &required_size, ", ", 2);
}
switch (core_alg) {
%(algorithm_cases)s
default:
%(algorithm_code)s{
append_integer(&buffer, buffer_size, &required_size,
"0x%%08lx", (unsigned long) core_alg);
}
break;
}
if (core_alg != alg) {
if (length_modifier != NO_LENGTH_MODIFIER) {
append(&buffer, buffer_size, &required_size, ", ", 2);
append_integer(&buffer, buffer_size, &required_size,
"%%lu", length_modifier);
}
append(&buffer, buffer_size, &required_size, ")", 1);
}
buffer[0] = 0;
return (int) required_size;
}
static int psa_snprint_key_usage(char *buffer, size_t buffer_size,
psa_key_usage_t usage)
{
size_t required_size = 0;
if (usage == 0) {
if (buffer_size > 1) {
buffer[0] = '0';
buffer[1] = 0;
} else if (buffer_size == 1) {
buffer[0] = 0;
}
return 1;
}
%(key_usage_code)s
if (usage != 0) {
if (required_size != 0) {
append(&buffer, buffer_size, &required_size, " | ", 3);
}
append_integer(&buffer, buffer_size, &required_size,
"0x%%08lx", (unsigned long) usage);
} else {
buffer[0] = 0;
}
return (int) required_size;
}
/* End of automatically generated file. */
'''
KEY_TYPE_FROM_CURVE_TEMPLATE = '''if (%(tester)s(type)) {
append_with_curve(&buffer, buffer_size, &required_size,
"%(builder)s", %(builder_length)s,
PSA_KEY_TYPE_ECC_GET_FAMILY(type));
} else '''
KEY_TYPE_FROM_GROUP_TEMPLATE = '''if (%(tester)s(type)) {
append_with_group(&buffer, buffer_size, &required_size,
"%(builder)s", %(builder_length)s,
PSA_KEY_TYPE_DH_GET_FAMILY(type));
} else '''
ALGORITHM_FROM_HASH_TEMPLATE = '''if (%(tester)s(core_alg)) {
append(&buffer, buffer_size, &required_size,
"%(builder)s(", %(builder_length)s + 1);
append_with_alg(&buffer, buffer_size, &required_size,
psa_hash_algorithm_name,
PSA_ALG_GET_HASH(core_alg));
append(&buffer, buffer_size, &required_size, ")", 1);
} else '''
BIT_TEST_TEMPLATE = '''\
if (%(var)s & %(flag)s) {
if (required_size != 0) {
append(&buffer, buffer_size, &required_size, " | ", 3);
}
append(&buffer, buffer_size, &required_size, "%(flag)s", %(length)d);
%(var)s ^= %(flag)s;
}\
'''
class CaseBuilder(macro_collector.PSAMacroCollector):
"""Collect PSA crypto macro definitions and write value recognition functions.
1. Call `read_file` on the input header file(s).
2. Call `write_file` to write ``psa_constant_names_generated.c``.
"""
def __init__(self):
super().__init__(include_intermediate=True)
@staticmethod
def _make_return_case(name):
return 'case %(name)s: return "%(name)s";' % {'name': name}
@staticmethod
def _make_append_case(name):
template = ('case %(name)s: '
'append(&buffer, buffer_size, &required_size, "%(name)s", %(length)d); '
'break;')
return template % {'name': name, 'length': len(name)}
@staticmethod
def _make_bit_test(var, flag):
return BIT_TEST_TEMPLATE % {'var': var,
'flag': flag,
'length': len(flag)}
def _make_status_cases(self):
return '\n '.join(map(self._make_return_case,
sorted(self.statuses)))
def _make_ecc_curve_cases(self):
return '\n '.join(map(self._make_return_case,
sorted(self.ecc_curves)))
def _make_dh_group_cases(self):
return '\n '.join(map(self._make_return_case,
sorted(self.dh_groups)))
def _make_key_type_cases(self):
return '\n '.join(map(self._make_append_case,
sorted(self.key_types)))
@staticmethod
def _make_key_type_from_curve_code(builder, tester):
return KEY_TYPE_FROM_CURVE_TEMPLATE % {'builder': builder,
'builder_length': len(builder),
'tester': tester}
@staticmethod
def _make_key_type_from_group_code(builder, tester):
return KEY_TYPE_FROM_GROUP_TEMPLATE % {'builder': builder,
'builder_length': len(builder),
'tester': tester}
def _make_ecc_key_type_code(self):
d = self.key_types_from_curve
make = self._make_key_type_from_curve_code
return ''.join([make(k, d[k]) for k in sorted(d.keys())])
def _make_dh_key_type_code(self):
d = self.key_types_from_group
make = self._make_key_type_from_group_code
return ''.join([make(k, d[k]) for k in sorted(d.keys())])
def _make_hash_algorithm_cases(self):
return '\n '.join(map(self._make_return_case,
sorted(self.hash_algorithms)))
def _make_ka_algorithm_cases(self):
return '\n '.join(map(self._make_return_case,
sorted(self.ka_algorithms)))
def _make_algorithm_cases(self):
return '\n '.join(map(self._make_append_case,
sorted(self.algorithms)))
@staticmethod
def _make_algorithm_from_hash_code(builder, tester):
return ALGORITHM_FROM_HASH_TEMPLATE % {'builder': builder,
'builder_length': len(builder),
'tester': tester}
def _make_algorithm_code(self):
d = self.algorithms_from_hash
make = self._make_algorithm_from_hash_code
return ''.join([make(k, d[k]) for k in sorted(d.keys())])
def _make_key_usage_code(self):
return '\n'.join([self._make_bit_test('usage', bit)
for bit in sorted(self.key_usage_flags)])
def write_file(self, output_file):
"""Generate the pretty-printer function code from the gathered
constant definitions.
"""
data = {}
data['status_cases'] = self._make_status_cases()
data['ecc_curve_cases'] = self._make_ecc_curve_cases()
data['dh_group_cases'] = self._make_dh_group_cases()
data['key_type_cases'] = self._make_key_type_cases()
data['key_type_code'] = (self._make_ecc_key_type_code() +
self._make_dh_key_type_code())
data['hash_algorithm_cases'] = self._make_hash_algorithm_cases()
data['ka_algorithm_cases'] = self._make_ka_algorithm_cases()
data['algorithm_cases'] = self._make_algorithm_cases()
data['algorithm_code'] = self._make_algorithm_code()
data['key_usage_code'] = self._make_key_usage_code()
output_file.write(OUTPUT_TEMPLATE % data)
def generate_psa_constants(header_file_names, output_file_name):
collector = CaseBuilder()
for header_file_name in header_file_names:
with open(header_file_name, 'rb') as header_file:
collector.read_file(header_file)
temp_file_name = output_file_name + '.tmp'
with open(temp_file_name, 'w') as output_file:
collector.write_file(output_file)
os.replace(temp_file_name, output_file_name)
if __name__ == '__main__':
build_tree.chdir_to_root()
# Allow to change the directory where psa_constant_names_generated.c is written to.
OUTPUT_FILE_DIR = sys.argv[1] if len(sys.argv) == 2 else "programs/psa"
generate_psa_constants(['tf-psa-crypto/include/psa/crypto_values.h',
'tf-psa-crypto/include/psa/crypto_extra.h'],
OUTPUT_FILE_DIR + '/psa_constant_names_generated.c')
-116
View File
@@ -1,116 +0,0 @@
#! /usr/bin/env perl
# Generate query_config.c
#
# The file query_config.c contains a C function that can be used to check if
# a configuration macro is defined and to retrieve its expansion in string
# form (if any). This facilitates querying the compile time configuration of
# the library, for example, for testing.
#
# The query_config.c is generated from the default configuration files
# include/mbedtls/mbedtls_config.h and include/psa/crypto_config.h.
# The idea is that mbedtls_config.h and crypto_config.h contain ALL the
# compile time configurations available in Mbed TLS (commented or uncommented).
# This script extracts the configuration macros from the two files and this
# information is used to automatically generate the body of the query_config()
# function by using the template in scripts/data_files/query_config.fmt.
#
# Usage: scripts/generate_query_config.pl without arguments, or
# generate_query_config.pl mbedtls_config_file psa_crypto_config_file template_file output_file
#
# Copyright The Mbed TLS Contributors
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
use strict;
my ($mbedtls_config_file, $psa_crypto_config_file, $query_config_format_file, $query_config_file);
my $default_mbedtls_config_file = "./include/mbedtls/mbedtls_config.h";
my $default_psa_crypto_config_file = "./tf-psa-crypto/include/psa/crypto_config.h";
my $default_query_config_format_file = "./scripts/data_files/query_config.fmt";
my $default_query_config_file = "./programs/test/query_config.c";
if( @ARGV ) {
die "Invalid number of arguments - usage: $0 [MBED_TLS_CONFIG_FILE PSA_CRYPTO_CONFIG_FILE TEMPLATE_FILE OUTPUT_FILE]" if scalar @ARGV != 4;
($mbedtls_config_file, $psa_crypto_config_file, $query_config_format_file, $query_config_file) = @ARGV;
-f $mbedtls_config_file or die "No such file: $mbedtls_config_file";
-f $psa_crypto_config_file or die "No such file: $psa_crypto_config_file";
-f $query_config_format_file or die "No such file: $query_config_format_file";
} else {
$mbedtls_config_file = $default_mbedtls_config_file;
$psa_crypto_config_file = $default_psa_crypto_config_file;
$query_config_format_file = $default_query_config_format_file;
$query_config_file = $default_query_config_file;
unless(-f $mbedtls_config_file && -f $query_config_format_file && -f $psa_crypto_config_file) {
chdir '..' or die;
-f $mbedtls_config_file && -f $query_config_format_file && -f $psa_crypto_config_file
or die "No arguments supplied, must be run from project root or a first-level subdirectory\n";
}
}
# Excluded macros from the generated query_config.c. For example, macros that
# have commas or function-like macros cannot be transformed into strings easily
# using the preprocessor, so they should be excluded or the preprocessor will
# throw errors.
my @excluded = qw(
MBEDTLS_SSL_CIPHERSUITES
);
my $excluded_re = join '|', @excluded;
# This variable will contain the string to replace in the CHECK_CONFIG of the
# format file
my $config_check = "";
my $list_config = "";
for my $config_file ($mbedtls_config_file, $psa_crypto_config_file) {
next unless defined($config_file); # we might not have been given a PSA crypto config file
open(CONFIG_FILE, "<", $config_file) or die "Opening config file '$config_file': $!";
while (my $line = <CONFIG_FILE>) {
if ($line =~ /^(\/\/)?\s*#\s*define\s+(MBEDTLS_\w+|PSA_WANT_\w+).*/) {
my $name = $2;
# Skip over the macro if it is in the excluded list
next if $name =~ /$excluded_re/;
$config_check .= <<EOT;
#if defined($name)
if( strcmp( "$name", config ) == 0 )
{
MACRO_EXPANSION_TO_STR( $name );
return( 0 );
}
#endif /* $name */
EOT
$list_config .= <<EOT;
#if defined($name)
OUTPUT_MACRO_NAME_VALUE($name);
#endif /* $name */
EOT
}
}
close(CONFIG_FILE);
}
# Read the full format file into a string
local $/;
open(FORMAT_FILE, "<", $query_config_format_file) or die "Opening query config format file '$query_config_format_file': $!";
my $query_config_format = <FORMAT_FILE>;
close(FORMAT_FILE);
# Replace the body of the query_config() function with the code we just wrote
$query_config_format =~ s/CHECK_CONFIG/$config_check/g;
$query_config_format =~ s/LIST_CONFIG/$list_config/g;
# Rewrite the query_config.c file
open(QUERY_CONFIG_FILE, ">", $query_config_file) or die "Opening destination file '$query_config_file': $!";
print QUERY_CONFIG_FILE $query_config_format;
close(QUERY_CONFIG_FILE);
-416
View File
@@ -1,416 +0,0 @@
#!/usr/bin/env python3
"""Generate library/ssl_debug_helpers_generated.c
The code generated by this module includes debug helper functions that can not be
implemented by fixed codes.
"""
# Copyright The Mbed TLS Contributors
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
import sys
import re
import os
import textwrap
import argparse
import framework_scripts_path # pylint: disable=unused-import
from mbedtls_framework import build_tree
def remove_c_comments(string):
"""
Remove C style comments from input string
"""
string_pattern = r"(?P<string>\".*?\"|\'.*?\')"
comment_pattern = r"(?P<comment>/\*.*?\*/|//[^\r\n]*$)"
pattern = re.compile(string_pattern + r'|' + comment_pattern,
re.MULTILINE | re.DOTALL)
def replacer(match):
if match.lastgroup == 'comment':
return ""
return match.group()
return pattern.sub(replacer, string)
class CondDirectiveNotMatch(Exception):
pass
def preprocess_c_source_code(source, *classes):
"""
Simple preprocessor for C source code.
Only processes condition directives without expanding them.
Yield object according to the classes input. Most match firstly
If the directive pair does not match , raise CondDirectiveNotMatch.
Assume source code does not include comments and compile pass.
"""
pattern = re.compile(r"^[ \t]*#[ \t]*" +
r"(?P<directive>(if[ \t]|ifndef[ \t]|ifdef[ \t]|else|endif))" +
r"[ \t]*(?P<param>(.*\\\n)*.*$)",
re.MULTILINE)
stack = []
def _yield_objects(s, d, p, st, end):
"""
Output matched source piece
"""
nonlocal stack
start_line, end_line = '', ''
if stack:
start_line = '#{} {}'.format(d, p)
if d == 'if':
end_line = '#endif /* {} */'.format(p)
elif d == 'ifdef':
end_line = '#endif /* defined({}) */'.format(p)
else:
end_line = '#endif /* !defined({}) */'.format(p)
has_instance = False
for cls in classes:
for instance in cls.extract(s, st, end):
if has_instance is False:
has_instance = True
yield pair_start, start_line
yield instance.span()[0], instance
if has_instance:
yield start, end_line
for match in pattern.finditer(source):
directive = match.groupdict()['directive'].strip()
param = match.groupdict()['param']
start, end = match.span()
if directive in ('if', 'ifndef', 'ifdef'):
stack.append((directive, param, start, end))
continue
if not stack:
raise CondDirectiveNotMatch()
pair_directive, pair_param, pair_start, pair_end = stack.pop()
yield from _yield_objects(source,
pair_directive,
pair_param,
pair_end,
start)
if directive == 'endif':
continue
if pair_directive == 'if':
directive = 'if'
param = "!( {} )".format(pair_param)
elif pair_directive == 'ifdef':
directive = 'ifndef'
param = pair_param
else:
directive = 'ifdef'
param = pair_param
stack.append((directive, param, start, end))
assert not stack, len(stack)
class EnumDefinition:
"""
Generate helper functions around enumeration.
Currently, it generate translation function from enum value to string.
Enum definition looks like:
[typedef] enum [prefix name] { [body] } [suffix name];
Known limitation:
- the '}' and ';' SHOULD NOT exist in different macro blocks. Like
```
enum test {
....
#if defined(A)
....
};
#else
....
};
#endif
```
"""
@classmethod
def extract(cls, source_code, start=0, end=-1):
enum_pattern = re.compile(r'enum\s*(?P<prefix_name>\w*)\s*' +
r'{\s*(?P<body>[^}]*)}' +
r'\s*(?P<suffix_name>\w*)\s*;',
re.MULTILINE | re.DOTALL)
for match in enum_pattern.finditer(source_code, start, end):
yield EnumDefinition(source_code,
span=match.span(),
group=match.groupdict())
def __init__(self, source_code, span=None, group=None):
assert isinstance(group, dict)
prefix_name = group.get('prefix_name', None)
suffix_name = group.get('suffix_name', None)
body = group.get('body', None)
assert prefix_name or suffix_name
assert body
assert span
# If suffix_name exists, it is a typedef
self._prototype = suffix_name if suffix_name else 'enum ' + prefix_name
self._name = suffix_name if suffix_name else prefix_name
self._body = body
self._source = source_code
self._span = span
def __repr__(self):
return 'Enum({},{})'.format(self._name, self._span)
def __str__(self):
return repr(self)
def span(self):
return self._span
def generate_translation_function(self):
"""
Generate function for translating value to string
"""
translation_table = []
for line in self._body.splitlines():
if line.strip().startswith('#'):
# Preprocess directive, keep it in table
translation_table.append(line.strip())
continue
if not line.strip():
continue
for field in line.strip().split(','):
if not field.strip():
continue
member = field.strip().split()[0]
translation_table.append(
'{space}case {member}:\n{space} return "{member}";'
.format(member=member, space=' '*8)
)
body = textwrap.dedent('''\
const char *{name}_str( {prototype} in )
{{
switch (in) {{
{translation_table}
default:
return "UNKNOWN_VALUE";
}}
}}
''')
body = body.format(translation_table='\n'.join(translation_table),
name=self._name,
prototype=self._prototype)
return body
class SignatureAlgorithmDefinition:
"""
Generate helper functions for signature algorithms.
It generates translation function from signature algorithm define to string.
Signature algorithm definition looks like:
#define MBEDTLS_TLS1_3_SIG_[ upper case signature algorithm ] [ value(hex) ]
Known limitation:
- the definitions SHOULD exist in same macro blocks.
"""
@classmethod
def extract(cls, source_code, start=0, end=-1):
sig_alg_pattern = re.compile(r'#define\s+(?P<name>MBEDTLS_TLS1_3_SIG_\w+)\s+' +
r'(?P<value>0[xX][0-9a-fA-F]+)$',
re.MULTILINE | re.DOTALL)
matches = list(sig_alg_pattern.finditer(source_code, start, end))
if matches:
yield SignatureAlgorithmDefinition(source_code, definitions=matches)
def __init__(self, source_code, definitions=None):
if definitions is None:
definitions = []
assert isinstance(definitions, list) and definitions
self._definitions = definitions
self._source = source_code
def __repr__(self):
return 'SigAlgs({})'.format(self._definitions[0].span())
def span(self):
return self._definitions[0].span()
def __str__(self):
"""
Generate function for translating value to string
"""
translation_table = []
for m in self._definitions:
name = m.groupdict()['name']
return_val = name[len('MBEDTLS_TLS1_3_SIG_'):].lower()
translation_table.append(
' case {}:\n return "{}";'.format(name, return_val))
body = textwrap.dedent('''\
const char *mbedtls_ssl_sig_alg_to_str( uint16_t in )
{{
switch( in )
{{
{translation_table}
}};
return "UNKNOWN";
}}''')
body = body.format(translation_table='\n'.join(translation_table))
return body
class NamedGroupDefinition:
"""
Generate helper functions for named group
It generates translation function from named group define to string.
Named group definition looks like:
#define MBEDTLS_SSL_IANA_TLS_GROUP_[ upper case named group ] [ value(hex) ]
Known limitation:
- the definitions SHOULD exist in same macro blocks.
"""
@classmethod
def extract(cls, source_code, start=0, end=-1):
named_group_pattern = re.compile(r'#define\s+(?P<name>MBEDTLS_SSL_IANA_TLS_GROUP_\w+)\s+' +
r'(?P<value>0[xX][0-9a-fA-F]+)$',
re.MULTILINE | re.DOTALL)
matches = list(named_group_pattern.finditer(source_code, start, end))
if matches:
yield NamedGroupDefinition(source_code, definitions=matches)
def __init__(self, source_code, definitions=None):
if definitions is None:
definitions = []
assert isinstance(definitions, list) and definitions
self._definitions = definitions
self._source = source_code
def __repr__(self):
return 'NamedGroup({})'.format(self._definitions[0].span())
def span(self):
return self._definitions[0].span()
def __str__(self):
"""
Generate function for translating value to string
"""
translation_table = []
for m in self._definitions:
name = m.groupdict()['name']
iana_name = name[len('MBEDTLS_SSL_IANA_TLS_GROUP_'):].lower()
translation_table.append(' case {}:\n return "{}";'.format(name, iana_name))
body = textwrap.dedent('''\
const char *mbedtls_ssl_named_group_to_str( uint16_t in )
{{
switch( in )
{{
{translation_table}
}};
return "UNKNOWN";
}}''')
body = body.format(translation_table='\n'.join(translation_table))
return body
OUTPUT_C_TEMPLATE = '''\
/* Automatically generated by generate_ssl_debug_helpers.py. DO NOT EDIT. */
/**
* \\file ssl_debug_helpers_generated.c
*
* \\brief Automatically generated helper functions for debugging
*/
/*
* Copyright The Mbed TLS Contributors
* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
*
*/
#include "common.h"
#if defined(MBEDTLS_DEBUG_C)
#include "ssl_debug_helpers.h"
{functions}
#endif /* MBEDTLS_DEBUG_C */
/* End of automatically generated file. */
'''
def generate_ssl_debug_helpers(output_directory, mbedtls_root):
"""
Generate functions of debug helps
"""
mbedtls_root = os.path.abspath(
mbedtls_root or build_tree.guess_mbedtls_root())
with open(os.path.join(mbedtls_root, 'include/mbedtls/ssl.h')) as f:
source_code = remove_c_comments(f.read())
definitions = dict()
for start, instance in preprocess_c_source_code(source_code,
EnumDefinition,
SignatureAlgorithmDefinition,
NamedGroupDefinition):
if start in definitions:
continue
if isinstance(instance, EnumDefinition):
definition = instance.generate_translation_function()
else:
definition = instance
definitions[start] = definition
function_definitions = [str(v) for _, v in sorted(definitions.items())]
if output_directory == sys.stdout:
sys.stdout.write(OUTPUT_C_TEMPLATE.format(
functions='\n'.join(function_definitions)))
else:
with open(os.path.join(output_directory, 'ssl_debug_helpers_generated.c'), 'w') as f:
f.write(OUTPUT_C_TEMPLATE.format(
functions='\n'.join(function_definitions)))
def main():
"""
Command line entry
"""
parser = argparse.ArgumentParser()
parser.add_argument('--mbedtls-root', nargs='?', default=None,
help='root directory of mbedtls source code')
parser.add_argument('output_directory', nargs='?',
default='library', help='source/header files location')
args = parser.parse_args()
generate_ssl_debug_helpers(args.output_directory, args.mbedtls_root)
return 0
if __name__ == '__main__':
sys.exit(main())
-304
View File
@@ -1,304 +0,0 @@
#!/usr/bin/env perl
# Generate main file, individual apps and solution files for
# MS Visual Studio 2017
#
# Must be run from Mbed TLS root or scripts directory.
# Takes no argument.
#
# Copyright The Mbed TLS Contributors
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
use warnings;
use strict;
use Digest::MD5 'md5_hex';
my $vsx_dir = "visualc/VS2017";
my $vsx_ext = "vcxproj";
my $vsx_app_tpl_file = "scripts/data_files/vs2017-app-template.$vsx_ext";
my $vsx_main_tpl_file = "scripts/data_files/vs2017-main-template.$vsx_ext";
my $vsx_main_file = "$vsx_dir/mbedTLS.$vsx_ext";
my $vsx_sln_tpl_file = "scripts/data_files/vs2017-sln-template.sln";
my $vsx_sln_file = "$vsx_dir/mbedTLS.sln";
my $programs_dir = 'programs';
my $mbedtls_header_dir = 'include/mbedtls';
my $drivers_builtin_header_dir = 'tf-psa-crypto/drivers/builtin/include/mbedtls';
my $psa_header_dir = 'tf-psa-crypto/include/psa';
my $tls_source_dir = 'library';
my $crypto_core_source_dir = 'tf-psa-crypto/core';
my $crypto_source_dir = 'tf-psa-crypto/drivers/builtin/src';
my $test_source_dir = 'tests/src';
my $test_header_dir = 'tests/include/test';
my $test_drivers_header_dir = 'tests/include/test/drivers';
my $test_drivers_source_dir = 'tests/src/drivers';
my @thirdparty_header_dirs = qw(
tf-psa-crypto/drivers/everest/include/everest
);
my @thirdparty_source_dirs = qw(
tf-psa-crypto/drivers/everest/library
tf-psa-crypto/drivers/everest/library/kremlib
tf-psa-crypto/drivers/everest/library/legacy
);
# Directories to add to the include path.
# Order matters in case there are files with the same name in more than
# one directory: the compiler will use the first match.
my @include_directories = qw(
include
tf-psa-crypto/include
tf-psa-crypto/drivers/builtin/include
tf-psa-crypto/drivers/everest/include/
tf-psa-crypto/drivers/everest/include/everest
tf-psa-crypto/drivers/everest/include/everest/vs2013
tf-psa-crypto/drivers/everest/include/everest/kremlib
tests/include
);
my $include_directories = join(';', map {"../../$_"} @include_directories);
# Directories to add to the include path when building the libraries, but not
# when building tests or applications.
my @library_include_directories = qw(
library
tf-psa-crypto/core
tf-psa-crypto/drivers/builtin/src
);
my $library_include_directories =
join(';', map {"../../$_"} (@library_include_directories,
@include_directories));
my @excluded_files = qw(
tf-psa-crypto/drivers/everest/library/Hacl_Curve25519.c
);
my %excluded_files = ();
foreach (@excluded_files) { $excluded_files{$_} = 1 }
my $vsx_hdr_tpl = <<EOT;
<ClInclude Include="..\\..\\{NAME}" />
EOT
my $vsx_src_tpl = <<EOT;
<ClCompile Include="..\\..\\{NAME}" />
EOT
my $vsx_sln_app_entry_tpl = <<EOT;
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "{APPNAME}", "{APPNAME}.vcxproj", "{GUID}"
ProjectSection(ProjectDependencies) = postProject
{46CF2D25-6A36-4189-B59C-E4815388E554} = {46CF2D25-6A36-4189-B59C-E4815388E554}
EndProjectSection
EndProject
EOT
my $vsx_sln_conf_entry_tpl = <<EOT;
{GUID}.Debug|Win32.ActiveCfg = Debug|Win32
{GUID}.Debug|Win32.Build.0 = Debug|Win32
{GUID}.Debug|x64.ActiveCfg = Debug|x64
{GUID}.Debug|x64.Build.0 = Debug|x64
{GUID}.Release|Win32.ActiveCfg = Release|Win32
{GUID}.Release|Win32.Build.0 = Release|Win32
{GUID}.Release|x64.ActiveCfg = Release|x64
{GUID}.Release|x64.Build.0 = Release|x64
EOT
exit( main() );
sub check_dirs {
foreach my $d (@thirdparty_header_dirs, @thirdparty_source_dirs) {
if (not (-d $d)) { return 0; }
}
return -d $vsx_dir
&& -d $mbedtls_header_dir
&& -d $drivers_builtin_header_dir
&& -d $psa_header_dir
&& -d $tls_source_dir
&& -d $crypto_core_source_dir
&& -d $crypto_source_dir
&& -d $test_source_dir
&& -d $test_drivers_source_dir
&& -d $test_header_dir
&& -d $test_drivers_header_dir
&& -d $programs_dir;
}
sub slurp_file {
my ($filename) = @_;
local $/ = undef;
open my $fh, '<:crlf', $filename or die "Could not read $filename\n";
my $content = <$fh>;
close $fh;
return $content;
}
sub content_to_file {
my ($content, $filename) = @_;
open my $fh, '>:crlf', $filename or die "Could not write to $filename\n";
print $fh $content;
close $fh;
}
sub gen_app_guid {
my ($path) = @_;
my $guid = md5_hex( "mbedTLS:$path" );
$guid =~ s/(.{8})(.{4})(.{4})(.{4})(.{12})/\U{$1-$2-$3-$4-$5}/;
return $guid;
}
sub gen_app {
my ($path, $template, $dir, $ext) = @_;
my $guid = gen_app_guid( $path );
$path =~ s!/!\\!g;
(my $appname = $path) =~ s/.*\\//;
my $is_test_app = ($path =~ m/^test\\/);
my $srcs = "<ClCompile Include=\"..\\..\\programs\\$path.c\" \/>";
if( $appname eq "ssl_client2" or $appname eq "ssl_server2" or
$appname eq "query_compile_time_config" ) {
$srcs .= "\n <ClCompile Include=\"..\\..\\programs\\test\\query_config.c\" \/>";
}
if( $appname eq "ssl_client2" or $appname eq "ssl_server2" ) {
$srcs .= "\n <ClCompile Include=\"..\\..\\programs\\ssl\\ssl_test_lib.c\" \/>";
}
my $content = $template;
$content =~ s/<SOURCES>/$srcs/g;
$content =~ s/<APPNAME>/$appname/g;
$content =~ s/<GUID>/$guid/g;
$content =~ s/INCLUDE_DIRECTORIES\n/($is_test_app ?
$library_include_directories :
$include_directories)/ge;
content_to_file( $content, "$dir/$appname.$ext" );
}
sub get_app_list {
my $makefile_contents = slurp_file('programs/Makefile');
$makefile_contents =~ /\n\s*APPS\s*=[\\\s]*(.*?)(?<!\\)[\#\n]/s
or die "Cannot find APPS = ... in programs/Makefile\n";
return split /(?:\s|\\)+/, $1;
}
sub gen_app_files {
my @app_list = @_;
my $vsx_tpl = slurp_file( $vsx_app_tpl_file );
for my $app ( @app_list ) {
gen_app( $app, $vsx_tpl, $vsx_dir, $vsx_ext );
}
}
sub gen_entry_list {
my ($tpl, @names) = @_;
my $entries;
for my $name (@names) {
(my $entry = $tpl) =~ s/{NAME}/$name/g;
$entries .= $entry;
}
return $entries;
}
sub gen_main_file {
my ($headers, $sources,
$hdr_tpl, $src_tpl,
$main_tpl, $main_out) = @_;
my $header_entries = gen_entry_list( $hdr_tpl, @$headers );
my $source_entries = gen_entry_list( $src_tpl, @$sources );
my $out = slurp_file( $main_tpl );
$out =~ s/SOURCE_ENTRIES\n/$source_entries/m;
$out =~ s/HEADER_ENTRIES\n/$header_entries/m;
$out =~ s/INCLUDE_DIRECTORIES\n/$library_include_directories/g;
content_to_file( $out, $main_out );
}
sub gen_vsx_solution {
my (@app_names) = @_;
my ($app_entries, $conf_entries);
for my $path (@app_names) {
my $guid = gen_app_guid( $path );
(my $appname = $path) =~ s!.*/!!;
my $app_entry = $vsx_sln_app_entry_tpl;
$app_entry =~ s/{APPNAME}/$appname/g;
$app_entry =~ s/{GUID}/$guid/g;
$app_entries .= $app_entry;
my $conf_entry = $vsx_sln_conf_entry_tpl;
$conf_entry =~ s/{GUID}/$guid/g;
$conf_entries .= $conf_entry;
}
my $out = slurp_file( $vsx_sln_tpl_file );
$out =~ s/APP_ENTRIES\n/$app_entries/m;
$out =~ s/CONF_ENTRIES\n/$conf_entries/m;
content_to_file( $out, $vsx_sln_file );
}
sub del_vsx_files {
unlink glob "'$vsx_dir/*.$vsx_ext'";
unlink $vsx_main_file;
unlink $vsx_sln_file;
}
sub main {
if( ! check_dirs() ) {
chdir '..' or die;
check_dirs or die "Must be run from Mbed TLS root or scripts dir\n";
}
# Remove old files to ensure that, for example, project files from deleted
# apps are not kept
del_vsx_files();
my @app_list = get_app_list();
my @header_dirs = (
$mbedtls_header_dir,
$drivers_builtin_header_dir,
$psa_header_dir,
$test_header_dir,
$test_drivers_header_dir,
$tls_source_dir,
$crypto_core_source_dir,
$crypto_source_dir,
@thirdparty_header_dirs,
);
my @headers = (map { <$_/*.h> } @header_dirs);
my @source_dirs = (
$tls_source_dir,
$crypto_core_source_dir,
$crypto_source_dir,
$test_source_dir,
$test_drivers_source_dir,
@thirdparty_source_dirs,
);
my @sources = (map { <$_/*.c> } @source_dirs);
@headers = grep { ! $excluded_files{$_} } @headers;
@sources = grep { ! $excluded_files{$_} } @sources;
map { s!/!\\!g } @headers;
map { s!/!\\!g } @sources;
gen_app_files( @app_list );
gen_main_file( \@headers, \@sources,
$vsx_hdr_tpl, $vsx_src_tpl,
$vsx_main_tpl_file, $vsx_main_file );
gen_vsx_solution( @app_list );
return 0;
}
-94
View File
@@ -1,94 +0,0 @@
#!/bin/sh
help () {
cat <<EOF
Usage: $0 [-r]
Collect coverage statistics of library code into an HTML report.
General instructions:
1. Build the library with CFLAGS="--coverage -O0 -g3" and link the test
programs with LDFLAGS="--coverage".
This can be an out-of-tree build.
For example (in-tree):
make CFLAGS="--coverage -O0 -g3" LDFLAGS="--coverage"
Or (out-of-tree):
mkdir build-coverage && cd build-coverage &&
cmake -D CMAKE_BUILD_TYPE=Coverage .. && make
2. Run whatever tests you want.
3. Run this script from the parent of the directory containing the library
object files and coverage statistics files.
4. Browse the coverage report in Coverage/index.html.
5. After rework, run "$0 -r", then re-test and run "$0" to get a fresh report.
Options
-r Reset traces. Run this before re-testing to get fresh measurements.
EOF
}
# Copyright The Mbed TLS Contributors
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
set -eu
# Project detection
PROJECT_NAME_FILE='./scripts/project_name.txt'
if read -r PROJECT_NAME < "$PROJECT_NAME_FILE"; then :; else
echo "$PROJECT_NAME_FILE does not exist... Exiting..." >&2
exit 1
fi
in_mbedtls_repo () {
test "$PROJECT_NAME" = "Mbed TLS"
}
# Collect stats and build a HTML report.
lcov_library_report () {
rm -rf Coverage
mkdir Coverage Coverage/tmp
# Pass absolute paths as lcov output files. This works around a bug
# whereby lcov tries to create the output file in the root directory
# if it has emitted a warning. A fix was released in lcov 1.13 in 2016.
# Ubuntu 16.04 is affected, 18.04 and above are not.
# https://github.com/linux-test-project/lcov/commit/632c25a0d1f5e4d2f4fd5b28ce7c8b86d388c91f
COVTMP=$PWD/Coverage/tmp
lcov --capture --initial --directory $library_dir -o "$COVTMP/files.info"
lcov --rc lcov_branch_coverage=1 --capture --directory $library_dir -o "$COVTMP/tests.info"
lcov --rc lcov_branch_coverage=1 --add-tracefile "$COVTMP/files.info" --add-tracefile "$COVTMP/tests.info" -o "$COVTMP/all.info"
lcov --rc lcov_branch_coverage=1 --remove "$COVTMP/all.info" -o "$COVTMP/final.info" '*.h'
gendesc tests/Descriptions.txt -o "$COVTMP/descriptions"
genhtml --title "$title" --description-file "$COVTMP/descriptions" --keep-descriptions --legend --branch-coverage -o Coverage "$COVTMP/final.info"
rm -f "$COVTMP/"*.info "$COVTMP/descriptions"
echo "Coverage report in: Coverage/index.html"
}
# Reset the traces to 0.
lcov_reset_traces () {
# Location with plain make
rm -f $library_dir/*.gcda
# Location with CMake
rm -f $library_dir/CMakeFiles/*.dir/*.gcda
}
if [ $# -gt 0 ] && [ "$1" = "--help" ]; then
help
exit
fi
if in_mbedtls_repo; then
library_dir='library'
title='Mbed TLS'
else
library_dir='core'
title='TF-PSA-Crypto'
fi
main=lcov_library_report
while getopts r OPTLET; do
case $OPTLET in
r) main=lcov_reset_traces;;
*) help 2>&1; exit 120;;
esac
done
shift $((OPTIND - 1))
"$main" "$@"
-10
View File
@@ -1,10 +0,0 @@
# Python packages that are only useful to Mbed TLS maintainers.
-r ci.requirements.txt
# For source code analyses
clang
# For building some test vectors
pycryptodomex
pycryptodome-test-vectors
-32
View File
@@ -1,32 +0,0 @@
@rem Generate automatically-generated configuration-independent source files
@rem and build scripts.
@rem Requirements:
@rem * Perl must be on the PATH ("perl" command).
@rem * Python 3.8 or above must be on the PATH ("python" command).
@rem * Either a C compiler called "cc" must be on the PATH, or
@rem the "CC" environment variable must point to a C compiler.
@rem @@@@ library\** @@@@
@rem psa_crypto_driver_wrappers.h needs to be generated prior to
@rem generate_visualc_files.pl being invoked.
python scripts\generate_driver_wrappers.py || exit /b 1
perl scripts\generate_errors.pl || exit /b 1
perl scripts\generate_query_config.pl || exit /b 1
perl scripts\generate_features.pl || exit /b 1
python scripts\generate_ssl_debug_helpers.py || exit /b 1
@rem @@@@ Build @@@@
perl scripts\generate_visualc_files.pl || exit /b 1
@rem @@@@ programs\** @@@@
python scripts\generate_psa_constants.py || exit /b 1
@rem @@@@ tests\** @@@@
python framework\scripts\generate_bignum_tests.py --directory tf-psa-crypto\tests\suites || exit /b 1
python framework\scripts\generate_config_tests.py tests\suites\test_suite_config.mbedtls_boolean.data || exit /b 1
python framework\scripts\generate_config_tests.py --directory tf-psa-crypto\tests\suites tests\suites\test_suite_config.psa_boolean.data || exit /b 1
python framework\scripts\generate_ecp_tests.py --directory tf-psa-crypto\tests\suites || exit /b 1
python framework\scripts\generate_psa_tests.py --directory tf-psa-crypto\tests\suites || exit /b 1
python framework\scripts\generate_test_keys.py --output tests\src\test_keys.h || exit /b 1
python framework\scripts\generate_test_cert_macros.py --output tests\src\test_certs.h || exit /b 1
python tests\scripts\generate_tls13_compat_tests.py || exit /b 1
-36
View File
@@ -1,36 +0,0 @@
#!/usr/bin/env perl
# Parse a massif.out.xxx file and output peak total memory usage
#
# Copyright The Mbed TLS Contributors
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
use warnings;
use strict;
use utf8;
use open qw(:std utf8);
die unless @ARGV == 1;
my @snaps;
open my $fh, '<', $ARGV[0] or die;
{ local $/ = 'snapshot='; @snaps = <$fh>; }
close $fh or die;
my ($max, $max_heap, $max_he, $max_stack) = (0, 0, 0, 0);
for (@snaps)
{
my ($heap, $heap_extra, $stack) = m{
mem_heap_B=(\d+)\n
mem_heap_extra_B=(\d+)\n
mem_stacks_B=(\d+)
}xm;
next unless defined $heap;
my $total = $heap + $heap_extra + $stack;
if( $total > $max ) {
($max, $max_heap, $max_he, $max_stack) = ($total, $heap, $heap_extra, $stack);
}
}
printf "$max (heap $max_heap+$max_he, stack $max_stack)\n";
@@ -0,0 +1,167 @@
"""Discover all the test cases (unit tests and SSL tests)."""
# Copyright The Mbed TLS Contributors
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
import glob
import os
import re
import subprocess
import sys
import scripts_path # pylint: disable=unused-import
from mbedtls_framework import build_tree
class ScriptOutputError(ValueError):
"""A kind of ValueError that indicates we found
the script doesn't list test cases in an expected
pattern.
"""
@property
def script_name(self):
return super().args[0]
@property
def idx(self):
return super().args[1]
@property
def line(self):
return super().args[2]
class Results:
"""Store file and line information about errors or warnings in test suites."""
def __init__(self, options):
self.errors = 0
self.warnings = 0
self.ignore_warnings = options.quiet
def error(self, file_name, line_number, fmt, *args):
sys.stderr.write(('{}:{}:ERROR:' + fmt + '\n').
format(file_name, line_number, *args))
self.errors += 1
def warning(self, file_name, line_number, fmt, *args):
if not self.ignore_warnings:
sys.stderr.write(('{}:{}:Warning:' + fmt + '\n')
.format(file_name, line_number, *args))
self.warnings += 1
class TestDescriptionExplorer:
"""An iterator over test cases with descriptions.
The test cases that have descriptions are:
* Individual unit tests (entries in a .data file) in test suites.
* Individual test cases in ssl-opt.sh.
This is an abstract class. To use it, derive a class that implements
the process_test_case method, and call walk_all().
"""
def process_test_case(self, per_file_state,
file_name, line_number, description):
"""Process a test case.
per_file_state: an object created by new_per_file_state() at the beginning
of each file.
file_name: a relative path to the file containing the test case.
line_number: the line number in the given file.
description: the test case description as a byte string.
"""
raise NotImplementedError
def new_per_file_state(self):
"""Return a new per-file state object.
The default per-file state object is None. Child classes that require per-file
state may override this method.
"""
#pylint: disable=no-self-use
return None
def walk_test_suite(self, data_file_name):
"""Iterate over the test cases in the given unit test data file."""
in_paragraph = False
descriptions = self.new_per_file_state() # pylint: disable=assignment-from-none
with open(data_file_name, 'rb') as data_file:
for line_number, line in enumerate(data_file, 1):
line = line.rstrip(b'\r\n')
if not line:
in_paragraph = False
continue
if line.startswith(b'#'):
continue
if not in_paragraph:
# This is a test case description line.
self.process_test_case(descriptions,
data_file_name, line_number, line)
in_paragraph = True
def collect_from_script(self, script_name):
"""Collect the test cases in a script by calling its listing test cases
option"""
descriptions = self.new_per_file_state() # pylint: disable=assignment-from-none
listed = subprocess.check_output(['sh', script_name, '--list-test-cases'])
# Assume test file is responsible for printing identical format of
# test case description between --list-test-cases and its OUTCOME.CSV
#
# idx indicates the number of test case since there is no line number
# in the script for each test case.
for idx, line in enumerate(listed.splitlines()):
# We are expecting the script to list the test cases in
# `<suite_name>;<description>` pattern.
script_outputs = line.split(b';', 1)
if len(script_outputs) == 2:
suite_name, description = script_outputs
else:
raise ScriptOutputError(script_name, idx, line.decode("utf-8"))
self.process_test_case(descriptions,
suite_name.decode('utf-8'),
idx,
description.rstrip())
@staticmethod
def collect_test_directories():
"""Get the relative path for the TLS and Crypto test directories."""
mbedtls_root = build_tree.guess_mbedtls_root()
directories = [os.path.join(mbedtls_root, 'tests'),
os.path.join(mbedtls_root, 'tf-psa-crypto', 'tests')]
directories = [os.path.relpath(p) for p in directories]
return directories
def walk_all(self):
"""Iterate over all named test cases."""
test_directories = self.collect_test_directories()
for directory in test_directories:
for data_file_name in glob.glob(os.path.join(directory, 'suites',
'*.data')):
self.walk_test_suite(data_file_name)
for sh_file in ['ssl-opt.sh', 'compat.sh']:
sh_file = os.path.join(directory, sh_file)
if os.path.isfile(sh_file):
self.collect_from_script(sh_file)
class TestDescriptions(TestDescriptionExplorer):
"""Collect the available test cases."""
def __init__(self):
super().__init__()
self.descriptions = set()
def process_test_case(self, _per_file_state,
file_name, _line_number, description):
"""Record an available test case."""
base_name = re.sub(r'\.[^.]*$', '', re.sub(r'.*/', '', file_name))
key = ';'.join([base_name, description.decode('utf-8')])
self.descriptions.add(key)
def collect_available_test_cases():
"""Collect the available test cases."""
explorer = TestDescriptions()
explorer.walk_all()
return sorted(explorer.descriptions)
@@ -0,0 +1,362 @@
"""Outcome file analysis code.
This module is the bulk of the code of tests/scripts/analyze_outcomes.py
in each consuming branch. The consuming script is expected to derive
the classes with branch-specific customizations such as ignore lists.
"""
# Copyright The Mbed TLS Contributors
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
import argparse
import sys
import traceback
import re
import subprocess
import os
import typing
import collect_test_cases
# `ComponentOutcomes` is a named tuple which is defined as:
# ComponentOutcomes(
# successes = {
# "<suite_case>",
# ...
# },
# failures = {
# "<suite_case>",
# ...
# }
# )
# suite_case = "<suite>;<case>"
ComponentOutcomes = typing.NamedTuple('ComponentOutcomes',
[('successes', typing.Set[str]),
('failures', typing.Set[str])])
# `Outcomes` is a representation of the outcomes file,
# which defined as:
# Outcomes = {
# "<component>": ComponentOutcomes,
# ...
# }
Outcomes = typing.Dict[str, ComponentOutcomes]
class Results:
"""Process analysis results."""
def __init__(self):
self.error_count = 0
self.warning_count = 0
def new_section(self, fmt, *args, **kwargs):
self._print_line('\n*** ' + fmt + ' ***\n', *args, **kwargs)
def info(self, fmt, *args, **kwargs):
self._print_line('Info: ' + fmt, *args, **kwargs)
def error(self, fmt, *args, **kwargs):
self.error_count += 1
self._print_line('Error: ' + fmt, *args, **kwargs)
def warning(self, fmt, *args, **kwargs):
self.warning_count += 1
self._print_line('Warning: ' + fmt, *args, **kwargs)
@staticmethod
def _print_line(fmt, *args, **kwargs):
sys.stderr.write((fmt + '\n').format(*args, **kwargs))
def execute_reference_driver_tests(results: Results, ref_component: str, driver_component: str, \
outcome_file: str) -> None:
"""Run the tests specified in ref_component and driver_component. Results
are stored in the output_file and they will be used for the following
coverage analysis"""
results.new_section("Test {} and {}", ref_component, driver_component)
shell_command = "tests/scripts/all.sh --outcome-file " + outcome_file + \
" " + ref_component + " " + driver_component
results.info("Running: {}", shell_command)
ret_val = subprocess.run(shell_command.split(), check=False).returncode
if ret_val != 0:
results.error("failed to run reference/driver components")
IgnoreEntry = typing.Union[str, typing.Pattern]
def name_matches_pattern(name: str, str_or_re: IgnoreEntry) -> bool:
"""Check if name matches a pattern, that may be a string or regex.
- If the pattern is a string, name must be equal to match.
- If the pattern is a regex, name must fully match.
"""
# The CI's python is too old for re.Pattern
#if isinstance(str_or_re, re.Pattern):
if not isinstance(str_or_re, str):
return str_or_re.fullmatch(name) is not None
else:
return str_or_re == name
def read_outcome_file(outcome_file: str) -> Outcomes:
"""Parse an outcome file and return an outcome collection.
"""
outcomes = {}
with open(outcome_file, 'r', encoding='utf-8') as input_file:
for line in input_file:
(_platform, component, suite, case, result, _cause) = line.split(';')
# Note that `component` is not unique. If a test case passes on Linux
# and fails on FreeBSD, it'll end up in both the successes set and
# the failures set.
suite_case = ';'.join([suite, case])
if component not in outcomes:
outcomes[component] = ComponentOutcomes(set(), set())
if result == 'PASS':
outcomes[component].successes.add(suite_case)
elif result == 'FAIL':
outcomes[component].failures.add(suite_case)
return outcomes
class Task:
"""Base class for outcome analysis tasks."""
# Override the following in child classes.
# Map test suite names (with the test_suite_prefix) to a list of ignored
# test cases. Each element in the list can be either a string or a regex;
# see the `name_matches_pattern` function.
IGNORED_TESTS = {} #type: typing.Dict[str, typing.List[IgnoreEntry]]
def __init__(self, options) -> None:
"""Pass command line options to the tasks.
Each task decides which command line options it cares about.
"""
pass
def section_name(self) -> str:
"""The section name to use in results."""
raise NotImplementedError
def ignored_tests(self, test_suite: str) -> typing.Iterator[IgnoreEntry]:
"""Generate the ignore list for the specified test suite."""
if test_suite in self.IGNORED_TESTS:
yield from self.IGNORED_TESTS[test_suite]
pos = test_suite.find('.')
if pos != -1:
base_test_suite = test_suite[:pos]
if base_test_suite in self.IGNORED_TESTS:
yield from self.IGNORED_TESTS[base_test_suite]
def is_test_case_ignored(self, test_suite: str, test_string: str) -> bool:
"""Check if the specified test case is ignored."""
for str_or_re in self.ignored_tests(test_suite):
if name_matches_pattern(test_string, str_or_re):
return True
return False
def run(self, results: Results, outcomes: Outcomes):
"""Run the analysis on the specified outcomes.
Signal errors via the results objects
"""
raise NotImplementedError
class CoverageTask(Task):
"""Analyze test coverage."""
# Test cases whose suite and description are matched by an entry in
# IGNORED_TESTS are expected to be never executed.
# All other test cases are expected to be executed at least once.
def __init__(self, options) -> None:
super().__init__(options)
self.full_coverage = options.full_coverage #type: bool
@staticmethod
def section_name() -> str:
return "Analyze coverage"
def run(self, results: Results, outcomes: Outcomes) -> None:
"""Check that all available test cases are executed at least once."""
# Make sure that the generated data files are present (and up-to-date).
# This allows analyze_outcomes.py to run correctly on a fresh Git
# checkout.
cp = subprocess.run(['make', 'generated_files'],
cwd='tests',
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
check=False)
if cp.returncode != 0:
sys.stderr.write(cp.stdout.decode('utf-8'))
results.error("Failed \"make generated_files\" in tests. "
"Coverage analysis may be incorrect.")
available = collect_test_cases.collect_available_test_cases()
for suite_case in available:
hit = any(suite_case in comp_outcomes.successes or
suite_case in comp_outcomes.failures
for comp_outcomes in outcomes.values())
(test_suite, test_description) = suite_case.split(';')
ignored = self.is_test_case_ignored(test_suite, test_description)
if not hit and not ignored:
if self.full_coverage:
results.error('Test case not executed: {}', suite_case)
else:
results.warning('Test case not executed: {}', suite_case)
elif hit and ignored:
# If a test case is no longer always skipped, we should remove
# it from the ignore list.
if self.full_coverage:
results.error('Test case was executed but marked as ignored for coverage: {}',
suite_case)
else:
results.warning('Test case was executed but marked as ignored for coverage: {}',
suite_case)
class DriverVSReference(Task):
"""Compare outcomes from testing with and without a driver.
There are 2 options to use analyze_driver_vs_reference_xxx locally:
1. Run tests and then analysis:
- tests/scripts/all.sh --outcome-file "$PWD/out.csv" <component_ref> <component_driver>
- tests/scripts/analyze_outcomes.py out.csv analyze_driver_vs_reference_xxx
2. Let this script run both automatically:
- tests/scripts/analyze_outcomes.py out.csv analyze_driver_vs_reference_xxx
"""
# Override the following in child classes.
# Configuration name (all.sh component) used as the reference.
REFERENCE = ''
# Configuration name (all.sh component) used as the driver.
DRIVER = ''
# Ignored test suites (without the test_suite_ prefix).
IGNORED_SUITES = [] #type: typing.List[str]
def __init__(self, options) -> None:
super().__init__(options)
self.ignored_suites = frozenset('test_suite_' + x
for x in self.IGNORED_SUITES)
def section_name(self) -> str:
return f"Analyze driver {self.DRIVER} vs reference {self.REFERENCE}"
def run(self, results: Results, outcomes: Outcomes) -> None:
"""Check that all tests passing in the driver component are also
passing in the corresponding reference component.
Skip:
- full test suites provided in ignored_suites list
- only some specific test inside a test suite, for which the corresponding
output string is provided
"""
ref_outcomes = outcomes.get("component_" + self.REFERENCE)
driver_outcomes = outcomes.get("component_" + self.DRIVER)
if ref_outcomes is None or driver_outcomes is None:
results.error("required components are missing: bad outcome file?")
return
if not ref_outcomes.successes:
results.error("no passing test in reference component: bad outcome file?")
return
for suite_case in ref_outcomes.successes:
# suite_case is like "test_suite_foo.bar;Description of test case"
(full_test_suite, test_string) = suite_case.split(';')
test_suite = full_test_suite.split('.')[0] # retrieve main part of test suite name
# Immediately skip fully-ignored test suites
if test_suite in self.ignored_suites or \
full_test_suite in self.ignored_suites:
continue
# For ignored test cases inside test suites, just remember and:
# don't issue an error if they're skipped with drivers,
# but issue an error if they're not (means we have a bad entry).
ignored = self.is_test_case_ignored(full_test_suite, test_string)
if not ignored and not suite_case in driver_outcomes.successes:
results.error("SKIP/FAIL -> PASS: {}", suite_case)
if ignored and suite_case in driver_outcomes.successes:
results.error("uselessly ignored: {}", suite_case)
def main(known_tasks: typing.Dict[str, typing.Type[Task]]) -> None:
main_results = Results()
try:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('outcomes', metavar='OUTCOMES.CSV',
help='Outcome file to analyze')
parser.add_argument('specified_tasks', default='all', nargs='?',
help='Analysis to be done. By default, run all tasks. '
'With one or more TASK, run only those. '
'TASK can be the name of a single task or '
'comma/space-separated list of tasks. ')
parser.add_argument('--list', action='store_true',
help='List all available tasks and exit.')
parser.add_argument('--require-full-coverage', action='store_true',
dest='full_coverage', help="Require all available "
"test cases to be executed and issue an error "
"otherwise. This flag is ignored if 'task' is "
"neither 'all' nor 'analyze_coverage'")
options = parser.parse_args()
if options.list:
for task_name in known_tasks:
print(task_name)
sys.exit(0)
if options.specified_tasks == 'all':
tasks_list = list(known_tasks.keys())
else:
tasks_list = re.split(r'[, ]+', options.specified_tasks)
for task_name in tasks_list:
if task_name not in known_tasks:
sys.stderr.write('invalid task: {}\n'.format(task_name))
sys.exit(2)
# If the outcome file exists, parse it once and share the result
# among tasks to improve performance.
# Otherwise, it will be generated by execute_reference_driver_tests.
if not os.path.exists(options.outcomes):
if len(tasks_list) > 1:
sys.stderr.write("mutiple tasks found, please provide a valid outcomes file.\n")
sys.exit(2)
task_name = tasks_list[0]
task_class = known_tasks[task_name]
if not issubclass(task_class, DriverVSReference):
sys.stderr.write("please provide valid outcomes file for {}.\n".format(task_name))
sys.exit(2)
# mypy isn't smart enough to know that REFERENCE and DRIVER
# are *class* attributes of all classes derived from
# DriverVSReference. (It would be smart enough if we had an
# instance of task_class, but we can't construct an instance
# until we have the outcome data, so at this point we only
# have the class.) So we use indirection to access the class
# attributes.
execute_reference_driver_tests(main_results,
getattr(task_class, 'REFERENCE'),
getattr(task_class, 'DRIVER'),
options.outcomes)
outcomes = read_outcome_file(options.outcomes)
for task_name in tasks_list:
task_constructor = known_tasks[task_name]
task_instance = task_constructor(options)
main_results.new_section(task_instance.section_name())
task_instance.run(main_results, outcomes)
main_results.info("Overall results: {} warnings and {} errors",
main_results.warning_count, main_results.error_count)
sys.exit(0 if (main_results.error_count == 0) else 1)
except Exception: # pylint: disable=broad-except
# Print the backtrace and exit explicitly with our chosen status.
traceback.print_exc()
sys.exit(120)
-129
View File
@@ -1,129 +0,0 @@
#!/bin/sh
# Measure memory usage of a minimal client using a small configuration
# Currently hardwired to ccm-psk and suite-b, may be expanded later
#
# Use different build options for measuring executable size and memory usage,
# since for memory we want debug information.
#
# Copyright The Mbed TLS Contributors
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
set -eu
CONFIG_H='include/mbedtls/mbedtls_config.h'
CLIENT='mini_client'
CFLAGS_EXEC='-fno-asynchronous-unwind-tables -Wl,--gc-section -ffunction-sections -fdata-sections'
CFLAGS_MEM=-g3
if [ -r $CONFIG_H ]; then :; else
echo "$CONFIG_H not found" >&2
exit 1
fi
if grep -i cmake Makefile >/dev/null; then
echo "Not compatible with CMake" >&2
exit 1
fi
if [ $( uname ) != Linux ]; then
echo "Only work on Linux" >&2
exit 1
fi
if git status | grep -F $CONFIG_H >/dev/null 2>&1; then
echo "mbedtls_config.h not clean" >&2
exit 1
fi
# make measurements with one configuration
# usage: do_config <name> <unset-list> <server-args>
do_config()
{
NAME=$1
UNSET_LIST=$2
SERVER_ARGS=$3
echo ""
echo "config-$NAME:"
cp configs/config-$NAME.h $CONFIG_H
scripts/config.py unset MBEDTLS_SSL_SRV_C
for FLAG in $UNSET_LIST; do
scripts/config.py unset $FLAG
done
grep -F SSL_MAX_CONTENT_LEN $CONFIG_H || echo 'SSL_MAX_CONTENT_LEN=16384'
printf " Executable size... "
make clean
CFLAGS=$CFLAGS_EXEC make OFLAGS=-Os lib >/dev/null 2>&1
cd programs
CFLAGS=$CFLAGS_EXEC make OFLAGS=-Os ssl/$CLIENT >/dev/null
strip ssl/$CLIENT
stat -c '%s' ssl/$CLIENT
cd ..
printf " Peak ram usage... "
make clean
CFLAGS=$CFLAGS_MEM make OFLAGS=-Os lib >/dev/null 2>&1
cd programs
CFLAGS=$CFLAGS_MEM make OFLAGS=-Os ssl/$CLIENT >/dev/null
cd ..
./ssl_server2 $SERVER_ARGS >/dev/null &
SRV_PID=$!
sleep 1;
if valgrind --tool=massif --stacks=yes programs/ssl/$CLIENT >/dev/null 2>&1
then
FAILED=0
else
echo "client failed" >&2
FAILED=1
fi
kill $SRV_PID
wait $SRV_PID
scripts/massif_max.pl massif.out.*
mv massif.out.* massif-$NAME.$$
}
# preparation
CONFIG_BAK=${CONFIG_H}.bak
cp $CONFIG_H $CONFIG_BAK
rm -f massif.out.*
printf "building server... "
make clean
make lib >/dev/null 2>&1
(cd programs && make ssl/ssl_server2) >/dev/null
cp programs/ssl/ssl_server2 .
echo "done"
# actual measurements
do_config "ccm-psk-tls1_2" \
"" \
"psk=000102030405060708090A0B0C0D0E0F"
do_config "suite-b" \
"MBEDTLS_BASE64_C MBEDTLS_PEM_PARSE_C" \
""
# cleanup
mv $CONFIG_BAK $CONFIG_H
make clean
rm ssl_server2
exit $FAILED
-129
View File
@@ -1,129 +0,0 @@
#!/usr/bin/env python3
"""Install all the required Python packages, with the minimum Python version.
"""
# Copyright The Mbed TLS Contributors
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
import argparse
import os
import re
import subprocess
import sys
import tempfile
import typing
from typing import List, Optional
import framework_scripts_path # pylint: disable=unused-import
from mbedtls_framework import typing_util
def pylint_doesn_t_notice_that_certain_types_are_used_in_annotations(
_list: List[typing.Any],
) -> None:
pass
class Requirements:
"""Collect and massage Python requirements."""
def __init__(self) -> None:
self.requirements = [] #type: List[str]
def adjust_requirement(self, req: str) -> str:
"""Adjust a requirement to the minimum specified version."""
# allow inheritance #pylint: disable=no-self-use
# If a requirement specifies a minimum version, impose that version.
split_req = req.split(';', 1)
split_req[0] = re.sub(r'>=|~=', r'==', split_req[0])
return ';'.join(split_req)
def add_file(self, filename: str) -> None:
"""Add requirements from the specified file.
This method supports a subset of pip's requirement file syntax:
* One requirement specifier per line, which is passed to
`adjust_requirement`.
* Comments (``#`` at the beginning of the line or after whitespace).
* ``-r FILENAME`` to include another file.
"""
for line in open(filename):
line = line.strip()
line = re.sub(r'(\A|\s+)#.*', r'', line)
if not line:
continue
m = re.match(r'-r\s+', line)
if m:
nested_file = os.path.join(os.path.dirname(filename),
line[m.end(0):])
self.add_file(nested_file)
continue
self.requirements.append(self.adjust_requirement(line))
def write(self, out: typing_util.Writable) -> None:
"""List the gathered requirements."""
for req in self.requirements:
out.write(req + '\n')
def install(
self,
pip_general_options: Optional[List[str]] = None,
pip_install_options: Optional[List[str]] = None,
) -> None:
"""Call pip to install the requirements."""
if pip_general_options is None:
pip_general_options = []
if pip_install_options is None:
pip_install_options = []
with tempfile.TemporaryDirectory() as temp_dir:
# This is more complicated than it needs to be for the sake
# of Windows. Use a temporary file rather than the command line
# to avoid quoting issues. Use a temporary directory rather
# than NamedTemporaryFile because with a NamedTemporaryFile on
# Windows, the subprocess can't open the file because this process
# has an exclusive lock on it.
req_file_name = os.path.join(temp_dir, 'requirements.txt')
with open(req_file_name, 'w') as req_file:
self.write(req_file)
subprocess.check_call([sys.executable, '-m', 'pip'] +
pip_general_options +
['install'] + pip_install_options +
['-r', req_file_name])
DEFAULT_REQUIREMENTS_FILE = 'ci.requirements.txt'
def main() -> None:
"""Command line entry point."""
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--no-act', '-n',
action='store_true',
help="Don't act, just print what will be done")
parser.add_argument('--pip-install-option',
action='append', dest='pip_install_options',
help="Pass this option to pip install")
parser.add_argument('--pip-option',
action='append', dest='pip_general_options',
help="Pass this general option to pip")
parser.add_argument('--user',
action='append_const', dest='pip_install_options',
const='--user',
help="Install to the Python user install directory"
" (short for --pip-install-option --user)")
parser.add_argument('files', nargs='*', metavar='FILE',
help="Requirement files"
" (default: {} in the script's directory)" \
.format(DEFAULT_REQUIREMENTS_FILE))
options = parser.parse_args()
if not options.files:
options.files = [os.path.join(os.path.dirname(__file__),
DEFAULT_REQUIREMENTS_FILE)]
reqs = Requirements()
for filename in options.files:
reqs.add_file(filename)
reqs.write(sys.stdout)
if not options.no_act:
reqs.install(pip_general_options=options.pip_general_options,
pip_install_options=options.pip_install_options)
if __name__ == '__main__':
main()
-187
View File
@@ -1,187 +0,0 @@
#! /usr/bin/env sh
# output_env.sh
#
# Copyright The Mbed TLS Contributors
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
#
# Purpose
#
# To print out all the relevant information about the development environment.
#
# This includes:
# - architecture of the system
# - type and version of the operating system
# - version of make and cmake
# - version of armcc, clang, gcc-arm and gcc compilers
# - version of libc, clang, asan and valgrind if installed
# - version of gnuTLS and OpenSSL
print_version()
{
BIN="$1"
shift
ARGS="$1"
shift
VARIANT="$1"
shift
if [ -n "$VARIANT" ]; then
VARIANT=" ($VARIANT)"
fi
if ! type "$BIN" > /dev/null 2>&1; then
echo " * ${BIN##*/}$VARIANT: Not found."
return 0
fi
BIN=`which "$BIN"`
VERSION_STR=`$BIN $ARGS 2>&1`
# Apply all filters
while [ $# -gt 0 ]; do
FILTER="$1"
shift
VERSION_STR=`echo "$VERSION_STR" | $FILTER`
done
if [ -z "$VERSION_STR" ]; then
VERSION_STR="Version could not be determined."
fi
echo " * ${BIN##*/}$VARIANT: ${BIN} : ${VERSION_STR} "
}
echo "** Platform:"
echo
if [ `uname -s` = "Linux" ]; then
echo "Linux variant"
lsb_release -d -c
else
echo "Unknown Unix variant"
fi
echo
print_version "uname" "-a" ""
echo
echo
echo "** Tool Versions:"
echo
print_version "make" "--version" "" "head -n 1"
echo
print_version "cmake" "--version" "" "head -n 1"
echo
if [ "${RUN_ARMCC:-1}" -ne 0 ]; then
: "${ARMC5_CC:=armcc}"
print_version "$ARMC5_CC" "--vsn" "" "head -n 2"
echo
: "${ARMC6_CC:=armclang}"
print_version "$ARMC6_CC" "--vsn" "" "head -n 2"
echo
fi
print_version "arm-none-eabi-gcc" "--version" "" "head -n 1"
echo
print_version "gcc" "--version" "" "head -n 1"
echo
if [ -n "${GCC_EARLIEST+set}" ]; then
print_version "${GCC_EARLIEST}" "--version" "" "head -n 1"
else
echo " GCC_EARLIEST : Not configured."
fi
echo
if [ -n "${GCC_LATEST+set}" ]; then
print_version "${GCC_LATEST}" "--version" "" "head -n 1"
else
echo " GCC_LATEST : Not configured."
fi
echo
print_version "clang" "--version" "" "head -n 2"
echo
if [ -n "${CLANG_EARLIEST+set}" ]; then
print_version "${CLANG_EARLIEST}" "--version" "" "head -n 2"
else
echo " CLANG_EARLIEST : Not configured."
fi
echo
if [ -n "${CLANG_LATEST+set}" ]; then
print_version "${CLANG_LATEST}" "--version" "" "head -n 2"
else
echo " CLANG_LATEST : Not configured."
fi
echo
print_version "ldd" "--version" "" "head -n 1"
echo
print_version "valgrind" "--version" ""
echo
print_version "gdb" "--version" "" "head -n 1"
echo
print_version "perl" "--version" "" "head -n 2" "grep ."
echo
print_version "python" "--version" "" "head -n 1"
echo
print_version "python3" "--version" "" "head -n 1"
echo
# Find the installed version of Pylint. Installed as a distro package this can
# be pylint3 and as a PEP egg, pylint. In test scripts We prefer pylint over
# pylint3
if type pylint >/dev/null 2>/dev/null; then
print_version "pylint" "--version" "" "sed /^.*config/d" "grep pylint"
elif type pylint3 >/dev/null 2>/dev/null; then
print_version "pylint3" "--version" "" "sed /^.*config/d" "grep pylint"
else
echo " * pylint or pylint3: Not found."
fi
echo
: ${OPENSSL:=openssl}
print_version "$OPENSSL" "version" "default"
echo
if [ -n "${OPENSSL_NEXT+set}" ]; then
print_version "$OPENSSL_NEXT" "version" "next"
else
echo " * openssl (next): Not configured."
fi
echo
: ${GNUTLS_CLI:=gnutls-cli}
print_version "$GNUTLS_CLI" "--version" "default" "head -n 1"
echo
: ${GNUTLS_SERV:=gnutls-serv}
print_version "$GNUTLS_SERV" "--version" "default" "head -n 1"
echo
echo " * Installed asan versions:"
if type dpkg-query >/dev/null 2>/dev/null; then
if ! dpkg-query -f '${Status} ${Package}: ${Version}\n' -W 'libasan*' |
awk '$3 == "installed" && $4 !~ /-/ {print $4, $5}' |
grep .
then
echo " No asan versions installed."
fi
else
echo " Unable to determine the asan version without dpkg."
fi
echo
-70
View File
@@ -1,70 +0,0 @@
#!/bin/bash
print_usage()
{
cat <<EOF
Usage: $0 [OPTION]...
Prepare the source tree for a release.
Options:
-u Prepare for development (undo the release preparation)
EOF
}
# Copyright The Mbed TLS Contributors
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
set -eu
if [ $# -ne 0 ] && [ "$1" = "--help" ]; then
print_usage
exit
fi
unrelease= # if non-empty, we're in undo-release mode
while getopts u OPTLET; do
case $OPTLET in
u) unrelease=1;;
\?)
echo 1>&2 "$0: unknown option: -$OPTLET"
echo 1>&2 "Try '$0 --help' for more information."
exit 3;;
esac
done
#### .gitignore processing ####
GITIGNORES=$(find . -name ".gitignore")
for GITIGNORE in $GITIGNORES; do
if [ -n "$unrelease" ]; then
sed -i '/###START_COMMENTED_GENERATED_FILES###/,/###END_COMMENTED_GENERATED_FILES###/s/^#//' $GITIGNORE
sed -i 's/###START_COMMENTED_GENERATED_FILES###/###START_GENERATED_FILES###/' $GITIGNORE
sed -i 's/###END_COMMENTED_GENERATED_FILES###/###END_GENERATED_FILES###/' $GITIGNORE
else
sed -i '/###START_GENERATED_FILES###/,/###END_GENERATED_FILES###/s/^/#/' $GITIGNORE
sed -i 's/###START_GENERATED_FILES###/###START_COMMENTED_GENERATED_FILES###/' $GITIGNORE
sed -i 's/###END_GENERATED_FILES###/###END_COMMENTED_GENERATED_FILES###/' $GITIGNORE
fi
done
#### Build scripts ####
# GEN_FILES defaults on (non-empty) in development, off (empty) in releases
if [ -n "$unrelease" ]; then
r=' yes'
else
r=''
fi
sed -i 's/^\(GEN_FILES[ ?:]*=\)\([^#]*\)/\1'"$r/" Makefile */Makefile
# GEN_FILES defaults on in development, off in releases
if [ -n "$unrelease" ]; then
r='ON'
else
r='OFF'
fi
sed -i '/[Oo][Ff][Ff] in development/! s/^\( *option *( *GEN_FILES *"[^"]*" *\)\([A-Za-z0-9][A-Za-z0-9]*\)/\1'"$r/" CMakeLists.txt
-1
View File
@@ -1 +0,0 @@
Mbed TLS
-47
View File
@@ -1,47 +0,0 @@
#!/bin/bash
# Temporarily (de)ignore Makefiles generated by CMake to allow easier
# git development
#
# Copyright The Mbed TLS Contributors
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
IGNORE=""
# Parse arguments
#
until [ -z "$1" ]
do
case "$1" in
-u|--undo)
IGNORE="0"
;;
-v|--verbose)
# Be verbose
VERBOSE="1"
;;
-h|--help)
# print help
echo "Usage: $0"
echo -e " -h|--help\t\tPrint this help."
echo -e " -u|--undo\t\tRemove ignores and continue tracking."
echo -e " -v|--verbose\t\tVerbose."
exit 1
;;
*)
# print error
echo "Unknown argument: '$1'"
exit 1
;;
esac
shift
done
if [ "X" = "X$IGNORE" ];
then
[ $VERBOSE ] && echo "Ignoring Makefiles"
git update-index --assume-unchanged Makefile library/Makefile programs/Makefile tests/Makefile
else
[ $VERBOSE ] && echo "Tracking Makefiles"
git update-index --no-assume-unchanged Makefile library/Makefile programs/Makefile tests/Makefile
fi
-20
View File
@@ -1,20 +0,0 @@
@rem Build and test Mbed TLS with Visual Studio using msbuild.
@rem Usage: windows_msbuild [RETARGET]
@rem RETARGET: version of Visual Studio to emulate
@rem https://docs.microsoft.com/en-us/cpp/build/how-to-modify-the-target-framework-and-platform-toolset
@rem These parameters are hard-coded for now.
set "arch=x64" & @rem "x86" or "x64"
set "cfg=Release" & @rem "Debug" or "Release"
set "vcvarsall=C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsall.bat"
if not "%~1"=="" set "retarget=,PlatformToolset=%1"
@rem If the %USERPROFILE%\Source directory exists, then running
@rem vcvarsall.bat will silently change the directory to that directory.
@rem Setting the VSCMD_START_DIR environment variable causes it to change
@rem to that directory instead.
set "VSCMD_START_DIR=%~dp0\..\visualc\VS2017"
"%vcvarsall%" x64 && ^
msbuild /t:Rebuild /p:Configuration=%cfg%%retarget% /m mbedTLS.sln