Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Shell scripts must keep LF line endings, they are run by bash on all platforms.
*.sh text eol=lf
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ jobs:
- name: Prepare
run: |
sudo apt-get update -qq
sudo apt-get install -y libcppunit-dev libssl-dev p11-kit
sudo apt-get install -y libcppunit-dev libssl-dev p11-kit pkcs11-provider
- name: Build
# Once all OpenSSL deprecations fixed, uncomment this
# env:
Expand Down
1 change: 1 addition & 0 deletions src/bin/util/test/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/tokens-*/
13 changes: 12 additions & 1 deletion src/bin/util/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,17 @@ add_test(
bash ${CMAKE_CURRENT_SOURCE_DIR}/mlkem512-import-key-test.sh
)

add_test(
NAME ${PROJECT_NAME}-p11prov
COMMAND ${CMAKE_COMMAND} -E env
top_builddir=${CMAKE_BINARY_DIR}
top_srcdir=${CMAKE_SOURCE_DIR}
srcdir=${CMAKE_CURRENT_SOURCE_DIR}
builddir=${CMAKE_CURRENT_BINARY_DIR}
bash ${CMAKE_CURRENT_SOURCE_DIR}/p11prov-test.sh
)

# Make tests returning code 77 considered as skipped, to avoid marking the test suite as failed when running on an unsupported OpenSSL version or with a Botan-based SoftHSM2.
set_tests_properties(${PROJECT_NAME}-ml-dsa PROPERTIES SKIP_RETURN_CODE 77)
set_tests_properties(${PROJECT_NAME}-ml-kem PROPERTIES SKIP_RETURN_CODE 77)
set_tests_properties(${PROJECT_NAME}-ml-kem PROPERTIES SKIP_RETURN_CODE 77)
set_tests_properties(${PROJECT_NAME}-p11prov PROPERTIES SKIP_RETURN_CODE 77)
6 changes: 4 additions & 2 deletions src/bin/util/test/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ MAINTAINERCLEANFILES = $(srcdir)/Makefile.in

TESTS_ENVIRONMENT = top_builddir='$(top_builddir)' top_srcdir='$(top_srcdir)' srcdir='$(srcdir)' builddir='$(builddir)'

TESTS = mldsa44-import-key-test.sh mlkem512-import-key-test.sh
TESTS = mldsa44-import-key-test.sh mlkem512-import-key-test.sh p11prov-test.sh

check_SCRIPTS = $(TESTS)

EXTRA_DIST = $(srcdir)/CMakeLists.txt \
$(srcdir)/test-common.sh \
$(srcdir)/import-key-test-common.sh \
$(srcdir)/mldsa44-import-key-test.sh \
$(srcdir)/mlkem512-import-key-test.sh
$(srcdir)/mlkem512-import-key-test.sh \
$(srcdir)/p11prov-test.sh
133 changes: 7 additions & 126 deletions src/bin/util/test/import-key-test-common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,78 +18,13 @@ if [[ "$CI" == "true" ]] && [[ "$CI_TEST_ENABLED" != "true" ]] ; then
exit 77
fi

# ---------- locate the SoftHSM library ----------
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/test-common.sh"

if test "$RUNNER_OS" = "Windows" ; then
if test -n "${CONFIG:-${CMAKE_BUILD_TYPE:-}}" ; then
_PROBE_DIRS="${CONFIG:-$CMAKE_BUILD_TYPE}"
else
_PROBE_DIRS="Debug Release RelWithDebInfo MinSizeRel"
fi
D=
for _CFG in $_PROBE_DIRS ; do
if test -d "../../../lib/$_CFG/" ; then
D=$(cd "../../../lib/$_CFG/" 2>/dev/null && pwd)
test -n "$D" || continue
CONFIG="$_CFG"
break
fi
done
else
if ! D=$(cd ../../../lib/.libs/ 2>/dev/null && pwd) ; then
D=
fi
fi

if test -z "$D" ; then
echo "unexpectedly missing library directory" >&2
exit 99
fi

P11MODULE=
for S in so dll ; do
for F in "$D"/*softhsm2."$S" ; do
test -f "$F" || continue
P11MODULE="$F"
break
done
test -n "$P11MODULE" && break
done
if test -z "$P11MODULE" ; then
echo "error: unexpected module suffix" >&2
exit 1
fi
if command -v realpath > /dev/null ; then
P11MODULE=$(realpath "$P11MODULE")
fi

IS_BOTAN=$(strings "$P11MODULE" | grep botan | wc -l)
if [[ "$IS_BOTAN" -gt 0 ]] ; then
if is_botan_module ; then
echo "This test is not compatible with the Botan2-based SoftHSM2, skipping." >&2
exit 77
fi

CWD=$(pwd)

# ---------- binaries ----------

OPENSSL=${OPENSSL-openssl}
OPENSSL=$(command -v "$OPENSSL")
if test -z "$OPENSSL" ; then
echo "error: openssl utility not found" >&2
exit 1
fi

openssl() {
"$OPENSSL" ${1+"$@"}
}

openssl_version=$(openssl version) || exit $?
if test -z "$openssl_version" ; then
echo "cannot determine OpenSSL version" >&2
exit 1
fi

case $openssl_version in
*"OpenSSL 0.9."*|\
*"OpenSSL 1."*|\
Expand All @@ -104,51 +39,9 @@ case $openssl_version in
esac
# NOTE OpenSSL > 3.5

# ---------- helper functions ----------

softhsm2_tool() {
if test "$RUNNER_OS" = "Windows" ; then
"$CWD"/../"${CONFIG:-Debug}"/softhsm2-util.exe --module "$P11MODULE" ${1+"$@"}
else
"$CWD"/../softhsm2-util --module "$P11MODULE" ${1+"$@"}
fi
}

clean_tokendir() {
rm -rf "$TOKEN_DIR"
}

# ---------- token configuration ----------

TOKEN_DIR="$CWD"/"$TOKEN_DIR_NAME"
clean_tokendir
export SOFTHSM2_CONF="$TOKEN_DIR"/softhsm2.conf

mkdir -p "$TOKEN_DIR"

if test "$RUNNER_OS" = "Windows" ; then
WINDOWS_TOKEN_DIR=$(realpath "$TOKEN_DIR")
WINDOWS_TOKEN_DIR=$(cygpath -w "$WINDOWS_TOKEN_DIR")
cat > "$SOFTHSM2_CONF" <<EOF
directories.tokendir = $WINDOWS_TOKEN_DIR
objectstore.backend = file
slots.removable = false
slots.mechanisms = ALL
log.level = DEBUG
log.file = $WINDOWS_TOKEN_DIR\token.log
EOF
else
cat > "$SOFTHSM2_CONF" <<EOF
directories.tokendir = $TOKEN_DIR
objectstore.backend = file
slots.removable = false
slots.mechanisms = ALL
log.level = DEBUG
log.file = $TOKEN_DIR/token.log
EOF
fi

SOFTHSM2_CONF=$(realpath "$SOFTHSM2_CONF")
setup_tokendir

# ---------- execution ----------

Expand All @@ -161,29 +54,17 @@ IMPORT_OUT="$TOKEN_DIR"/import.out
INIT_OUT="$TOKEN_DIR"/init.out

if ! softhsm2_tool --init-token --label test0 --slot free --so-pin 12345678 --pin $TOKEN_PIN >"$INIT_OUT" 2>&1; then
echo "Failed to init token"
cat "$INIT_OUT"
cat "$TOKEN_DIR/token.log"
clean_tokendir
exit 1
fail "Failed to init token" "$INIT_OUT"
fi

openssl genpkey -algorithm "$OPENSSL_ALGO" -out "$KEY_FILE"

if ! softhsm2_tool --import "$KEY_FILE" --import-type keypair --id $TOKEN_ID --label test_key --token test0 --pin $TOKEN_PIN >"$IMPORT_OUT" 2>&1; then
echo "Failed to import $ALGO_NAME key"
cat "$IMPORT_OUT"
cat "$TOKEN_DIR/token.log"
clean_tokendir
exit 1
fail "Failed to import $ALGO_NAME key" "$IMPORT_OUT"
fi

if ! grep -q "The $ALGO_NAME key pair with label=test_key has been imported." "$IMPORT_OUT"; then
echo "ERROR: Expected $ALGO_NAME import success message"
cat "$IMPORT_OUT"
cat "$TOKEN_DIR/token.log"
clean_tokendir
exit 1
fail "ERROR: Expected $ALGO_NAME import success message" "$IMPORT_OUT"
fi

clean_tokendir
clean_tokendir
126 changes: 126 additions & 0 deletions src/bin/util/test/p11prov-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
#! /bin/bash
#
# Copyright (c) 2026 SoftHSMv2 contributors
#
# SPDX-License-Identifier: BSD-2-Clause
#
# Regression test for the process-exit crash reported in issues #729 and
# #780: an OpenSSL application using the pkcs11-provider closes its cached
# PKCS#11 sessions and finalises the module from an atexit handler, after
# the C++ runtime has started destroying static objects. SoftHSM2 must stay
# usable until C_Finalize is called, so the openssl process must exit
# cleanly.
#
# Requires OpenSSL 3 and the pkcs11-provider module. The provider is
# looked up in the OpenSSL MODULESDIR; set PKCS11_PROVIDER_MODULE to
# override the location. The test is skipped when either is missing.

. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/test-common.sh"

case $openssl_version in
*"OpenSSL 0.9."*|\
*"OpenSSL 1."*)
echo "$openssl_version has no provider support, skipping." >&2
exit 77
;;
*"OpenSSL "*)
;;
*)
echo "unsupported: $openssl_version, skipping." >&2
exit 77
;;
esac

# ---------- locate the PKCS#11 provider ----------

if test -n "$PKCS11_PROVIDER_MODULE" ; then
if ! test -f "$PKCS11_PROVIDER_MODULE" ; then
echo "error: PKCS11_PROVIDER_MODULE=$PKCS11_PROVIDER_MODULE does not exist" >&2
exit 1
fi
else
MODULESDIR=$(openssl version -m | sed -n 's/^MODULESDIR: *"\{0,1\}\([^"]*\)"\{0,1\}$/\1/p')
if test -z "$MODULESDIR" ; then
echo "cannot determine OpenSSL MODULESDIR, skipping." >&2
exit 77
fi
for N in pkcs11 libpkcs11 ; do
for S in so dll ; do
test -f "$MODULESDIR/$N.$S" || continue
PKCS11_PROVIDER_MODULE="$MODULESDIR/$N.$S"
break
done
test -n "$PKCS11_PROVIDER_MODULE" && break
done
if test -z "$PKCS11_PROVIDER_MODULE" ; then
echo "pkcs11-provider not found in $MODULESDIR, skipping." >&2
exit 77
fi
fi

# ---------- token and OpenSSL configuration ----------

TOKEN_DIR_NAME="tokens-p11prov"
setup_tokendir

OPENSSL_CONF="$TOKEN_DIR"/openssl.cnf
cat > "$OPENSSL_CONF" <<EOC
openssl_conf = openssl_init

[openssl_init]
providers = provider_sect

[provider_sect]
default = default_sect
pkcs11 = pkcs11_sect

[default_sect]
activate = 1

[pkcs11_sect]
module = $(native_path "$PKCS11_PROVIDER_MODULE")
pkcs11-module-path = $(native_path "$P11MODULE")
activate = 1
EOC
export OPENSSL_CONF

# ---------- execution ----------

set -e

TOKEN_PIN=4321
TOKEN_ID=01
KEY_URI="pkcs11:id=%$TOKEN_ID;type=private"
KEY_FILE="$TOKEN_DIR"/openssl_test_key
DATA_FILE="$TOKEN_DIR"/data
SIG_FILE="$TOKEN_DIR"/data.sig
INIT_OUT="$TOKEN_DIR"/init.out
IMPORT_OUT="$TOKEN_DIR"/import.out
SIGN_OUT="$TOKEN_DIR"/sign.out
VERIFY_OUT="$TOKEN_DIR"/verify.out

if ! softhsm2_tool --init-token --label test0 --slot free --so-pin 12345678 --pin $TOKEN_PIN >"$INIT_OUT" 2>&1; then
fail "Failed to init token" "$INIT_OUT"
fi

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out "$KEY_FILE" 2>/dev/null

if ! softhsm2_tool --import "$KEY_FILE" --import-type keypair --id $TOKEN_ID --label test_key --token test0 --pin $TOKEN_PIN >"$IMPORT_OUT" 2>&1; then
fail "Failed to import RSA key" "$IMPORT_OUT"
fi

echo "SoftHSM2 pkcs11-provider exit test" > "$DATA_FILE"

# Sign through the provider. The process must exit cleanly: before the fix
# for #729/#780 it produced the signature and then crashed with SIGSEGV in
# C_CloseSession during OpenSSL's atexit cleanup.
openssl pkeyutl -sign -inkey "$KEY_URI" -passin pass:$TOKEN_PIN -rawin -in "$DATA_FILE" -out "$SIG_FILE" >"$SIGN_OUT" 2>&1 \
|| fail "openssl pkeyutl -sign via pkcs11-provider failed with exit status $?" "$SIGN_OUT"

# Verify with the software copy of the key to make sure the signature
# really came from the imported key.
if ! openssl pkeyutl -verify -inkey "$KEY_FILE" -rawin -in "$DATA_FILE" -sigfile "$SIG_FILE" >"$VERIFY_OUT" 2>&1; then
fail "Signature verification failed" "$VERIFY_OUT"
fi

clean_tokendir
Loading
Loading