chore: import local project into Gitea

This commit is contained in:
2026-05-20 10:03:16 -07:00
commit 7bd88d29b5
4043 changed files with 1056916 additions and 0 deletions

View File

@@ -0,0 +1,37 @@
#!/bin/bash -eu
# all-in-docker.sh
#
# Purpose
# -------
# This runs all.sh (except for armcc) in a Docker container.
#
# Notes for users
# ---------------
# See docker_env.sh for prerequisites and other information.
#
# See also all.sh for notes about invocation of that script.
# Copyright (C) 2006-2019, Arm Limited (or its affiliates), All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This file is part of Mbed TLS (https://tls.mbed.org)
source tests/scripts/docker_env.sh
# Run tests that are possible with openly available compilers
run_in_docker tests/scripts/all.sh \
--no-armcc \
$@

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,278 @@
#!/bin/sh
# basic-build-tests.sh
#
# Copyright (c) 2016, ARM Limited, All Rights Reserved
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This file is part of Mbed TLS (https://tls.mbed.org)
#
# Purpose
#
# Executes the basic test suites, captures the results, and generates a simple
# test report and code coverage report.
#
# The tests include:
# * Unit tests - executed using tests/scripts/run-test-suite.pl
# * Self-tests - executed using the test suites above
# * System tests - executed using tests/ssl-opt.sh
# * Interoperability tests - executed using tests/compat.sh
#
# The tests focus on functionality and do not consider performance.
#
# Note the tests self-adapt due to configurations in include/mbedtls/config.h
# which can lead to some tests being skipped, and can cause the number of
# available tests to fluctuate.
#
# This script has been written to be generic and should work on any shell.
#
# Usage: basic-build-tests.sh
#
# Abort on errors (and uninitiliased variables)
set -eu
if [ -d library -a -d include -a -d tests ]; then :; else
echo "Must be run from mbed TLS root" >&2
exit 1
fi
: ${OPENSSL:="openssl"}
: ${OPENSSL_LEGACY:="$OPENSSL"}
: ${GNUTLS_CLI:="gnutls-cli"}
: ${GNUTLS_SERV:="gnutls-serv"}
: ${GNUTLS_LEGACY_CLI:="$GNUTLS_CLI"}
: ${GNUTLS_LEGACY_SERV:="$GNUTLS_SERV"}
# Used to make ssl-opt.sh deterministic.
#
# See also RELEASE_SEED in all.sh. Debugging is easier if both values are kept
# in sync. If you change the value here because it breaks some tests, you'll
# definitely want to change it in all.sh as well.
: ${SEED:=1}
export SEED
# To avoid setting OpenSSL and GnuTLS for each call to compat.sh and ssl-opt.sh
# we just export the variables they require
export OPENSSL_CMD="$OPENSSL"
export GNUTLS_CLI="$GNUTLS_CLI"
export GNUTLS_SERV="$GNUTLS_SERV"
CONFIG_H='include/mbedtls/config.h'
CONFIG_BAK="$CONFIG_H.bak"
# Step 0 - print build environment info
OPENSSL="$OPENSSL" \
OPENSSL_LEGACY="$OPENSSL_LEGACY" \
GNUTLS_CLI="$GNUTLS_CLI" \
GNUTLS_SERV="$GNUTLS_SERV" \
GNUTLS_LEGACY_CLI="$GNUTLS_LEGACY_CLI" \
GNUTLS_LEGACY_SERV="$GNUTLS_LEGACY_SERV" \
scripts/output_env.sh
echo
# Step 1 - Make and instrumented build for code coverage
export CFLAGS=' --coverage -g3 -O0 '
export LDFLAGS=' --coverage'
make clean
cp "$CONFIG_H" "$CONFIG_BAK"
scripts/config.py full
make -j
# Step 2 - Execute the tests
TEST_OUTPUT=out_${PPID}
cd tests
if [ ! -f "seedfile" ]; then
dd if=/dev/urandom of="seedfile" bs=64 count=1
fi
echo
# Step 2a - Unit Tests (keep going even if some tests fail)
echo '################ Unit tests ################'
perl scripts/run-test-suites.pl -v 2 |tee unit-test-$TEST_OUTPUT
echo '^^^^^^^^^^^^^^^^ Unit tests ^^^^^^^^^^^^^^^^'
echo
# Step 2b - System Tests (keep going even if some tests fail)
echo
echo '################ ssl-opt.sh ################'
sh ssl-opt.sh |tee sys-test-$TEST_OUTPUT
echo '^^^^^^^^^^^^^^^^ ssl-opt.sh ^^^^^^^^^^^^^^^^'
echo
# Step 2c - Compatibility tests (keep going even if some tests fail)
echo '################ compat.sh ################'
{
echo '#### compat.sh: Default versions'
sh compat.sh -m 'tls1 tls1_1 tls1_2 dtls1 dtls1_2'
echo
echo '#### compat.sh: legacy (SSLv3)'
OPENSSL_CMD="$OPENSSL_LEGACY" sh compat.sh -m 'ssl3'
echo
echo '#### compat.sh: legacy (null, DES, RC4)'
OPENSSL_CMD="$OPENSSL_LEGACY" \
GNUTLS_CLI="$GNUTLS_LEGACY_CLI" GNUTLS_SERV="$GNUTLS_LEGACY_SERV" \
sh compat.sh -e '^$' -f 'NULL\|DES\|RC4\|ARCFOUR'
echo
echo '#### compat.sh: next (ARIA, ChaCha)'
OPENSSL_CMD="$OPENSSL_NEXT" sh compat.sh -e '^$' -f 'ARIA\|CHACHA'
echo
} | tee compat-test-$TEST_OUTPUT
echo '^^^^^^^^^^^^^^^^ compat.sh ^^^^^^^^^^^^^^^^'
echo
# Step 3 - Process the coverage report
cd ..
{
make lcov
echo SUCCESS
} | tee tests/cov-$TEST_OUTPUT
if [ "$(tail -n1 tests/cov-$TEST_OUTPUT)" != "SUCCESS" ]; then
echo >&2 "Fatal: 'make lcov' failed"
exit 2
fi
# Step 4 - Summarise the test report
echo
echo "========================================================================="
echo "Test Report Summary"
echo
cd tests
# Step 4a - Unit tests
echo "Unit tests - tests/scripts/run-test-suites.pl"
PASSED_TESTS=$(tail -n6 unit-test-$TEST_OUTPUT|sed -n -e 's/test cases passed :[\t]*\([0-9]*\)/\1/p'| tr -d ' ')
SKIPPED_TESTS=$(tail -n6 unit-test-$TEST_OUTPUT|sed -n -e 's/skipped :[ \t]*\([0-9]*\)/\1/p'| tr -d ' ')
TOTAL_SUITES=$(tail -n6 unit-test-$TEST_OUTPUT|sed -n -e 's/.* (\([0-9]*\) .*, [0-9]* tests run)/\1/p'| tr -d ' ')
FAILED_TESTS=$(tail -n6 unit-test-$TEST_OUTPUT|sed -n -e 's/failed :[\t]*\([0-9]*\)/\1/p' |tr -d ' ')
echo "No test suites : $TOTAL_SUITES"
echo "Passed : $PASSED_TESTS"
echo "Failed : $FAILED_TESTS"
echo "Skipped : $SKIPPED_TESTS"
echo "Total exec'd tests : $(($PASSED_TESTS + $FAILED_TESTS))"
echo "Total avail tests : $(($PASSED_TESTS + $FAILED_TESTS + $SKIPPED_TESTS))"
echo
TOTAL_PASS=$PASSED_TESTS
TOTAL_FAIL=$FAILED_TESTS
TOTAL_SKIP=$SKIPPED_TESTS
TOTAL_AVAIL=$(($PASSED_TESTS + $FAILED_TESTS + $SKIPPED_TESTS))
TOTAL_EXED=$(($PASSED_TESTS + $FAILED_TESTS))
# Step 4b - TLS Options tests
echo "TLS Options tests - tests/ssl-opt.sh"
PASSED_TESTS=$(tail -n5 sys-test-$TEST_OUTPUT|sed -n -e 's/.* (\([0-9]*\) \/ [0-9]* tests ([0-9]* skipped))$/\1/p')
SKIPPED_TESTS=$(tail -n5 sys-test-$TEST_OUTPUT|sed -n -e 's/.* ([0-9]* \/ [0-9]* tests (\([0-9]*\) skipped))$/\1/p')
TOTAL_TESTS=$(tail -n5 sys-test-$TEST_OUTPUT|sed -n -e 's/.* ([0-9]* \/ \([0-9]*\) tests ([0-9]* skipped))$/\1/p')
FAILED_TESTS=$(($TOTAL_TESTS - $PASSED_TESTS))
echo "Passed : $PASSED_TESTS"
echo "Failed : $FAILED_TESTS"
echo "Skipped : $SKIPPED_TESTS"
echo "Total exec'd tests : $TOTAL_TESTS"
echo "Total avail tests : $(($TOTAL_TESTS + $SKIPPED_TESTS))"
echo
TOTAL_PASS=$(($TOTAL_PASS+$PASSED_TESTS))
TOTAL_FAIL=$(($TOTAL_FAIL+$FAILED_TESTS))
TOTAL_SKIP=$(($TOTAL_SKIP+$SKIPPED_TESTS))
TOTAL_AVAIL=$(($TOTAL_AVAIL + $TOTAL_TESTS + $SKIPPED_TESTS))
TOTAL_EXED=$(($TOTAL_EXED + $TOTAL_TESTS))
# Step 4c - System Compatibility tests
echo "System/Compatibility tests - tests/compat.sh"
PASSED_TESTS=$(cat compat-test-$TEST_OUTPUT | sed -n -e 's/.* (\([0-9]*\) \/ [0-9]* tests ([0-9]* skipped))$/\1/p' | awk 'BEGIN{ s = 0 } { s += $1 } END{ print s }')
SKIPPED_TESTS=$(cat compat-test-$TEST_OUTPUT | sed -n -e 's/.* ([0-9]* \/ [0-9]* tests (\([0-9]*\) skipped))$/\1/p' | awk 'BEGIN{ s = 0 } { s += $1 } END{ print s }')
EXED_TESTS=$(cat compat-test-$TEST_OUTPUT | sed -n -e 's/.* ([0-9]* \/ \([0-9]*\) tests ([0-9]* skipped))$/\1/p' | awk 'BEGIN{ s = 0 } { s += $1 } END{ print s }')
FAILED_TESTS=$(($EXED_TESTS - $PASSED_TESTS))
echo "Passed : $PASSED_TESTS"
echo "Failed : $FAILED_TESTS"
echo "Skipped : $SKIPPED_TESTS"
echo "Total exec'd tests : $EXED_TESTS"
echo "Total avail tests : $(($EXED_TESTS + $SKIPPED_TESTS))"
echo
TOTAL_PASS=$(($TOTAL_PASS+$PASSED_TESTS))
TOTAL_FAIL=$(($TOTAL_FAIL+$FAILED_TESTS))
TOTAL_SKIP=$(($TOTAL_SKIP+$SKIPPED_TESTS))
TOTAL_AVAIL=$(($TOTAL_AVAIL + $EXED_TESTS + $SKIPPED_TESTS))
TOTAL_EXED=$(($TOTAL_EXED + $EXED_TESTS))
# Step 4d - Grand totals
echo "-------------------------------------------------------------------------"
echo "Total tests"
echo "Total Passed : $TOTAL_PASS"
echo "Total Failed : $TOTAL_FAIL"
echo "Total Skipped : $TOTAL_SKIP"
echo "Total exec'd tests : $TOTAL_EXED"
echo "Total avail tests : $TOTAL_AVAIL"
echo
# Step 4e - Coverage
echo "Coverage"
LINES_TESTED=$(tail -n4 cov-$TEST_OUTPUT|sed -n -e 's/ lines......: [0-9]*.[0-9]% (\([0-9]*\) of [0-9]* lines)/\1/p')
LINES_TOTAL=$(tail -n4 cov-$TEST_OUTPUT|sed -n -e 's/ lines......: [0-9]*.[0-9]% ([0-9]* of \([0-9]*\) lines)/\1/p')
FUNCS_TESTED=$(tail -n4 cov-$TEST_OUTPUT|sed -n -e 's/ functions..: [0-9]*.[0-9]% (\([0-9]*\) of [0-9]* functions)$/\1/p')
FUNCS_TOTAL=$(tail -n4 cov-$TEST_OUTPUT|sed -n -e 's/ functions..: [0-9]*.[0-9]% ([0-9]* of \([0-9]*\) functions)$/\1/p')
BRANCHES_TESTED=$(tail -n4 cov-$TEST_OUTPUT|sed -n -e 's/ branches...: [0-9]*.[0-9]% (\([0-9]*\) of [0-9]* branches)$/\1/p')
BRANCHES_TOTAL=$(tail -n4 cov-$TEST_OUTPUT|sed -n -e 's/ branches...: [0-9]*.[0-9]% ([0-9]* of \([0-9]*\) branches)$/\1/p')
LINES_PERCENT=$((1000*$LINES_TESTED/$LINES_TOTAL))
LINES_PERCENT="$(($LINES_PERCENT/10)).$(($LINES_PERCENT-($LINES_PERCENT/10)*10))"
FUNCS_PERCENT=$((1000*$FUNCS_TESTED/$FUNCS_TOTAL))
FUNCS_PERCENT="$(($FUNCS_PERCENT/10)).$(($FUNCS_PERCENT-($FUNCS_PERCENT/10)*10))"
BRANCHES_PERCENT=$((1000*$BRANCHES_TESTED/$BRANCHES_TOTAL))
BRANCHES_PERCENT="$(($BRANCHES_PERCENT/10)).$(($BRANCHES_PERCENT-($BRANCHES_PERCENT/10)*10))"
echo "Lines Tested : $LINES_TESTED of $LINES_TOTAL $LINES_PERCENT%"
echo "Functions Tested : $FUNCS_TESTED of $FUNCS_TOTAL $FUNCS_PERCENT%"
echo "Branches Tested : $BRANCHES_TESTED of $BRANCHES_TOTAL $BRANCHES_PERCENT%"
echo
rm unit-test-$TEST_OUTPUT
rm sys-test-$TEST_OUTPUT
rm compat-test-$TEST_OUTPUT
rm cov-$TEST_OUTPUT
cd ..
make clean
if [ -f "$CONFIG_BAK" ]; then
mv "$CONFIG_BAK" "$CONFIG_H"
fi
if [ $TOTAL_FAIL -ne 0 ]; then
exit 1
fi

View File

@@ -0,0 +1,50 @@
#!/bin/bash -eu
# basic-in-docker.sh
#
# Purpose
# -------
# This runs a rough equivalent of the travis.yml in a Docker container.
# The tests are run for both clang and gcc.
#
# Notes for users
# ---------------
# See docker_env.sh for prerequisites and other information.
# Copyright (C) 2006-2019, Arm Limited (or its affiliates), All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This file is part of Mbed TLS (https://tls.mbed.org)
source tests/scripts/docker_env.sh
run_in_docker tests/scripts/recursion.pl library/*.c
run_in_docker tests/scripts/check-generated-files.sh
run_in_docker tests/scripts/check-doxy-blocks.pl
run_in_docker tests/scripts/check-names.sh
run_in_docker tests/scripts/check-files.py
run_in_docker tests/scripts/doxygen.sh
for compiler in clang gcc; do
run_in_docker -e CC=${compiler} cmake -D CMAKE_BUILD_TYPE:String="Check" .
run_in_docker -e CC=${compiler} make
run_in_docker -e CC=${compiler} make test
run_in_docker programs/test/selftest
run_in_docker -e OSSL_NO_DTLS=1 tests/compat.sh
run_in_docker tests/ssl-opt.sh -e '\(DTLS\|SCSV\).*openssl'
run_in_docker tests/scripts/test-ref-configs.pl
run_in_docker tests/scripts/curves.pl
run_in_docker tests/scripts/key-exchanges.pl
done

View File

@@ -0,0 +1,81 @@
#!/usr/bin/env perl
# Detect comment blocks that are likely meant to be doxygen blocks but aren't.
#
# More precisely, look for normal comment block containing '\'.
# Of course one could use doxygen warnings, eg with:
# sed -e '/EXTRACT/s/YES/NO/' doxygen/mbedtls.doxyfile | doxygen -
# but that would warn about any undocumented item, while our goal is to find
# items that are documented, but not marked as such by mistake.
#
# Copyright (C) 2012-2016, Arm Limited, All Rights Reserved
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This file is part of Mbed TLS (https://tls.mbed.org)
use warnings;
use strict;
use File::Basename;
# C/header files in the following directories will be checked
my @directories = qw(include/mbedtls library doxygen/input);
# very naive pattern to find directives:
# everything with a backslach except '\0' and backslash at EOL
my $doxy_re = qr/\\(?!0|\n)/;
# Return an error code to the environment if a potential error in the
# source code is found.
my $exit_code = 0;
sub check_file {
my ($fname) = @_;
open my $fh, '<', $fname or die "Failed to open '$fname': $!\n";
# first line of the last normal comment block,
# or 0 if not in a normal comment block
my $block_start = 0;
while (my $line = <$fh>) {
$block_start = $. if $line =~ m/\/\*(?![*!])/;
$block_start = 0 if $line =~ m/\*\//;
if ($block_start and $line =~ m/$doxy_re/) {
print "$fname:$block_start: directive on line $.\n";
$block_start = 0; # report only one directive per block
$exit_code = 1;
}
}
close $fh;
}
sub check_dir {
my ($dirname) = @_;
for my $file (<$dirname/*.[ch]>) {
check_file($file);
}
}
# Check that the script is being run from the project's root directory.
for my $dir (@directories) {
if (! -d $dir) {
die "This script must be run from the mbed TLS root directory";
} else {
check_dir($dir)
}
}
exit $exit_code;
__END__

View File

@@ -0,0 +1,356 @@
#!/usr/bin/env python3
# Copyright (c) 2018, Arm Limited, All Rights Reserved
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This file is part of Mbed TLS (https://tls.mbed.org)
"""
This script checks the current state of the source code for minor issues,
including incorrect file permissions, presence of tabs, non-Unix line endings,
trailing whitespace, and presence of UTF-8 BOM.
Note: requires python 3, must be run from Mbed TLS root.
"""
import os
import argparse
import logging
import codecs
import re
import subprocess
import sys
class FileIssueTracker:
"""Base class for file-wide issue tracking.
To implement a checker that processes a file as a whole, inherit from
this class and implement `check_file_for_issue` and define ``heading``.
``suffix_exemptions``: files whose name ends with a string in this set
will not be checked.
``path_exemptions``: files whose path (relative to the root of the source
tree) matches this regular expression will not be checked. This can be
``None`` to match no path. Paths are normalized and converted to ``/``
separators before matching.
``heading``: human-readable description of the issue
"""
suffix_exemptions = frozenset()
path_exemptions = None
# heading must be defined in derived classes.
# pylint: disable=no-member
def __init__(self):
self.files_with_issues = {}
@staticmethod
def normalize_path(filepath):
"""Normalize ``filepath`` with / as the directory separator."""
filepath = os.path.normpath(filepath)
# On Windows, we may have backslashes to separate directories.
# We need slashes to match exemption lists.
seps = os.path.sep
if os.path.altsep is not None:
seps += os.path.altsep
return '/'.join(filepath.split(seps))
def should_check_file(self, filepath):
"""Whether the given file name should be checked.
Files whose name ends with a string listed in ``self.suffix_exemptions``
or whose path matches ``self.path_exemptions`` will not be checked.
"""
for files_exemption in self.suffix_exemptions:
if filepath.endswith(files_exemption):
return False
if self.path_exemptions and \
re.match(self.path_exemptions, self.normalize_path(filepath)):
return False
return True
def check_file_for_issue(self, filepath):
"""Check the specified file for the issue that this class is for.
Subclasses must implement this method.
"""
raise NotImplementedError
def record_issue(self, filepath, line_number):
"""Record that an issue was found at the specified location."""
if filepath not in self.files_with_issues.keys():
self.files_with_issues[filepath] = []
self.files_with_issues[filepath].append(line_number)
def output_file_issues(self, logger):
"""Log all the locations where the issue was found."""
if self.files_with_issues.values():
logger.info(self.heading)
for filename, lines in sorted(self.files_with_issues.items()):
if lines:
logger.info("{}: {}".format(
filename, ", ".join(str(x) for x in lines)
))
else:
logger.info(filename)
logger.info("")
BINARY_FILE_PATH_RE_LIST = [
r'docs/.*\.pdf\Z',
r'programs/fuzz/corpuses/[^.]+\Z',
r'tests/data_files/[^.]+\Z',
r'tests/data_files/.*\.(crt|csr|db|der|key|pubkey)\Z',
r'tests/data_files/.*\.req\.[^/]+\Z',
r'tests/data_files/.*malformed[^/]+\Z',
r'tests/data_files/format_pkcs12\.fmt\Z',
]
BINARY_FILE_PATH_RE = re.compile('|'.join(BINARY_FILE_PATH_RE_LIST))
class LineIssueTracker(FileIssueTracker):
"""Base class for line-by-line issue tracking.
To implement a checker that processes files line by line, inherit from
this class and implement `line_with_issue`.
"""
# Exclude binary files.
path_exemptions = BINARY_FILE_PATH_RE
def issue_with_line(self, line, filepath):
"""Check the specified line for the issue that this class is for.
Subclasses must implement this method.
"""
raise NotImplementedError
def check_file_line(self, filepath, line, line_number):
if self.issue_with_line(line, filepath):
self.record_issue(filepath, line_number)
def check_file_for_issue(self, filepath):
"""Check the lines of the specified file.
Subclasses must implement the ``issue_with_line`` method.
"""
with open(filepath, "rb") as f:
for i, line in enumerate(iter(f.readline, b"")):
self.check_file_line(filepath, line, i + 1)
def is_windows_file(filepath):
_root, ext = os.path.splitext(filepath)
return ext in ('.bat', '.dsp', '.dsw', '.sln', '.vcxproj')
class PermissionIssueTracker(FileIssueTracker):
"""Track files with bad permissions.
Files that are not executable scripts must not be executable."""
heading = "Incorrect permissions:"
def check_file_for_issue(self, filepath):
is_executable = os.access(filepath, os.X_OK)
should_be_executable = filepath.endswith((".sh", ".pl", ".py"))
if is_executable != should_be_executable:
self.files_with_issues[filepath] = None
class EndOfFileNewlineIssueTracker(FileIssueTracker):
"""Track files that end with an incomplete line
(no newline character at the end of the last line)."""
heading = "Missing newline at end of file:"
path_exemptions = BINARY_FILE_PATH_RE
def check_file_for_issue(self, filepath):
with open(filepath, "rb") as f:
try:
f.seek(-1, 2)
except OSError:
# This script only works on regular files. If we can't seek
# 1 before the end, it means that this position is before
# the beginning of the file, i.e. that the file is empty.
return
if f.read(1) != b"\n":
self.files_with_issues[filepath] = None
class Utf8BomIssueTracker(FileIssueTracker):
"""Track files that start with a UTF-8 BOM.
Files should be ASCII or UTF-8. Valid UTF-8 does not start with a BOM."""
heading = "UTF-8 BOM present:"
suffix_exemptions = frozenset([".vcxproj", ".sln"])
path_exemptions = BINARY_FILE_PATH_RE
def check_file_for_issue(self, filepath):
with open(filepath, "rb") as f:
if f.read().startswith(codecs.BOM_UTF8):
self.files_with_issues[filepath] = None
class UnixLineEndingIssueTracker(LineIssueTracker):
"""Track files with non-Unix line endings (i.e. files with CR)."""
heading = "Non-Unix line endings:"
def should_check_file(self, filepath):
if not super().should_check_file(filepath):
return False
return not is_windows_file(filepath)
def issue_with_line(self, line, _filepath):
return b"\r" in line
class WindowsLineEndingIssueTracker(LineIssueTracker):
"""Track files with non-Windows line endings (i.e. CR or LF not in CRLF)."""
heading = "Non-Windows line endings:"
def should_check_file(self, filepath):
if not super().should_check_file(filepath):
return False
return is_windows_file(filepath)
def issue_with_line(self, line, _filepath):
return not line.endswith(b"\r\n") or b"\r" in line[:-2]
class TrailingWhitespaceIssueTracker(LineIssueTracker):
"""Track lines with trailing whitespace."""
heading = "Trailing whitespace:"
suffix_exemptions = frozenset([".dsp", ".md"])
def issue_with_line(self, line, _filepath):
return line.rstrip(b"\r\n") != line.rstrip()
class TabIssueTracker(LineIssueTracker):
"""Track lines with tabs."""
heading = "Tabs present:"
suffix_exemptions = frozenset([
".pem", # some openssl dumps have tabs
".sln",
"/Makefile",
"/Makefile.inc",
"/generate_visualc_files.pl",
])
def issue_with_line(self, line, _filepath):
return b"\t" in line
class MergeArtifactIssueTracker(LineIssueTracker):
"""Track lines with merge artifacts.
These are leftovers from a ``git merge`` that wasn't fully edited."""
heading = "Merge artifact:"
def issue_with_line(self, line, _filepath):
# Detect leftover git conflict markers.
if line.startswith(b'<<<<<<< ') or line.startswith(b'>>>>>>> '):
return True
if line.startswith(b'||||||| '): # from merge.conflictStyle=diff3
return True
if line.rstrip(b'\r\n') == b'=======' and \
not _filepath.endswith('.md'):
return True
return False
class IntegrityChecker:
"""Sanity-check files under the current directory."""
def __init__(self, log_file):
"""Instantiate the sanity checker.
Check files under the current directory.
Write a report of issues to log_file."""
self.check_repo_path()
self.logger = None
self.setup_logger(log_file)
self.issues_to_check = [
PermissionIssueTracker(),
EndOfFileNewlineIssueTracker(),
Utf8BomIssueTracker(),
UnixLineEndingIssueTracker(),
WindowsLineEndingIssueTracker(),
TrailingWhitespaceIssueTracker(),
TabIssueTracker(),
MergeArtifactIssueTracker(),
]
@staticmethod
def check_repo_path():
if not all(os.path.isdir(d) for d in ["include", "library", "tests"]):
raise Exception("Must be run from Mbed TLS root")
def setup_logger(self, log_file, level=logging.INFO):
self.logger = logging.getLogger()
self.logger.setLevel(level)
if log_file:
handler = logging.FileHandler(log_file)
self.logger.addHandler(handler)
else:
console = logging.StreamHandler()
self.logger.addHandler(console)
@staticmethod
def collect_files():
bytes_output = subprocess.check_output(['git', 'ls-files', '-z'])
bytes_filepaths = bytes_output.split(b'\0')[:-1]
ascii_filepaths = map(lambda fp: fp.decode('ascii'), bytes_filepaths)
# Prepend './' to files in the top-level directory so that
# something like `'/Makefile' in fp` matches in the top-level
# directory as well as in subdirectories.
return [fp if os.path.dirname(fp) else os.path.join(os.curdir, fp)
for fp in ascii_filepaths]
def check_files(self):
for issue_to_check in self.issues_to_check:
for filepath in self.collect_files():
if issue_to_check.should_check_file(filepath):
issue_to_check.check_file_for_issue(filepath)
def output_issues(self):
integrity_return_code = 0
for issue_to_check in self.issues_to_check:
if issue_to_check.files_with_issues:
integrity_return_code = 1
issue_to_check.output_file_issues(self.logger)
return integrity_return_code
def run_main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"-l", "--log_file", type=str, help="path to optional output log",
)
check_args = parser.parse_args()
integrity_check = IntegrityChecker(check_args.log_file)
integrity_check.check_files()
return_code = integrity_check.output_issues()
sys.exit(return_code)
if __name__ == "__main__":
run_main()

View File

@@ -0,0 +1,83 @@
#! /usr/bin/env sh
# Copyright (c) 2018, ARM Limited, All Rights Reserved
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This file is part of Mbed TLS (https://tls.mbed.org)
#
# Purpose
#
# Check if generated files are up-to-date.
set -eu
if [ -d library -a -d include -a -d tests ]; then :; else
echo "Must be run from mbed TLS root" >&2
exit 1
fi
check()
{
SCRIPT=$1
TO_CHECK=$2
PATTERN=""
FILES=""
if [ -d $TO_CHECK ]; then
for FILE in $TO_CHECK/*; do
FILES="$FILE $FILES"
done
else
FILES=$TO_CHECK
fi
for FILE in $FILES; do
cp $FILE $FILE.bak
done
$SCRIPT
# Compare the script output to the old files and remove backups
for FILE in $FILES; do
if ! diff $FILE $FILE.bak >/dev/null 2>&1; then
echo "'$FILE' was either modified or deleted by '$SCRIPT'"
exit 1
fi
mv $FILE.bak $FILE
if [ -d $TO_CHECK ]; then
# Create a grep regular expression that we can check against the
# directory contents to test whether new files have been created
if [ -z $PATTERN ]; then
PATTERN="$(basename $FILE)"
else
PATTERN="$PATTERN\|$(basename $FILE)"
fi
fi
done
if [ -d $TO_CHECK ]; then
# Check if there are any new files
if ls -1 $TO_CHECK | grep -v "$PATTERN" >/dev/null 2>&1; then
echo "Files were created by '$SCRIPT'"
exit 1
fi
fi
}
check scripts/generate_errors.pl library/error.c
check scripts/generate_query_config.pl programs/test/query_config.c
check scripts/generate_features.pl library/version_features.c
check scripts/generate_visualc_files.pl visualc/VS2010

View File

@@ -0,0 +1,134 @@
#!/bin/sh
#
# Copyright (c) 2015-2019, ARM Limited, All Rights Reserved
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This file is part of Mbed TLS (https://tls.mbed.org)
set -eu
if [ $# -ne 0 ] && [ "$1" = "--help" ]; then
cat <<EOF
$0 [-v]
This script confirms that the naming of all symbols and identifiers in mbed
TLS are consistent with the house style and are also self-consistent.
-v If the script fails unexpectedly, print a command trace.
EOF
exit
fi
if grep --version|head -n1|grep GNU >/dev/null; then :; else
echo "This script requires GNU grep.">&2
exit 1
fi
trace=
if [ $# -ne 0 ] && [ "$1" = "-v" ]; then
shift
trace='-x'
exec 2>check-names.err
trap 'echo "FAILED UNEXPECTEDLY, status=$?";
cat check-names.err' EXIT
set -x
fi
printf "Analysing source code...\n"
sh $trace tests/scripts/list-macros.sh
tests/scripts/list-enum-consts.pl
sh $trace tests/scripts/list-identifiers.sh
sh $trace tests/scripts/list-symbols.sh
FAIL=0
printf "\nExported symbols declared in header: "
UNDECLARED=$( diff exported-symbols identifiers | sed -n -e 's/^< //p' )
if [ "x$UNDECLARED" = "x" ]; then
echo "PASS"
else
echo "FAIL"
echo "$UNDECLARED"
FAIL=1
fi
diff macros identifiers | sed -n -e 's/< //p' > actual-macros
for THING in actual-macros enum-consts; do
printf "Names of $THING: "
test -r $THING
BAD=$( grep -E -v '^(MBEDTLS|PSA)_[0-9A-Z_]*[0-9A-Z]$' $THING || true )
UNDERSCORES=$( grep -E '.*__.*' $THING || true )
if [ "x$BAD" = "x" ] && [ "x$UNDERSCORES" = "x" ]; then
echo "PASS"
else
echo "FAIL"
echo "$BAD"
echo "$UNDERSCORES"
FAIL=1
fi
done
for THING in identifiers; do
printf "Names of $THING: "
test -r $THING
BAD=$( grep -E -v '^(mbedtls|psa)_[0-9a-z_]*[0-9a-z]$' $THING || true )
if [ "x$BAD" = "x" ]; then
echo "PASS"
else
echo "FAIL"
echo "$BAD"
FAIL=1
fi
done
printf "Likely typos: "
sort -u actual-macros enum-consts > _caps
HEADERS=$( ls include/mbedtls/*.h include/psa/*.h | egrep -v 'compat-1\.3\.h' )
HEADERS="$HEADERS 3rdparty/everest/include/everest/everest.h 3rdparty/everest/include/everest/x25519.h"
LIBRARY="$( ls library/*.c )"
LIBRARY="$LIBRARY 3rdparty/everest/library/everest.c 3rdparty/everest/library/x25519.c"
NL='
'
sed -n 's/MBED..._[A-Z0-9_]*/\'"$NL"'&\'"$NL"/gp \
$HEADERS $LIBRARY \
| grep MBEDTLS | sort -u > _MBEDTLS_XXX
TYPOS=$( diff _caps _MBEDTLS_XXX | sed -n 's/^> //p' \
| egrep -v 'XXX|__|_$|^MBEDTLS_.*CONFIG_FILE$' || true )
rm _MBEDTLS_XXX _caps
if [ "x$TYPOS" = "x" ]; then
echo "PASS"
else
echo "FAIL"
echo "$TYPOS"
FAIL=1
fi
if [ -n "$trace" ]; then
set +x
trap - EXIT
rm check-names.err
fi
printf "\nOverall: "
if [ "$FAIL" -eq 0 ]; then
rm macros actual-macros enum-consts identifiers exported-symbols
echo "PASSED"
exit 0
else
echo "FAILED"
exit 1
fi

View File

@@ -0,0 +1,31 @@
#! /usr/bin/env sh
# Copyright (c) 2018, Arm Limited, All Rights Reserved
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This file is part of Mbed TLS (https://tls.mbed.org)
#
# Purpose:
#
# Run 'pylint' on Python files for programming errors and helps enforcing
# PEP8 coding standards.
if type python3 >/dev/null 2>/dev/null; then
PYTHON=python3
else
PYTHON=python
fi
$PYTHON -m pylint -j 2 scripts/*.py tests/scripts/*.py

View File

@@ -0,0 +1,136 @@
#!/usr/bin/env python3
"""Sanity checks for test data.
"""
# Copyright (C) 2019, Arm Limited, All Rights Reserved
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This file is part of Mbed TLS (https://tls.mbed.org)
import argparse
import glob
import os
import re
import sys
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
def collect_test_directories():
"""Get the relative path for the TLS and Crypto test directories."""
if os.path.isdir('tests'):
tests_dir = 'tests'
elif os.path.isdir('suites'):
tests_dir = '.'
elif os.path.isdir('../suites'):
tests_dir = '..'
directories = [tests_dir]
return directories
def check_description(results, seen, file_name, line_number, description):
"""Check test case descriptions for errors."""
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 check_test_suite(results, data_file_name):
"""Check the test cases in the given unit test data file."""
in_paragraph = False
descriptions = {}
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.
check_description(results, descriptions,
data_file_name, line_number, line)
in_paragraph = True
def check_ssl_opt_sh(results, file_name):
"""Check the test cases in ssl-opt.sh or a file with a similar format."""
descriptions = {}
with open(file_name, 'rb') as file_contents:
for line_number, line in enumerate(file_contents, 1):
# Assume that all run_test calls have the same simple form
# with the test description entirely on the same line as the
# function name.
m = re.match(br'\s*run_test\s+"((?:[^\\"]|\\.)*)"', line)
if not m:
continue
description = m.group(1)
check_description(results, descriptions,
file_name, line_number, description)
def main():
parser = argparse.ArgumentParser(description=__doc__)
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()
test_directories = collect_test_directories()
results = Results(options)
for directory in test_directories:
for data_file_name in glob.glob(os.path.join(directory, 'suites',
'*.data')):
check_test_suite(results, data_file_name)
ssl_opt_sh = os.path.join(directory, 'ssl-opt.sh')
if os.path.exists(ssl_opt_sh):
check_ssl_opt_sh(results, ssl_opt_sh)
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()

View File

@@ -0,0 +1,83 @@
#!/usr/bin/env perl
# curves.pl
#
# Copyright (c) 2014-2016, ARM Limited, All Rights Reserved
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This file is part of Mbed TLS (https://tls.mbed.org)
#
# Purpose
#
# To test the code dependencies on individual curves in each test suite. This
# is a verification step to ensure we don't ship test suites that do not work
# for some build options.
#
# The process is:
# for each possible curve
# build the library and test suites with the curve disabled
# execute the test suites
#
# And any test suite with the wrong dependencies will fail.
#
# Usage: tests/scripts/curves.pl
#
# This script should be executed from the root of the project directory.
#
# For best effect, run either with cmake disabled, or cmake enabled in a mode
# that includes -Werror.
use warnings;
use strict;
-d 'library' && -d 'include' && -d 'tests' or die "Must be run from root\n";
my $sed_cmd = 's/^#define \(MBEDTLS_ECP_DP.*_ENABLED\)/\1/p';
my $config_h = 'include/mbedtls/config.h';
my @curves = split( /\s+/, `sed -n -e '$sed_cmd' $config_h` );
system( "cp $config_h $config_h.bak" ) and die;
sub abort {
system( "mv $config_h.bak $config_h" ) and warn "$config_h not restored\n";
# use an exit code between 1 and 124 for git bisect (die returns 255)
warn $_[0];
exit 1;
}
for my $curve (@curves) {
system( "cp $config_h.bak $config_h" ) and die "$config_h not restored\n";
system( "make clean" ) and die;
# depends on a specific curve. Also, ignore error if it wasn't enabled
system( "scripts/config.py unset MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED" );
print "\n******************************************\n";
print "* Testing without curve: $curve\n";
print "******************************************\n";
$ENV{MBEDTLS_TEST_CONFIGURATION} = "-$curve";
system( "scripts/config.py unset $curve" )
and abort "Failed to disable $curve\n";
system( "CFLAGS='-Werror -Wall -Wextra' make lib" )
and abort "Failed to build lib: $curve\n";
system( "make" ) and abort "Failed to build tests: $curve\n";
system( "make test" ) and abort "Failed test suite: $curve\n";
}
system( "mv $config_h.bak $config_h" ) and die "$config_h not restored\n";
system( "make clean" ) and die;
exit 0;

View File

@@ -0,0 +1,103 @@
#!/usr/bin/env perl
# depends-hashes.pl
#
# Copyright (c) 2017, ARM Limited, All Rights Reserved
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This file is part of Mbed TLS (https://tls.mbed.org)
#
# Purpose
#
# To test the code dependencies on individual hashes in each test suite. This
# is a verification step to ensure we don't ship test suites that do not work
# for some build options.
#
# The process is:
# for each possible hash
# build the library and test suites with the hash disabled
# execute the test suites
#
# And any test suite with the wrong dependencies will fail.
#
# Usage: tests/scripts/depends-hashes.pl
#
# This script should be executed from the root of the project directory.
#
# For best effect, run either with cmake disabled, or cmake enabled in a mode
# that includes -Werror.
use warnings;
use strict;
-d 'library' && -d 'include' && -d 'tests' or die "Must be run from root\n";
my $config_h = 'include/mbedtls/config.h';
# as many SSL options depend on specific hashes,
# and SSL is not in the test suites anyways,
# disable it to avoid dependcies issues
my $ssl_sed_cmd = 's/^#define \(MBEDTLS_SSL.*\)/\1/p';
my @ssl = split( /\s+/, `sed -n -e '$ssl_sed_cmd' $config_h` );
# for md we want to catch MD5_C but not MD_C, hence the extra dot
my $mdx_sed_cmd = 's/^#define \(MBEDTLS_MD..*_C\)/\1/p';
my $sha_sed_cmd = 's/^#define \(MBEDTLS_SHA.*_C\)/\1/p';
my @hash_modules = split( /\s+/,
`sed -n -e '$mdx_sed_cmd' -e '$sha_sed_cmd' $config_h` );
# there are also negative options for truncated variants, disabled by default
my $sha_trunc_sed_cmd = 's/^\/\/#define \(MBEDTLS_SHA..._NO_.*\)/\1/p';
my @hash_negatives = split( /\s+/,
`sed -n -e '$sha_trunc_sed_cmd' $config_h` );
# list hash options with corresponding actions
my @hashes = ((map { "unset $_" } @hash_modules),
(map { "set $_" } @hash_negatives));
system( "cp $config_h $config_h.bak" ) and die;
sub abort {
system( "mv $config_h.bak $config_h" ) and warn "$config_h not restored\n";
# use an exit code between 1 and 124 for git bisect (die returns 255)
warn $_[0];
exit 1;
}
for my $hash (@hashes) {
system( "cp $config_h.bak $config_h" ) and die "$config_h not restored\n";
system( "make clean" ) and die;
print "\n******************************************\n";
print "* Testing hash option: $hash\n";
print "******************************************\n";
$ENV{MBEDTLS_TEST_CONFIGURATION} = "-$hash";
system( "scripts/config.py $hash" )
and abort "Failed to $hash\n";
for my $opt (@ssl) {
system( "scripts/config.py unset $opt" )
and abort "Failed to disable $opt\n";
}
system( "CFLAGS='-Werror -Wall -Wextra' make lib" )
and abort "Failed to build lib: $hash\n";
system( "cd tests && make" ) and abort "Failed to build tests: $hash\n";
system( "make test" ) and abort "Failed test suite: $hash\n";
}
system( "mv $config_h.bak $config_h" ) and die "$config_h not restored\n";
system( "make clean" ) and die;
exit 0;

View File

@@ -0,0 +1,107 @@
#!/usr/bin/env perl
# depends-pkalgs.pl
#
# Copyright (c) 2017, ARM Limited, All Rights Reserved
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This file is part of Mbed TLS (https://tls.mbed.org)
#
# Purpose
#
# To test the code dependencies on individual PK algs (those that can be used
# from the PK layer, so currently signature and encryption but not key
# exchange) in each test suite. This is a verification step to ensure we don't
# ship test suites that do not work for some build options.
#
# The process is:
# for each possible PK alg
# build the library and test suites with that alg disabled
# execute the test suites
#
# And any test suite with the wrong dependencies will fail.
#
# Usage: tests/scripts/depends-pkalgs.pl
#
# This script should be executed from the root of the project directory.
#
# For best effect, run either with cmake disabled, or cmake enabled in a mode
# that includes -Werror.
use warnings;
use strict;
-d 'library' && -d 'include' && -d 'tests' or die "Must be run from root\n";
my $config_h = 'include/mbedtls/config.h';
# Some algorithms can't be disabled on their own as others depend on them, so
# we list those reverse-dependencies here to keep check_config.h happy.
my %algs = (
'MBEDTLS_ECDSA_C' => ['MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED'],
'MBEDTLS_ECP_C' => ['MBEDTLS_ECDSA_C',
'MBEDTLS_ECDH_C',
'MBEDTLS_ECJPAKE_C',
'MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED',
'MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED',
'MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED',
'MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED',
'MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED'],
'MBEDTLS_X509_RSASSA_PSS_SUPPORT' => [],
'MBEDTLS_PKCS1_V21' => ['MBEDTLS_X509_RSASSA_PSS_SUPPORT'],
'MBEDTLS_PKCS1_V15' => ['MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED',
'MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED',
'MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED',
'MBEDTLS_KEY_EXCHANGE_RSA_ENABLED'],
'MBEDTLS_RSA_C' => ['MBEDTLS_X509_RSASSA_PSS_SUPPORT',
'MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED',
'MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED',
'MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED',
'MBEDTLS_KEY_EXCHANGE_RSA_ENABLED'],
);
system( "cp $config_h $config_h.bak" ) and die;
sub abort {
system( "mv $config_h.bak $config_h" ) and warn "$config_h not restored\n";
# use an exit code between 1 and 124 for git bisect (die returns 255)
warn $_[0];
exit 1;
}
while( my ($alg, $extras) = each %algs ) {
system( "cp $config_h.bak $config_h" ) and die "$config_h not restored\n";
system( "make clean" ) and die;
print "\n******************************************\n";
print "* Testing without alg: $alg\n";
print "******************************************\n";
$ENV{MBEDTLS_TEST_CONFIGURATION} = "-$alg";
system( "scripts/config.py unset $alg" )
and abort "Failed to disable $alg\n";
for my $opt (@$extras) {
system( "scripts/config.py unset $opt" )
and abort "Failed to disable $opt\n";
}
system( "CFLAGS='-Werror -Wall -Wextra' make lib" )
and abort "Failed to build lib: $alg\n";
system( "cd tests && make" ) and abort "Failed to build tests: $alg\n";
system( "make test" ) and abort "Failed test suite: $alg\n";
}
system( "mv $config_h.bak $config_h" ) and die "$config_h not restored\n";
system( "make clean" ) and die;
exit 0;

View File

@@ -0,0 +1,93 @@
#!/bin/bash -eu
# docker_env.sh
#
# Purpose
# -------
#
# This is a helper script to enable running tests under a Docker container,
# thus making it easier to get set up as well as isolating test dependencies
# (which include legacy/insecure configurations of openssl and gnutls).
#
# Notes for users
# ---------------
# This script expects a Linux x86_64 system with a recent version of Docker
# installed and available for use, as well as http/https access. If a proxy
# server must be used, invoke this script with the usual environment variables
# (http_proxy and https_proxy) set appropriately. If an alternate Docker
# registry is needed, specify MBEDTLS_DOCKER_REGISTRY to point at the
# host name.
#
#
# Running this script directly will check for Docker availability and set up
# the Docker image.
# Copyright (C) 2006-2019, Arm Limited (or its affiliates), All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This file is part of Mbed TLS (https://tls.mbed.org)
# default values, can be overridden by the environment
: ${MBEDTLS_DOCKER_GUEST:=bionic}
DOCKER_IMAGE_TAG="armmbed/mbedtls-test:${MBEDTLS_DOCKER_GUEST}"
# Make sure docker is available
if ! which docker > /dev/null; then
echo "Docker is required but doesn't seem to be installed. See https://www.docker.com/ to get started"
exit 1
fi
# Figure out if we need to 'sudo docker'
if groups | grep docker > /dev/null; then
DOCKER="docker"
else
echo "Using sudo to invoke docker since you're not a member of the docker group..."
DOCKER="sudo docker"
fi
# Build the Docker image
echo "Getting docker image up to date (this may take a few minutes)..."
${DOCKER} image build \
-t ${DOCKER_IMAGE_TAG} \
--cache-from=${DOCKER_IMAGE_TAG} \
--build-arg MAKEFLAGS_PARALLEL="-j $(nproc)" \
--network host \
${http_proxy+--build-arg http_proxy=${http_proxy}} \
${https_proxy+--build-arg https_proxy=${https_proxy}} \
${MBEDTLS_DOCKER_REGISTRY+--build-arg MY_REGISTRY="${MBEDTLS_DOCKER_REGISTRY}/"} \
tests/docker/${MBEDTLS_DOCKER_GUEST}
run_in_docker()
{
ENV_ARGS=""
while [ "$1" == "-e" ]; do
ENV_ARGS="${ENV_ARGS} $1 $2"
shift 2
done
${DOCKER} container run -it --rm \
--cap-add SYS_PTRACE \
--user "$(id -u):$(id -g)" \
--volume $PWD:$PWD \
--workdir $PWD \
-e MAKEFLAGS \
-e PYLINTHOME=/tmp/.pylintd \
${ENV_ARGS} \
${DOCKER_IMAGE_TAG} \
$@
}

View File

@@ -0,0 +1,46 @@
#!/bin/sh
# Make sure the doxygen documentation builds without warnings
#
# Copyright (C) 2016, Arm Limited, All Rights Reserved
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This file is part of Mbed TLS (https://tls.mbed.org)
# Abort on errors (and uninitiliased variables)
set -eu
if [ -d library -a -d include -a -d tests ]; then :; else
echo "Must be run from mbed TLS root" >&2
exit 1
fi
if scripts/apidoc_full.sh > doc.out 2>doc.err; then :; else
cat doc.err
echo "FAIL" >&2
exit 1;
fi
cat doc.out doc.err | \
grep -v "warning: ignoring unsupported tag" \
> doc.filtered
if egrep "(warning|error):" doc.filtered; then
echo "FAIL" >&2
exit 1;
fi
make apidoc_clean
rm -f doc.out doc.err doc.filtered

View File

@@ -0,0 +1,110 @@
#!/usr/bin/env perl
#
# Based on NIST CTR_DRBG.rsp validation file
# Only uses AES-256-CTR cases that use a Derivation function
# and concats nonce and personalization for initialization.
#
# Copyright (C) 2011, Arm Limited, All Rights Reserved
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This file is part of Mbed TLS (https://tls.mbed.org)
use strict;
my $file = shift;
open(TEST_DATA, "$file") or die "Opening test cases '$file': $!";
sub get_suite_val($)
{
my $name = shift;
my $val = "";
my $line = <TEST_DATA>;
($val) = ($line =~ /\[$name\s\=\s(\w+)\]/);
return $val;
}
sub get_val($)
{
my $name = shift;
my $val = "";
my $line;
while($line = <TEST_DATA>)
{
next if($line !~ /=/);
last;
}
($val) = ($line =~ /^$name = (\w+)/);
return $val;
}
my $cnt = 1;;
while (my $line = <TEST_DATA>)
{
next if ($line !~ /^\[AES-256 use df/);
my $PredictionResistanceStr = get_suite_val("PredictionResistance");
my $PredictionResistance = 0;
$PredictionResistance = 1 if ($PredictionResistanceStr eq 'True');
my $EntropyInputLen = get_suite_val("EntropyInputLen");
my $NonceLen = get_suite_val("NonceLen");
my $PersonalizationStringLen = get_suite_val("PersonalizationStringLen");
my $AdditionalInputLen = get_suite_val("AdditionalInputLen");
for ($cnt = 0; $cnt < 15; $cnt++)
{
my $Count = get_val("COUNT");
my $EntropyInput = get_val("EntropyInput");
my $Nonce = get_val("Nonce");
my $PersonalizationString = get_val("PersonalizationString");
my $AdditionalInput1 = get_val("AdditionalInput");
my $EntropyInputPR1 = get_val("EntropyInputPR") if ($PredictionResistance == 1);
my $EntropyInputReseed = get_val("EntropyInputReseed") if ($PredictionResistance == 0);
my $AdditionalInputReseed = get_val("AdditionalInputReseed") if ($PredictionResistance == 0);
my $AdditionalInput2 = get_val("AdditionalInput");
my $EntropyInputPR2 = get_val("EntropyInputPR") if ($PredictionResistance == 1);
my $ReturnedBits = get_val("ReturnedBits");
if ($PredictionResistance == 1)
{
print("CTR_DRBG NIST Validation (AES-256 use df,$PredictionResistanceStr,$EntropyInputLen,$NonceLen,$PersonalizationStringLen,$AdditionalInputLen) #$Count\n");
print("ctr_drbg_validate_pr");
print(":\"$Nonce$PersonalizationString\"");
print(":\"$EntropyInput$EntropyInputPR1$EntropyInputPR2\"");
print(":\"$AdditionalInput1\"");
print(":\"$AdditionalInput2\"");
print(":\"$ReturnedBits\"");
print("\n\n");
}
else
{
print("CTR_DRBG NIST Validation (AES-256 use df,$PredictionResistanceStr,$EntropyInputLen,$NonceLen,$PersonalizationStringLen,$AdditionalInputLen) #$Count\n");
print("ctr_drbg_validate_nopr");
print(":\"$Nonce$PersonalizationString\"");
print(":\"$EntropyInput$EntropyInputReseed\"");
print(":\"$AdditionalInput1\"");
print(":\"$AdditionalInputReseed\"");
print(":\"$AdditionalInput2\"");
print(":\"$ReturnedBits\"");
print("\n\n");
}
}
}
close(TEST_DATA);

View File

@@ -0,0 +1,115 @@
#!/usr/bin/env perl
#
# Based on NIST gcmDecryptxxx.rsp validation files
# Only first 3 of every set used for compile time saving
#
# Copyright (C) 2012-2013, Arm Limited, All Rights Reserved
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This file is part of Mbed TLS (https://tls.mbed.org)
use strict;
my $file = shift;
open(TEST_DATA, "$file") or die "Opening test cases '$file': $!";
sub get_suite_val($)
{
my $name = shift;
my $val = "";
while(my $line = <TEST_DATA>)
{
next if ($line !~ /^\[/);
($val) = ($line =~ /\[$name\s\=\s(\w+)\]/);
last;
}
return $val;
}
sub get_val($)
{
my $name = shift;
my $val = "";
my $line;
while($line = <TEST_DATA>)
{
next if($line !~ /=/);
last;
}
($val) = ($line =~ /^$name = (\w+)/);
return $val;
}
sub get_val_or_fail($)
{
my $name = shift;
my $val = "FAIL";
my $line;
while($line = <TEST_DATA>)
{
next if($line !~ /=/ && $line !~ /FAIL/);
last;
}
($val) = ($line =~ /^$name = (\w+)/) if ($line =~ /=/);
return $val;
}
my $cnt = 1;;
while (my $line = <TEST_DATA>)
{
my $key_len = get_suite_val("Keylen");
next if ($key_len !~ /\d+/);
my $iv_len = get_suite_val("IVlen");
my $pt_len = get_suite_val("PTlen");
my $add_len = get_suite_val("AADlen");
my $tag_len = get_suite_val("Taglen");
for ($cnt = 0; $cnt < 3; $cnt++)
{
my $Count = get_val("Count");
my $key = get_val("Key");
my $iv = get_val("IV");
my $ct = get_val("CT");
my $add = get_val("AAD");
my $tag = get_val("Tag");
my $pt = get_val_or_fail("PT");
print("GCM NIST Validation (AES-$key_len,$iv_len,$pt_len,$add_len,$tag_len) #$Count\n");
print("gcm_decrypt_and_verify");
print(":\"$key\"");
print(":\"$ct\"");
print(":\"$iv\"");
print(":\"$add\"");
print(":$tag_len");
print(":\"$tag\"");
print(":\"$pt\"");
print(":0");
print("\n\n");
}
}
print("GCM Selftest\n");
print("gcm_selftest:\n\n");
close(TEST_DATA);

View File

@@ -0,0 +1,98 @@
#!/usr/bin/env perl
#
# Based on NIST gcmEncryptIntIVxxx.rsp validation files
# Only first 3 of every set used for compile time saving
#
# Copyright (C) 2012-2013, Arm Limited, All Rights Reserved
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This file is part of Mbed TLS (https://tls.mbed.org)
use strict;
my $file = shift;
open(TEST_DATA, "$file") or die "Opening test cases '$file': $!";
sub get_suite_val($)
{
my $name = shift;
my $val = "";
while(my $line = <TEST_DATA>)
{
next if ($line !~ /^\[/);
($val) = ($line =~ /\[$name\s\=\s(\w+)\]/);
last;
}
return $val;
}
sub get_val($)
{
my $name = shift;
my $val = "";
my $line;
while($line = <TEST_DATA>)
{
next if($line !~ /=/);
last;
}
($val) = ($line =~ /^$name = (\w+)/);
return $val;
}
my $cnt = 1;;
while (my $line = <TEST_DATA>)
{
my $key_len = get_suite_val("Keylen");
next if ($key_len !~ /\d+/);
my $iv_len = get_suite_val("IVlen");
my $pt_len = get_suite_val("PTlen");
my $add_len = get_suite_val("AADlen");
my $tag_len = get_suite_val("Taglen");
for ($cnt = 0; $cnt < 3; $cnt++)
{
my $Count = get_val("Count");
my $key = get_val("Key");
my $pt = get_val("PT");
my $add = get_val("AAD");
my $iv = get_val("IV");
my $ct = get_val("CT");
my $tag = get_val("Tag");
print("GCM NIST Validation (AES-$key_len,$iv_len,$pt_len,$add_len,$tag_len) #$Count\n");
print("gcm_encrypt_and_tag");
print(":\"$key\"");
print(":\"$pt\"");
print(":\"$iv\"");
print(":\"$add\"");
print(":\"$ct\"");
print(":$tag_len");
print(":\"$tag\"");
print(":0");
print("\n\n");
}
}
print("GCM Selftest\n");
print("gcm_selftest:\n\n");
close(TEST_DATA);

View File

@@ -0,0 +1,88 @@
#!/usr/bin/env perl
#
# Copyright (C) 2011-2015, Arm Limited, All Rights Reserved
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This file is part of Mbed TLS (https://tls.mbed.org)
use strict;
my $file = shift;
open(TEST_DATA, "$file") or die "Opening test cases '$file': $!";
sub get_val($$)
{
my $str = shift;
my $name = shift;
my $val = "";
while(my $line = <TEST_DATA>)
{
next if($line !~ /^# $str/);
last;
}
while(my $line = <TEST_DATA>)
{
last if($line eq "\r\n");
$val .= $line;
}
$val =~ s/[ \r\n]//g;
return $val;
}
my $state = 0;
my $val_n = "";
my $val_e = "";
my $val_p = "";
my $val_q = "";
my $mod = 0;
my $cnt = 1;
while (my $line = <TEST_DATA>)
{
next if ($line !~ /^# Example/);
( $mod ) = ($line =~ /A (\d+)/);
$val_n = get_val("RSA modulus n", "N");
$val_e = get_val("RSA public exponent e", "E");
$val_p = get_val("Prime p", "P");
$val_q = get_val("Prime q", "Q");
for(my $i = 1; $i <= 6; $i++)
{
my $val_m = get_val("Message to be", "M");
my $val_salt = get_val("Salt", "Salt");
my $val_sig = get_val("Signature", "Sig");
print("RSASSA-PSS Signature Example ${cnt}_${i}\n");
print("pkcs1_rsassa_pss_sign:$mod:16:\"$val_p\":16:\"$val_q\":16:\"$val_n\":16:\"$val_e\":SIG_RSA_SHA1:MBEDTLS_MD_SHA1");
print(":\"$val_m\"");
print(":\"$val_salt\"");
print(":\"$val_sig\":0");
print("\n\n");
print("RSASSA-PSS Signature Example ${cnt}_${i} (verify)\n");
print("pkcs1_rsassa_pss_verify:$mod:16:\"$val_n\":16:\"$val_e\":SIG_RSA_SHA1:MBEDTLS_MD_SHA1");
print(":\"$val_m\"");
print(":\"$val_salt\"");
print(":\"$val_sig\":0");
print("\n\n");
}
$cnt++;
}
close(TEST_DATA);

View File

@@ -0,0 +1,85 @@
#!/bin/sh
# This script splits the data test files containing the test cases into
# individual files (one test case per file) suitable for use with afl
# (American Fuzzy Lop). http://lcamtuf.coredump.cx/afl/
#
# Usage: generate-afl-tests.sh <test data file path>
# <test data file path> - should be the path to one of the test suite files
# such as 'test_suite_mpi.data'
#
# Copyright (C) 2016, Arm Limited, All Rights Reserved
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This file is part of Mbed TLS (https://tls.mbed.org)
# Abort on errors
set -e
if [ -z $1 ]
then
echo " [!] No test file specified" >&2
echo "Usage: $0 <test data file>" >&2
exit 1
fi
SRC_FILEPATH=$(dirname $1)/$(basename $1)
TESTSUITE=$(basename $1 .data)
THIS_DIR=$(basename $PWD)
if [ -d ../library -a -d ../include -a -d ../tests -a $THIS_DIR == "tests" ];
then :;
else
echo " [!] Must be run from mbed TLS tests directory" >&2
exit 1
fi
DEST_TESTCASE_DIR=$TESTSUITE-afl-tests
DEST_OUTPUT_DIR=$TESTSUITE-afl-out
echo " [+] Creating output directories" >&2
if [ -e $DEST_OUTPUT_DIR/* ];
then :
echo " [!] Test output files already exist." >&2
exit 1
else
mkdir -p $DEST_OUTPUT_DIR
fi
if [ -e $DEST_TESTCASE_DIR/* ];
then :
echo " [!] Test output files already exist." >&2
else
mkdir -p $DEST_TESTCASE_DIR
fi
echo " [+] Creating test cases" >&2
cd $DEST_TESTCASE_DIR
split -p '^\s*$' ../$SRC_FILEPATH
for f in *;
do
# Strip out any blank lines (no trim on OS X)
sed '/^\s*$/d' $f >testcase_$f
rm $f
done
cd ..
echo " [+] Test cases in $DEST_TESTCASE_DIR" >&2

View File

@@ -0,0 +1,78 @@
#!/usr/bin/env perl
# key-exchanges.pl
#
# Copyright (c) 2015-2017, ARM Limited, All Rights Reserved
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This file is part of Mbed TLS (https://tls.mbed.org)
#
# Purpose
#
# To test the code dependencies on individual key exchanges in the SSL module.
# is a verification step to ensure we don't ship SSL code that do not work
# for some build options.
#
# The process is:
# for each possible key exchange
# build the library with all but that key exchange disabled
#
# Usage: tests/scripts/key-exchanges.pl
#
# This script should be executed from the root of the project directory.
#
# For best effect, run either with cmake disabled, or cmake enabled in a mode
# that includes -Werror.
use warnings;
use strict;
-d 'library' && -d 'include' && -d 'tests' or die "Must be run from root\n";
my $sed_cmd = 's/^#define \(MBEDTLS_KEY_EXCHANGE_.*_ENABLED\)/\1/p';
my $config_h = 'include/mbedtls/config.h';
my @kexes = split( /\s+/, `sed -n -e '$sed_cmd' $config_h` );
system( "cp $config_h $config_h.bak" ) and die;
sub abort {
system( "mv $config_h.bak $config_h" ) and warn "$config_h not restored\n";
# use an exit code between 1 and 124 for git bisect (die returns 255)
warn $_[0];
exit 1;
}
for my $kex (@kexes) {
system( "cp $config_h.bak $config_h" ) and die "$config_h not restored\n";
system( "make clean" ) and die;
print "\n******************************************\n";
print "* Testing with key exchange: $kex\n";
print "******************************************\n";
$ENV{MBEDTLS_TEST_CONFIGURATION} = $kex;
# full config with all key exchanges disabled except one
system( "scripts/config.py full" ) and abort "Failed config full\n";
for my $k (@kexes) {
next if $k eq $kex;
system( "scripts/config.py unset $k" )
and abort "Failed to disable $k\n";
}
system( "make lib CFLAGS='-Os -Werror'" ) and abort "Failed to build lib: $kex\n";
}
system( "mv $config_h.bak $config_h" ) and die "$config_h not restored\n";
system( "make clean" ) and die;
exit 0;

View File

@@ -0,0 +1,55 @@
#!/usr/bin/env perl
#
# Copyright (C) 2015-2019, Arm Limited, All Rights Reserved
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This file is part of Mbed TLS (https://tls.mbed.org)
use warnings;
use strict;
use utf8;
use open qw(:std utf8);
-d 'include/mbedtls' or die "$0: must be run from root\n";
@ARGV = grep { ! /compat-1\.3\.h/ } <include/mbedtls/*.h>;
push @ARGV, "3rdparty/everest/include/everest/everest.h";
push @ARGV, "3rdparty/everest/include/everest/x25519.h";
my @consts;
my $state = 'out';
while (<>)
{
if( $state eq 'out' and /^(typedef )?enum \{/ ) {
$state = 'in';
} elsif( $state eq 'out' and /^(typedef )?enum/ ) {
$state = 'start';
} elsif( $state eq 'start' and /{/ ) {
$state = 'in';
} elsif( $state eq 'in' and /}/ ) {
$state = 'out';
} elsif( $state eq 'in' and not /^#/) {
s/=.*//; s!/\*.*!!; s/,.*//; s/\s+//g; chomp;
push @consts, $_ if $_;
}
}
open my $fh, '>', 'enum-consts' or die;
print $fh "$_\n" for sort @consts;
close $fh or die;
printf "%8d enum-consts\n", scalar @consts;

View File

@@ -0,0 +1,80 @@
#!/bin/bash
#
# Create a file named identifiers containing identifiers from internal header
# files or all header files, based on --internal flag.
# Outputs the line count of the file to stdout.
#
# Usage: list-identifiers.sh [ -i | --internal ]
#
# Copyright (C) 2015-2019, Arm Limited, All Rights Reserved
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This file is part of Mbed TLS (https://tls.mbed.org)
set -eu
if [ -d include/mbedtls ]; then :; else
echo "$0: must be run from root" >&2
exit 1
fi
INTERNAL=""
until [ -z "${1-}" ]
do
case "$1" in
-i|--internal)
INTERNAL="1"
;;
*)
# print error
echo "Unknown argument: '$1'"
exit 1
;;
esac
shift
done
if [ $INTERNAL ]
then
HEADERS=$( ls include/mbedtls/*_internal.h library/*.h | egrep -v 'compat-1\.3\.h|bn_mul' )
else
HEADERS=$( ls include/mbedtls/*.h include/psa/*.h library/*.h | egrep -v 'compat-1\.3\.h|bn_mul' )
HEADERS="$HEADERS 3rdparty/everest/include/everest/everest.h 3rdparty/everest/include/everest/x25519.h"
fi
rm -f identifiers
grep '^[^ /#{]' $HEADERS | \
sed -e 's/^[^:]*://' | \
egrep -v '^(extern "C"|(typedef )?(struct|union|enum)( {)?$|};?$)' \
> _decls
if true; then
sed -n -e 's/.* \**\([a-zA-Z_][a-zA-Z0-9_]*\)(.*/\1/p' \
-e 's/.*(\*\(.*\))(.*/\1/p' _decls
grep -v '(' _decls | sed -e 's/\([a-zA-Z0-9_]*\)[;[].*/\1/' -e 's/.* \**//'
fi > _identifiers
if [ $( wc -l < _identifiers ) -eq $( wc -l < _decls ) ]; then
rm _decls
egrep -v '^(u?int(16|32|64)_t)$' _identifiers | sort > identifiers
rm _identifiers
else
echo "$0: oops, lost some identifiers" 2>&1
exit 1
fi
wc -l identifiers

View File

@@ -0,0 +1,34 @@
#!/bin/sh
#
# Copyright (C) 2015-2019, Arm Limited, All Rights Reserved
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This file is part of Mbed TLS (https://tls.mbed.org)
set -eu
if [ -d include/mbedtls ]; then :; else
echo "$0: must be run from root" >&2
exit 1
fi
HEADERS=$( ls include/mbedtls/*.h include/psa/*.h | egrep -v 'compat-1\.3\.h' )
HEADERS="$HEADERS 3rdparty/everest/include/everest/everest.h 3rdparty/everest/include/everest/x25519.h"
sed -n -e 's/.*#define \([a-zA-Z0-9_]*\).*/\1/p' $HEADERS \
| egrep -v '^(asm|inline|EMIT|_CRT_SECURE_NO_DEPRECATE)$|^MULADDC_' \
| sort -u > macros
wc -l macros

View File

@@ -0,0 +1,56 @@
#!/bin/sh
#
# Copyright (C) 2015-2019, Arm Limited, All Rights Reserved
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This file is part of Mbed TLS (https://tls.mbed.org)
set -eu
if [ -d include/mbedtls ]; then :; else
echo "$0: must be run from root" >&2
exit 1
fi
if grep -i cmake Makefile >/dev/null; then
echo "$0: not compatible with cmake" >&2
exit 1
fi
cp include/mbedtls/config.h include/mbedtls/config.h.bak
scripts/config.py full
make clean
make_ret=
CFLAGS=-fno-asynchronous-unwind-tables make lib \
>list-symbols.make.log 2>&1 ||
{
make_ret=$?
echo "Build failure: CFLAGS=-fno-asynchronous-unwind-tables make lib"
cat list-symbols.make.log >&2
}
rm list-symbols.make.log
mv include/mbedtls/config.h.bak include/mbedtls/config.h
if [ -n "$make_ret" ]; then
exit "$make_ret"
fi
if uname | grep -F Darwin >/dev/null; then
nm -gUj library/libmbed*.a 2>/dev/null | sed -n -e 's/^_//p' | grep -v -e ^FStar -e ^Hacl
elif uname | grep -F Linux >/dev/null; then
nm -og library/libmbed*.a | grep -v '^[^ ]*: *U \|^$\|^[^ ]*:$' | sed 's/^[^ ]* . //' | grep -v -e ^FStar -e ^Hacl
fi | sort > exported-symbols
make clean
wc -l exported-symbols

View File

@@ -0,0 +1,384 @@
#!/usr/bin/env python3
# Greentea host test script for Mbed TLS on-target test suite testing.
#
# Copyright (C) 2018, Arm Limited, All Rights Reserved
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This file is part of Mbed TLS (https://tls.mbed.org)
"""
Mbed TLS on-target test suite tests are implemented as Greentea
tests. Greentea tests are implemented in two parts: target test and
host test. Target test is a C application that is built for the
target platform and executes on the target. Host test is a Python
class derived from mbed_host_tests.BaseHostTest. Target communicates
with the host over serial for the test data and sends back the result.
Python tool mbedgt (Greentea) is responsible for flashing the test
binary on to the target and dynamically loading this host test module.
Greentea documentation can be found here:
https://github.com/ARMmbed/greentea
"""
import re
import os
import binascii
from mbed_host_tests import BaseHostTest, event_callback # pylint: disable=import-error
class TestDataParserError(Exception):
"""Indicates error in test data, read from .data file."""
pass
class TestDataParser:
"""
Parses test name, dependencies, test function name and test parameters
from the data file.
"""
def __init__(self):
"""
Constructor
"""
self.tests = []
def parse(self, data_file):
"""
Data file parser.
:param data_file: Data file path
"""
with open(data_file, 'r') as data_f:
self.__parse(data_f)
@staticmethod
def __escaped_split(inp_str, split_char):
"""
Splits inp_str on split_char except when escaped.
:param inp_str: String to split
:param split_char: Split character
:return: List of splits
"""
split_colon_fn = lambda x: re.sub(r'\\' + split_char, split_char, x)
if len(split_char) > 1:
raise ValueError('Expected split character. Found string!')
out = list(map(split_colon_fn, re.split(r'(?<!\\)' + split_char, inp_str)))
out = [x for x in out if x]
return out
def __parse(self, data_f):
"""
Parses data file using supplied file object.
:param data_f: Data file object
:return:
"""
for line in data_f:
line = line.strip()
if not line:
continue
# Read test name
name = line
# Check dependencies
dependencies = []
line = next(data_f).strip()
match = re.search('depends_on:(.*)', line)
if match:
dependencies = [int(x) for x in match.group(1).split(':')]
line = next(data_f).strip()
# Read test vectors
line = line.replace('\\n', '\n')
parts = self.__escaped_split(line, ':')
function_name = int(parts[0])
args = parts[1:]
args_count = len(args)
if args_count % 2 != 0:
err_str_fmt = "Number of test arguments({}) should be even: {}"
raise TestDataParserError(err_str_fmt.format(args_count, line))
grouped_args = [(args[i * 2], args[(i * 2) + 1])
for i in range(int(len(args)/2))]
self.tests.append((name, function_name, dependencies,
grouped_args))
def get_test_data(self):
"""
Returns test data.
"""
return self.tests
class MbedTlsTest(BaseHostTest):
"""
Host test for Mbed TLS unit tests. This script is loaded at
run time by Greentea for executing Mbed TLS test suites. Each
communication from the target is received in this object as
an event, which is then handled by the event handler method
decorated by the associated event. Ex: @event_callback('GO').
Target test sends requests for dispatching next test. It reads
tests from the intermediate data file and sends test function
identifier, dependency identifiers, expression identifiers and
the test data in binary form. Target test checks dependencies
, evaluate integer constant expressions and dispatches the test
function with received test parameters. After test function is
finished, target sends the result. This class handles the result
event and prints verdict in the form that Greentea understands.
"""
# status/error codes from suites/helpers.function
DEPENDENCY_SUPPORTED = 0
KEY_VALUE_MAPPING_FOUND = DEPENDENCY_SUPPORTED
DISPATCH_TEST_SUCCESS = DEPENDENCY_SUPPORTED
KEY_VALUE_MAPPING_NOT_FOUND = -1 # Expression Id not found.
DEPENDENCY_NOT_SUPPORTED = -2 # Dependency not supported.
DISPATCH_TEST_FN_NOT_FOUND = -3 # Test function not found.
DISPATCH_INVALID_TEST_DATA = -4 # Invalid parameter type.
DISPATCH_UNSUPPORTED_SUITE = -5 # Test suite not supported/enabled.
def __init__(self):
"""
Constructor initialises test index to 0.
"""
super(MbedTlsTest, self).__init__()
self.tests = []
self.test_index = -1
self.dep_index = 0
self.suite_passed = True
self.error_str = dict()
self.error_str[self.DEPENDENCY_SUPPORTED] = \
'DEPENDENCY_SUPPORTED'
self.error_str[self.KEY_VALUE_MAPPING_NOT_FOUND] = \
'KEY_VALUE_MAPPING_NOT_FOUND'
self.error_str[self.DEPENDENCY_NOT_SUPPORTED] = \
'DEPENDENCY_NOT_SUPPORTED'
self.error_str[self.DISPATCH_TEST_FN_NOT_FOUND] = \
'DISPATCH_TEST_FN_NOT_FOUND'
self.error_str[self.DISPATCH_INVALID_TEST_DATA] = \
'DISPATCH_INVALID_TEST_DATA'
self.error_str[self.DISPATCH_UNSUPPORTED_SUITE] = \
'DISPATCH_UNSUPPORTED_SUITE'
def setup(self):
"""
Setup hook implementation. Reads test suite data file and parses out
tests.
"""
binary_path = self.get_config_item('image_path')
script_dir = os.path.split(os.path.abspath(__file__))[0]
suite_name = os.path.splitext(os.path.basename(binary_path))[0]
data_file = ".".join((suite_name, 'datax'))
data_file = os.path.join(script_dir, '..', 'mbedtls',
suite_name, data_file)
if os.path.exists(data_file):
self.log("Running tests from %s" % data_file)
parser = TestDataParser()
parser.parse(data_file)
self.tests = parser.get_test_data()
self.print_test_info()
else:
self.log("Data file not found: %s" % data_file)
self.notify_complete(False)
def print_test_info(self):
"""
Prints test summary read by Greentea to detect test cases.
"""
self.log('{{__testcase_count;%d}}' % len(self.tests))
for name, _, _, _ in self.tests:
self.log('{{__testcase_name;%s}}' % name)
@staticmethod
def align_32bit(data_bytes):
"""
4 byte aligns input byte array.
:return:
"""
data_bytes += bytearray((4 - (len(data_bytes))) % 4)
@staticmethod
def hex_str_bytes(hex_str):
"""
Converts Hex string representation to byte array
:param hex_str: Hex in string format.
:return: Output Byte array
"""
if hex_str[0] != '"' or hex_str[len(hex_str) - 1] != '"':
raise TestDataParserError("HEX test parameter missing '\"':"
" %s" % hex_str)
hex_str = hex_str.strip('"')
if len(hex_str) % 2 != 0:
raise TestDataParserError("HEX parameter len should be mod of "
"2: %s" % hex_str)
data_bytes = binascii.unhexlify(hex_str)
return data_bytes
@staticmethod
def int32_to_big_endian_bytes(i):
"""
Coverts i to byte array in big endian format.
:param i: Input integer
:return: Output bytes array in big endian or network order
"""
data_bytes = bytearray([((i >> x) & 0xff) for x in [24, 16, 8, 0]])
return data_bytes
def test_vector_to_bytes(self, function_id, dependencies, parameters):
"""
Converts test vector into a byte array that can be sent to the target.
:param function_id: Test Function Identifier
:param dependencies: Dependency list
:param parameters: Test function input parameters
:return: Byte array and its length
"""
data_bytes = bytearray([len(dependencies)])
if dependencies:
data_bytes += bytearray(dependencies)
data_bytes += bytearray([function_id, len(parameters)])
for typ, param in parameters:
if typ in ('int', 'exp'):
i = int(param, 0)
data_bytes += b'I' if typ == 'int' else b'E'
self.align_32bit(data_bytes)
data_bytes += self.int32_to_big_endian_bytes(i)
elif typ == 'char*':
param = param.strip('"')
i = len(param) + 1 # + 1 for null termination
data_bytes += b'S'
self.align_32bit(data_bytes)
data_bytes += self.int32_to_big_endian_bytes(i)
data_bytes += bytearray(param, encoding='ascii')
data_bytes += b'\0' # Null terminate
elif typ == 'hex':
binary_data = self.hex_str_bytes(param)
data_bytes += b'H'
self.align_32bit(data_bytes)
i = len(binary_data)
data_bytes += self.int32_to_big_endian_bytes(i)
data_bytes += binary_data
length = self.int32_to_big_endian_bytes(len(data_bytes))
return data_bytes, length
def run_next_test(self):
"""
Fetch next test information and execute the test.
"""
self.test_index += 1
self.dep_index = 0
if self.test_index < len(self.tests):
name, function_id, dependencies, args = self.tests[self.test_index]
self.run_test(name, function_id, dependencies, args)
else:
self.notify_complete(self.suite_passed)
def run_test(self, name, function_id, dependencies, args):
"""
Execute the test on target by sending next test information.
:param name: Test name
:param function_id: function identifier
:param dependencies: Dependencies list
:param args: test parameters
:return:
"""
self.log("Running: %s" % name)
param_bytes, length = self.test_vector_to_bytes(function_id,
dependencies, args)
self.send_kv(
''.join('{:02x}'.format(x) for x in length),
''.join('{:02x}'.format(x) for x in param_bytes)
)
@staticmethod
def get_result(value):
"""
Converts result from string type to integer
:param value: Result code in string
:return: Integer result code. Value is from the test status
constants defined under the MbedTlsTest class.
"""
try:
return int(value)
except ValueError:
ValueError("Result should return error number. "
"Instead received %s" % value)
@event_callback('GO')
def on_go(self, _key, _value, _timestamp):
"""
Sent by the target to start first test.
:param _key: Event key
:param _value: Value. ignored
:param _timestamp: Timestamp ignored.
:return:
"""
self.run_next_test()
@event_callback("R")
def on_result(self, _key, value, _timestamp):
"""
Handle result. Prints test start, finish required by Greentea
to detect test execution.
:param _key: Event key
:param value: Value. ignored
:param _timestamp: Timestamp ignored.
:return:
"""
int_val = self.get_result(value)
name, _, _, _ = self.tests[self.test_index]
self.log('{{__testcase_start;%s}}' % name)
self.log('{{__testcase_finish;%s;%d;%d}}' % (name, int_val == 0,
int_val != 0))
if int_val != 0:
self.suite_passed = False
self.run_next_test()
@event_callback("F")
def on_failure(self, _key, value, _timestamp):
"""
Handles test execution failure. That means dependency not supported or
Test function not supported. Hence marking test as skipped.
:param _key: Event key
:param value: Value. ignored
:param _timestamp: Timestamp ignored.
:return:
"""
int_val = self.get_result(value)
if int_val in self.error_str:
err = self.error_str[int_val]
else:
err = 'Unknown error'
# For skip status, do not write {{__testcase_finish;...}}
self.log("Error: %s" % err)
self.run_next_test()

View File

@@ -0,0 +1,142 @@
#!/usr/bin/env python3
"""Describe the test coverage of PSA functions in terms of return statuses.
1. Build Mbed Crypto with -DRECORD_PSA_STATUS_COVERAGE_LOG
2. Run psa_collect_statuses.py
The output is a series of line of the form "psa_foo PSA_ERROR_XXX". Each
function/status combination appears only once.
This script must be run from the top of an Mbed Crypto source tree.
The build command is "make -DRECORD_PSA_STATUS_COVERAGE_LOG", which is
only supported with make (as opposed to CMake or other build methods).
"""
# Copyright (C) 2019, Arm Limited, All Rights Reserved
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This file is part of Mbed TLS (https://tls.mbed.org)
import argparse
import os
import subprocess
import sys
DEFAULT_STATUS_LOG_FILE = 'tests/statuses.log'
DEFAULT_PSA_CONSTANT_NAMES = 'programs/psa/psa_constant_names'
class Statuses:
"""Information about observed return statues of API functions."""
def __init__(self):
self.functions = {}
self.codes = set()
self.status_names = {}
def collect_log(self, log_file_name):
"""Read logs from RECORD_PSA_STATUS_COVERAGE_LOG.
Read logs produced by running Mbed Crypto test suites built with
-DRECORD_PSA_STATUS_COVERAGE_LOG.
"""
with open(log_file_name) as log:
for line in log:
value, function, tail = line.split(':', 2)
if function not in self.functions:
self.functions[function] = {}
fdata = self.functions[function]
if value not in self.functions[function]:
fdata[value] = []
fdata[value].append(tail)
self.codes.add(int(value))
def get_constant_names(self, psa_constant_names):
"""Run psa_constant_names to obtain names for observed numerical values."""
values = [str(value) for value in self.codes]
cmd = [psa_constant_names, 'status'] + values
output = subprocess.check_output(cmd).decode('ascii')
for value, name in zip(values, output.rstrip().split('\n')):
self.status_names[value] = name
def report(self):
"""Report observed return values for each function.
The report is a series of line of the form "psa_foo PSA_ERROR_XXX".
"""
for function in sorted(self.functions.keys()):
fdata = self.functions[function]
names = [self.status_names[value] for value in fdata.keys()]
for name in sorted(names):
sys.stdout.write('{} {}\n'.format(function, name))
def collect_status_logs(options):
"""Build and run unit tests and report observed function return statuses.
Build Mbed Crypto with -DRECORD_PSA_STATUS_COVERAGE_LOG, run the
test suites and display information about observed return statuses.
"""
rebuilt = False
if not options.use_existing_log and os.path.exists(options.log_file):
os.remove(options.log_file)
if not os.path.exists(options.log_file):
if options.clean_before:
subprocess.check_call(['make', 'clean'],
cwd='tests',
stdout=sys.stderr)
with open(os.devnull, 'w') as devnull:
make_q_ret = subprocess.call(['make', '-q', 'lib', 'tests'],
stdout=devnull, stderr=devnull)
if make_q_ret != 0:
subprocess.check_call(['make', 'RECORD_PSA_STATUS_COVERAGE_LOG=1'],
stdout=sys.stderr)
rebuilt = True
subprocess.check_call(['make', 'test'],
stdout=sys.stderr)
data = Statuses()
data.collect_log(options.log_file)
data.get_constant_names(options.psa_constant_names)
if rebuilt and options.clean_after:
subprocess.check_call(['make', 'clean'],
cwd='tests',
stdout=sys.stderr)
return data
def main():
parser = argparse.ArgumentParser(description=globals()['__doc__'])
parser.add_argument('--clean-after',
action='store_true',
help='Run "make clean" after rebuilding')
parser.add_argument('--clean-before',
action='store_true',
help='Run "make clean" before regenerating the log file)')
parser.add_argument('--log-file', metavar='FILE',
default=DEFAULT_STATUS_LOG_FILE,
help='Log file location (default: {})'.format(
DEFAULT_STATUS_LOG_FILE
))
parser.add_argument('--psa-constant-names', metavar='PROGRAM',
default=DEFAULT_PSA_CONSTANT_NAMES,
help='Path to psa_constant_names (default: {})'.format(
DEFAULT_PSA_CONSTANT_NAMES
))
parser.add_argument('--use-existing-log', '-e',
action='store_true',
help='Don\'t regenerate the log file if it exists')
options = parser.parse_args()
data = collect_status_logs(options)
data.report()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,61 @@
#!/usr/bin/env perl
# Find functions making recursive calls to themselves.
# (Multiple recursion where a() calls b() which calls a() not covered.)
#
# When the recursion depth might depend on data controlled by the attacker in
# an unbounded way, those functions should use interation instead.
#
# Typical usage: scripts/recursion.pl library/*.c
#
# Copyright (C) 2014-2015, Arm Limited, All Rights Reserved
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This file is part of Mbed TLS (https://tls.mbed.org)
use warnings;
use strict;
use utf8;
use open qw(:std utf8);
# exclude functions that are ok:
# - mpi_write_hlp: bounded by size of mbedtls_mpi, a compile-time constant
# - x509_crt_verify_child: bounded by MBEDTLS_X509_MAX_INTERMEDIATE_CA
my $known_ok = qr/mpi_write_hlp|x509_crt_verify_child/;
my $cur_name;
my $inside;
my @funcs;
die "Usage: $0 file.c [...]\n" unless @ARGV;
while (<>)
{
if( /^[^\/#{}\s]/ && ! /\[.*]/ ) {
chomp( $cur_name = $_ ) unless $inside;
} elsif( /^{/ && $cur_name ) {
$inside = 1;
$cur_name =~ s/.* ([^ ]*)\(.*/$1/;
} elsif( /^}/ && $inside ) {
undef $inside;
undef $cur_name;
} elsif( $inside && /\b\Q$cur_name\E\([^)]/ ) {
push @funcs, $cur_name unless /$known_ok/;
}
}
print "$_\n" for @funcs;
exit @funcs;

View File

@@ -0,0 +1,165 @@
#!/usr/bin/env perl
# run-test-suites.pl
#
# Copyright (c) 2015-2018, ARM Limited, All Rights Reserved
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This file is part of Mbed TLS (https://tls.mbed.org)
=head1 SYNOPSIS
Execute all the test suites and print a summary of the results.
run-test-suites.pl [[-v|--verbose] [VERBOSITY]] [--skip=SUITE[...]]
Options:
-v|--verbose Print detailed failure information.
-v 2|--verbose=2 Print detailed failure information and summary messages.
-v 3|--verbose=3 Print detailed information about every test case.
--skip=SUITE[,SUITE...]
Skip the specified SUITE(s). This option can be used
multiple times.
=cut
use warnings;
use strict;
use utf8;
use open qw(:std utf8);
use Getopt::Long qw(:config auto_help gnu_compat);
use Pod::Usage;
my $verbose = 0;
my @skip_patterns = ();
GetOptions(
'skip=s' => \@skip_patterns,
'verbose|v:1' => \$verbose,
) or die;
# All test suites = executable files, excluding source files, debug
# and profiling information, etc. We can't just grep {! /\./} because
# some of our test cases' base names contain a dot.
my @suites = grep { -x $_ || /\.exe$/ } glob 'test_suite_*';
@suites = grep { !/\.c$/ && !/\.data$/ && -f } @suites;
die "$0: no test suite found\n" unless @suites;
# "foo" as a skip pattern skips "test_suite_foo" and "test_suite_foo.bar"
# but not "test_suite_foobar".
my $skip_re =
( '\Atest_suite_(' .
join('|', map {
s/[ ,;]/|/g; # allow any of " ,;|" as separators
s/\./\./g; # "." in the input means ".", not "any character"
$_
} @skip_patterns) .
')(\z|\.)' );
# in case test suites are linked dynamically
$ENV{'LD_LIBRARY_PATH'} = '../library';
$ENV{'DYLD_LIBRARY_PATH'} = '../library';
my $prefix = $^O eq "MSWin32" ? '' : './';
my ($failed_suites, $total_tests_run, $failed, $suite_cases_passed,
$suite_cases_failed, $suite_cases_skipped, $total_cases_passed,
$total_cases_failed, $total_cases_skipped );
my $suites_skipped = 0;
sub pad_print_center {
my( $width, $padchar, $string ) = @_;
my $padlen = ( $width - length( $string ) - 2 ) / 2;
print $padchar x( $padlen ), " $string ", $padchar x( $padlen ), "\n";
}
for my $suite (@suites)
{
print "$suite ", "." x ( 72 - length($suite) - 2 - 4 ), " ";
if( $suite =~ /$skip_re/o ) {
print "SKIP\n";
++$suites_skipped;
next;
}
my $command = "$prefix$suite";
if( $verbose ) {
$command .= ' -v';
}
my $result = `$command`;
$suite_cases_passed = () = $result =~ /.. PASS/g;
$suite_cases_failed = () = $result =~ /.. FAILED/g;
$suite_cases_skipped = () = $result =~ /.. ----/g;
if( $? == 0 ) {
print "PASS\n";
if( $verbose > 2 ) {
pad_print_center( 72, '-', "Begin $suite" );
print $result;
pad_print_center( 72, '-', "End $suite" );
}
} else {
$failed_suites++;
print "FAIL\n";
if( $verbose ) {
pad_print_center( 72, '-', "Begin $suite" );
print $result;
pad_print_center( 72, '-', "End $suite" );
}
}
my ($passed, $tests, $skipped) = $result =~ /([0-9]*) \/ ([0-9]*) tests.*?([0-9]*) skipped/;
$total_tests_run += $tests - $skipped;
if( $verbose > 1 ) {
print "(test cases passed:", $suite_cases_passed,
" failed:", $suite_cases_failed,
" skipped:", $suite_cases_skipped,
" of total:", ($suite_cases_passed + $suite_cases_failed +
$suite_cases_skipped),
")\n"
}
$total_cases_passed += $suite_cases_passed;
$total_cases_failed += $suite_cases_failed;
$total_cases_skipped += $suite_cases_skipped;
}
print "-" x 72, "\n";
print $failed_suites ? "FAILED" : "PASSED";
printf( " (%d suites, %d tests run%s)\n",
scalar(@suites) - $suites_skipped,
$total_tests_run,
$suites_skipped ? ", $suites_skipped suites skipped" : "" );
if( $verbose > 1 ) {
print " test cases passed :", $total_cases_passed, "\n";
print " failed :", $total_cases_failed, "\n";
print " skipped :", $total_cases_skipped, "\n";
print " of tests executed :", ( $total_cases_passed + $total_cases_failed ),
"\n";
print " of available tests :",
( $total_cases_passed + $total_cases_failed + $total_cases_skipped ),
"\n";
if( $suites_skipped != 0 ) {
print "Note: $suites_skipped suites were skipped.\n";
}
}
exit( $failed_suites ? 1 : 0 );

View File

@@ -0,0 +1,103 @@
#!/usr/bin/env perl
# A simple TCP client that sends some data and expects a response.
# Usage: tcp_client.pl HOSTNAME PORT DATA1 RESPONSE1
# DATA: hex-encoded data to send to the server
# RESPONSE: regexp that must match the server's response
#
# Copyright (C) 2017, Arm Limited, All Rights Reserved
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This file is part of Mbed TLS (https://tls.mbed.org)
use warnings;
use strict;
use IO::Socket::INET;
# Pack hex digits into a binary string, ignoring whitespace.
sub parse_hex {
my ($hex) = @_;
$hex =~ s/\s+//g;
return pack('H*', $hex);
}
## Open a TCP connection to the specified host and port.
sub open_connection {
my ($host, $port) = @_;
my $socket = IO::Socket::INET->new(PeerAddr => $host,
PeerPort => $port,
Proto => 'tcp',
Timeout => 1);
die "Cannot connect to $host:$port: $!" unless $socket;
return $socket;
}
## Close the TCP connection.
sub close_connection {
my ($connection) = @_;
$connection->shutdown(2);
# Ignore shutdown failures (at least for now)
return 1;
}
## Write the given data, expressed as hexadecimal
sub write_data {
my ($connection, $hexdata) = @_;
my $data = parse_hex($hexdata);
my $total_sent = 0;
while ($total_sent < length($data)) {
my $sent = $connection->send($data, 0);
if (!defined $sent) {
die "Unable to send data: $!";
}
$total_sent += $sent;
}
return 1;
}
## Read a response and check it against an expected prefix
sub read_response {
my ($connection, $expected_hex) = @_;
my $expected_data = parse_hex($expected_hex);
my $start_offset = 0;
while ($start_offset < length($expected_data)) {
my $actual_data;
my $ok = $connection->recv($actual_data, length($expected_data));
if (!defined $ok) {
die "Unable to receive data: $!";
}
if (($actual_data ^ substr($expected_data, $start_offset)) =~ /[^\000]/) {
printf STDERR ("Received \\x%02x instead of \\x%02x at offset %d\n",
ord(substr($actual_data, $-[0], 1)),
ord(substr($expected_data, $start_offset + $-[0], 1)),
$start_offset + $-[0]);
return 0;
}
$start_offset += length($actual_data);
}
return 1;
}
if (@ARGV != 4) {
print STDERR "Usage: $0 HOSTNAME PORT DATA1 RESPONSE1\n";
exit(3);
}
my ($host, $port, $data1, $response1) = @ARGV;
my $connection = open_connection($host, $port);
write_data($connection, $data1);
if (!read_response($connection, $response1)) {
exit(1);
}
close_connection($connection);

View File

@@ -0,0 +1,127 @@
#!/usr/bin/env perl
# test-ref-configs.pl
#
# Copyright (c) 2013-2016, ARM Limited, All Rights Reserved
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This file is part of Mbed TLS (https://tls.mbed.org)
#
# Purpose
#
# For each reference configuration file in the configs directory, build the
# configuration, run the test suites and compat.sh
#
# Usage: tests/scripts/test-ref-configs.pl [config-name [...]]
use warnings;
use strict;
my %configs = (
'config-mini-tls1_1.h' => {
'compat' => '-m tls1_1 -f \'^DES-CBC3-SHA$\|^TLS-RSA-WITH-3DES-EDE-CBC-SHA$\'', #'
},
'config-suite-b.h' => {
'compat' => "-m tls1_2 -f 'ECDHE-ECDSA.*AES.*GCM' -p mbedTLS",
},
'config-symmetric-only.h' => {
},
'config-ccm-psk-tls1_2.h' => {
'compat' => '-m tls1_2 -f \'^TLS-PSK-WITH-AES-...-CCM-8\'',
},
'config-thread.h' => {
'opt' => '-f ECJPAKE.*nolog',
},
);
# If no config-name is provided, use all known configs.
# Otherwise, use the provided names only.
if ($#ARGV >= 0) {
my %configs_ori = ( %configs );
%configs = ();
foreach my $conf_name (@ARGV) {
if( ! exists $configs_ori{$conf_name} ) {
die "Unknown configuration: $conf_name\n";
} else {
$configs{$conf_name} = $configs_ori{$conf_name};
}
}
}
-d 'library' && -d 'include' && -d 'tests' or die "Must be run from root\n";
my $config_h = 'include/mbedtls/config.h';
system( "cp $config_h $config_h.bak" ) and die;
sub abort {
system( "mv $config_h.bak $config_h" ) and warn "$config_h not restored\n";
# use an exit code between 1 and 124 for git bisect (die returns 255)
warn $_[0];
exit 1;
}
# Create a seedfile for configurations that enable MBEDTLS_ENTROPY_NV_SEED.
# For test purposes, this doesn't have to be cryptographically random.
if (!-e "tests/seedfile" || -s "tests/seedfile" < 64) {
local *SEEDFILE;
open SEEDFILE, ">tests/seedfile" or die;
print SEEDFILE "*" x 64 or die;
close SEEDFILE or die;
}
while( my ($conf, $data) = each %configs ) {
system( "cp $config_h.bak $config_h" ) and die;
system( "make clean" ) and die;
print "\n******************************************\n";
print "* Testing configuration: $conf\n";
print "******************************************\n";
$ENV{MBEDTLS_TEST_CONFIGURATION} = $conf;
system( "cp configs/$conf $config_h" )
and abort "Failed to activate $conf\n";
system( "CFLAGS='-Os -Werror -Wall -Wextra' make" ) and abort "Failed to build: $conf\n";
system( "make test" ) and abort "Failed test suite: $conf\n";
my $compat = $data->{'compat'};
if( $compat )
{
print "\nrunning compat.sh $compat\n";
system( "tests/compat.sh $compat" )
and abort "Failed compat.sh: $conf\n";
}
else
{
print "\nskipping compat.sh\n";
}
my $opt = $data->{'opt'};
if( $opt )
{
print "\nrunning ssl-opt.sh $opt\n";
system( "tests/ssl-opt.sh $opt" )
and abort "Failed ssl-opt.sh: $conf\n";
}
else
{
print "\nskipping ssl-opt.sh\n";
}
}
system( "mv $config_h.bak $config_h" ) and warn "$config_h not restored\n";
system( "make clean" );
exit 0;

View File

@@ -0,0 +1,188 @@
#!/usr/bin/env python3
"""Test helper for the Mbed TLS configuration file tool
Run config.py with various parameters and write the results to files.
This is a harness to help regression testing, not a functional tester.
Sample usage:
test_config_script.py -d old
## Modify config.py and/or config.h ##
test_config_script.py -d new
diff -ru old new
"""
## Copyright (C) 2019, ARM Limited, All Rights Reserved
## SPDX-License-Identifier: Apache-2.0
##
## Licensed under the Apache License, Version 2.0 (the "License"); you may
## not use this file except in compliance with the License.
## You may obtain a copy of the License at
##
## http://www.apache.org/licenses/LICENSE-2.0
##
## Unless required by applicable law or agreed to in writing, software
## distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
## WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
## See the License for the specific language governing permissions and
## limitations under the License.
##
## This file is part of Mbed TLS (https://tls.mbed.org)
import argparse
import glob
import os
import re
import shutil
import subprocess
OUTPUT_FILE_PREFIX = 'config-'
def output_file_name(directory, stem, extension):
return os.path.join(directory,
'{}{}.{}'.format(OUTPUT_FILE_PREFIX,
stem, extension))
def cleanup_directory(directory):
"""Remove old output files."""
for extension in []:
pattern = output_file_name(directory, '*', extension)
filenames = glob.glob(pattern)
for filename in filenames:
os.remove(filename)
def prepare_directory(directory):
"""Create the output directory if it doesn't exist yet.
If there are old output files, remove them.
"""
if os.path.exists(directory):
cleanup_directory(directory)
else:
os.makedirs(directory)
def guess_presets_from_help(help_text):
"""Figure out what presets the script supports.
help_text should be the output from running the script with --help.
"""
# Try the output format from config.py
hits = re.findall(r'\{([-\w,]+)\}', help_text)
for hit in hits:
words = set(hit.split(','))
if 'get' in words and 'set' in words and 'unset' in words:
words.remove('get')
words.remove('set')
words.remove('unset')
return words
# Try the output format from config.pl
hits = re.findall(r'\n +([-\w]+) +- ', help_text)
if hits:
return hits
raise Exception("Unable to figure out supported presets. Pass the '-p' option.")
def list_presets(options):
"""Return the list of presets to test.
The list is taken from the command line if present, otherwise it is
extracted from running the config script with --help.
"""
if options.presets:
return re.split(r'[ ,]+', options.presets)
else:
help_text = subprocess.run([options.script, '--help'],
check=False, # config.pl --help returns 255
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT).stdout
return guess_presets_from_help(help_text.decode('ascii'))
def run_one(options, args, stem_prefix='', input_file=None):
"""Run the config script with the given arguments.
Take the original content from input_file if specified, defaulting
to options.input_file if input_file is None.
Write the following files, where xxx contains stem_prefix followed by
a filename-friendly encoding of args:
* config-xxx.h: modified file.
* config-xxx.out: standard output.
* config-xxx.err: standard output.
* config-xxx.status: exit code.
Return ("xxx+", "path/to/config-xxx.h") which can be used as
stem_prefix and input_file to call this function again with new args.
"""
if input_file is None:
input_file = options.input_file
stem = stem_prefix + '-'.join(args)
data_filename = output_file_name(options.output_directory, stem, 'h')
stdout_filename = output_file_name(options.output_directory, stem, 'out')
stderr_filename = output_file_name(options.output_directory, stem, 'err')
status_filename = output_file_name(options.output_directory, stem, 'status')
shutil.copy(input_file, data_filename)
# Pass only the file basename, not the full path, to avoid getting the
# directory name in error messages, which would make comparisons
# between output directories more difficult.
cmd = [os.path.abspath(options.script),
'-f', os.path.basename(data_filename)]
with open(stdout_filename, 'wb') as out:
with open(stderr_filename, 'wb') as err:
status = subprocess.call(cmd + args,
cwd=options.output_directory,
stdin=subprocess.DEVNULL,
stdout=out, stderr=err)
with open(status_filename, 'w') as status_file:
status_file.write('{}\n'.format(status))
return stem + "+", data_filename
### A list of symbols to test with.
### This script currently tests what happens when you change a symbol from
### having a value to not having a value or vice versa. This is not
### necessarily useful behavior, and we may not consider it a bug if
### config.py stops handling that case correctly.
TEST_SYMBOLS = [
'CUSTOM_SYMBOL', # does not exist
'MBEDTLS_AES_C', # set, no value
'MBEDTLS_MPI_MAX_SIZE', # unset, has a value
'MBEDTLS_NO_UDBL_DIVISION', # unset, in "System support"
'MBEDTLS_PLATFORM_ZEROIZE_ALT', # unset, in "Customisation configuration options"
]
def run_all(options):
"""Run all the command lines to test."""
presets = list_presets(options)
for preset in presets:
run_one(options, [preset])
for symbol in TEST_SYMBOLS:
run_one(options, ['get', symbol])
(stem, filename) = run_one(options, ['set', symbol])
run_one(options, ['get', symbol], stem_prefix=stem, input_file=filename)
run_one(options, ['--force', 'set', symbol])
(stem, filename) = run_one(options, ['set', symbol, 'value'])
run_one(options, ['get', symbol], stem_prefix=stem, input_file=filename)
run_one(options, ['--force', 'set', symbol, 'value'])
run_one(options, ['unset', symbol])
def main():
"""Command line entry point."""
parser = argparse.ArgumentParser(description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument('-d', metavar='DIR',
dest='output_directory', required=True,
help="""Output directory.""")
parser.add_argument('-f', metavar='FILE',
dest='input_file', default='include/mbedtls/config.h',
help="""Config file (default: %(default)s).""")
parser.add_argument('-p', metavar='PRESET,...',
dest='presets',
help="""Presets to test (default: guessed from --help).""")
parser.add_argument('-s', metavar='FILE',
dest='script', default='scripts/config.py',
help="""Configuration script (default: %(default)s).""")
options = parser.parse_args()
prepare_directory(options.output_directory)
run_all(options)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,476 @@
#!/usr/bin/env python3
"""Test the program psa_constant_names.
Gather constant names from header files and test cases. Compile a C program
to print out their numerical values, feed these numerical values to
psa_constant_names, and check that the output is the original name.
Return 0 if all test cases pass, 1 if the output was not always as expected,
or 1 (with a Python backtrace) if there was an operational error.
"""
# Copyright (C) 2018-2020, Arm Limited, All Rights Reserved
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This file is part of Mbed TLS (https://tls.mbed.org)
import argparse
from collections import namedtuple
import itertools
import os
import platform
import re
import subprocess
import sys
import tempfile
class ReadFileLineException(Exception):
def __init__(self, filename, line_number):
message = 'in {} at {}'.format(filename, line_number)
super(ReadFileLineException, self).__init__(message)
self.filename = filename
self.line_number = line_number
class read_file_lines:
# Dear Pylint, conventionally, a context manager class name is lowercase.
# pylint: disable=invalid-name,too-few-public-methods
"""Context manager to read a text file line by line.
```
with read_file_lines(filename) as lines:
for line in lines:
process(line)
```
is equivalent to
```
with open(filename, 'r') as input_file:
for line in input_file:
process(line)
```
except that if process(line) raises an exception, then the read_file_lines
snippet annotates the exception with the file name and line number.
"""
def __init__(self, filename, binary=False):
self.filename = filename
self.line_number = 'entry'
self.generator = None
self.binary = binary
def __enter__(self):
self.generator = enumerate(open(self.filename,
'rb' if self.binary else 'r'))
return self
def __iter__(self):
for line_number, content in self.generator:
self.line_number = line_number
yield content
self.line_number = 'exit'
def __exit__(self, exc_type, exc_value, exc_traceback):
if exc_type is not None:
raise ReadFileLineException(self.filename, self.line_number) \
from exc_value
class Inputs:
# pylint: disable=too-many-instance-attributes
"""Accumulate information about macros to test.
This includes macro names as well as information about their arguments
when applicable.
"""
def __init__(self):
self.all_declared = set()
# Sets of names per type
self.statuses = set(['PSA_SUCCESS'])
self.algorithms = set(['0xffffffff'])
self.ecc_curves = set(['0xff'])
self.dh_groups = set(['0xff'])
self.key_types = set(['0xffff'])
self.key_usage_flags = set(['0x80000000'])
# Hard-coded value for unknown algorithms
self.hash_algorithms = set(['0x010000fe'])
self.mac_algorithms = set(['0x02ff00ff'])
self.ka_algorithms = set(['0x30fc0000'])
self.kdf_algorithms = set(['0x200000ff'])
# For AEAD algorithms, the only variability is over the tag length,
# and this only applies to known algorithms, so don't test an
# unknown algorithm.
self.aead_algorithms = set()
# Identifier prefixes
self.table_by_prefix = {
'ERROR': self.statuses,
'ALG': self.algorithms,
'ECC_CURVE': self.ecc_curves,
'DH_GROUP': self.dh_groups,
'KEY_TYPE': self.key_types,
'KEY_USAGE': self.key_usage_flags,
}
# Test functions
self.table_by_test_function = {
# Any function ending in _algorithm also gets added to
# self.algorithms.
'key_type': [self.key_types],
'block_cipher_key_type': [self.key_types],
'stream_cipher_key_type': [self.key_types],
'ecc_key_family': [self.ecc_curves],
'ecc_key_types': [self.ecc_curves],
'dh_key_family': [self.dh_groups],
'dh_key_types': [self.dh_groups],
'hash_algorithm': [self.hash_algorithms],
'mac_algorithm': [self.mac_algorithms],
'cipher_algorithm': [],
'hmac_algorithm': [self.mac_algorithms],
'aead_algorithm': [self.aead_algorithms],
'key_derivation_algorithm': [self.kdf_algorithms],
'key_agreement_algorithm': [self.ka_algorithms],
'asymmetric_signature_algorithm': [],
'asymmetric_signature_wildcard': [self.algorithms],
'asymmetric_encryption_algorithm': [],
'other_algorithm': [],
}
# macro name -> list of argument names
self.argspecs = {}
# argument name -> list of values
self.arguments_for = {
'mac_length': ['1', '63'],
'tag_length': ['1', '63'],
}
def get_names(self, type_word):
"""Return the set of known names of values of the given type."""
return {
'status': self.statuses,
'algorithm': self.algorithms,
'ecc_curve': self.ecc_curves,
'dh_group': self.dh_groups,
'key_type': self.key_types,
'key_usage': self.key_usage_flags,
}[type_word]
def gather_arguments(self):
"""Populate the list of values for macro arguments.
Call this after parsing all the inputs.
"""
self.arguments_for['hash_alg'] = sorted(self.hash_algorithms)
self.arguments_for['mac_alg'] = sorted(self.mac_algorithms)
self.arguments_for['ka_alg'] = sorted(self.ka_algorithms)
self.arguments_for['kdf_alg'] = sorted(self.kdf_algorithms)
self.arguments_for['aead_alg'] = sorted(self.aead_algorithms)
self.arguments_for['curve'] = sorted(self.ecc_curves)
self.arguments_for['group'] = sorted(self.dh_groups)
@staticmethod
def _format_arguments(name, arguments):
"""Format a macro call with arguments.."""
return name + '(' + ', '.join(arguments) + ')'
def distribute_arguments(self, name):
"""Generate macro calls with each tested argument set.
If name is a macro without arguments, just yield "name".
If name is a macro with arguments, yield a series of
"name(arg1,...,argN)" where each argument takes each possible
value at least once.
"""
try:
if name not in self.argspecs:
yield name
return
argspec = self.argspecs[name]
if argspec == []:
yield name + '()'
return
argument_lists = [self.arguments_for[arg] for arg in argspec]
arguments = [values[0] for values in argument_lists]
yield self._format_arguments(name, arguments)
# Dear Pylint, enumerate won't work here since we're modifying
# the array.
# pylint: disable=consider-using-enumerate
for i in range(len(arguments)):
for value in argument_lists[i][1:]:
arguments[i] = value
yield self._format_arguments(name, arguments)
arguments[i] = argument_lists[0][0]
except BaseException as e:
raise Exception('distribute_arguments({})'.format(name)) from e
def generate_expressions(self, names):
return itertools.chain(*map(self.distribute_arguments, names))
_argument_split_re = re.compile(r' *, *')
@classmethod
def _argument_split(cls, arguments):
return re.split(cls._argument_split_re, arguments)
# Regex for interesting header lines.
# Groups: 1=macro name, 2=type, 3=argument list (optional).
_header_line_re = \
re.compile(r'#define +' +
r'(PSA_((?:(?:DH|ECC|KEY)_)?[A-Z]+)_\w+)' +
r'(?:\(([^\n()]*)\))?')
# Regex of macro names to exclude.
_excluded_name_re = re.compile(r'_(?:GET|IS|OF)_|_(?:BASE|FLAG|MASK)\Z')
# Additional excluded macros.
_excluded_names = set([
# Macros that provide an alternative way to build the same
# algorithm as another macro.
'PSA_ALG_AEAD_WITH_DEFAULT_TAG_LENGTH',
'PSA_ALG_FULL_LENGTH_MAC',
# Auxiliary macro whose name doesn't fit the usual patterns for
# auxiliary macros.
'PSA_ALG_AEAD_WITH_DEFAULT_TAG_LENGTH_CASE',
])
def parse_header_line(self, line):
"""Parse a C header line, looking for "#define PSA_xxx"."""
m = re.match(self._header_line_re, line)
if not m:
return
name = m.group(1)
self.all_declared.add(name)
if re.search(self._excluded_name_re, name) or \
name in self._excluded_names:
return
dest = self.table_by_prefix.get(m.group(2))
if dest is None:
return
dest.add(name)
if m.group(3):
self.argspecs[name] = self._argument_split(m.group(3))
_nonascii_re = re.compile(rb'[^\x00-\x7f]+')
def parse_header(self, filename):
"""Parse a C header file, looking for "#define PSA_xxx"."""
with read_file_lines(filename, binary=True) as lines:
for line in lines:
line = re.sub(self._nonascii_re, rb'', line).decode('ascii')
self.parse_header_line(line)
_macro_identifier_re = re.compile(r'[A-Z]\w+')
def generate_undeclared_names(self, expr):
for name in re.findall(self._macro_identifier_re, expr):
if name not in self.all_declared:
yield name
def accept_test_case_line(self, function, argument):
#pylint: disable=unused-argument
undeclared = list(self.generate_undeclared_names(argument))
if undeclared:
raise Exception('Undeclared names in test case', undeclared)
return True
def add_test_case_line(self, function, argument):
"""Parse a test case data line, looking for algorithm metadata tests."""
sets = []
if function.endswith('_algorithm'):
sets.append(self.algorithms)
if function == 'key_agreement_algorithm' and \
argument.startswith('PSA_ALG_KEY_AGREEMENT('):
# We only want *raw* key agreement algorithms as such, so
# exclude ones that are already chained with a KDF.
# Keep the expression as one to test as an algorithm.
function = 'other_algorithm'
sets += self.table_by_test_function[function]
if self.accept_test_case_line(function, argument):
for s in sets:
s.add(argument)
# Regex matching a *.data line containing a test function call and
# its arguments. The actual definition is partly positional, but this
# regex is good enough in practice.
_test_case_line_re = re.compile(r'(?!depends_on:)(\w+):([^\n :][^:\n]*)')
def parse_test_cases(self, filename):
"""Parse a test case file (*.data), looking for algorithm metadata tests."""
with read_file_lines(filename) as lines:
for line in lines:
m = re.match(self._test_case_line_re, line)
if m:
self.add_test_case_line(m.group(1), m.group(2))
def gather_inputs(headers, test_suites, inputs_class=Inputs):
"""Read the list of inputs to test psa_constant_names with."""
inputs = inputs_class()
for header in headers:
inputs.parse_header(header)
for test_cases in test_suites:
inputs.parse_test_cases(test_cases)
inputs.gather_arguments()
return inputs
def remove_file_if_exists(filename):
"""Remove the specified file, ignoring errors."""
if not filename:
return
try:
os.remove(filename)
except OSError:
pass
def run_c(type_word, expressions, include_path=None, keep_c=False):
"""Generate and run a program to print out numerical values for expressions."""
if include_path is None:
include_path = []
if type_word == 'status':
cast_to = 'long'
printf_format = '%ld'
else:
cast_to = 'unsigned long'
printf_format = '0x%08lx'
c_name = None
exe_name = None
try:
c_fd, c_name = tempfile.mkstemp(prefix='tmp-{}-'.format(type_word),
suffix='.c',
dir='programs/psa')
exe_suffix = '.exe' if platform.system() == 'Windows' else ''
exe_name = c_name[:-2] + exe_suffix
remove_file_if_exists(exe_name)
c_file = os.fdopen(c_fd, 'w', encoding='ascii')
c_file.write('/* Generated by test_psa_constant_names.py for {} values */'
.format(type_word))
c_file.write('''
#include <stdio.h>
#include <psa/crypto.h>
int main(void)
{
''')
for expr in expressions:
c_file.write(' printf("{}\\n", ({}) {});\n'
.format(printf_format, cast_to, expr))
c_file.write(''' return 0;
}
''')
c_file.close()
cc = os.getenv('CC', 'cc')
subprocess.check_call([cc] +
['-I' + dir for dir in include_path] +
['-o', exe_name, c_name])
if keep_c:
sys.stderr.write('List of {} tests kept at {}\n'
.format(type_word, c_name))
else:
os.remove(c_name)
output = subprocess.check_output([exe_name])
return output.decode('ascii').strip().split('\n')
finally:
remove_file_if_exists(exe_name)
NORMALIZE_STRIP_RE = re.compile(r'\s+')
def normalize(expr):
"""Normalize the C expression so as not to care about trivial differences.
Currently "trivial differences" means whitespace.
"""
return re.sub(NORMALIZE_STRIP_RE, '', expr)
def collect_values(inputs, type_word, include_path=None, keep_c=False):
"""Generate expressions using known macro names and calculate their values.
Return a list of pairs of (expr, value) where expr is an expression and
value is a string representation of its integer value.
"""
names = inputs.get_names(type_word)
expressions = sorted(inputs.generate_expressions(names))
values = run_c(type_word, expressions,
include_path=include_path, keep_c=keep_c)
return expressions, values
class Tests:
"""An object representing tests and their results."""
Error = namedtuple('Error',
['type', 'expression', 'value', 'output'])
def __init__(self, options):
self.options = options
self.count = 0
self.errors = []
def run_one(self, inputs, type_word):
"""Test psa_constant_names for the specified type.
Run the program on the names for this type.
Use the inputs to figure out what arguments to pass to macros that
take arguments.
"""
expressions, values = collect_values(inputs, type_word,
include_path=self.options.include,
keep_c=self.options.keep_c)
output = subprocess.check_output([self.options.program, type_word] +
values)
outputs = output.decode('ascii').strip().split('\n')
self.count += len(expressions)
for expr, value, output in zip(expressions, values, outputs):
if self.options.show:
sys.stdout.write('{} {}\t{}\n'.format(type_word, value, output))
if normalize(expr) != normalize(output):
self.errors.append(self.Error(type=type_word,
expression=expr,
value=value,
output=output))
def run_all(self, inputs):
"""Run psa_constant_names on all the gathered inputs."""
for type_word in ['status', 'algorithm', 'ecc_curve', 'dh_group',
'key_type', 'key_usage']:
self.run_one(inputs, type_word)
def report(self, out):
"""Describe each case where the output is not as expected.
Write the errors to ``out``.
Also write a total.
"""
for error in self.errors:
out.write('For {} "{}", got "{}" (value: {})\n'
.format(error.type, error.expression,
error.output, error.value))
out.write('{} test cases'.format(self.count))
if self.errors:
out.write(', {} FAIL\n'.format(len(self.errors)))
else:
out.write(' PASS\n')
HEADERS = ['psa/crypto.h', 'psa/crypto_extra.h', 'psa/crypto_values.h']
TEST_SUITES = ['tests/suites/test_suite_psa_crypto_metadata.data']
def main():
parser = argparse.ArgumentParser(description=globals()['__doc__'])
parser.add_argument('--include', '-I',
action='append', default=['include'],
help='Directory for header files')
parser.add_argument('--keep-c',
action='store_true', dest='keep_c', default=False,
help='Keep the intermediate C file')
parser.add_argument('--no-keep-c',
action='store_false', dest='keep_c',
help='Don\'t keep the intermediate C file (default)')
parser.add_argument('--program',
default='programs/psa/psa_constant_names',
help='Program to test')
parser.add_argument('--show',
action='store_true',
help='Keep the intermediate C file')
parser.add_argument('--no-show',
action='store_false', dest='show',
help='Don\'t show tested values (default)')
options = parser.parse_args()
headers = [os.path.join(options.include[0], h) for h in HEADERS]
inputs = gather_inputs(headers, TEST_SUITES)
tests = Tests(options)
tests.run_all(inputs)
tests.report(sys.stdout)
if tests.errors:
sys.exit(1)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,78 @@
# test_zeroize.gdb
#
# Copyright (c) 2018, Arm Limited, All Rights Reserved
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This file is part of Mbed TLS (https://tls.mbed.org)
#
# Purpose
#
# Run a test using the debugger to check that the mbedtls_platform_zeroize()
# function in platform_util.h is not being optimized out by the compiler. To do
# so, the script loads the test program at programs/test/zeroize.c and sets a
# breakpoint at the last return statement in main(). When the breakpoint is
# hit, the debugger manually checks the contents to be zeroized and checks that
# it is actually cleared.
#
# The mbedtls_platform_zeroize() test is debugger driven because there does not
# seem to be a mechanism to reliably check whether the zeroize calls are being
# eliminated by compiler optimizations from within the compiled program. The
# problem is that a compiler would typically remove what it considers to be
# "unnecessary" assignments as part of redundant code elimination. To identify
# such code, the compilar will create some form dependency graph between
# reads and writes to variables (among other situations). It will then use this
# data structure to remove redundant code that does not have an impact on the
# program's observable behavior. In the case of mbedtls_platform_zeroize(), an
# intelligent compiler could determine that this function clears a block of
# memory that is not accessed later in the program, so removing the call to
# mbedtls_platform_zeroize() does not have an observable behavior. However,
# inserting a test after a call to mbedtls_platform_zeroize() to check whether
# the block of memory was correctly zeroed would force the compiler to not
# eliminate the mbedtls_platform_zeroize() call. If this does not occur, then
# the compiler potentially has a bug.
#
# Note: This test requires that the test program is compiled with -g3.
set confirm off
file ./programs/test/zeroize
search GDB_BREAK_HERE
break $_
set args ./programs/test/zeroize.c
run
set $i = 0
set $len = sizeof(buf)
set $buf = buf
while $i < $len
if $buf[$i++] != 0
echo The buffer at was not zeroized\n
quit 1
end
end
echo The buffer was correctly zeroized\n
continue
if $_exitcode != 0
echo The program did not terminate correctly\n
quit 1
end
quit 0

View File

@@ -0,0 +1,49 @@
#!/bin/sh
# travis-log-failure.sh
#
# Copyright (c) 2016, ARM Limited, All Rights Reserved
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This file is part of Mbed TLS (https://tls.mbed.org)
#
# Purpose
#
# List the server and client logs on failed ssl-opt.sh and compat.sh tests.
# This script is used to make the logs show up in the Travis test results.
#
# Some of the logs can be very long: this means usually a couple of megabytes
# but it can be much more. For example, the client log of test 273 in ssl-opt.sh
# is more than 630 Megabytes long.
if [ -d include/mbedtls ]; then :; else
echo "$0: must be run from root" >&2
exit 1
fi
FILES="o-srv-*.log o-cli-*.log c-srv-*.log c-cli-*.log o-pxy-*.log"
MAX_LOG_SIZE=1048576
for PATTERN in $FILES; do
for LOG in $( ls tests/$PATTERN 2>/dev/null ); do
echo
echo "****** BEGIN file: $LOG ******"
echo
tail -c $MAX_LOG_SIZE $LOG
echo "****** END file: $LOG ******"
echo
rm $LOG
done
done