#!/usr/bin/env bash set -euo pipefail # Verifies CRLs generated by the C API unit tests (tests/api.c). # Uses OpenSSL to validate CRL structure, signature, and revocation behavior. # RSA: ca-cert.pem + server-cert.pem (revoked) + client-ca-cert.pem (good). # ECC: ca-ecc-cert.pem + server-ecc.pem (revoked) + client-ecc-ca-cert.pem # (good). OPENSSL=${OPENSSL:-openssl} UNIT_TEST=${UNIT_TEST:-./scripts/unit.test} CRL_GEN_SUBTEST=${CRL_GEN_SUBTEST:-test_sk_X509_CRL_encode} if ! command -v "$OPENSSL" >/dev/null 2>&1; then echo "skipping crl-gen-openssl.test: openssl not found" exit 77 fi if [ ! -x "$UNIT_TEST" ]; then # Fallback for out-of-tree/in-tree differences. if [ -x "./tests/unit.test" ]; then UNIT_TEST="./tests/unit.test" elif [ -x "./scripts/unit.test" ]; then UNIT_TEST="./scripts/unit.test" fi fi if [ ! -x "$UNIT_TEST" ]; then echo "skipping crl-gen-openssl.test: unit.test not found" exit 77 fi # Run the CRL unit test to generate the CRL files and avoid race conditions # with the full unit test run. echo "Generating CRLs with: $UNIT_TEST --api -$CRL_GEN_SUBTEST" "$UNIT_TEST" --api "-$CRL_GEN_SUBTEST" normalize_dn() { sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' \ -e 's/^issuer=//' -e 's/^subject=//' \ -e 's/[[:space:]]*=[[:space:]]*/=/g' \ -e 's/[[:space:]]*,[[:space:]]*/,/g' } check_crl() { local crl="$1" local ca_cert="$2" local revoked_cert="$3" local good_cert="$4" local label="$5" echo "Checking $label CRL: $crl" local issuer subject issuer=$("$OPENSSL" crl -in "$crl" -noout -issuer | normalize_dn) subject=$("$OPENSSL" x509 -in "$ca_cert" -noout -subject | normalize_dn) if [ "$issuer" != "$subject" ]; then echo "issuer mismatch for $label CRL" echo "issuer : $issuer" echo "subject: $subject" return 1 fi local last_update next_update last_update=$("$OPENSSL" crl -in "$crl" -noout -lastupdate) next_update=$("$OPENSSL" crl -in "$crl" -noout -nextupdate) if [ -z "$last_update" ] || [ -z "$next_update" ]; then echo "missing lastUpdate/nextUpdate for $label CRL" return 1 fi if ! "$OPENSSL" crl -in "$crl" -noout -verify -CAfile "$ca_cert" \ >/dev/null 2>&1; then echo "CRL signature verification failed for $label" return 1 fi local revoked_count revoked_count=$("$OPENSSL" crl -in "$crl" -text -noout | \ grep -c "Serial Number" || true) if [ "$revoked_count" -ne 4 ]; then echo "unexpected revoked count for $label CRL: $revoked_count " \ "(expected 4)" return 1 fi local serial found serial=$("$OPENSSL" x509 -in "$revoked_cert" -noout -serial | cut -d= -f2 \ | tr 'A-F' 'a-f' | tr -d ':' | sed 's/^0*//') if [ -z "$serial" ]; then serial=0 fi found=0 while IFS= read -r s; do s=$(printf '%s' "$s" | tr 'A-F' 'a-f' | tr -d '[:space:]:' \ | sed 's/^0*//') if [ -z "$s" ]; then s=0 fi if [ "$s" = "$serial" ]; then found=1 break fi done < <("$OPENSSL" crl -in "$crl" -text -noout | \ awk -F: '/Serial Number/ {print $2}') if [ "$found" -ne 1 ]; then echo "revoked serial not found in $label CRL: $serial" return 1 fi local verify_out verify_rc verify_out_norm # Capture both stdout and stderr so we can reliably detect and print the # revocation text. verify_out=$("$OPENSSL" verify -CAfile "$ca_cert" -crl_check \ -CRLfile "$crl" \ "$revoked_cert" 2>&1) || verify_rc=$? verify_rc=${verify_rc:-0} # Avoid pipefail/SIGPIPE false negatives with `grep -q` in a pipeline. if ! grep -qi "revoked" <<< "$verify_out"; then echo "expected revoked verification failure for $label CRL" echo "$verify_out" return 1 fi if [ "$verify_rc" -eq 0 ]; then # Some OpenSSL builds return success even when reporting revocation. # Treat revoked output as authoritative. : fi if [ -n "$good_cert" ]; then if ! "$OPENSSL" verify -CAfile "$ca_cert" -crl_check -CRLfile "$crl" \ "$good_cert" >/dev/null; then echo "expected successful verification for $label CRL with " \ "$good_cert" return 1 fi fi } crl_rsa="certs/crl/crlRsaOut.pem" crl_ecc="certs/crl/crlEccOut.pem" if [ ! -f "$crl_rsa" ] && [ ! -f "$crl_ecc" ]; then echo "skipping crl-gen-openssl.test: CRL outputs not found" exit 77 fi if [ -f "$crl_rsa" ]; then ca_rsa="certs/ca-cert.pem" revoked_rsa="certs/server-cert.pem" good_rsa="certs/client-ca-cert.pem" check_crl "$crl_rsa" "$ca_rsa" "$revoked_rsa" "$good_rsa" "RSA" fi if [ -f "$crl_ecc" ]; then ca_ecc="certs/ca-ecc-cert.pem" revoked_ecc="certs/server-ecc.pem" good_ecc="certs/client-ecc-ca-cert.pem" check_crl "$crl_ecc" "$ca_ecc" "$revoked_ecc" "${good_ecc:-}" "ECC" fi echo "crl-gen-openssl.test: OK"