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
Binary file added certs/slhdsa/entity-slhdsa-shake-128f-priv.der
Binary file not shown.
4 changes: 4 additions & 0 deletions certs/slhdsa/entity-slhdsa-shake-128f-priv.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-----BEGIN PRIVATE KEY-----
MFICAQAwCwYJYIZIAWUDBAMbBEBRJWNRVa9SISo3g3sk/GEnwHJR7f26z5qDX5v9
QaTCdhp4N9VSWJzaIVXWZSs2Atxlng8nDbMnaTuaqm3GqNYE
-----END PRIVATE KEY-----
Binary file added certs/slhdsa/entity-slhdsa-shake-128f.der
Binary file not shown.
370 changes: 370 additions & 0 deletions certs/slhdsa/entity-slhdsa-shake-128f.pem

Large diffs are not rendered by default.

144 changes: 144 additions & 0 deletions certs/slhdsa/gen-slhdsa-entity-cert.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/* gen-slhdsa-entity-cert.c
*
* Regenerate the self-signed SLH-DSA-SHAKE-128f entity certificate and key
* used by tests/test-tls13-slhdsa-entity.conf.
*
* OpenSSL < 3.5 cannot emit SLH-DSA certificates, so wolfSSL's own certificate
* generation is used here instead. SLH-DSA-SHAKE-128f is chosen because its
* ~17KB signature makes the TLS 1.3 CertificateVerify message exceed a single
* record, exercising fragmented CertificateVerify send + reassembly.
*
* Build (from the wolfSSL source root, after configuring with SLH-DSA + cert
* generation, e.g. ./configure --enable-slhdsa --enable-certgen --enable-keygen
* --enable-certext):
*
* gcc certs/slhdsa/gen-slhdsa-entity-cert.c -I. \
* -L./src/.libs -lwolfssl -lm -o gen-slhdsa-entity-cert
* LD_LIBRARY_PATH=./src/.libs ./gen-slhdsa-entity-cert
*
* The committed PEM/DER fixtures under this directory are authoritative; this
* program is for renewal only.
*/
#include <wolfssl/options.h>
#include <wolfssl/wolfcrypt/settings.h>
#include <wolfssl/wolfcrypt/asn_public.h>
#include <wolfssl/wolfcrypt/wc_slhdsa.h>
#include <wolfssl/wolfcrypt/random.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define GEN_BUF_SZ 70000

static int save(const char* path, const byte* buf, int sz)
{
FILE* f = fopen(path, "wb");
if (f == NULL) {
printf("Unable to open %s for writing\n", path);
return -1;
}
fwrite(buf, 1, (size_t)sz, f);
fclose(f);
return 0;
}

int main(void)
{
WC_RNG rng;
SlhDsaKey key;
Cert cert;
byte* der = NULL;
byte* keyder = NULL;
byte* pem = NULL;
int ret, certSz, keySz, pemSz;

wolfCrypt_Init();

if (wc_InitRng(&rng) != 0) {
printf("RNG init failed\n");
return 1;
}
if ((ret = wc_SlhDsaKey_Init(&key, SLHDSA_SHAKE128F, NULL,
INVALID_DEVID)) != 0) {
printf("wc_SlhDsaKey_Init failed: %d\n", ret);
return 1;
}
if ((ret = wc_SlhDsaKey_MakeKey(&key, &rng)) != 0) {
printf("wc_SlhDsaKey_MakeKey failed: %d\n", ret);
return 1;
}

der = (byte*)malloc(GEN_BUF_SZ);
keyder = (byte*)malloc(GEN_BUF_SZ);
pem = (byte*)malloc(GEN_BUF_SZ);
if (der == NULL || keyder == NULL || pem == NULL) {
printf("Out of memory\n");
return 1;
}

/* PKCS#8 private key (DER + PEM). */
keySz = wc_SlhDsaKey_PrivateKeyToDer(&key, keyder, GEN_BUF_SZ);
if (keySz < 0) {
printf("wc_SlhDsaKey_PrivateKeyToDer failed: %d\n", keySz);
return 1;
}
save("certs/slhdsa/entity-slhdsa-shake-128f-priv.der", keyder, keySz);

/* Self-signed certificate (DER + PEM). */
wc_InitCert(&cert);
strncpy(cert.subject.country, "US", CTC_NAME_SIZE);
strncpy(cert.subject.state, "Montana", CTC_NAME_SIZE);
strncpy(cert.subject.locality, "Bozeman", CTC_NAME_SIZE);
strncpy(cert.subject.org, "wolfSSL_SLH-DSA", CTC_NAME_SIZE);
strncpy(cert.subject.unit, "Entity-SLHDSA-shake-128f", CTC_NAME_SIZE);
strncpy(cert.subject.commonName, "www.wolfssl.com", CTC_NAME_SIZE);
strncpy(cert.subject.email, "facts@wolfssl.com", CTC_NAME_SIZE);
cert.daysValid = 1000;
cert.selfSigned = 1;
cert.isCA = 1; /* self-signed, also usable as its own trust anchor */
cert.sigType = CTC_SLH_DSA_SHAKE_128F;

if ((ret = wc_SetSubjectKeyIdFromPublicKey_ex(&cert,
SLH_DSA_SHAKE_128F_TYPE, &key)) < 0) {
printf("wc_SetSubjectKeyIdFromPublicKey_ex failed: %d\n", ret);
return 1;
}
if ((ret = wc_SetAuthKeyIdFromPublicKey_ex(&cert,
SLH_DSA_SHAKE_128F_TYPE, &key)) < 0) {
printf("wc_SetAuthKeyIdFromPublicKey_ex failed: %d\n", ret);
return 1;
}

ret = wc_MakeCert_ex(&cert, der, GEN_BUF_SZ, SLH_DSA_SHAKE_128F_TYPE,
&key, &rng);
if (ret < 0) {
printf("wc_MakeCert_ex failed: %d\n", ret);
return 1;
}
ret = wc_SignCert_ex(cert.bodySz, cert.sigType, der, GEN_BUF_SZ,
SLH_DSA_SHAKE_128F_TYPE, &key, &rng);
if (ret < 0) {
printf("wc_SignCert_ex failed: %d\n", ret);
return 1;
}
certSz = ret;
save("certs/slhdsa/entity-slhdsa-shake-128f.der", der, certSz);

pemSz = wc_DerToPem(der, certSz, pem, GEN_BUF_SZ, CERT_TYPE);
if (pemSz > 0)
save("certs/slhdsa/entity-slhdsa-shake-128f.pem", pem, pemSz);
pemSz = wc_DerToPem(keyder, keySz, pem, GEN_BUF_SZ, PKCS8_PRIVATEKEY_TYPE);
if (pemSz > 0)
save("certs/slhdsa/entity-slhdsa-shake-128f-priv.pem", pem, pemSz);

printf("Generated SLH-DSA-SHAKE-128f entity cert (certSz=%d keySz=%d)\n",
certSz, keySz);

free(der);
free(keyder);
free(pem);
wc_SlhDsaKey_Free(&key);
wc_FreeRng(&rng);
wolfCrypt_Cleanup();
return 0;
}
7 changes: 6 additions & 1 deletion certs/slhdsa/include.am
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,9 @@ EXTRA_DIST += \
certs/slhdsa/server-mldsa44-sha2.pem \
certs/slhdsa/server-mldsa44-sha2.der \
certs/slhdsa/client-mldsa44-sha2.pem \
certs/slhdsa/client-mldsa44-sha2.der
certs/slhdsa/client-mldsa44-sha2.der \
certs/slhdsa/gen-slhdsa-entity-cert.c \
certs/slhdsa/entity-slhdsa-shake-128f-priv.pem \
certs/slhdsa/entity-slhdsa-shake-128f-priv.der \
certs/slhdsa/entity-slhdsa-shake-128f.pem \
certs/slhdsa/entity-slhdsa-shake-128f.der
1 change: 1 addition & 0 deletions scripts/include.am
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ if !BUILD_IPV6
dist_noinst_SCRIPTS+= scripts/external.test
dist_noinst_SCRIPTS+= scripts/google.test
dist_noinst_SCRIPTS+= scripts/openssl.test
EXTRA_DIST+= scripts/slhdsa-oqs-interop.sh

if BUILD_OCSP
dist_noinst_SCRIPTS+= scripts/ocsp.test
Expand Down
138 changes: 138 additions & 0 deletions scripts/slhdsa-oqs-interop.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
#!/usr/bin/env bash
#
# SLH-DSA TLS 1.3 interoperability test: wolfSSL <-> OpenSSL + oqs-provider.
#
# Exercises the SLH-DSA handshake signature (CertificateVerify) in both
# directions against an independent implementation, per draft-reddy-tls-slhdsa
# (TLS SignatureScheme code points 0x0911-0x091C). The larger 'f' parameter
# sets produce >16KB signatures, so this also exercises fragmented
# CertificateVerify send + reassembly across the two stacks.
#
# OpenSSL < 3.5 has no native SLH-DSA and OpenSSL >= 3.5 does not expose
# SLH-DSA as a TLS signature scheme, while oqs-provider drops SLH-DSA when the
# underlying OpenSSL provides it natively. OpenSSL 3.4 (provider TLS-sigalg
# support, no native SLH-DSA) is therefore the reference peer here, with
# liboqs + oqs-provider supplying the SLH-DSA algorithms and the 0x0911 code
# points that wolfSSL matches.
#
# Prerequisites (built from source, see build steps below):
# - liboqs (main) built with OQS_USE_OPENSSL=OFF
# - oqs-provider (main) built against OpenSSL 3.4
# - OpenSSL 3.4 (shared)
# - wolfSSL configured with: --enable-tls13 --enable-slhdsa [CFLAGS=-DWOLFSSL_SLHDSA_SHA2]
#
# Build steps (run once; OQS_ROOT defaults to $HOME/oqs-interop):
# git clone https://github.com/open-quantum-safe/liboqs
# cmake -S liboqs -B liboqs/build -GNinja -DCMAKE_INSTALL_PREFIX=$OQS_ROOT/local \
# -DBUILD_SHARED_LIBS=ON -DOQS_BUILD_ONLY_LIB=ON -DOQS_USE_OPENSSL=OFF
# ninja -C liboqs/build install
# git clone -b openssl-3.4 https://github.com/openssl/openssl $OQS_ROOT/openssl34
# (cd $OQS_ROOT/openssl34 && ./Configure --prefix=$OQS_ROOT/ossl34 shared no-docs \
# -Wl,-rpath,$OQS_ROOT/ossl34/lib64 && make -j"$(nproc)" && make install_sw)
# git clone https://github.com/open-quantum-safe/oqs-provider
# cmake -S oqs-provider -B oqs-provider/build -GNinja \
# -DOPENSSL_ROOT_DIR=$OQS_ROOT/ossl34 \
# -Dliboqs_DIR=$OQS_ROOT/local/lib/cmake/liboqs
# ninja -C oqs-provider/build oqsprovider
#
# Usage: run from the wolfSSL source root after `./configure ... && make`.
# OQS_ROOT=/path/to/oqs-interop ./scripts/slhdsa-oqs-interop.sh

set -u

OQS_ROOT="${OQS_ROOT:-$HOME/oqs-interop}"
OSSL_DIR="${OSSL_DIR:-$OQS_ROOT/ossl34}"
OQS_MODULES="${OQS_MODULES:-$OQS_ROOT/oqs-provider/build/lib}"
OQS_LIBS="${OQS_LIBS:-$OQS_ROOT/local/lib}"
OSSL="$OSSL_DIR/bin/openssl"
PORT="${PORT:-11801}"
WOLF_SRV=./examples/server/server
WOLF_CLI=./examples/client/client
CERTDIR=certs/slhdsa
TMP="$(mktemp -d)"
trap 'rm -rf "$TMP"; pkill -f "s_server -tls1_3 -accept $PORT" 2>/dev/null' EXIT

# Environment for driving the OpenSSL side with oqs-provider.
osslenv() {
env LD_LIBRARY_PATH="$OSSL_DIR/lib64:$OQS_LIBS" \
OPENSSL_MODULES="$OQS_MODULES" \
OPENSSL_CONF="${OPENSSL_CONF:-/usr/lib/ssl/openssl.cnf}" "$@"
}
# Environment for driving the wolfSSL examples.
wolfenv() { env LD_LIBRARY_PATH=./src/.libs "$@"; }

fail=0
pass() { echo "PASS: $1"; }
fatal() { echo "FAIL: $1"; fail=1; }

[ -x "$OSSL" ] || { echo "OpenSSL 3.4 not found at $OSSL (see build steps)"; exit 1; }
[ -f "$OQS_MODULES/oqsprovider.so" ] || { echo "oqsprovider.so not found (see build steps)"; exit 1; }

echo "wolfSSL: $(cat wolfssl/version.h 2>/dev/null | grep LIBWOLFSSL_VERSION_STRING | awk '{print $3}' | tr -d '\"')"
echo "OpenSSL: $(osslenv "$OSSL" version)"
echo "oqs SLH-DSA sig algs: $(osslenv "$OSSL" list -signature-algorithms -provider oqsprovider 2>/dev/null | grep -c slhdsa)"
echo

# ---- Direction A: wolfSSL server signs, OpenSSL+oqs client verifies ----
# param=SLH-DSA scheme name (openssl), cert/key = wolfSSL-generated SLH-DSA leaf
dirA() {
local name="$1" cert="$2" key="$3" sigalg="$4"
wolfenv timeout 30 "$WOLF_SRV" -v 4 -c "$cert" -k "$key" -d -p "$PORT" \
> "$TMP/a_srv.log" 2>&1 &
local sp=$!; sleep 1
echo | osslenv timeout 25 "$OSSL" s_client -tls1_3 -connect 127.0.0.1:"$PORT" \
-CAfile "$cert" -provider oqsprovider -provider default \
-sigalgs "$sigalg" -verify_return_error > "$TMP/a_cli.log" 2>&1
wait $sp 2>/dev/null
if grep -q "Peer signature type: $sigalg" "$TMP/a_cli.log" \
&& grep -q "Verify return code: 0 (ok)" "$TMP/a_cli.log"; then
pass "A [$name] wolfSSL signs -> OpenSSL+oqs verifies"
else
fatal "A [$name] wolfSSL signs -> OpenSSL+oqs verifies"
tail -3 "$TMP/a_cli.log"; tail -2 "$TMP/a_srv.log"
fi
}

# ---- Direction B: OpenSSL+oqs server signs, wolfSSL client verifies ----
dirB() {
local name="$1" sigalg="$2"
# OpenSSL generates its own SLH-DSA leaf so it can load the private key.
osslenv "$OSSL" req -x509 -new -newkey "$sigalg" \
-provider oqsprovider -provider default -nodes \
-keyout "$TMP/o_key.pem" -out "$TMP/o_cert.pem" \
-subj "/CN=openssl-$sigalg" -days 365 > "$TMP/b_gen.log" 2>&1
osslenv timeout 30 "$OSSL" s_server -tls1_3 -accept "$PORT" \
-provider oqsprovider -provider default \
-cert "$TMP/o_cert.pem" -key "$TMP/o_key.pem" -naccept 1 -www \
> "$TMP/b_srv.log" 2>&1 &
local sp=$!; sleep 1
wolfenv timeout 25 "$WOLF_CLI" -v 4 -A "$TMP/o_cert.pem" -h 127.0.0.1 -p "$PORT" -g \
> "$TMP/b_cli.log" 2>&1
wait $sp 2>/dev/null
if grep -q "Doing SLH-DSA peer cert verify" "$TMP/b_cli.log" \
&& grep -q "DoTls13CertificateVerify, return 0" "$TMP/b_cli.log"; then
pass "B [$name] OpenSSL+oqs signs -> wolfSSL verifies"
else
fatal "B [$name] OpenSSL+oqs signs -> wolfSSL verifies"
tail -4 "$TMP/b_cli.log"
fi
}

# SHAKE-128f: ~17KB signature -> fragmented CertificateVerify (both directions).
if [ -f "$CERTDIR/entity-slhdsa-shake-128f.pem" ]; then
dirA "SHAKE-128f (fragmented)" \
"$CERTDIR/entity-slhdsa-shake-128f.pem" \
"$CERTDIR/entity-slhdsa-shake-128f-priv.pem" slhdsashake128f
fi
dirB "SHAKE-128f (fragmented)" slhdsashake128f

# SHA2-128s: single-record signature; exercises the SHA2 parameter family.
if [ -f "$CERTDIR/root-slhdsa-sha2-128s.pem" ]; then
dirA "SHA2-128s" \
"$CERTDIR/root-slhdsa-sha2-128s.pem" \
"$CERTDIR/root-slhdsa-sha2-128s-priv.pem" slhdsasha2128s
fi

echo
[ $fail -eq 0 ] && echo "All SLH-DSA interop scenarios passed." || echo "Some scenarios FAILED."
exit $fail
Loading