From cab3f3bc079b228d7ad9098a97250341b092d597 Mon Sep 17 00:00:00 2001 From: Michal Nowak Date: Tue, 23 Jul 2024 16:17:40 +0200 Subject: [PATCH] Rewrite keyfromlabel system test to pytest (cherry picked from commit 409f394d6eb5352f1f7ca38af43b166bc4a1a3bd) --- .gitlab-ci.yml | 2 +- bin/tests/system/isctest/mark.py | 11 + bin/tests/system/isctest/run.py | 5 + bin/tests/system/keyfromlabel/setup.sh | 23 -- bin/tests/system/keyfromlabel/tests.sh | 91 -------- .../system/keyfromlabel/tests_keyfromlabel.py | 199 ++++++++++++++++++ .../keyfromlabel/tests_sh_keyfromlabel.py | 32 --- .../prereq.sh => vulture_ignore_list.py} | 13 +- 8 files changed, 218 insertions(+), 158 deletions(-) delete mode 100644 bin/tests/system/keyfromlabel/setup.sh delete mode 100644 bin/tests/system/keyfromlabel/tests.sh create mode 100644 bin/tests/system/keyfromlabel/tests_keyfromlabel.py delete mode 100644 bin/tests/system/keyfromlabel/tests_sh_keyfromlabel.py rename bin/tests/system/{keyfromlabel/prereq.sh => vulture_ignore_list.py} (53%) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d409d8fdf1..e0c4e37aa8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -587,7 +587,7 @@ pylint: script: - pylint --rcfile $CI_PROJECT_DIR/.pylintrc $(git ls-files '*.py' | grep -vE '(ans\.py|dangerfile\.py|^bin/tests/system/|^contrib/)') # Ignore Pylint wrong-import-position error in system test to enable use of pytest.importorskip - - pylint --rcfile $CI_PROJECT_DIR/.pylintrc --disable=wrong-import-position $(git ls-files 'bin/tests/system/*.py' | grep -vE 'ans\.py') + - pylint --rcfile $CI_PROJECT_DIR/.pylintrc --disable=wrong-import-position $(git ls-files 'bin/tests/system/*.py' | grep -vE '(ans\.py|vulture_ignore_list\.py)') reuse: <<: *precheck_job diff --git a/bin/tests/system/isctest/mark.py b/bin/tests/system/isctest/mark.py index fd6f620276..7a8af8bf0f 100644 --- a/bin/tests/system/isctest/mark.py +++ b/bin/tests/system/isctest/mark.py @@ -13,6 +13,7 @@ import os from pathlib import Path +import shutil import ssl import subprocess @@ -71,6 +72,16 @@ supported_openssl_version = pytest.mark.skipif( ) +softhsm2_environment = pytest.mark.skipif( + not ( + os.getenv("SOFTHSM2_CONF") + and os.getenv("SOFTHSM2_MODULE") + and shutil.which("pkcs11-tool") + and shutil.which("softhsm2-util") + ), + reason="SOFTHSM2_CONF and SOFTHSM2_MODULE environmental variables must be set and pkcs11-tool and softhsm2-util tools present", +) + try: import flaky as flaky_pkg # type: ignore except ModuleNotFoundError: diff --git a/bin/tests/system/isctest/run.py b/bin/tests/system/isctest/run.py index cdf0029b8e..8430981e93 100644 --- a/bin/tests/system/isctest/run.py +++ b/bin/tests/system/isctest/run.py @@ -30,6 +30,7 @@ def cmd( log_stderr=True, input_text: Optional[bytes] = None, raise_on_exception=True, + env: Optional[dict] = None, ): """Execute a command with given args as subprocess.""" isctest.log.debug(f"command: {' '.join(args)}") @@ -45,6 +46,9 @@ def cmd( f"~~~ cmd stderr ~~~\n{procdata.stderr.decode('utf-8')}\n~~~~~~~~~~~~~~~~~~" ) + if env is None: + env = dict(os.environ) + try: proc = subprocess.run( args, @@ -54,6 +58,7 @@ def cmd( check=True, cwd=cwd, timeout=timeout, + env=env, ) print_debug_logs(proc) return proc diff --git a/bin/tests/system/keyfromlabel/setup.sh b/bin/tests/system/keyfromlabel/setup.sh deleted file mode 100644 index eae23c08b6..0000000000 --- a/bin/tests/system/keyfromlabel/setup.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/sh -# -# Copyright (C) Internet Systems Consortium, Inc. ("ISC") -# -# SPDX-License-Identifier: MPL-2.0 -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, you can obtain one at https://mozilla.org/MPL/2.0/. -# -# See the COPYRIGHT file distributed with this work for additional -# information regarding copyright ownership. - -# shellcheck source=conf.sh -. ../conf.sh - -set -e - -softhsm2-util --delete-token --token "softhsm2-keyfromlabel" >/dev/null 2>&1 || echo_i "softhsm2-keyfromlabel token not found for cleaning" -softhsm2-util --init-token --free --pin 1234 --so-pin 1234 --label "softhsm2-keyfromlabel" | awk '/^The token has been initialized and is reassigned to slot/ { print $NF }' - -printf '%s' "${HSMPIN:-1234}" >pin -PWD=$(pwd) diff --git a/bin/tests/system/keyfromlabel/tests.sh b/bin/tests/system/keyfromlabel/tests.sh deleted file mode 100644 index 6c38ec6e6b..0000000000 --- a/bin/tests/system/keyfromlabel/tests.sh +++ /dev/null @@ -1,91 +0,0 @@ -#!/bin/sh -# -# Copyright (C) Internet Systems Consortium, Inc. ("ISC") -# -# SPDX-License-Identifier: MPL-2.0 -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, you can obtain one at https://mozilla.org/MPL/2.0/. -# -# See the COPYRIGHT file distributed with this work for additional -# information regarding copyright ownership. - -set -e - -# shellcheck source=conf.sh -. ../conf.sh - -PWD=$(pwd) - -keygen() { - type="$1" - bits="$2" - zone="$3" - id="$4" - - label="${id}-${zone}" - p11id=$(echo "${label}" | openssl sha1 -r | awk '{print $1}') - pkcs11-tool --module $SOFTHSM2_MODULE --token-label "softhsm2-keyfromlabel" -l -k --key-type $type:$bits --label "${label}" --id "${p11id}" --pin $(cat $PWD/pin) >pkcs11-tool.out.$zone.$id || return 1 -} - -keyfromlabel() { - alg="$1" - zone="$2" - id="$3" - shift 3 - - $KEYFRLAB -E pkcs11 -a $alg -l "token=softhsm2-keyfromlabel;object=${id}-${zone};pin-source=$PWD/pin" "$@" $zone >>keyfromlabel.out.$zone.$id 2>>/dev/null || return 1 - cat keyfromlabel.out.$zone.$id -} - -infile="template.db.in" -for algtypebits in rsasha256:rsa:2048 rsasha512:rsa:2048 \ - ecdsap256sha256:EC:prime256v1 ecdsap384sha384:EC:prime384v1; do # Edwards curves are not yet supported by OpenSC - # ed25519:EC:edwards25519 ed448:EC:edwards448 - alg=$(echo "$algtypebits" | cut -f 1 -d :) - type=$(echo "$algtypebits" | cut -f 2 -d :) - bits=$(echo "$algtypebits" | cut -f 3 -d :) - - if $SHELL ../testcrypto.sh $alg; then - zone="$alg.example" - zonefile="zone.$alg.example.db" - ret=0 - - echo_i "Generate keys $alg $type:$bits for zone $zone" - keygen $type $bits $zone keyfromlabel-zsk || ret=1 - keygen $type $bits $zone keyfromlabel-ksk || ret=1 - test "$ret" -eq 0 || echo_i "failed" - status=$((status + ret)) - - # Skip dnssec-keyfromlabel if key generation failed. - test $ret -eq 0 || continue - - echo_i "Get ZSK $alg $zone $type:$bits" - ret=0 - zsk=$(keyfromlabel $alg $zone keyfromlabel-zsk) - test -z "$zsk" && ret=1 - test "$ret" -eq 0 || echo_i "failed (zsk=$zsk)" - status=$((status + ret)) - - echo_i "Get KSK $alg $zone $type:$bits" - ret=0 - ksk=$(keyfromlabel $alg $zone keyfromlabel-ksk -f KSK) - test -z "$ksk" && ret=1 - test "$ret" -eq 0 || echo_i "failed (ksk=$ksk)" - status=$((status + ret)) - - # Skip signing if dnssec-keyfromlabel failed. - test $ret -eq 0 || continue - - echo_i "Sign zone with $ksk $zsk" - ret=0 - cat "$infile" "$ksk.key" "$zsk.key" >"$zonefile" - $SIGNER -E pkcs11 -S -a -g -o "$zone" "$zonefile" >signer.out.$zone || ret=1 - test "$ret" -eq 0 || echo_i "failed" - status=$((status + ret)) - fi -done - -echo_i "exit status: $status" -[ $status -eq 0 ] || exit 1 diff --git a/bin/tests/system/keyfromlabel/tests_keyfromlabel.py b/bin/tests/system/keyfromlabel/tests_keyfromlabel.py new file mode 100644 index 0000000000..705dff2934 --- /dev/null +++ b/bin/tests/system/keyfromlabel/tests_keyfromlabel.py @@ -0,0 +1,199 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +import hashlib +import os +import re +import shutil + +import pytest + +import isctest.mark + + +pytestmark = [ + isctest.mark.supported_openssl_version, + isctest.mark.softhsm2_environment, + pytest.mark.extra_artifacts( + [ + "*.example.db", + "*.example.db.signed", + "K*", + "dsset-*", + "keyfromlabel.out.*", + "pin", + "pkcs11-tool.out.*", + "signer.out.*", + ], + ), +] + + +EMPTY_OPENSSL_CONF_ENV = {**os.environ, "OPENSSL_CONF": ""} + +HSMPIN = "1234" + + +@pytest.fixture(autouse=True) +def token_init_and_cleanup(): + + # Create pin file for the $KEYFRLAB command + with open("pin", "w", encoding="utf-8") as pinfile: + pinfile.write(HSMPIN) + + token_init_command = [ + "softhsm2-util", + "--init-token", + "--free", + "--pin", + HSMPIN, + "--so-pin", + HSMPIN, + "--label", + "softhsm2-keyfromlabel", + ] + + token_cleanup_command = [ + "softhsm2-util", + "--delete-token", + "--token", + "softhsm2-keyfromlabel", + ] + + isctest.run.cmd( + token_cleanup_command, + env=EMPTY_OPENSSL_CONF_ENV, + log_stderr=False, + raise_on_exception=False, + ) + + try: + output = isctest.run.cmd( + token_init_command, env=EMPTY_OPENSSL_CONF_ENV, log_stdout=True + ).stdout.decode("utf-8") + assert "The token has been initialized and is reassigned to slot" in output + yield + finally: + output = isctest.run.cmd( + token_cleanup_command, env=EMPTY_OPENSSL_CONF_ENV, log_stdout=True + ).stdout.decode("utf-8") + assert re.search("Found token (.*) with matching token label", output) + assert re.search("The token (.*) has been deleted", output) + + +# pylint: disable-msg=too-many-locals +@pytest.mark.parametrize( + "alg_name,alg_type,alg_bits", + [ + ("rsasha256", "rsa", "2048"), + ("rsasha512", "rsa", "2048"), + ("ecdsap256sha256", "EC", "prime256v1"), + ("ecdsap384sha384", "EC", "prime384v1"), + # Edwards curves are not yet supported by OpenSC + # ("ed25519","EC","edwards25519"), + # ("ed448","EC","edwards448") + ], +) +def test_keyfromlabel(alg_name, alg_type, alg_bits): + + def keygen(alg_type, alg_bits, zone, key_id): + label = f"{key_id}-{zone}" + p11_id = hashlib.sha1(label.encode("utf-8")).hexdigest() + + pkcs11_command = [ + "pkcs11-tool", + "--module", + os.environ.get("SOFTHSM2_MODULE"), + "--token-label", + "softhsm2-keyfromlabel", + "-l", + "-k", + "--key-type", + f"{alg_type}:{alg_bits}", + "--label", + label, + "--id", + p11_id, + "--pin", + HSMPIN, + ] + + output = isctest.run.cmd( + pkcs11_command, env=EMPTY_OPENSSL_CONF_ENV, log_stdout=True + ).stdout.decode("utf-8") + + assert "Key pair generated" in output + + def keyfromlabel(alg_name, zone, key_id, key_flag): + key_flag = key_flag.split() if key_flag else [] + + keyfrlab_command = [ + os.environ["KEYFRLAB"], + "-E", + "pkcs11", + "-a", + alg_name, + "-l", + f"pkcs11:token=softhsm2-keyfromlabel;object={key_id}-{zone};pin-source=pin", + *key_flag, + zone, + ] + + output = isctest.run.cmd(keyfrlab_command, log_stdout=True) + output_decoded = output.stdout.decode("utf-8").rstrip() + ".key" + + assert os.path.exists(output_decoded) + + return output_decoded + + if ( + isctest.run.cmd( + [os.environ["SHELL"], "../testcrypto.sh", alg_name], + raise_on_exception=False, + ).returncode + != 0 + ): + pytest.skip(f"{alg_name} is not supported") + + # Generate keys for the $zone zone + zone = f"{alg_name}.example" + + keygen(alg_type, alg_bits, zone, "keyfromlabel-zsk") + keygen(alg_type, alg_bits, zone, "keyfromlabel-ksk") + + # Get ZSK + zsk_file = keyfromlabel(alg_name, zone, "keyfromlabel-zsk", "") + + # Get KSK + ksk_file = keyfromlabel(alg_name, zone, "keyfromlabel-ksk", "-f KSK") + + # Sign zone with KSK and ZSK + zone_file = f"zone.{alg_name}.example.db" + + with open(zone_file, "w", encoding="utf-8") as outfile: + for f in ["template.db.in", ksk_file, zsk_file]: + with open(f, "r", encoding="utf-8") as fd: + shutil.copyfileobj(fd, outfile) + + signer_command = [ + os.environ["SIGNER"], + "-E", + "pkcs11", + "-S", + "-a", + "-g", + "-o", + zone, + zone_file, + ] + isctest.run.cmd(signer_command, log_stdout=True) + + assert os.path.exists(f"{zone_file}.signed") diff --git a/bin/tests/system/keyfromlabel/tests_sh_keyfromlabel.py b/bin/tests/system/keyfromlabel/tests_sh_keyfromlabel.py deleted file mode 100644 index 133569bf1a..0000000000 --- a/bin/tests/system/keyfromlabel/tests_sh_keyfromlabel.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (C) Internet Systems Consortium, Inc. ("ISC") -# -# SPDX-License-Identifier: MPL-2.0 -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, you can obtain one at https://mozilla.org/MPL/2.0/. -# -# See the COPYRIGHT file distributed with this work for additional -# information regarding copyright ownership. - -import pytest - -import isctest.mark - -pytestmark = pytest.mark.extra_artifacts( - [ - "*.example.db", - "*.example.db.signed", - "K*", - "dsset-*", - "keyfromlabel.out.*", - "pin", - "pkcs11-tool.out.*", - "signer.out.*", - ] -) - - -@isctest.mark.supported_openssl_version -def test_keyfromlabel(run_tests_sh): - run_tests_sh() diff --git a/bin/tests/system/keyfromlabel/prereq.sh b/bin/tests/system/vulture_ignore_list.py similarity index 53% rename from bin/tests/system/keyfromlabel/prereq.sh rename to bin/tests/system/vulture_ignore_list.py index 66a7f817aa..f9b3dadf28 100644 --- a/bin/tests/system/keyfromlabel/prereq.sh +++ b/bin/tests/system/vulture_ignore_list.py @@ -1,21 +1,12 @@ -#!/bin/sh -e -# # Copyright (C) Internet Systems Consortium, Inc. ("ISC") # # SPDX-License-Identifier: MPL-2.0 # # This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this +# License, v. 2.0. If a copy of the MPL was not distributed with this # file, you can obtain one at https://mozilla.org/MPL/2.0/. # # See the COPYRIGHT file distributed with this work for additional # information regarding copyright ownership. -. ../conf.sh - -if [ -n "${SOFTHSM2_MODULE}" ] && command -v pkcs11-tool >/dev/null && command -v softhsm2-util >/dev/null; then - exit 0 -fi - -echo_i "skip: softhsm2-util or pkcs11-tool not available" -exit 255 +token_init_and_cleanup # unused function (keyfromlabel/tests_keyfromlabel.py:43)