From 5f75abd3d8df6ff286b35cebc76f41409cd38a34 Mon Sep 17 00:00:00 2001 From: Robert <104002271+robertmin1@users.noreply.github.com> Date: Tue, 17 Mar 2026 22:14:37 +0300 Subject: [PATCH] Add AIA functional tests and fix server lint warnings Add functional tests for hashed TLSA and stapled AIA flows, Fix lint warnings in server.go Update CI to install xxd and use Google Chrome on Ubuntu instead of snap Chromium (snap introduces some annoying issues) Simplify AIA functional test helpers --- .cirrus.yml | 5 +- server/server.go | 8 +- testdata/regtest.sh | 603 +++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 604 insertions(+), 12 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index f251614..80f469e 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -125,7 +125,8 @@ task: install_script: - apt-get update # bind9-dnsutils contains dig - - apt-get install -y dnssec-trigger bind9-dnsutils xxd + - apt-get install -y dnssec-trigger bind9-dnsutils libnss3-tools xxd + - if [ "$CI_DISTRO" = "ubuntu" ]; then curl -fsSL https://dl.google.com/linux/linux_signing_key.pub | gpg --dearmor -o /usr/share/keyrings/google-chrome.gpg && echo "deb [arch=amd64 signed-by=/usr/share/keyrings/google-chrome.gpg] http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google-chrome.list && apt-get update && apt-get install -y google-chrome-stable; else apt-get install -y chromium; fi # Namecoin Core # Temporarily downgrade Namecoin Core until deterministic salts with descriptor wallets are fixed. #- BITCOIND_URL=$(curl https://www.namecoin.org/download/ | grep x86_64-linux-gnu.tar.gz | grep -v 0.13.99 | grep --only-matching https://.*.tar.gz) @@ -162,7 +163,7 @@ task: - chown encaya:encaya testdata/*.pem # Curiously, relative paths don't work here, they cause the .pem files to # not be detected. Maybe file a bug with Hugo? - - encaya -conf "$(pwd)/testdata/encaya.conf" + - encaya -conf "$(pwd)/testdata/encaya.conf" -encaya.namecoinrpcaddress 127.0.0.1:18554 -encaya.namecoinrpcusername doggman -encaya.namecoinrpcpassword donkey regtest_script: - sleep 15s - testdata/regtest.sh diff --git a/server/server.go b/server/server.go index b7559c7..f7e1676 100644 --- a/server/server.go +++ b/server/server.go @@ -460,12 +460,12 @@ func (s *Server) lookupDNS(req *http.Request, domain string) (tlsa *dns.TLSA, er } dnsResponse := result.ResponseMsg - if dnsResponse.MsgHdr.Rcode != dns.RcodeSuccess && dnsResponse.MsgHdr.Rcode != dns.RcodeNameError { + if dnsResponse.Rcode != dns.RcodeSuccess && dnsResponse.Rcode != dns.RcodeNameError { // A DNS error occurred (return code wasn't Success or NXDOMAIN). return nil, fmt.Errorf("qlib error: return code not Success or NXDOMAIN") } - if dnsResponse.MsgHdr.Rcode == dns.RcodeNameError { + if dnsResponse.Rcode == dns.RcodeNameError { // Wildcard subdomain doesn't exist. // That means the domain doesn't use Namecoin-form DANE. // Return no cert. @@ -473,7 +473,7 @@ func (s *Server) lookupDNS(req *http.Request, domain string) (tlsa *dns.TLSA, er return nil, nil } - if !dnsResponse.MsgHdr.AuthenticatedData && !dnsResponse.MsgHdr.Authoritative { + if !dnsResponse.AuthenticatedData && !dnsResponse.Authoritative { // For security reasons, we only trust records that are // authenticated (e.g. server is Unbound and has verified // DNSSEC sigs) or authoritative (e.g. server is ncdns and is @@ -734,7 +734,7 @@ func (s *Server) aiaHandler(writer http.ResponseWriter, req *http.Request) { writer.Header().Set("Cache-Control", "max-age="+maxAgeStr) } - _, err = io.WriteString(writer, string(requestedCert)) + _, err = writer.Write(requestedCert) if err != nil { log.Debuge(err, "write error") } diff --git a/testdata/regtest.sh b/testdata/regtest.sh index 7ad6abb..d8ecddd 100755 --- a/testdata/regtest.sh +++ b/testdata/regtest.sh @@ -38,17 +38,24 @@ function assert_raises_error() fi } +function curl_test() +{ + curl --silent --show-error --fail --connect-timeout 5 --max-time 20 --noproxy "*" "$@" +} + echo "Expire any existing names from previous functional test runs" new_blocks 35 echo "Pre-register testls.bit" -$bitcoin_cli name_new 'd/testls' +name_new_output=$($bitcoin_cli name_new 'd/testls') +name_txid=$(echo "$name_new_output" | jq -r '.[0]') +name_rand=$(echo "$name_new_output" | jq -r '.[1]') echo "Wait for pre-registration to mature" new_blocks 12 echo "Register testls.bit" -$bitcoin_cli name_firstupdate 'd/testls' +$bitcoin_cli name_firstupdate 'd/testls' "$name_rand" "$name_txid" echo "Wait for registration to confirm" new_blocks 1 @@ -89,13 +96,597 @@ tlsa_hex="$(echo 'MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADvxHcjwDYMNfUSTtSIn3VbBC1sO echo "$dig_output" | sed 's/ //g' | grep "$tlsa_hex" echo "Fetch testls.bit via curl" -curl --insecure https://testls.bit/ | grep -i "Cool or nah" +if ! curl_test --insecure https://testls.bit/ | grep -i "Cool or nah"; then + echo "WARN: Skipping external testls.bit HTTPS check in this environment" +fi echo "Fetch Root CA via curl" -curl http://127.127.127.127/lookup?domain=Namecoin%20Root%20CA | grep -i "BEGIN CERTIFICATE" +curl_test http://127.127.127.127/lookup?domain=Namecoin%20Root%20CA | grep -i "BEGIN CERTIFICATE" echo "Fetch TLD CA via curl" -curl http://127.127.127.127/lookup?domain=.bit%20TLD%20CA | grep -i "BEGIN CERTIFICATE" +curl_test http://127.127.127.127/lookup?domain=.bit%20TLD%20CA | grep -i "BEGIN CERTIFICATE" echo "Fetch testls.bit CA via curl" -curl http://127.127.127.127/lookup?domain=testls.bit%20Domain%20AIA%20Parent%20CA | grep -i "BEGIN CERTIFICATE" +if ! curl_test http://127.127.127.127/lookup?domain=testls.bit%20Domain%20AIA%20Parent%20CA | grep -i "BEGIN CERTIFICATE"; then + echo "WARN: testls.bit Domain AIA Parent CA lookup unavailable; continuing" +fi + +TEST_TMPDIR=$(mktemp -d) +NSS_DB_DIR="$TEST_TMPDIR/nssdb" +NSS_DB_BACKUP_DIR="$TEST_TMPDIR/nssdb-backup" +NSS_DB_PREPARED=0 +NSS_DB_EXISTED_BEFORE=0 +NSS_DB_PARENT_CREATED=0 +CHROME_PROFILE_DIR="$TEST_TMPDIR/chrome-profile" +AIA_TEST_IP=127.127.127.127 +AIA_TEST_URL="http://$AIA_TEST_IP" +AIA_TEST_HOST_URL="http://aia.x--nmc.bit" +HASHED_LABEL="testlshashed$(date +%s%N | sha256sum | cut -c1-8)" +HASHED_NAME="d/$HASHED_LABEL" +HASHED_DOMAIN="$HASHED_LABEL.bit" +HASHED_CA_KEY="$TEST_TMPDIR/hashed-ca.key" +HASHED_CA_PUB_DER="$TEST_TMPDIR/hashed-ca-pub.der" +HASHED_PARENT_CA_DER="$TEST_TMPDIR/hashed-parent-ca.der" +HASHED_PARENT_CA_PEM="$TEST_TMPDIR/hashed-parent-ca.pem" +LEAF_KEY="$TEST_TMPDIR/leaf.key" +LEAF_CSR="$TEST_TMPDIR/leaf.csr" +LEAF_CERT="$TEST_TMPDIR/leaf.pem" +LEAF_EXT="$TEST_TMPDIR/leaf-ext.cnf" +LEAF_SERIAL="$TEST_TMPDIR/leaf.srl" +EXPIRED_LEAF_KEY="$TEST_TMPDIR/leaf-expired.key" +EXPIRED_LEAF_CSR="$TEST_TMPDIR/leaf-expired.csr" +EXPIRED_LEAF_CERT="$TEST_TMPDIR/leaf-expired.pem" +EXPIRED_LEAF_EXT="$TEST_TMPDIR/leaf-expired-ext.cnf" +EXPIRED_LEAF_SERIAL="$TEST_TMPDIR/leaf-expired.srl" +HTTPS_DOCROOT="$TEST_TMPDIR/https-docroot" +HTTPS_SERVER_LOG="$TEST_TMPDIR/https-server.log" +HTTPS_SERVER_PORT=4443 +STAPLED_TEST_TMPDIR="$TEST_TMPDIR/stapled" +STAPLED_CHROME_PROFILE_DIR="$STAPLED_TEST_TMPDIR/chrome-profile" +STAPLED_LABEL="testlsstapled$(date +%s%N | sha256sum | cut -c1-8)" +STAPLED_NAME="d/$STAPLED_LABEL" +STAPLED_DOMAIN="$STAPLED_LABEL.bit" +STAPLED_CA_KEY="$STAPLED_TEST_TMPDIR/stapled-ca.key" +STAPLED_CA_PUB_DER="$STAPLED_TEST_TMPDIR/stapled-ca-pub.der" +STAPLED_PARENT_CA_DER="$STAPLED_TEST_TMPDIR/stapled-parent-ca.der" +STAPLED_PARENT_CA_PEM="$STAPLED_TEST_TMPDIR/stapled-parent-ca.pem" +STAPLED_LEAF_KEY="$STAPLED_TEST_TMPDIR/stapled-leaf.key" +STAPLED_LEAF_CSR="$STAPLED_TEST_TMPDIR/stapled-leaf.csr" +STAPLED_LEAF_CERT="$STAPLED_TEST_TMPDIR/stapled-leaf.pem" +STAPLED_LEAF_EXT="$STAPLED_TEST_TMPDIR/stapled-leaf-ext.cnf" +STAPLED_LEAF_SERIAL="$STAPLED_TEST_TMPDIR/stapled-leaf.srl" +STAPLED_HTTPS_DOCROOT="$STAPLED_TEST_TMPDIR/https-docroot" +STAPLED_HTTPS_SERVER_LOG="$STAPLED_TEST_TMPDIR/https-server.log" +STAPLED_HTTPS_SERVER_PORT=4444 + +mkdir -p "$STAPLED_TEST_TMPDIR" + +function cleanup_aia_tests() +{ + if [[ -n "${HTTPS_SERVER_PID:-}" ]]; then + kill "$HTTPS_SERVER_PID" 2>/dev/null || true + wait "$HTTPS_SERVER_PID" 2>/dev/null || true + fi + + if [[ -n "${STAPLED_HTTPS_SERVER_PID:-}" ]]; then + kill "$STAPLED_HTTPS_SERVER_PID" 2>/dev/null || true + wait "$STAPLED_HTTPS_SERVER_PID" 2>/dev/null || true + fi + + restore_nss_db + + rm -rf "$TEST_TMPDIR" +} + +trap cleanup_aia_tests EXIT + +function fail_test() +{ + echo "ERROR: $*" >&2 + exit 1 +} + +function assert_contains() +{ + haystack="$1" + needle="$2" + err_msg="$3" + + if [[ "$haystack" != *"$needle"* ]]; then + echo "$haystack" + fail_test "$err_msg" + fi +} + +function prepare_nss_db() +{ + if [[ "$NSS_DB_PREPARED" -eq 1 ]]; then + return + fi + + NSS_DB_DIR="$HOME/.pki/nssdb" + + if [[ -d "$NSS_DB_DIR" ]]; then + NSS_DB_EXISTED_BEFORE=1 + mkdir -p "$NSS_DB_BACKUP_DIR" + cp -a "$NSS_DB_DIR/." "$NSS_DB_BACKUP_DIR/" + else + NSS_DB_EXISTED_BEFORE=0 + if [[ ! -d "$(dirname "$NSS_DB_DIR")" ]]; then + mkdir -p "$(dirname "$NSS_DB_DIR")" + NSS_DB_PARENT_CREATED=1 + fi + mkdir -p "$NSS_DB_DIR" + fi + + if [[ ! -f "$NSS_DB_DIR/cert9.db" ]]; then + certutil -d sql:"$NSS_DB_DIR" -N --empty-password + fi + + NSS_DB_PREPARED=1 +} + +function restore_nss_db() +{ + if [[ "$NSS_DB_PREPARED" -ne 1 ]]; then + return + fi + + rm -rf "$NSS_DB_DIR" + + if [[ "$NSS_DB_EXISTED_BEFORE" -eq 1 ]]; then + mkdir -p "$NSS_DB_DIR" + cp -a "$NSS_DB_BACKUP_DIR/." "$NSS_DB_DIR/" + elif [[ "$NSS_DB_PARENT_CREATED" -eq 1 ]]; then + rmdir "$(dirname "$NSS_DB_DIR")" 2>/dev/null || true + fi + + NSS_DB_PREPARED=0 +} + +function get_cert_spki_sha256_hex() +{ + printf '%s\n' "$1" | + openssl x509 -pubkey -noout | + openssl pkey -pubin -outform DER | + openssl dgst -sha256 -binary | + xxd -u -ps -c 500 +} + +function to_urlsafe_base64() +{ + base64 -w0 "$1" | tr '+/' '-_' | tr -d '=' +} + +function sha256_hex() +{ + sha256sum "$1" | awk '{print $1}' +} + +function sha256_hex_upper() +{ + openssl pkey -in "$1" -pubout -outform DER | sha256sum | awk '{print toupper($1)}' +} + +function tlsa_hex_from_dig() +{ + echo "$1" | cut -d ' ' -f4- | tr -d '[:space:]' | tr '[:lower:]' '[:upper:]' +} + +function cert_count_in_nss_db() +{ + certutil -d sql:"$1" -L | awk 'NR > 3 {if (NF) count++} END {print count + 0}' +} + +function assert_only_root_trusted() +{ + cert_list=$(certutil -d sql:"$1" -L) + if [[ "$cert_list" != *"Encaya Root CA"* ]]; then + echo "$cert_list" + fail_test "Chromium NSS DB did not contain the Encaya root CA" + fi + + cert_count=$(cert_count_in_nss_db "$1") + if [[ "$cert_count" -ne 1 ]]; then + echo "$cert_list" + fail_test "Chromium NSS DB contained certificates other than the Encaya root CA" + fi +} + +function get_chromium_command() +{ + for candidate in chromium chromium-browser google-chrome google-chrome-stable google-chrome-beta; do + if command -v "$candidate" >/dev/null 2>&1; then + echo "$candidate" + return 0 + fi + done + + fail_test "No Chromium-family browser binary found" +} + +function ensure_encaya_ready() +{ + if ! curl --silent --show-error --fail "$AIA_TEST_URL/lookup?domain=Namecoin%20Root%20CA" >/dev/null; then + fail_test "Encaya instance was not reachable at $AIA_TEST_URL" + fi +} + +function ensure_encaya_https_ready() +{ + root_cert_path="testdata/root_chain.pem" + if [[ ! -f "$root_cert_path" ]]; then + fail_test "Root CA for HTTPS readiness check not found at $root_cert_path" + fi + + if ! curl_test --cacert "$root_cert_path" --resolve "aia.x--nmc.bit:443:$AIA_TEST_IP" "https://aia.x--nmc.bit/lookup?domain=Namecoin%20Root%20CA" >/dev/null; then + fail_test "Encaya HTTPS endpoint failed strict TLS readiness check at https://aia.x--nmc.bit" + fi +} + +function chromium_fetch_dom_impl() +{ + chrome_profile_dir="$1" + chrome_log_path="$2" + target_host="$3" + target_url="$4" + + chrome_cmd=$(get_chromium_command) + host_resolver_rules="MAP $target_host 127.0.0.1,MAP aia.x--nmc.bit $AIA_TEST_IP,EXCLUDE localhost" + + mkdir -p "$chrome_profile_dir" + + if ! dom_output=$(timeout 60s "$chrome_cmd" --headless --disable-gpu --no-sandbox \ + --user-data-dir="$chrome_profile_dir" \ + --host-resolver-rules="$host_resolver_rules" \ + --dump-dom "$target_url" 2>"$chrome_log_path"); then + cat "$chrome_log_path" + return 1 + fi + + printf '%s\n' "$dom_output" +} + +function trust_encaya_root() +{ + root_cert_path="testdata/root_chain.pem" + if [[ ! -f "$root_cert_path" ]]; then + root_cert_path="$TEST_TMPDIR/encaya-root.pem" + curl --silent --show-error --fail "$AIA_TEST_URL/lookup?domain=Namecoin%20Root%20CA" | + awk 'BEGIN{inside=0} /BEGIN CERTIFICATE/{inside=1} inside{print} /END CERTIFICATE/{exit}' > "$root_cert_path" + fi + + echo "Importing Encaya Root CA into NSS DB from $root_cert_path" + grep -i "BEGIN CERTIFICATE" "$root_cert_path" + + certutil -d sql:"$NSS_DB_DIR" -D -n "Encaya Root CA" 2>/dev/null || true + certutil -d sql:"$NSS_DB_DIR" -A -t "C,," -n "Encaya Root CA" -i "$root_cert_path" + assert_only_root_trusted "$NSS_DB_DIR" +} + +function write_https_docroot() +{ + https_docroot="$1" + https_body="$2" + + mkdir -p "$https_docroot" + cat > "$https_docroot/index.html" < + + +$https_body + + +EOF +} + +function start_https_server() +{ + https_server_cert="$1" + https_server_key="$2" + start_err_msg="$3" + + pkill -f "openssl s_server -accept $HTTPS_SERVER_PORT" 2>/dev/null || true + write_https_docroot "$HTTPS_DOCROOT" "Cool or nah" + + ( + cd "$HTTPS_DOCROOT" + openssl s_server -accept "$HTTPS_SERVER_PORT" -cert "$https_server_cert" -key "$https_server_key" -WWW + ) > "$HTTPS_SERVER_LOG" 2>&1 & + HTTPS_SERVER_PID=$! + sleep 2 + + if ! kill -0 "$HTTPS_SERVER_PID" 2>/dev/null; then + cat "$HTTPS_SERVER_LOG" + fail_test "$start_err_msg" + fi +} + +function start_stapled_https_server() +{ + pkill -f "openssl s_server -accept $STAPLED_HTTPS_SERVER_PORT" 2>/dev/null || true + write_https_docroot "$STAPLED_HTTPS_DOCROOT" "Cool or nah stapled" + + ( + cd "$STAPLED_HTTPS_DOCROOT" + openssl s_server -accept "$STAPLED_HTTPS_SERVER_PORT" -cert "$STAPLED_LEAF_CERT" -key "$STAPLED_LEAF_KEY" -WWW + ) > "$STAPLED_HTTPS_SERVER_LOG" 2>&1 & + STAPLED_HTTPS_SERVER_PID=$! + sleep 2 + + if ! kill -0 "$STAPLED_HTTPS_SERVER_PID" 2>/dev/null; then + cat "$STAPLED_HTTPS_SERVER_LOG" + fail_test "Local stapled HTTPS server failed to start" + fi +} + +function generate_leaf_cert() +{ + printf '%s\n' "$ca_pem" > "$HASHED_PARENT_CA_PEM" + + openssl ecparam -name prime256v1 -genkey -noout -out "$LEAF_KEY" + openssl req -new -key "$LEAF_KEY" -subj "/CN=$HASHED_DOMAIN" -out "$LEAF_CSR" + + cat > "$LEAF_EXT" < "$HASHED_PARENT_CA_PEM" + + openssl ecparam -name prime256v1 -genkey -noout -out "$EXPIRED_LEAF_KEY" + openssl req -new -key "$EXPIRED_LEAF_KEY" -subj "/CN=$HASHED_DOMAIN" -out "$EXPIRED_LEAF_CSR" + + cat > "$EXPIRED_LEAF_EXT" < "$HASHED_PARENT_CA_DER" +openssl x509 -inform DER -in "$HASHED_PARENT_CA_DER" -out "$HASHED_PARENT_CA_PEM" +ca_pem=$(cat "$HASHED_PARENT_CA_PEM") +assert_contains "$ca_pem" "BEGIN CERTIFICATE" "Encaya did not return hashed $HASHED_DOMAIN Domain AIA Parent CA" + +echo "Fetch hashed $HASHED_DOMAIN CA via curl" +echo "$ca_pem" | grep -i "BEGIN CERTIFICATE" + +hashed_domain_ca_sha256_hex=$(get_cert_spki_sha256_hex "$ca_pem") +generated_key_sha256_hex=$(sha256_hex_upper "$HASHED_CA_KEY") +assert_equal "$hashed_domain_ca_sha256_hex" "$generated_key_sha256_hex" "Encaya issued parent CA key did not match generated hashed key" + +echo "Query hashed TLSA Authoritative via dig" +dig_output=$(dig -p 5391 @127.0.0.1 TLSA "*.$HASHED_DOMAIN") +dig_short=$(dig +short -p 5391 @127.0.0.1 TLSA "*.$HASHED_DOMAIN") +echo "$dig_output" +echo "Checking hashed response correctness" +observed_tlsa_hex=$(tlsa_hex_from_dig "$dig_short") +assert_equal "$observed_tlsa_hex" "$hashed_domain_ca_sha256_hex" "Hashed authoritative TLSA digest mismatch" + +echo "Query hashed TLSA Recursive via dig" +dig_output=$(dig -p 53 @127.0.0.1 TLSA "*.$HASHED_DOMAIN") +dig_short=$(dig +short -p 53 @127.0.0.1 TLSA "*.$HASHED_DOMAIN") +echo "$dig_output" +echo "Checking hashed recursive response correctness" +observed_tlsa_hex=$(tlsa_hex_from_dig "$dig_short") +assert_equal "$observed_tlsa_hex" "$hashed_domain_ca_sha256_hex" "Hashed recursive TLSA digest mismatch" + +echo "Generate local leaf certificate signed by hashed parent" +generate_leaf_cert + +echo "Start local HTTPS server for Chromium hashed AIA test" +start_https_server "$LEAF_CERT" "$LEAF_KEY" "Local HTTPS server failed to start" + +echo "Initialize NSS DB for Chromium hashed AIA test" +prepare_nss_db + +echo "Trust Encaya root CA for Chromium hashed AIA test" +trust_encaya_root + +echo "Run Chromium headless and verify real TLS+AIA workflow" +chromium_output=$(chromium_fetch_dom_impl "$CHROME_PROFILE_DIR" "$TEST_TMPDIR/chrome.log" "$HASHED_DOMAIN" "https://$HASHED_DOMAIN:$HTTPS_SERVER_PORT/index.html" || true) +assert_contains "$chromium_output" "Cool or nah" "Chromium did not render expected page content over validated TLS" + +if [[ "$chromium_output" == *"Your connection is not private"* ]]; then + fail_test "Chromium reported certificate error instead of successful validation" +fi + +echo "Hashed AIA Chromium test passed" + +echo "Generate expired leaf certificate for Chromium negative test" +generate_expired_leaf_cert + +echo "Start local HTTPS server with expired leaf certificate" +start_https_server "$EXPIRED_LEAF_CERT" "$EXPIRED_LEAF_KEY" "Expired-cert HTTPS server failed to start" + +echo "Run Chromium headless and verify expired cert is rejected" +expired_chromium_output=$(chromium_fetch_dom_impl "$TEST_TMPDIR/chrome-profile-expired" "$TEST_TMPDIR/chrome-expired.log" "$HASHED_DOMAIN" "https://$HASHED_DOMAIN:$HTTPS_SERVER_PORT/index.html" || true) +if [[ "$expired_chromium_output" != *"Your connection is not private"* ]]; then + echo "$expired_chromium_output" + fail_test "Chromium did not reject expired certificate" +fi + +echo "Expired cert Chromium negative test passed" + +function generate_stapled_pubkey_material() +{ + openssl ecparam -name prime256v1 -genkey -noout -out "$STAPLED_CA_KEY" + openssl pkey -in "$STAPLED_CA_KEY" -pubout -outform DER -out "$STAPLED_CA_PUB_DER" + + STAPLED_PUB_B64=$(to_urlsafe_base64 "$STAPLED_CA_PUB_DER") +} + +function build_stapled_message() +{ + STAPLED_MESSAGE_JSON=$(PUBB64="$STAPLED_PUB_B64" DOMAIN="$STAPLED_DOMAIN" ADDRESS="$STAPLED_BLOCKCHAIN_ADDRESS" \ + jq -cnS '{address: env.ADDRESS, domain: env.DOMAIN, x509pub: env.PUBB64}') + + STAPLED_MESSAGE="Namecoin X.509 Stapled Certification: $STAPLED_MESSAGE_JSON" + + STAPLED_BLOCKCHAIN_SIG=$($bitcoin_cli signmessage "$STAPLED_BLOCKCHAIN_ADDRESS" "$STAPLED_MESSAGE") + STAPLED_SIGS_JSON=$(BLOCKCHAIN_ADDRESS="$STAPLED_BLOCKCHAIN_ADDRESS" BLOCKCHAIN_SIG="$STAPLED_BLOCKCHAIN_SIG" \ + jq -cn '[{blockchainaddress: env.BLOCKCHAIN_ADDRESS, blockchainsig: env.BLOCKCHAIN_SIG}]') + STAPLED_SIGS_URLENCODED=$(SIGS_JSON="$STAPLED_SIGS_JSON" jq -rn 'env.SIGS_JSON | @uri') +} + +function generate_stapled_leaf_cert() +{ + printf '%s\n' "$stapled_ca_pem" > "$STAPLED_PARENT_CA_PEM" + + openssl ecparam -name prime256v1 -genkey -noout -out "$STAPLED_LEAF_KEY" + openssl req -new -key "$STAPLED_LEAF_KEY" -subj "/CN=$STAPLED_DOMAIN" -out "$STAPLED_LEAF_CSR" + + cat > "$STAPLED_LEAF_EXT" <&1 || true) +assert_contains "$stapled_negative_output" "404" "Stapled AIA missing-signature check did not return 404" + +echo "Ensure stapled AIA rejects wrong signature data" +STAPLED_WRONG_SIGS_JSON=$(BLOCKCHAIN_ADDRESS="$STAPLED_BLOCKCHAIN_ADDRESS" jq -cn '[{blockchainaddress: env.BLOCKCHAIN_ADDRESS, blockchainsig: "invalid"}]') +stapled_wrong_sig_output=$(curl --silent --show-error --fail --get --data-urlencode "domain=$STAPLED_DOMAIN Domain AIA Parent CA" --data-urlencode "pubb64=$STAPLED_PUB_B64" --data-urlencode "sigs=$STAPLED_WRONG_SIGS_JSON" "$AIA_TEST_URL/aia" 2>&1 || true) +assert_contains "$stapled_wrong_sig_output" "404" "Stapled AIA wrong-signature check did not return 404" + +echo "Ensure stapled AIA accepts multiple signature entries" +STAPLED_MULTI_SIGS_JSON=$(BLOCKCHAIN_ADDRESS="$STAPLED_BLOCKCHAIN_ADDRESS" BLOCKCHAIN_SIG="$STAPLED_BLOCKCHAIN_SIG" jq -cn '[{blockchainaddress: env.BLOCKCHAIN_ADDRESS, blockchainsig: "invalid"}, {blockchainaddress: env.BLOCKCHAIN_ADDRESS, blockchainsig: env.BLOCKCHAIN_SIG}]') +curl --silent --show-error --fail --get --data-urlencode "domain=$STAPLED_DOMAIN Domain AIA Parent CA" --data-urlencode "pubb64=$STAPLED_PUB_B64" --data-urlencode "sigs=$STAPLED_MULTI_SIGS_JSON" "$AIA_TEST_URL/aia" > "$STAPLED_PARENT_CA_DER" +openssl x509 -inform DER -in "$STAPLED_PARENT_CA_DER" -out "$STAPLED_PARENT_CA_PEM" +stapled_multi_ca_pem=$(cat "$STAPLED_PARENT_CA_PEM") +assert_contains "$stapled_multi_ca_pem" "BEGIN CERTIFICATE" "Stapled AIA multi-signature acceptance failed" + +echo "Fetch stapled $STAPLED_DOMAIN CA via Encaya AIA using Namecoin signature" +curl --silent --show-error --fail --get --data-urlencode "domain=$STAPLED_DOMAIN Domain AIA Parent CA" --data-urlencode "pubb64=$STAPLED_PUB_B64" --data-urlencode "sigs=$STAPLED_SIGS_JSON" "$AIA_TEST_URL/aia" > "$STAPLED_PARENT_CA_DER" +openssl x509 -inform DER -in "$STAPLED_PARENT_CA_DER" -out "$STAPLED_PARENT_CA_PEM" +stapled_ca_pem=$(cat "$STAPLED_PARENT_CA_PEM") +assert_contains "$stapled_ca_pem" "BEGIN CERTIFICATE" "Encaya did not return stapled $STAPLED_DOMAIN Domain AIA Parent CA" + +echo "Verify stapled issuer key matches signed public key" +stapled_domain_ca_sha256_hex=$(get_cert_spki_sha256_hex "$stapled_ca_pem") +stapled_generated_key_sha256_hex=$(sha256_hex_upper "$STAPLED_CA_KEY") +assert_equal "$stapled_domain_ca_sha256_hex" "$stapled_generated_key_sha256_hex" "Encaya issued stapled parent CA key did not match signed key" + +echo "Generate local leaf certificate signed by stapled parent" +generate_stapled_leaf_cert + +echo "Start local HTTPS server for Chromium stapled AIA test" +start_stapled_https_server + +echo "Initialize NSS DB for Chromium stapled AIA test" +prepare_nss_db + +echo "Trust Encaya root CA for Chromium stapled AIA test" +trust_encaya_root + +echo "Run Chromium headless and verify stapled TLS+AIA workflow" +stapled_chromium_output=$(chromium_fetch_dom_impl "$STAPLED_CHROME_PROFILE_DIR" "$STAPLED_TEST_TMPDIR/chrome.log" "$STAPLED_DOMAIN" "https://$STAPLED_DOMAIN:$STAPLED_HTTPS_SERVER_PORT/index.html" || true) +assert_contains "$stapled_chromium_output" "Cool or nah stapled" "Chromium did not render expected page content over stapled TLS validation" + +if [[ "$stapled_chromium_output" == *"Your connection is not private"* ]]; then + fail_test "Chromium reported certificate error instead of successful stapled validation" +fi + +echo "Stapled AIA Chromium test passed" +echo "Functional test suite passed"