Merge branch 'development_3.0' into change_config_h_defaults
This commit is contained in:
commit
e66f49c3ce
341 changed files with 25642 additions and 20354 deletions
|
@ -51,9 +51,9 @@
|
|||
# * arm-gcc and mingw-gcc
|
||||
# * ArmCC 5 and ArmCC 6, unless invoked with --no-armcc
|
||||
# * OpenSSL and GnuTLS command line tools, recent enough for the
|
||||
# interoperability tests. If they don't support SSLv3 then a legacy
|
||||
# version of these tools must be present as well (search for LEGACY
|
||||
# below).
|
||||
# interoperability tests. If they don't support old features which we want
|
||||
# to test, then a legacy version of these tools must be present as well
|
||||
# (search for LEGACY below).
|
||||
# See the invocation of check_tools below for details.
|
||||
#
|
||||
# This script must be invoked from the toplevel directory of a git
|
||||
|
@ -254,7 +254,7 @@ Tool path options:
|
|||
--gnutls-legacy-cli=<GnuTLS_cli_path> GnuTLS client executable to use for legacy tests.
|
||||
--gnutls-legacy-serv=<GnuTLS_serv_path> GnuTLS server executable to use for legacy tests.
|
||||
--openssl=<OpenSSL_path> OpenSSL executable to use for most tests.
|
||||
--openssl-legacy=<OpenSSL_path> OpenSSL executable to use for legacy tests e.g. SSLv3.
|
||||
--openssl-legacy=<OpenSSL_path> OpenSSL executable to use for legacy tests..
|
||||
--openssl-next=<OpenSSL_path> OpenSSL executable to use for recent things like ARIA
|
||||
EOF
|
||||
}
|
||||
|
@ -798,43 +798,15 @@ component_test_psa_crypto_key_id_encodes_owner () {
|
|||
make test
|
||||
}
|
||||
|
||||
component_test_zlib_make() {
|
||||
msg "build: zlib enabled, make"
|
||||
scripts/config.py set MBEDTLS_ZLIB_SUPPORT
|
||||
make ZLIB=1 CFLAGS='-Werror -O1'
|
||||
|
||||
msg "test: main suites (zlib, make)"
|
||||
make test
|
||||
|
||||
msg "test: ssl-opt.sh (zlib, make)"
|
||||
if_build_succeeded tests/ssl-opt.sh
|
||||
}
|
||||
support_test_zlib_make () {
|
||||
base=support_test_zlib_$$
|
||||
cat <<'EOF' > ${base}.c
|
||||
#include "zlib.h"
|
||||
int main(void) { return 0; }
|
||||
EOF
|
||||
gcc -o ${base}.exe ${base}.c -lz 2>/dev/null
|
||||
ret=$?
|
||||
rm -f ${base}.*
|
||||
return $ret
|
||||
}
|
||||
|
||||
component_test_zlib_cmake() {
|
||||
msg "build: zlib enabled, cmake"
|
||||
scripts/config.py set MBEDTLS_ZLIB_SUPPORT
|
||||
cmake -D ENABLE_ZLIB_SUPPORT=On -D CMAKE_BUILD_TYPE:String=Check .
|
||||
component_test_psa_crypto_client () {
|
||||
msg "build: default config - PSA_CRYPTO_C + PSA_CRYPTO_CLIENT, make"
|
||||
scripts/config.py unset MBEDTLS_PSA_CRYPTO_C
|
||||
scripts/config.py unset MBEDTLS_PSA_CRYPTO_STORAGE_C
|
||||
scripts/config.py set MBEDTLS_PSA_CRYPTO_CLIENT
|
||||
make
|
||||
|
||||
msg "test: main suites (zlib, cmake)"
|
||||
msg "test: default config - PSA_CRYPTO_C + PSA_CRYPTO_CLIENT, make"
|
||||
make test
|
||||
|
||||
msg "test: ssl-opt.sh (zlib, cmake)"
|
||||
if_build_succeeded tests/ssl-opt.sh
|
||||
}
|
||||
support_test_zlib_cmake () {
|
||||
support_test_zlib_make "$@"
|
||||
}
|
||||
|
||||
component_test_ref_configs () {
|
||||
|
@ -843,26 +815,6 @@ component_test_ref_configs () {
|
|||
record_status tests/scripts/test-ref-configs.pl
|
||||
}
|
||||
|
||||
component_test_sslv3 () {
|
||||
msg "build: Default + SSLv3 (ASan build)" # ~ 6 min
|
||||
scripts/config.py set MBEDTLS_SSL_PROTO_SSL3
|
||||
CC=gcc cmake -D CMAKE_BUILD_TYPE:String=Asan .
|
||||
make
|
||||
|
||||
msg "test: SSLv3 - main suites (inc. selftests) (ASan build)" # ~ 50s
|
||||
make test
|
||||
|
||||
msg "build: SSLv3 - compat.sh (ASan build)" # ~ 6 min
|
||||
if_build_succeeded tests/compat.sh -m 'tls1 tls1_1 tls1_2 dtls1 dtls1_2'
|
||||
if_build_succeeded env OPENSSL_CMD="$OPENSSL_LEGACY" tests/compat.sh -m 'ssl3'
|
||||
|
||||
msg "build: SSLv3 - ssl-opt.sh (ASan build)" # ~ 6 min
|
||||
if_build_succeeded tests/ssl-opt.sh
|
||||
|
||||
msg "build: SSLv3 - context-info.sh (ASan build)" # ~ 15 sec
|
||||
if_build_succeeded tests/context-info.sh
|
||||
}
|
||||
|
||||
component_test_no_renegotiation () {
|
||||
msg "build: Default + !MBEDTLS_SSL_RENEGOTIATION (ASan build)" # ~ 6 min
|
||||
scripts/config.py unset MBEDTLS_SSL_RENEGOTIATION
|
||||
|
@ -911,50 +863,146 @@ component_test_rsa_no_crt () {
|
|||
if_build_succeeded tests/context-info.sh
|
||||
}
|
||||
|
||||
component_test_no_ctr_drbg () {
|
||||
msg "build: Full minus CTR_DRBG"
|
||||
component_test_no_ctr_drbg_classic () {
|
||||
msg "build: Full minus CTR_DRBG, classic crypto in TLS"
|
||||
scripts/config.py full
|
||||
scripts/config.py unset MBEDTLS_CTR_DRBG_C
|
||||
scripts/config.py unset MBEDTLS_USE_PSA_CRYPTO
|
||||
|
||||
CC=gcc cmake -D CMAKE_BUILD_TYPE:String=Asan .
|
||||
make
|
||||
|
||||
msg "test: no CTR_DRBG"
|
||||
msg "test: Full minus CTR_DRBG, classic crypto - main suites"
|
||||
make test
|
||||
|
||||
# no ssl-opt.sh/compat.sh as they all depend on CTR_DRBG so far
|
||||
# In this configuration, the TLS test programs use HMAC_DRBG.
|
||||
# The SSL tests are slow, so run a small subset, just enough to get
|
||||
# confidence that the SSL code copes with HMAC_DRBG.
|
||||
msg "test: Full minus CTR_DRBG, classic crypto - ssl-opt.sh (subset)"
|
||||
if_build_succeeded tests/ssl-opt.sh -f 'Default\|SSL async private.*delay=\|tickets enabled on server'
|
||||
|
||||
msg "test: Full minus CTR_DRBG, classic crypto - compat.sh (subset)"
|
||||
if_build_succeeded tests/compat.sh -m tls1_2 -t 'ECDSA PSK' -V NO -p OpenSSL
|
||||
}
|
||||
|
||||
component_test_no_hmac_drbg () {
|
||||
msg "build: Full minus HMAC_DRBG"
|
||||
component_test_no_ctr_drbg_use_psa () {
|
||||
msg "build: Full minus CTR_DRBG, PSA crypto in TLS"
|
||||
scripts/config.py full
|
||||
scripts/config.py unset MBEDTLS_CTR_DRBG_C
|
||||
scripts/config.py set MBEDTLS_USE_PSA_CRYPTO
|
||||
|
||||
CC=gcc cmake -D CMAKE_BUILD_TYPE:String=Asan .
|
||||
make
|
||||
|
||||
msg "test: Full minus CTR_DRBG, USE_PSA_CRYPTO - main suites"
|
||||
make test
|
||||
|
||||
# In this configuration, the TLS test programs use HMAC_DRBG.
|
||||
# The SSL tests are slow, so run a small subset, just enough to get
|
||||
# confidence that the SSL code copes with HMAC_DRBG.
|
||||
msg "test: Full minus CTR_DRBG, USE_PSA_CRYPTO - ssl-opt.sh (subset)"
|
||||
if_build_succeeded tests/ssl-opt.sh -f 'Default\|SSL async private.*delay=\|tickets enabled on server'
|
||||
|
||||
msg "test: Full minus CTR_DRBG, USE_PSA_CRYPTO - compat.sh (subset)"
|
||||
if_build_succeeded tests/compat.sh -m tls1_2 -t 'ECDSA PSK' -V NO -p OpenSSL
|
||||
}
|
||||
|
||||
component_test_no_hmac_drbg_classic () {
|
||||
msg "build: Full minus HMAC_DRBG, classic crypto in TLS"
|
||||
scripts/config.py full
|
||||
scripts/config.py unset MBEDTLS_HMAC_DRBG_C
|
||||
scripts/config.py unset MBEDTLS_ECDSA_DETERMINISTIC # requires HMAC_DRBG
|
||||
scripts/config.py unset MBEDTLS_USE_PSA_CRYPTO
|
||||
|
||||
CC=gcc cmake -D CMAKE_BUILD_TYPE:String=Asan .
|
||||
make
|
||||
|
||||
msg "test: no HMAC_DRBG"
|
||||
msg "test: Full minus HMAC_DRBG, classic crypto - main suites"
|
||||
make test
|
||||
|
||||
# No ssl-opt.sh/compat.sh as they never use HMAC_DRBG so far,
|
||||
# so there's little value in running those lengthy tests here.
|
||||
# Normally our ECDSA implementation uses deterministic ECDSA. But since
|
||||
# HMAC_DRBG is disabled in this configuration, randomized ECDSA is used
|
||||
# instead.
|
||||
# Test SSL with non-deterministic ECDSA. Only test features that
|
||||
# might be affected by how ECDSA signature is performed.
|
||||
msg "test: Full minus HMAC_DRBG, classic crypto - ssl-opt.sh (subset)"
|
||||
if_build_succeeded tests/ssl-opt.sh -f 'Default\|SSL async private: sign'
|
||||
|
||||
# To save time, only test one protocol version, since this part of
|
||||
# the protocol is identical in (D)TLS up to 1.2.
|
||||
msg "test: Full minus HMAC_DRBG, classic crypto - compat.sh (ECDSA)"
|
||||
if_build_succeeded tests/compat.sh -m tls1_2 -t 'ECDSA'
|
||||
}
|
||||
|
||||
component_test_psa_external_rng_no_drbg () {
|
||||
msg "build: PSA_CRYPTO_EXTERNAL_RNG minus *_DRBG"
|
||||
component_test_no_hmac_drbg_use_psa () {
|
||||
msg "build: Full minus HMAC_DRBG, PSA crypto in TLS"
|
||||
scripts/config.py full
|
||||
scripts/config.py unset MBEDTLS_HMAC_DRBG_C
|
||||
scripts/config.py unset MBEDTLS_ECDSA_DETERMINISTIC # requires HMAC_DRBG
|
||||
scripts/config.py set MBEDTLS_USE_PSA_CRYPTO
|
||||
|
||||
CC=gcc cmake -D CMAKE_BUILD_TYPE:String=Asan .
|
||||
make
|
||||
|
||||
msg "test: Full minus HMAC_DRBG, USE_PSA_CRYPTO - main suites"
|
||||
make test
|
||||
|
||||
# Normally our ECDSA implementation uses deterministic ECDSA. But since
|
||||
# HMAC_DRBG is disabled in this configuration, randomized ECDSA is used
|
||||
# instead.
|
||||
# Test SSL with non-deterministic ECDSA. Only test features that
|
||||
# might be affected by how ECDSA signature is performed.
|
||||
msg "test: Full minus HMAC_DRBG, USE_PSA_CRYPTO - ssl-opt.sh (subset)"
|
||||
if_build_succeeded tests/ssl-opt.sh -f 'Default\|SSL async private: sign'
|
||||
|
||||
# To save time, only test one protocol version, since this part of
|
||||
# the protocol is identical in (D)TLS up to 1.2.
|
||||
msg "test: Full minus HMAC_DRBG, USE_PSA_CRYPTO - compat.sh (ECDSA)"
|
||||
if_build_succeeded tests/compat.sh -m tls1_2 -t 'ECDSA'
|
||||
}
|
||||
|
||||
component_test_psa_external_rng_no_drbg_classic () {
|
||||
msg "build: PSA_CRYPTO_EXTERNAL_RNG minus *_DRBG, classic crypto in TLS"
|
||||
scripts/config.py full
|
||||
scripts/config.py unset MBEDTLS_USE_PSA_CRYPTO
|
||||
scripts/config.py set MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG
|
||||
scripts/config.py unset MBEDTLS_ENTROPY_C
|
||||
scripts/config.py unset MBEDTLS_ENTROPY_NV_SEED
|
||||
scripts/config.py unset MBEDTLS_PLATFORM_NV_SEED_ALT
|
||||
scripts/config.py unset MBEDTLS_CTR_DRBG_C
|
||||
scripts/config.py unset MBEDTLS_HMAC_DRBG_C
|
||||
scripts/config.py unset MBEDTLS_ECDSA_DETERMINISTIC # requires HMAC_DRBG
|
||||
scripts/config.py set MBEDTLS_ECP_NO_INTERNAL_RNG
|
||||
# When MBEDTLS_USE_PSA_CRYPTO is disabled and there is no DRBG,
|
||||
# the SSL test programs don't have an RNG and can't work. Explicitly
|
||||
# make them use the PSA RNG with -DMBEDTLS_TEST_USE_PSA_CRYPTO_RNG.
|
||||
make CFLAGS="$ASAN_CFLAGS -O2 -DMBEDTLS_TEST_USE_PSA_CRYPTO_RNG" LDFLAGS="$ASAN_CFLAGS"
|
||||
|
||||
msg "test: PSA_CRYPTO_EXTERNAL_RNG minus *_DRBG, classic crypto - main suites"
|
||||
make test
|
||||
|
||||
msg "test: PSA_CRYPTO_EXTERNAL_RNG minus *_DRBG, classic crypto - ssl-opt.sh (subset)"
|
||||
if_build_succeeded tests/ssl-opt.sh -f 'Default'
|
||||
}
|
||||
|
||||
component_test_psa_external_rng_no_drbg_use_psa () {
|
||||
msg "build: PSA_CRYPTO_EXTERNAL_RNG minus *_DRBG, PSA crypto in TLS"
|
||||
scripts/config.py full
|
||||
scripts/config.py set MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG
|
||||
scripts/config.py unset MBEDTLS_ENTROPY_C
|
||||
scripts/config.py unset MBEDTLS_ENTROPY_NV_SEED
|
||||
scripts/config.py unset MBEDTLS_PLATFORM_NV_SEED_ALT
|
||||
scripts/config.py unset MBEDTLS_CTR_DRBG_C
|
||||
scripts/config.py unset MBEDTLS_HMAC_DRBG_C
|
||||
scripts/config.py unset MBEDTLS_ECDSA_DETERMINISTIC # requires HMAC_DRBG
|
||||
scripts/config.py set MBEDTLS_ECP_NO_INTERNAL_RNG
|
||||
make CFLAGS="$ASAN_CFLAGS -O2" LDFLAGS="$ASAN_CFLAGS"
|
||||
|
||||
msg "test: PSA_CRYPTO_EXTERNAL_RNG minus *_DRBG"
|
||||
msg "test: PSA_CRYPTO_EXTERNAL_RNG minus *_DRBG, PSA crypto - main suites"
|
||||
make test
|
||||
|
||||
# No ssl-opt.sh/compat.sh because they require CTR_DRBG.
|
||||
msg "test: PSA_CRYPTO_EXTERNAL_RNG minus *_DRBG, PSA crypto - ssl-opt.sh (subset)"
|
||||
if_build_succeeded tests/ssl-opt.sh -f 'Default\|opaque'
|
||||
}
|
||||
|
||||
component_test_psa_external_rng_use_psa_crypto () {
|
||||
|
@ -968,7 +1016,8 @@ component_test_psa_external_rng_use_psa_crypto () {
|
|||
msg "test: full + PSA_CRYPTO_EXTERNAL_RNG + USE_PSA_CRYPTO minus CTR_DRBG"
|
||||
make test
|
||||
|
||||
# No ssl-opt.sh/compat.sh because they require CTR_DRBG.
|
||||
msg "test: full + PSA_CRYPTO_EXTERNAL_RNG + USE_PSA_CRYPTO minus CTR_DRBG"
|
||||
if_build_succeeded tests/ssl-opt.sh -f 'Default\|opaque'
|
||||
}
|
||||
|
||||
component_test_ecp_no_internal_rng () {
|
||||
|
@ -1336,14 +1385,63 @@ component_test_no_use_psa_crypto_full_cmake_asan() {
|
|||
}
|
||||
|
||||
component_test_psa_crypto_config_basic() {
|
||||
# full plus MBEDTLS_PSA_CRYPTO_CONFIG
|
||||
msg "build: full + MBEDTLS_PSA_CRYPTO_CONFIG"
|
||||
# Test the library excluding all Mbed TLS cryptographic support for which
|
||||
# we have an accelerator support. Acceleration is faked with the
|
||||
# transparent test driver.
|
||||
msg "test: full + MBEDTLS_PSA_CRYPTO_CONFIG + as much acceleration as supported"
|
||||
scripts/config.py full
|
||||
scripts/config.py set MBEDTLS_PSA_CRYPTO_CONFIG
|
||||
scripts/config.py set MBEDTLS_PSA_CRYPTO_DRIVERS
|
||||
scripts/config.py unset MBEDTLS_USE_PSA_CRYPTO
|
||||
|
||||
# There is no intended accelerator support for ALG STREAM_CIPHER and
|
||||
# ALG_ECB_NO_PADDING. Therefore, asking for them in the build implies the
|
||||
# inclusion of the Mbed TLS cipher operations. As we want to test here with
|
||||
# cipher operations solely supported by accelerators, disabled those
|
||||
# PSA configuration options.
|
||||
scripts/config.py -f include/psa/crypto_config.h unset PSA_WANT_ALG_STREAM_CIPHER
|
||||
scripts/config.py -f include/psa/crypto_config.h unset PSA_WANT_ALG_ECB_NO_PADDING
|
||||
|
||||
# Don't test DES encryption as:
|
||||
# 1) It is not an issue if we don't test all cipher types here.
|
||||
# 2) That way we don't have to modify in psa_crypto.c the compilation
|
||||
# guards MBEDTLS_PSA_BUILTIN_KEY_TYPE_DES for the code they guard to be
|
||||
# available to the test driver. Modifications that we would need to
|
||||
# revert when we move to compile the test driver separately.
|
||||
# We also disable MBEDTLS_DES_C as the dependencies on DES in PSA test
|
||||
# suites are still based on MBEDTLS_DES_C and not PSA_WANT_KEY_TYPE_DES.
|
||||
scripts/config.py -f include/psa/crypto_config.h unset PSA_WANT_KEY_TYPE_DES
|
||||
scripts/config.py unset MBEDTLS_DES_C
|
||||
|
||||
# Need to define the correct symbol and include the test driver header path in order to build with the test driver
|
||||
make CC=gcc CFLAGS="$ASAN_CFLAGS -DPSA_CRYPTO_DRIVER_TEST -I../tests/include -O2" LDFLAGS="$ASAN_CFLAGS"
|
||||
loc_cflags="$ASAN_CFLAGS -DPSA_CRYPTO_DRIVER_TEST"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_KEY_TYPE_AES"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_KEY_TYPE_CAMELLIA"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_KEY_TYPE_ECC_KEY_PAIR"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_KEY_TYPE_RSA_KEY_PAIR"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_CBC_NO_PADDING"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_CBC_PKCS7"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_CTR"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_CFB"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_ECDSA"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_DETERMINISTIC_ECDSA"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_MD2"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_MD4"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_MD5"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_OFB"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_RIPEMD160"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_RSA_PKCS1V15_SIGN"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_RSA_PSS"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_SHA_1"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_SHA_224"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_SHA_256"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_SHA_384"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_SHA_512"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_XTS"
|
||||
loc_cflags="${loc_cflags} -I../tests/include -O2"
|
||||
|
||||
make CC=gcc CFLAGS="$loc_cflags" LDFLAGS="$ASAN_CFLAGS"
|
||||
unset loc_cflags
|
||||
|
||||
msg "test: full + MBEDTLS_PSA_CRYPTO_CONFIG"
|
||||
make test
|
||||
|
@ -2109,11 +2207,39 @@ component_test_se_default () {
|
|||
|
||||
component_test_psa_crypto_drivers () {
|
||||
msg "build: MBEDTLS_PSA_CRYPTO_DRIVERS w/ driver hooks"
|
||||
scripts/config.py full
|
||||
scripts/config.py set MBEDTLS_PSA_CRYPTO_DRIVERS
|
||||
# Need to define the correct symbol and include the test driver header path in order to build with the test driver
|
||||
make CC=gcc CFLAGS="$ASAN_CFLAGS -DPSA_CRYPTO_DRIVER_TEST -I../tests/include -O2" LDFLAGS="$ASAN_CFLAGS"
|
||||
loc_cflags="$ASAN_CFLAGS -DPSA_CRYPTO_DRIVER_TEST"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_KEY_TYPE_AES"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_KEY_TYPE_CAMELLIA"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_KEY_TYPE_ECC_KEY_PAIR"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_KEY_TYPE_RSA_KEY_PAIR"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_CBC_NO_PADDING"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_CBC_PKCS7"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_CTR"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_CFB"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_ECDSA"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_DETERMINISTIC_ECDSA"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_MD2"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_MD4"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_MD5"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_OFB"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_RIPEMD160"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_RSA_PKCS1V15_SIGN"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_RSA_PSS"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_SHA_1"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_SHA_224"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_SHA_256"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_SHA_384"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_SHA_512"
|
||||
loc_cflags="${loc_cflags} -DMBEDTLS_PSA_ACCEL_ALG_XTS"
|
||||
loc_cflags="${loc_cflags} -I../tests/include -O2"
|
||||
|
||||
msg "test: MBEDTLS_PSA_CRYPTO_DRIVERS, signature"
|
||||
make CC=gcc CFLAGS="${loc_cflags}" LDFLAGS="$ASAN_CFLAGS"
|
||||
unset loc_cflags
|
||||
|
||||
msg "test: full + MBEDTLS_PSA_CRYPTO_DRIVERS"
|
||||
make test
|
||||
}
|
||||
|
||||
|
@ -2375,21 +2501,6 @@ component_build_armcc () {
|
|||
armc6_build_test "--target=aarch64-arm-none-eabi -march=armv8.2-a"
|
||||
}
|
||||
|
||||
component_build_ssl_hw_record_accel() {
|
||||
msg "build: default config with MBEDTLS_SSL_HW_RECORD_ACCEL enabled"
|
||||
scripts/config.pl set MBEDTLS_SSL_HW_RECORD_ACCEL
|
||||
make CFLAGS='-Werror -O1'
|
||||
}
|
||||
|
||||
component_test_allow_sha1 () {
|
||||
msg "build: allow SHA1 in certificates by default"
|
||||
scripts/config.py set MBEDTLS_TLS_DEFAULT_ALLOW_SHA1_IN_CERTIFICATES
|
||||
make CFLAGS='-Werror -Wall -Wextra'
|
||||
msg "test: allow SHA1 in certificates by default"
|
||||
make test
|
||||
if_build_succeeded tests/ssl-opt.sh -f SHA-1
|
||||
}
|
||||
|
||||
component_test_tls13_experimental () {
|
||||
msg "build: default config with MBEDTLS_SSL_PROTO_TLS1_3_EXPERIMENTAL enabled"
|
||||
scripts/config.pl set MBEDTLS_SSL_PROTO_TLS1_3_EXPERIMENTAL
|
||||
|
|
|
@ -118,10 +118,6 @@ echo '################ compat.sh ################'
|
|||
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" \
|
||||
|
|
|
@ -106,3 +106,4 @@ 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
|
||||
check scripts/generate_psa_constants.py programs/psa/psa_constant_names_generated.c
|
||||
check tests/scripts/generate_psa_tests.py $(tests/scripts/generate_psa_tests.py --list)
|
||||
|
|
|
@ -28,11 +28,6 @@ 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
|
||||
|
@ -95,15 +90,16 @@ 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=$( ls include/mbedtls/*.h include/psa/*.h )
|
||||
HEADERS="$HEADERS library/*.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 \
|
||||
cat $HEADERS $LIBRARY \
|
||||
| grep -v -e '//no-check-names' -e '#error' \
|
||||
| sed -n 's/MBED..._[A-Z0-9_]*/\'"$NL"'&\'"$NL"/gp \
|
||||
| grep MBEDTLS | sort -u > _MBEDTLS_XXX
|
||||
TYPOS=$( diff _caps _MBEDTLS_XXX | sed -n 's/^> //p' \
|
||||
| egrep -v 'XXX|__|_$|^MBEDTLS_.*CONFIG_FILE$' || true )
|
||||
|
|
|
@ -14,11 +14,13 @@
|
|||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# Purpose:
|
||||
#
|
||||
# Run 'pylint' on Python files for programming errors and helps enforcing
|
||||
# PEP8 coding standards.
|
||||
|
||||
# Purpose: check Python files for potential programming errors or maintenance
|
||||
# hurdles. Run pylint to detect some potential mistakes and enforce PEP8
|
||||
# coding standards. If available, run mypy to perform static type checking.
|
||||
|
||||
# We'll keep going on errors and report the status at the end.
|
||||
ret=0
|
||||
|
||||
if type python3 >/dev/null 2>/dev/null; then
|
||||
PYTHON=python3
|
||||
|
@ -26,4 +28,56 @@ else
|
|||
PYTHON=python
|
||||
fi
|
||||
|
||||
$PYTHON -m pylint -j 2 scripts/*.py tests/scripts/*.py
|
||||
check_version () {
|
||||
$PYTHON - "$2" <<EOF
|
||||
import packaging.version
|
||||
import sys
|
||||
import $1 as package
|
||||
actual = package.__version__
|
||||
wanted = sys.argv[1]
|
||||
if packaging.version.parse(actual) < packaging.version.parse(wanted):
|
||||
sys.stderr.write("$1: version %s is too old (want %s)\n" % (actual, wanted))
|
||||
exit(1)
|
||||
EOF
|
||||
}
|
||||
|
||||
can_pylint () {
|
||||
# Pylint 1.5.2 from Ubuntu 16.04 is too old:
|
||||
# E: 34, 0: Unable to import 'mbedtls_dev' (import-error)
|
||||
# Pylint 1.8.3 from Ubuntu 18.04 passed on the first commit containing this line.
|
||||
check_version pylint 1.8.3
|
||||
}
|
||||
|
||||
can_mypy () {
|
||||
# mypy 0.770 is too old:
|
||||
# tests/scripts/test_psa_constant_names.py:34: error: Cannot find implementation or library stub for module named 'mbedtls_dev'
|
||||
# mypy 0.780 from pip passed on the first commit containing this line.
|
||||
check_version mypy.version 0.780
|
||||
}
|
||||
|
||||
# With just a --can-xxx option, check whether the tool for xxx is available
|
||||
# with an acceptable version, and exit without running any checks. The exit
|
||||
# status is true if the tool is available and acceptable and false otherwise.
|
||||
if [ "$1" = "--can-pylint" ]; then
|
||||
can_pylint
|
||||
exit
|
||||
elif [ "$1" = "--can-mypy" ]; then
|
||||
can_mypy
|
||||
exit
|
||||
fi
|
||||
|
||||
echo 'Running pylint ...'
|
||||
$PYTHON -m pylint -j 2 scripts/mbedtls_dev/*.py scripts/*.py tests/scripts/*.py || {
|
||||
echo >&2 "pylint reported errors"
|
||||
ret=1
|
||||
}
|
||||
|
||||
# Check types if mypy is available
|
||||
if can_mypy; then
|
||||
echo
|
||||
echo 'Running mypy ...'
|
||||
$PYTHON -m mypy scripts/*.py tests/scripts/*.py ||
|
||||
ret=1
|
||||
fi
|
||||
|
||||
exit $ret
|
||||
|
|
|
@ -29,6 +29,10 @@ import codecs
|
|||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
try:
|
||||
from typing import FrozenSet, Optional, Pattern # pylint: disable=unused-import
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
class FileIssueTracker:
|
||||
|
@ -48,8 +52,8 @@ class FileIssueTracker:
|
|||
``heading``: human-readable description of the issue
|
||||
"""
|
||||
|
||||
suffix_exemptions = frozenset()
|
||||
path_exemptions = None
|
||||
suffix_exemptions = frozenset() #type: FrozenSet[str]
|
||||
path_exemptions = None #type: Optional[Pattern[str]]
|
||||
# heading must be defined in derived classes.
|
||||
# pylint: disable=no-member
|
||||
|
||||
|
@ -161,13 +165,64 @@ class PermissionIssueTracker(FileIssueTracker):
|
|||
|
||||
heading = "Incorrect permissions:"
|
||||
|
||||
# .py files can be either full scripts or modules, so they may or may
|
||||
# not be executable.
|
||||
suffix_exemptions = frozenset({".py"})
|
||||
|
||||
def check_file_for_issue(self, filepath):
|
||||
is_executable = os.access(filepath, os.X_OK)
|
||||
should_be_executable = filepath.endswith((".sh", ".pl", ".py"))
|
||||
should_be_executable = filepath.endswith((".sh", ".pl"))
|
||||
if is_executable != should_be_executable:
|
||||
self.files_with_issues[filepath] = None
|
||||
|
||||
|
||||
class ShebangIssueTracker(FileIssueTracker):
|
||||
"""Track files with a bad, missing or extraneous shebang line.
|
||||
|
||||
Executable scripts must start with a valid shebang (#!) line.
|
||||
"""
|
||||
|
||||
heading = "Invalid shebang line:"
|
||||
|
||||
# Allow either /bin/sh, /bin/bash, or /usr/bin/env.
|
||||
# Allow at most one argument (this is a Linux limitation).
|
||||
# For sh and bash, the argument if present must be options.
|
||||
# For env, the argument must be the base name of the interpeter.
|
||||
_shebang_re = re.compile(rb'^#! ?(?:/bin/(bash|sh)(?: -[^\n ]*)?'
|
||||
rb'|/usr/bin/env ([^\n /]+))$')
|
||||
_extensions = {
|
||||
b'bash': 'sh',
|
||||
b'perl': 'pl',
|
||||
b'python3': 'py',
|
||||
b'sh': 'sh',
|
||||
}
|
||||
|
||||
def is_valid_shebang(self, first_line, filepath):
|
||||
m = re.match(self._shebang_re, first_line)
|
||||
if not m:
|
||||
return False
|
||||
interpreter = m.group(1) or m.group(2)
|
||||
if interpreter not in self._extensions:
|
||||
return False
|
||||
if not filepath.endswith('.' + self._extensions[interpreter]):
|
||||
return False
|
||||
return True
|
||||
|
||||
def check_file_for_issue(self, filepath):
|
||||
is_executable = os.access(filepath, os.X_OK)
|
||||
with open(filepath, "rb") as f:
|
||||
first_line = f.readline()
|
||||
if first_line.startswith(b'#!'):
|
||||
if not is_executable:
|
||||
# Shebang on a non-executable file
|
||||
self.files_with_issues[filepath] = None
|
||||
elif not self.is_valid_shebang(first_line, filepath):
|
||||
self.files_with_issues[filepath] = [1]
|
||||
elif is_executable:
|
||||
# Executable without a shebang
|
||||
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)."""
|
||||
|
@ -288,6 +343,7 @@ class IntegrityChecker:
|
|||
self.setup_logger(log_file)
|
||||
self.issues_to_check = [
|
||||
PermissionIssueTracker(),
|
||||
ShebangIssueTracker(),
|
||||
EndOfFileNewlineIssueTracker(),
|
||||
Utf8BomIssueTracker(),
|
||||
UnixLineEndingIssueTracker(),
|
||||
|
|
447
tests/scripts/generate_psa_tests.py
Executable file
447
tests/scripts/generate_psa_tests.py
Executable file
|
@ -0,0 +1,447 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Generate test data for PSA cryptographic mechanisms.
|
||||
|
||||
With no arguments, generate all test data. With non-option arguments,
|
||||
generate only the specified files.
|
||||
"""
|
||||
|
||||
# Copyright The Mbed TLS Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from typing import Callable, Dict, FrozenSet, Iterable, Iterator, List, Optional, TypeVar
|
||||
|
||||
import scripts_path # pylint: disable=unused-import
|
||||
from mbedtls_dev import crypto_knowledge
|
||||
from mbedtls_dev import macro_collector
|
||||
from mbedtls_dev import psa_storage
|
||||
from mbedtls_dev import test_case
|
||||
|
||||
T = TypeVar('T') #pylint: disable=invalid-name
|
||||
|
||||
|
||||
def psa_want_symbol(name: str) -> str:
|
||||
"""Return the PSA_WANT_xxx symbol associated with a PSA crypto feature."""
|
||||
if name.startswith('PSA_'):
|
||||
return name[:4] + 'WANT_' + name[4:]
|
||||
else:
|
||||
raise ValueError('Unable to determine the PSA_WANT_ symbol for ' + name)
|
||||
|
||||
def finish_family_dependency(dep: str, bits: int) -> str:
|
||||
"""Finish dep if it's a family dependency symbol prefix.
|
||||
|
||||
A family dependency symbol prefix is a PSA_WANT_ symbol that needs to be
|
||||
qualified by the key size. If dep is such a symbol, finish it by adjusting
|
||||
the prefix and appending the key size. Other symbols are left unchanged.
|
||||
"""
|
||||
return re.sub(r'_FAMILY_(.*)', r'_\1_' + str(bits), dep)
|
||||
|
||||
def finish_family_dependencies(dependencies: List[str], bits: int) -> List[str]:
|
||||
"""Finish any family dependency symbol prefixes.
|
||||
|
||||
Apply `finish_family_dependency` to each element of `dependencies`.
|
||||
"""
|
||||
return [finish_family_dependency(dep, bits) for dep in dependencies]
|
||||
|
||||
def automatic_dependencies(*expressions: str) -> List[str]:
|
||||
"""Infer dependencies of a test case by looking for PSA_xxx symbols.
|
||||
|
||||
The arguments are strings which should be C expressions. Do not use
|
||||
string literals or comments as this function is not smart enough to
|
||||
skip them.
|
||||
"""
|
||||
used = set()
|
||||
for expr in expressions:
|
||||
used.update(re.findall(r'PSA_(?:ALG|ECC_FAMILY|KEY_TYPE)_\w+', expr))
|
||||
return sorted(psa_want_symbol(name) for name in used)
|
||||
|
||||
# A temporary hack: at the time of writing, not all dependency symbols
|
||||
# are implemented yet. Skip test cases for which the dependency symbols are
|
||||
# not available. Once all dependency symbols are available, this hack must
|
||||
# be removed so that a bug in the dependency symbols proprely leads to a test
|
||||
# failure.
|
||||
def read_implemented_dependencies(filename: str) -> FrozenSet[str]:
|
||||
return frozenset(symbol
|
||||
for line in open(filename)
|
||||
for symbol in re.findall(r'\bPSA_WANT_\w+\b', line))
|
||||
IMPLEMENTED_DEPENDENCIES = read_implemented_dependencies('include/psa/crypto_config.h')
|
||||
def hack_dependencies_not_implemented(dependencies: List[str]) -> None:
|
||||
if not all(dep.lstrip('!') in IMPLEMENTED_DEPENDENCIES
|
||||
for dep in dependencies):
|
||||
dependencies.append('DEPENDENCY_NOT_IMPLEMENTED_YET')
|
||||
|
||||
|
||||
class Information:
|
||||
"""Gather information about PSA constructors."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.constructors = self.read_psa_interface()
|
||||
|
||||
@staticmethod
|
||||
def remove_unwanted_macros(
|
||||
constructors: macro_collector.PSAMacroCollector
|
||||
) -> None:
|
||||
# Mbed TLS doesn't support DSA. Don't attempt to generate any related
|
||||
# test case.
|
||||
constructors.key_types.discard('PSA_KEY_TYPE_DSA_KEY_PAIR')
|
||||
constructors.key_types.discard('PSA_KEY_TYPE_DSA_PUBLIC_KEY')
|
||||
constructors.algorithms_from_hash.pop('PSA_ALG_DSA', None)
|
||||
constructors.algorithms_from_hash.pop('PSA_ALG_DETERMINISTIC_DSA', None)
|
||||
|
||||
def read_psa_interface(self) -> macro_collector.PSAMacroCollector:
|
||||
"""Return the list of known key types, algorithms, etc."""
|
||||
constructors = macro_collector.PSAMacroCollector()
|
||||
header_file_names = ['include/psa/crypto_values.h',
|
||||
'include/psa/crypto_extra.h']
|
||||
for header_file_name in header_file_names:
|
||||
with open(header_file_name, 'rb') as header_file:
|
||||
constructors.read_file(header_file)
|
||||
self.remove_unwanted_macros(constructors)
|
||||
return constructors
|
||||
|
||||
|
||||
def test_case_for_key_type_not_supported(
|
||||
verb: str, key_type: str, bits: int,
|
||||
dependencies: List[str],
|
||||
*args: str,
|
||||
param_descr: str = ''
|
||||
) -> test_case.TestCase:
|
||||
"""Return one test case exercising a key creation method
|
||||
for an unsupported key type or size.
|
||||
"""
|
||||
hack_dependencies_not_implemented(dependencies)
|
||||
tc = test_case.TestCase()
|
||||
short_key_type = re.sub(r'PSA_(KEY_TYPE|ECC_FAMILY)_', r'', key_type)
|
||||
adverb = 'not' if dependencies else 'never'
|
||||
if param_descr:
|
||||
adverb = param_descr + ' ' + adverb
|
||||
tc.set_description('PSA {} {} {}-bit {} supported'
|
||||
.format(verb, short_key_type, bits, adverb))
|
||||
tc.set_dependencies(dependencies)
|
||||
tc.set_function(verb + '_not_supported')
|
||||
tc.set_arguments([key_type] + list(args))
|
||||
return tc
|
||||
|
||||
class NotSupported:
|
||||
"""Generate test cases for when something is not supported."""
|
||||
|
||||
def __init__(self, info: Information) -> None:
|
||||
self.constructors = info.constructors
|
||||
|
||||
ALWAYS_SUPPORTED = frozenset([
|
||||
'PSA_KEY_TYPE_DERIVE',
|
||||
'PSA_KEY_TYPE_RAW_DATA',
|
||||
])
|
||||
def test_cases_for_key_type_not_supported(
|
||||
self,
|
||||
kt: crypto_knowledge.KeyType,
|
||||
param: Optional[int] = None,
|
||||
param_descr: str = '',
|
||||
) -> Iterator[test_case.TestCase]:
|
||||
"""Return test cases exercising key creation when the given type is unsupported.
|
||||
|
||||
If param is present and not None, emit test cases conditioned on this
|
||||
parameter not being supported. If it is absent or None, emit test cases
|
||||
conditioned on the base type not being supported.
|
||||
"""
|
||||
if kt.name in self.ALWAYS_SUPPORTED:
|
||||
# Don't generate test cases for key types that are always supported.
|
||||
# They would be skipped in all configurations, which is noise.
|
||||
return
|
||||
import_dependencies = [('!' if param is None else '') +
|
||||
psa_want_symbol(kt.name)]
|
||||
if kt.params is not None:
|
||||
import_dependencies += [('!' if param == i else '') +
|
||||
psa_want_symbol(sym)
|
||||
for i, sym in enumerate(kt.params)]
|
||||
if kt.name.endswith('_PUBLIC_KEY'):
|
||||
generate_dependencies = []
|
||||
else:
|
||||
generate_dependencies = import_dependencies
|
||||
for bits in kt.sizes_to_test():
|
||||
yield test_case_for_key_type_not_supported(
|
||||
'import', kt.expression, bits,
|
||||
finish_family_dependencies(import_dependencies, bits),
|
||||
test_case.hex_string(kt.key_material(bits)),
|
||||
param_descr=param_descr,
|
||||
)
|
||||
if not generate_dependencies and param is not None:
|
||||
# If generation is impossible for this key type, rather than
|
||||
# supported or not depending on implementation capabilities,
|
||||
# only generate the test case once.
|
||||
continue
|
||||
yield test_case_for_key_type_not_supported(
|
||||
'generate', kt.expression, bits,
|
||||
finish_family_dependencies(generate_dependencies, bits),
|
||||
str(bits),
|
||||
param_descr=param_descr,
|
||||
)
|
||||
# To be added: derive
|
||||
|
||||
def test_cases_for_not_supported(self) -> Iterator[test_case.TestCase]:
|
||||
"""Generate test cases that exercise the creation of keys of unsupported types."""
|
||||
for key_type in sorted(self.constructors.key_types):
|
||||
kt = crypto_knowledge.KeyType(key_type)
|
||||
yield from self.test_cases_for_key_type_not_supported(kt)
|
||||
for curve_family in sorted(self.constructors.ecc_curves):
|
||||
for constr in ('PSA_KEY_TYPE_ECC_KEY_PAIR',
|
||||
'PSA_KEY_TYPE_ECC_PUBLIC_KEY'):
|
||||
kt = crypto_knowledge.KeyType(constr, [curve_family])
|
||||
yield from self.test_cases_for_key_type_not_supported(
|
||||
kt, param_descr='type')
|
||||
yield from self.test_cases_for_key_type_not_supported(
|
||||
kt, 0, param_descr='curve')
|
||||
|
||||
|
||||
class StorageKey(psa_storage.Key):
|
||||
"""Representation of a key for storage format testing."""
|
||||
|
||||
def __init__(self, *, description: str, **kwargs) -> None:
|
||||
super().__init__(**kwargs)
|
||||
self.description = description #type: str
|
||||
|
||||
class StorageFormat:
|
||||
"""Storage format stability test cases."""
|
||||
|
||||
def __init__(self, info: Information, version: int, forward: bool) -> None:
|
||||
"""Prepare to generate test cases for storage format stability.
|
||||
|
||||
* `info`: information about the API. See the `Information` class.
|
||||
* `version`: the storage format version to generate test cases for.
|
||||
* `forward`: if true, generate forward compatibility test cases which
|
||||
save a key and check that its representation is as intended. Otherwise
|
||||
generate backward compatibility test cases which inject a key
|
||||
representation and check that it can be read and used.
|
||||
"""
|
||||
self.constructors = info.constructors
|
||||
self.version = version
|
||||
self.forward = forward
|
||||
|
||||
def make_test_case(self, key: StorageKey) -> test_case.TestCase:
|
||||
"""Construct a storage format test case for the given key.
|
||||
|
||||
If ``forward`` is true, generate a forward compatibility test case:
|
||||
create a key and validate that it has the expected representation.
|
||||
Otherwise generate a backward compatibility test case: inject the
|
||||
key representation into storage and validate that it can be read
|
||||
correctly.
|
||||
"""
|
||||
verb = 'save' if self.forward else 'read'
|
||||
tc = test_case.TestCase()
|
||||
tc.set_description('PSA storage {}: {}'.format(verb, key.description))
|
||||
dependencies = automatic_dependencies(
|
||||
key.lifetime.string, key.type.string,
|
||||
key.usage.string, key.alg.string, key.alg2.string,
|
||||
)
|
||||
dependencies = finish_family_dependencies(dependencies, key.bits)
|
||||
tc.set_dependencies(dependencies)
|
||||
tc.set_function('key_storage_' + verb)
|
||||
if self.forward:
|
||||
extra_arguments = []
|
||||
else:
|
||||
# Some test keys have the RAW_DATA type and attributes that don't
|
||||
# necessarily make sense. We do this to validate numerical
|
||||
# encodings of the attributes.
|
||||
# Raw data keys have no useful exercise anyway so there is no
|
||||
# loss of test coverage.
|
||||
exercise = key.type.string != 'PSA_KEY_TYPE_RAW_DATA'
|
||||
extra_arguments = ['1' if exercise else '0']
|
||||
tc.set_arguments([key.lifetime.string,
|
||||
key.type.string, str(key.bits),
|
||||
key.usage.string, key.alg.string, key.alg2.string,
|
||||
'"' + key.material.hex() + '"',
|
||||
'"' + key.hex() + '"',
|
||||
*extra_arguments])
|
||||
return tc
|
||||
|
||||
def key_for_usage_flags(
|
||||
self,
|
||||
usage_flags: List[str],
|
||||
short: Optional[str] = None
|
||||
) -> StorageKey:
|
||||
"""Construct a test key for the given key usage."""
|
||||
usage = ' | '.join(usage_flags) if usage_flags else '0'
|
||||
if short is None:
|
||||
short = re.sub(r'\bPSA_KEY_USAGE_', r'', usage)
|
||||
description = 'usage: ' + short
|
||||
key = StorageKey(version=self.version,
|
||||
id=1, lifetime=0x00000001,
|
||||
type='PSA_KEY_TYPE_RAW_DATA', bits=8,
|
||||
usage=usage, alg=0, alg2=0,
|
||||
material=b'K',
|
||||
description=description)
|
||||
return key
|
||||
|
||||
def all_keys_for_usage_flags(self) -> Iterator[StorageKey]:
|
||||
"""Generate test keys covering usage flags."""
|
||||
known_flags = sorted(self.constructors.key_usage_flags)
|
||||
yield self.key_for_usage_flags(['0'])
|
||||
for usage_flag in known_flags:
|
||||
yield self.key_for_usage_flags([usage_flag])
|
||||
for flag1, flag2 in zip(known_flags,
|
||||
known_flags[1:] + [known_flags[0]]):
|
||||
yield self.key_for_usage_flags([flag1, flag2])
|
||||
yield self.key_for_usage_flags(known_flags, short='all known')
|
||||
|
||||
def keys_for_type(
|
||||
self,
|
||||
key_type: str,
|
||||
params: Optional[Iterable[str]] = None
|
||||
) -> Iterator[StorageKey]:
|
||||
"""Generate test keys for the given key type.
|
||||
|
||||
For key types that depend on a parameter (e.g. elliptic curve family),
|
||||
`param` is the parameter to pass to the constructor. Only a single
|
||||
parameter is supported.
|
||||
"""
|
||||
kt = crypto_knowledge.KeyType(key_type, params)
|
||||
for bits in kt.sizes_to_test():
|
||||
usage_flags = 'PSA_KEY_USAGE_EXPORT'
|
||||
alg = 0
|
||||
alg2 = 0
|
||||
key_material = kt.key_material(bits)
|
||||
short_expression = re.sub(r'\bPSA_(?:KEY_TYPE|ECC_FAMILY)_',
|
||||
r'',
|
||||
kt.expression)
|
||||
description = 'type: {} {}-bit'.format(short_expression, bits)
|
||||
key = StorageKey(version=self.version,
|
||||
id=1, lifetime=0x00000001,
|
||||
type=kt.expression, bits=bits,
|
||||
usage=usage_flags, alg=alg, alg2=alg2,
|
||||
material=key_material,
|
||||
description=description)
|
||||
yield key
|
||||
|
||||
def all_keys_for_types(self) -> Iterator[StorageKey]:
|
||||
"""Generate test keys covering key types and their representations."""
|
||||
for key_type in sorted(self.constructors.key_types):
|
||||
yield from self.keys_for_type(key_type)
|
||||
for key_type in sorted(self.constructors.key_types_from_curve):
|
||||
for curve in sorted(self.constructors.ecc_curves):
|
||||
yield from self.keys_for_type(key_type, [curve])
|
||||
## Diffie-Hellman (FFDH) is not supported yet, either in
|
||||
## crypto_knowledge.py or in Mbed TLS.
|
||||
# for key_type in sorted(self.constructors.key_types_from_group):
|
||||
# for group in sorted(self.constructors.dh_groups):
|
||||
# yield from self.keys_for_type(key_type, [group])
|
||||
|
||||
def keys_for_algorithm(self, alg: str) -> Iterator[StorageKey]:
|
||||
"""Generate test keys for the specified algorithm."""
|
||||
# For now, we don't have information on the compatibility of key
|
||||
# types and algorithms. So we just test the encoding of algorithms,
|
||||
# and not that operations can be performed with them.
|
||||
descr = alg
|
||||
usage = 'PSA_KEY_USAGE_EXPORT'
|
||||
key1 = StorageKey(version=self.version,
|
||||
id=1, lifetime=0x00000001,
|
||||
type='PSA_KEY_TYPE_RAW_DATA', bits=8,
|
||||
usage=usage, alg=alg, alg2=0,
|
||||
material=b'K',
|
||||
description='alg: ' + descr)
|
||||
yield key1
|
||||
key2 = StorageKey(version=self.version,
|
||||
id=1, lifetime=0x00000001,
|
||||
type='PSA_KEY_TYPE_RAW_DATA', bits=8,
|
||||
usage=usage, alg=0, alg2=alg,
|
||||
material=b'L',
|
||||
description='alg2: ' + descr)
|
||||
yield key2
|
||||
|
||||
def all_keys_for_algorithms(self) -> Iterator[StorageKey]:
|
||||
"""Generate test keys covering algorithm encodings."""
|
||||
for alg in sorted(self.constructors.algorithms):
|
||||
yield from self.keys_for_algorithm(alg)
|
||||
# To do: algorithm constructors with parameters
|
||||
|
||||
def all_test_cases(self) -> Iterator[test_case.TestCase]:
|
||||
"""Generate all storage format test cases."""
|
||||
for key in self.all_keys_for_usage_flags():
|
||||
yield self.make_test_case(key)
|
||||
for key in self.all_keys_for_types():
|
||||
yield self.make_test_case(key)
|
||||
for key in self.all_keys_for_algorithms():
|
||||
yield self.make_test_case(key)
|
||||
# To do: vary id, lifetime
|
||||
|
||||
|
||||
class TestGenerator:
|
||||
"""Generate test data."""
|
||||
|
||||
def __init__(self, options) -> None:
|
||||
self.test_suite_directory = self.get_option(options, 'directory',
|
||||
'tests/suites')
|
||||
self.info = Information()
|
||||
|
||||
@staticmethod
|
||||
def get_option(options, name: str, default: T) -> T:
|
||||
value = getattr(options, name, None)
|
||||
return default if value is None else value
|
||||
|
||||
def filename_for(self, basename: str) -> str:
|
||||
"""The location of the data file with the specified base name."""
|
||||
return os.path.join(self.test_suite_directory, basename + '.data')
|
||||
|
||||
def write_test_data_file(self, basename: str,
|
||||
test_cases: Iterable[test_case.TestCase]) -> None:
|
||||
"""Write the test cases to a .data file.
|
||||
|
||||
The output file is ``basename + '.data'`` in the test suite directory.
|
||||
"""
|
||||
filename = self.filename_for(basename)
|
||||
test_case.write_data_file(filename, test_cases)
|
||||
|
||||
TARGETS = {
|
||||
'test_suite_psa_crypto_not_supported.generated':
|
||||
lambda info: NotSupported(info).test_cases_for_not_supported(),
|
||||
'test_suite_psa_crypto_storage_format.current':
|
||||
lambda info: StorageFormat(info, 0, True).all_test_cases(),
|
||||
'test_suite_psa_crypto_storage_format.v0':
|
||||
lambda info: StorageFormat(info, 0, False).all_test_cases(),
|
||||
} #type: Dict[str, Callable[[Information], Iterable[test_case.TestCase]]]
|
||||
|
||||
def generate_target(self, name: str) -> None:
|
||||
test_cases = self.TARGETS[name](self.info)
|
||||
self.write_test_data_file(name, test_cases)
|
||||
|
||||
def main(args):
|
||||
"""Command line entry point."""
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--list', action='store_true',
|
||||
help='List available targets and exit')
|
||||
parser.add_argument('targets', nargs='*', metavar='TARGET',
|
||||
help='Target file to generate (default: all; "-": none)')
|
||||
options = parser.parse_args(args)
|
||||
generator = TestGenerator(options)
|
||||
if options.list:
|
||||
for name in sorted(generator.TARGETS):
|
||||
print(generator.filename_for(name))
|
||||
return
|
||||
if options.targets:
|
||||
# Allow "-" as a special case so you can run
|
||||
# ``generate_psa_tests.py - $targets`` and it works uniformly whether
|
||||
# ``$targets`` is empty or not.
|
||||
options.targets = [os.path.basename(re.sub(r'\.data\Z', r'', target))
|
||||
for target in options.targets
|
||||
if target != '-']
|
||||
else:
|
||||
options.targets = sorted(generator.TARGETS)
|
||||
for target in options.targets:
|
||||
generator.generate_target(target)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv[1:])
|
|
@ -23,10 +23,11 @@ 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>;
|
||||
@ARGV = <include/mbedtls/*.h>;
|
||||
push @ARGV, <library/*.h>;
|
||||
push @ARGV, "3rdparty/everest/include/everest/everest.h";
|
||||
push @ARGV, "3rdparty/everest/include/everest/x25519.h";
|
||||
|
||||
push @ARGV, glob("library/*.h");
|
||||
|
||||
my @consts;
|
||||
my $state = 'out';
|
||||
|
|
|
@ -47,9 +47,9 @@ done
|
|||
|
||||
if [ $INTERNAL ]
|
||||
then
|
||||
HEADERS=$( ls include/mbedtls/*_internal.h library/*.h | egrep -v 'compat-1\.3\.h|bn_mul' )
|
||||
HEADERS=$( ls library/*.h )
|
||||
else
|
||||
HEADERS=$( ls include/mbedtls/*.h include/psa/*.h library/*.h | egrep -v 'compat-1\.3\.h|bn_mul' )
|
||||
HEADERS=$( ls include/mbedtls/*.h include/psa/*.h library/*.h )
|
||||
HEADERS="$HEADERS 3rdparty/everest/include/everest/everest.h 3rdparty/everest/include/everest/x25519.h"
|
||||
fi
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ if [ -d include/mbedtls ]; then :; else
|
|||
exit 1
|
||||
fi
|
||||
|
||||
HEADERS=$( ls include/mbedtls/*.h include/psa/*.h | egrep -v 'compat-1\.3\.h' )
|
||||
HEADERS=$( ls include/mbedtls/*.h include/psa/*.h )
|
||||
HEADERS="$HEADERS library/*.h"
|
||||
HEADERS="$HEADERS 3rdparty/everest/include/everest/everest.h 3rdparty/everest/include/everest/x25519.h"
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ import re
|
|||
import os
|
||||
import binascii
|
||||
|
||||
from mbed_host_tests import BaseHostTest, event_callback # pylint: disable=import-error
|
||||
from mbed_host_tests import BaseHostTest, event_callback # type: ignore # pylint: disable=import-error
|
||||
|
||||
|
||||
class TestDataParserError(Exception):
|
||||
|
|
28
tests/scripts/scripts_path.py
Normal file
28
tests/scripts/scripts_path.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
"""Add our Python library directory to the module search path.
|
||||
|
||||
Usage:
|
||||
|
||||
import scripts_path # pylint: disable=unused-import
|
||||
"""
|
||||
|
||||
# Copyright The Mbed TLS Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__),
|
||||
os.path.pardir, os.path.pardir,
|
||||
'scripts'))
|
302
tests/scripts/set_psa_test_dependencies.py
Executable file
302
tests/scripts/set_psa_test_dependencies.py
Executable file
|
@ -0,0 +1,302 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
"""Edit test cases to use PSA dependencies instead of classic dependencies.
|
||||
"""
|
||||
|
||||
# Copyright The Mbed TLS Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
CLASSIC_DEPENDENCIES = frozenset([
|
||||
# This list is manually filtered from config.h.
|
||||
|
||||
# Mbed TLS feature support.
|
||||
# Only features that affect what can be done are listed here.
|
||||
# Options that control optimizations or alternative implementations
|
||||
# are omitted.
|
||||
'MBEDTLS_CIPHER_MODE_CBC',
|
||||
'MBEDTLS_CIPHER_MODE_CFB',
|
||||
'MBEDTLS_CIPHER_MODE_CTR',
|
||||
'MBEDTLS_CIPHER_MODE_OFB',
|
||||
'MBEDTLS_CIPHER_MODE_XTS',
|
||||
'MBEDTLS_CIPHER_NULL_CIPHER',
|
||||
'MBEDTLS_CIPHER_PADDING_PKCS7',
|
||||
'MBEDTLS_CIPHER_PADDING_ONE_AND_ZEROS',
|
||||
'MBEDTLS_CIPHER_PADDING_ZEROS_AND_LEN',
|
||||
'MBEDTLS_CIPHER_PADDING_ZEROS',
|
||||
#curve#'MBEDTLS_ECP_DP_SECP192R1_ENABLED',
|
||||
#curve#'MBEDTLS_ECP_DP_SECP224R1_ENABLED',
|
||||
#curve#'MBEDTLS_ECP_DP_SECP256R1_ENABLED',
|
||||
#curve#'MBEDTLS_ECP_DP_SECP384R1_ENABLED',
|
||||
#curve#'MBEDTLS_ECP_DP_SECP521R1_ENABLED',
|
||||
#curve#'MBEDTLS_ECP_DP_SECP192K1_ENABLED',
|
||||
#curve#'MBEDTLS_ECP_DP_SECP224K1_ENABLED',
|
||||
#curve#'MBEDTLS_ECP_DP_SECP256K1_ENABLED',
|
||||
#curve#'MBEDTLS_ECP_DP_BP256R1_ENABLED',
|
||||
#curve#'MBEDTLS_ECP_DP_BP384R1_ENABLED',
|
||||
#curve#'MBEDTLS_ECP_DP_BP512R1_ENABLED',
|
||||
#curve#'MBEDTLS_ECP_DP_CURVE25519_ENABLED',
|
||||
#curve#'MBEDTLS_ECP_DP_CURVE448_ENABLED',
|
||||
'MBEDTLS_ECDSA_DETERMINISTIC',
|
||||
#'MBEDTLS_GENPRIME', #needed for RSA key generation
|
||||
'MBEDTLS_PKCS1_V15',
|
||||
'MBEDTLS_PKCS1_V21',
|
||||
'MBEDTLS_SHA512_NO_SHA384',
|
||||
|
||||
# Mbed TLS modules.
|
||||
# Only modules that provide cryptographic mechanisms are listed here.
|
||||
# Platform, data formatting, X.509 or TLS modules are omitted.
|
||||
'MBEDTLS_AES_C',
|
||||
'MBEDTLS_ARC4_C',
|
||||
'MBEDTLS_BIGNUM_C',
|
||||
#cipher#'MBEDTLS_BLOWFISH_C',
|
||||
'MBEDTLS_CAMELLIA_C',
|
||||
'MBEDTLS_ARIA_C',
|
||||
'MBEDTLS_CCM_C',
|
||||
'MBEDTLS_CHACHA20_C',
|
||||
'MBEDTLS_CHACHAPOLY_C',
|
||||
'MBEDTLS_CMAC_C',
|
||||
'MBEDTLS_CTR_DRBG_C',
|
||||
'MBEDTLS_DES_C',
|
||||
'MBEDTLS_DHM_C',
|
||||
'MBEDTLS_ECDH_C',
|
||||
'MBEDTLS_ECDSA_C',
|
||||
'MBEDTLS_ECJPAKE_C',
|
||||
'MBEDTLS_ECP_C',
|
||||
'MBEDTLS_ENTROPY_C',
|
||||
'MBEDTLS_GCM_C',
|
||||
'MBEDTLS_HKDF_C',
|
||||
'MBEDTLS_HMAC_DRBG_C',
|
||||
'MBEDTLS_NIST_KW_C',
|
||||
'MBEDTLS_MD2_C',
|
||||
'MBEDTLS_MD4_C',
|
||||
'MBEDTLS_MD5_C',
|
||||
'MBEDTLS_PKCS5_C',
|
||||
'MBEDTLS_PKCS12_C',
|
||||
'MBEDTLS_POLY1305_C',
|
||||
'MBEDTLS_RIPEMD160_C',
|
||||
'MBEDTLS_RSA_C',
|
||||
'MBEDTLS_SHA1_C',
|
||||
'MBEDTLS_SHA256_C',
|
||||
'MBEDTLS_SHA512_C',
|
||||
'MBEDTLS_XTEA_C',
|
||||
])
|
||||
|
||||
def is_classic_dependency(dep):
|
||||
"""Whether dep is a classic dependency that PSA test cases should not use."""
|
||||
if dep.startswith('!'):
|
||||
dep = dep[1:]
|
||||
return dep in CLASSIC_DEPENDENCIES
|
||||
|
||||
def is_systematic_dependency(dep):
|
||||
"""Whether dep is a PSA dependency which is determined systematically."""
|
||||
if dep.startswith('PSA_WANT_ECC_'):
|
||||
return False
|
||||
return dep.startswith('PSA_WANT_')
|
||||
|
||||
WITHOUT_SYSTEMATIC_DEPENDENCIES = frozenset([
|
||||
'PSA_ALG_AEAD_WITH_SHORTENED_TAG', # only a modifier
|
||||
'PSA_ALG_ANY_HASH', # only meaningful in policies
|
||||
'PSA_ALG_KEY_AGREEMENT', # only a way to combine algorithms
|
||||
'PSA_ALG_TRUNCATED_MAC', # only a modifier
|
||||
'PSA_KEY_TYPE_NONE', # not a real key type
|
||||
'PSA_KEY_TYPE_DERIVE', # always supported, don't list it to reduce noise
|
||||
'PSA_KEY_TYPE_RAW_DATA', # always supported, don't list it to reduce noise
|
||||
'PSA_ALG_AT_LEAST_THIS_LENGTH_MAC', #only a modifier
|
||||
'PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG', #only a modifier
|
||||
])
|
||||
|
||||
SPECIAL_SYSTEMATIC_DEPENDENCIES = {
|
||||
'PSA_ALG_ECDSA_ANY': frozenset(['PSA_WANT_ALG_ECDSA']),
|
||||
'PSA_ALG_RSA_PKCS1V15_SIGN_RAW': frozenset(['PSA_WANT_ALG_RSA_PKCS1V15_SIGN']),
|
||||
}
|
||||
|
||||
def dependencies_of_symbol(symbol):
|
||||
"""Return the dependencies for a symbol that designates a cryptographic mechanism."""
|
||||
if symbol in WITHOUT_SYSTEMATIC_DEPENDENCIES:
|
||||
return frozenset()
|
||||
if symbol in SPECIAL_SYSTEMATIC_DEPENDENCIES:
|
||||
return SPECIAL_SYSTEMATIC_DEPENDENCIES[symbol]
|
||||
if symbol.startswith('PSA_ALG_CATEGORY_') or \
|
||||
symbol.startswith('PSA_KEY_TYPE_CATEGORY_'):
|
||||
# Categories are used in test data when an unsupported but plausible
|
||||
# mechanism number needed. They have no associated dependency.
|
||||
return frozenset()
|
||||
return {symbol.replace('_', '_WANT_', 1)}
|
||||
|
||||
def systematic_dependencies(file_name, function_name, arguments):
|
||||
"""List the systematically determined dependency for a test case."""
|
||||
deps = set()
|
||||
|
||||
# Run key policy negative tests even if the algorithm to attempt performing
|
||||
# is not supported but in the case where the test is to check an
|
||||
# incompatibility between a requested algorithm for a cryptographic
|
||||
# operation and a key policy. In the latter, we want to filter out the
|
||||
# cases # where PSA_ERROR_NOT_SUPPORTED is returned instead of
|
||||
# PSA_ERROR_NOT_PERMITTED.
|
||||
if function_name.endswith('_key_policy') and \
|
||||
arguments[-1].startswith('PSA_ERROR_') and \
|
||||
arguments[-1] != ('PSA_ERROR_NOT_PERMITTED'):
|
||||
arguments[-2] = ''
|
||||
if function_name == 'copy_fail' and \
|
||||
arguments[-1].startswith('PSA_ERROR_'):
|
||||
arguments[-2] = ''
|
||||
arguments[-3] = ''
|
||||
|
||||
# Storage format tests that only look at how the file is structured and
|
||||
# don't care about the format of the key material don't depend on any
|
||||
# cryptographic mechanisms.
|
||||
if os.path.basename(file_name) == 'test_suite_psa_crypto_persistent_key.data' and \
|
||||
function_name in {'format_storage_data_check',
|
||||
'parse_storage_data_check'}:
|
||||
return []
|
||||
|
||||
for arg in arguments:
|
||||
for symbol in re.findall(r'PSA_(?:ALG|KEY_TYPE)_\w+', arg):
|
||||
deps.update(dependencies_of_symbol(symbol))
|
||||
return sorted(deps)
|
||||
|
||||
def updated_dependencies(file_name, function_name, arguments, dependencies):
|
||||
"""Rework the list of dependencies into PSA_WANT_xxx.
|
||||
|
||||
Remove classic crypto dependencies such as MBEDTLS_RSA_C,
|
||||
MBEDTLS_PKCS1_V15, etc.
|
||||
|
||||
Add systematic PSA_WANT_xxx dependencies based on the called function and
|
||||
its arguments, replacing existing PSA_WANT_xxx dependencies.
|
||||
"""
|
||||
automatic = systematic_dependencies(file_name, function_name, arguments)
|
||||
manual = [dep for dep in dependencies
|
||||
if not (is_systematic_dependency(dep) or
|
||||
is_classic_dependency(dep))]
|
||||
return automatic + manual
|
||||
|
||||
def keep_manual_dependencies(file_name, function_name, arguments):
|
||||
#pylint: disable=unused-argument
|
||||
"""Declare test functions with unusual dependencies here."""
|
||||
# If there are no arguments, we can't do any useful work. Assume that if
|
||||
# there are dependencies, they are warranted.
|
||||
if not arguments:
|
||||
return True
|
||||
# When PSA_ERROR_NOT_SUPPORTED is expected, usually, at least one of the
|
||||
# constants mentioned in the test should not be supported. It isn't
|
||||
# possible to determine which one in a systematic way. So let the programmer
|
||||
# decide.
|
||||
if arguments[-1] == 'PSA_ERROR_NOT_SUPPORTED':
|
||||
return True
|
||||
return False
|
||||
|
||||
def process_data_stanza(stanza, file_name, test_case_number):
|
||||
"""Update PSA crypto dependencies in one Mbed TLS test case.
|
||||
|
||||
stanza is the test case text (including the description, the dependencies,
|
||||
the line with the function and arguments, and optionally comments). Return
|
||||
a new stanza with an updated dependency line, preserving everything else
|
||||
(description, comments, arguments, etc.).
|
||||
"""
|
||||
if not stanza.lstrip('\n'):
|
||||
# Just blank lines
|
||||
return stanza
|
||||
# Expect 2 or 3 non-comment lines: description, optional dependencies,
|
||||
# function-and-arguments.
|
||||
content_matches = list(re.finditer(r'^[\t ]*([^\t #].*)$', stanza, re.M))
|
||||
if len(content_matches) < 2:
|
||||
raise Exception('Not enough content lines in paragraph {} in {}'
|
||||
.format(test_case_number, file_name))
|
||||
if len(content_matches) > 3:
|
||||
raise Exception('Too many content lines in paragraph {} in {}'
|
||||
.format(test_case_number, file_name))
|
||||
arguments = content_matches[-1].group(0).split(':')
|
||||
function_name = arguments.pop(0)
|
||||
if keep_manual_dependencies(file_name, function_name, arguments):
|
||||
return stanza
|
||||
if len(content_matches) == 2:
|
||||
# Insert a line for the dependencies. If it turns out that there are
|
||||
# no dependencies, we'll remove that empty line below.
|
||||
dependencies_location = content_matches[-1].start()
|
||||
text_before = stanza[:dependencies_location]
|
||||
text_after = '\n' + stanza[dependencies_location:]
|
||||
old_dependencies = []
|
||||
dependencies_leader = 'depends_on:'
|
||||
else:
|
||||
dependencies_match = content_matches[-2]
|
||||
text_before = stanza[:dependencies_match.start()]
|
||||
text_after = stanza[dependencies_match.end():]
|
||||
old_dependencies = dependencies_match.group(0).split(':')
|
||||
dependencies_leader = old_dependencies.pop(0) + ':'
|
||||
if dependencies_leader != 'depends_on:':
|
||||
raise Exception('Next-to-last line does not start with "depends_on:"'
|
||||
' in paragraph {} in {}'
|
||||
.format(test_case_number, file_name))
|
||||
new_dependencies = updated_dependencies(file_name, function_name, arguments,
|
||||
old_dependencies)
|
||||
if new_dependencies:
|
||||
stanza = (text_before +
|
||||
dependencies_leader + ':'.join(new_dependencies) +
|
||||
text_after)
|
||||
else:
|
||||
# The dependencies have become empty. Remove the depends_on: line.
|
||||
assert text_after[0] == '\n'
|
||||
stanza = text_before + text_after[1:]
|
||||
return stanza
|
||||
|
||||
def process_data_file(file_name, old_content):
|
||||
"""Update PSA crypto dependencies in an Mbed TLS test suite data file.
|
||||
|
||||
Process old_content (the old content of the file) and return the new content.
|
||||
"""
|
||||
old_stanzas = old_content.split('\n\n')
|
||||
new_stanzas = [process_data_stanza(stanza, file_name, n)
|
||||
for n, stanza in enumerate(old_stanzas, start=1)]
|
||||
return '\n\n'.join(new_stanzas)
|
||||
|
||||
def update_file(file_name, old_content, new_content):
|
||||
"""Update the given file with the given new content.
|
||||
|
||||
Replace the existing file. The previous version is renamed to *.bak.
|
||||
Don't modify the file if the content was unchanged.
|
||||
"""
|
||||
if new_content == old_content:
|
||||
return
|
||||
backup = file_name + '.bak'
|
||||
tmp = file_name + '.tmp'
|
||||
with open(tmp, 'w', encoding='utf-8') as new_file:
|
||||
new_file.write(new_content)
|
||||
os.replace(file_name, backup)
|
||||
os.replace(tmp, file_name)
|
||||
|
||||
def process_file(file_name):
|
||||
"""Update PSA crypto dependencies in an Mbed TLS test suite data file.
|
||||
|
||||
Replace the existing file. The previous version is renamed to *.bak.
|
||||
Don't modify the file if the content was unchanged.
|
||||
"""
|
||||
old_content = open(file_name, encoding='utf-8').read()
|
||||
if file_name.endswith('.data'):
|
||||
new_content = process_data_file(file_name, old_content)
|
||||
else:
|
||||
raise Exception('File type not recognized: {}'
|
||||
.format(file_name))
|
||||
update_file(file_name, old_content, new_content)
|
||||
|
||||
def main(args):
|
||||
for file_name in args:
|
||||
process_file(file_name)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv[1:])
|
|
@ -20,21 +20,10 @@
|
|||
Unit tests for generate_test_code.py
|
||||
"""
|
||||
|
||||
# pylint: disable=wrong-import-order
|
||||
try:
|
||||
# Python 2
|
||||
from StringIO import StringIO
|
||||
except ImportError:
|
||||
# Python 3
|
||||
from io import StringIO
|
||||
from io import StringIO
|
||||
from unittest import TestCase, main as unittest_main
|
||||
try:
|
||||
# Python 2
|
||||
from mock import patch
|
||||
except ImportError:
|
||||
# Python 3
|
||||
from unittest.mock import patch
|
||||
# pylint: enable=wrong-import-order
|
||||
from unittest.mock import patch
|
||||
|
||||
from generate_test_code import gen_dependencies, gen_dependencies_one_line
|
||||
from generate_test_code import gen_function_wrapper, gen_dispatch
|
||||
from generate_test_code import parse_until_pattern, GeneratorInputError
|
||||
|
@ -317,25 +306,16 @@ class StringIOWrapper(StringIO):
|
|||
:return: Line read from file.
|
||||
"""
|
||||
parent = super(StringIOWrapper, self)
|
||||
if getattr(parent, 'next', None):
|
||||
# Python 2
|
||||
line = parent.next()
|
||||
else:
|
||||
# Python 3
|
||||
line = parent.__next__()
|
||||
line = parent.__next__()
|
||||
return line
|
||||
|
||||
# Python 3
|
||||
__next__ = next
|
||||
|
||||
def readline(self, length=0):
|
||||
def readline(self, _length=0):
|
||||
"""
|
||||
Wrap the base class readline.
|
||||
|
||||
:param length:
|
||||
:return:
|
||||
"""
|
||||
# pylint: disable=unused-argument
|
||||
line = super(StringIOWrapper, self).readline()
|
||||
if line is not None:
|
||||
self.line_no += 1
|
||||
|
@ -549,38 +529,6 @@ class ParseFunctionCode(TestCase):
|
|||
Test suite for testing parse_function_code()
|
||||
"""
|
||||
|
||||
def assert_raises_regex(self, exp, regex, func, *args):
|
||||
"""
|
||||
Python 2 & 3 portable wrapper of assertRaisesRegex(p)? function.
|
||||
|
||||
:param exp: Exception type expected to be raised by cb.
|
||||
:param regex: Expected exception message
|
||||
:param func: callable object under test
|
||||
:param args: variable positional arguments
|
||||
"""
|
||||
parent = super(ParseFunctionCode, self)
|
||||
|
||||
# Pylint does not appreciate that the super method called
|
||||
# conditionally can be available in other Python version
|
||||
# then that of Pylint.
|
||||
# Workaround is to call the method via getattr.
|
||||
# Pylint ignores that the method got via getattr is
|
||||
# conditionally executed. Method has to be a callable.
|
||||
# Hence, using a dummy callable for getattr default.
|
||||
dummy = lambda *x: None
|
||||
# First Python 3 assertRaisesRegex is checked, since Python 2
|
||||
# assertRaisesRegexp is also available in Python 3 but is
|
||||
# marked deprecated.
|
||||
for name in ('assertRaisesRegex', 'assertRaisesRegexp'):
|
||||
method = getattr(parent, name, dummy)
|
||||
if method is not dummy:
|
||||
method(exp, regex, func, *args)
|
||||
break
|
||||
else:
|
||||
raise AttributeError(" 'ParseFunctionCode' object has no attribute"
|
||||
" 'assertRaisesRegex' or 'assertRaisesRegexp'"
|
||||
)
|
||||
|
||||
def test_no_function(self):
|
||||
"""
|
||||
Test no test function found.
|
||||
|
@ -593,8 +541,8 @@ function
|
|||
'''
|
||||
stream = StringIOWrapper('test_suite_ut.function', data)
|
||||
err_msg = 'file: test_suite_ut.function - Test functions not found!'
|
||||
self.assert_raises_regex(GeneratorInputError, err_msg,
|
||||
parse_function_code, stream, [], [])
|
||||
self.assertRaisesRegex(GeneratorInputError, err_msg,
|
||||
parse_function_code, stream, [], [])
|
||||
|
||||
def test_no_end_case_comment(self):
|
||||
"""
|
||||
|
@ -609,8 +557,8 @@ void test_func()
|
|||
stream = StringIOWrapper('test_suite_ut.function', data)
|
||||
err_msg = r'file: test_suite_ut.function - '\
|
||||
'end case pattern .*? not found!'
|
||||
self.assert_raises_regex(GeneratorInputError, err_msg,
|
||||
parse_function_code, stream, [], [])
|
||||
self.assertRaisesRegex(GeneratorInputError, err_msg,
|
||||
parse_function_code, stream, [], [])
|
||||
|
||||
@patch("generate_test_code.parse_function_arguments")
|
||||
def test_function_called(self,
|
||||
|
@ -727,8 +675,8 @@ exit:
|
|||
data = 'int entropy_threshold( char * a, data_t * h, int result )'
|
||||
err_msg = 'file: test_suite_ut.function - Test functions not found!'
|
||||
stream = StringIOWrapper('test_suite_ut.function', data)
|
||||
self.assert_raises_regex(GeneratorInputError, err_msg,
|
||||
parse_function_code, stream, [], [])
|
||||
self.assertRaisesRegex(GeneratorInputError, err_msg,
|
||||
parse_function_code, stream, [], [])
|
||||
|
||||
@patch("generate_test_code.gen_dispatch")
|
||||
@patch("generate_test_code.gen_dependencies")
|
||||
|
|
|
@ -24,13 +24,14 @@ or 1 (with a Python backtrace) if there was an operational error.
|
|||
|
||||
import argparse
|
||||
from collections import namedtuple
|
||||
import itertools
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import scripts_path # pylint: disable=unused-import
|
||||
from mbedtls_dev import c_build_helper
|
||||
from mbedtls_dev import macro_collector
|
||||
|
||||
class ReadFileLineException(Exception):
|
||||
def __init__(self, filename, line_number):
|
||||
|
@ -77,7 +78,7 @@ class read_file_lines:
|
|||
raise ReadFileLineException(self.filename, self.line_number) \
|
||||
from exc_value
|
||||
|
||||
class Inputs:
|
||||
class InputsForTest(macro_collector.PSAMacroEnumerator):
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
"""Accumulate information about macros to test.
|
||||
|
||||
|
@ -86,27 +87,29 @@ class Inputs:
|
|||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
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'])
|
||||
self.statuses.add('PSA_SUCCESS')
|
||||
self.algorithms.add('0xffffffff')
|
||||
self.ecc_curves.add('0xff')
|
||||
self.dh_groups.add('0xff')
|
||||
self.key_types.add('0xffff')
|
||||
self.key_usage_flags.add('0x80000000')
|
||||
|
||||
# Hard-coded values for unknown algorithms
|
||||
#
|
||||
# These have to have values that are correct for their respective
|
||||
# PSA_ALG_IS_xxx macros, but are also not currently assigned and are
|
||||
# not likely to be assigned in the near future.
|
||||
self.hash_algorithms = set(['0x020000fe']) # 0x020000ff is PSA_ALG_ANY_HASH
|
||||
self.mac_algorithms = set(['0x0300ffff'])
|
||||
self.ka_algorithms = set(['0x09fc0000'])
|
||||
self.kdf_algorithms = set(['0x080000ff'])
|
||||
self.hash_algorithms.add('0x020000fe') # 0x020000ff is PSA_ALG_ANY_HASH
|
||||
self.mac_algorithms.add('0x03007fff')
|
||||
self.ka_algorithms.add('0x09fc0000')
|
||||
self.kdf_algorithms.add('0x080000ff')
|
||||
# For AEAD algorithms, the only variability is over the tag length,
|
||||
# and this only applies to known algorithms, so don't test an
|
||||
# unknown algorithm.
|
||||
self.aead_algorithms = set()
|
||||
|
||||
# Identifier prefixes
|
||||
self.table_by_prefix = {
|
||||
'ERROR': self.statuses,
|
||||
|
@ -139,13 +142,10 @@ class Inputs:
|
|||
'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'],
|
||||
}
|
||||
self.arguments_for['mac_length'] += ['1', '63']
|
||||
self.arguments_for['min_mac_length'] += ['1', '63']
|
||||
self.arguments_for['tag_length'] += ['1', '63']
|
||||
self.arguments_for['min_tag_length'] += ['1', '63']
|
||||
|
||||
def get_names(self, type_word):
|
||||
"""Return the set of known names of values of the given type."""
|
||||
|
@ -158,62 +158,6 @@ class Inputs:
|
|||
'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 = \
|
||||
|
@ -226,11 +170,11 @@ class Inputs:
|
|||
_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_AEAD_WITH_DEFAULT_LENGTH_TAG',
|
||||
'PSA_ALG_FULL_LENGTH_MAC',
|
||||
# Auxiliary macro whose name doesn't fit the usual patterns for
|
||||
# auxiliary macros.
|
||||
'PSA_ALG_AEAD_WITH_DEFAULT_TAG_LENGTH_CASE',
|
||||
'PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG_CASE',
|
||||
])
|
||||
def parse_header_line(self, line):
|
||||
"""Parse a C header line, looking for "#define PSA_xxx"."""
|
||||
|
@ -298,7 +242,7 @@ class Inputs:
|
|||
if m:
|
||||
self.add_test_case_line(m.group(1), m.group(2))
|
||||
|
||||
def gather_inputs(headers, test_suites, inputs_class=Inputs):
|
||||
def gather_inputs(headers, test_suites, inputs_class=InputsForTest):
|
||||
"""Read the list of inputs to test psa_constant_names with."""
|
||||
inputs = inputs_class()
|
||||
for header in headers:
|
||||
|
@ -308,63 +252,23 @@ def gather_inputs(headers, test_suites, inputs_class=Inputs):
|
|||
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 = []
|
||||
"""Generate and run a program to print out numerical values of C expressions."""
|
||||
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)
|
||||
return c_build_helper.get_c_expression_values(
|
||||
cast_to, printf_format,
|
||||
expressions,
|
||||
caller='test_psa_constant_names.py for {} values'.format(type_word),
|
||||
file_label=type_word,
|
||||
header='#include <psa/crypto.h>',
|
||||
include_path=include_path,
|
||||
keep_c=keep_c
|
||||
)
|
||||
|
||||
NORMALIZE_STRIP_RE = re.compile(r'\s+')
|
||||
def normalize(expr):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue