- net.accept_tls(listener, certfile, keyfile) -> Int (id 118, WO_B_MAX->118): accept (parks like net.accept), load+cache the server identity per path in the shard, run the blocking deadline-bounded server handshake, return a TLS conn fd. Real clients terminate against the runtime — no front proxy - wo_tls_conn refactored: holds the negotiated application keys (not an embedded driver), so read_tls/write_tls serve both client and server connections via the record layer; the handshake drivers are transient (heap, ~100KB, freed after). net.close drains a TLS conn's inbound before close() so it sends FIN not RST (clients send close_notify) - server handshake loops past the client's change_cipher_spec (TLS 1.3 middlebox-compat) before its Finished — the openssl-interop fix - private-key file loading: wo_tls_pem_one (any-label PEM block) + wo_pkey_parse; per-shard identity cache (vm->tls_id), freed in reap - docs/examples/tls-server + `just tls-server`: openssl s_client validates our hand-rolled server (EC + RSA certs) and gets the reply — 4/0; the outbound `just tls` gate stays 5/0 through the refactor - wiring: wob.h, loader.c, builtin.c dispatch, types.ml, vm.h Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> (cherry picked from commit 2d4c30033c36c88de5b7ab7cc1042c9537238297)
95 lines
4.5 KiB
Bash
Executable file
95 lines
4.5 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# scripts/tls-server-accept.sh — runtime-v2 9 phase G's gate: inbound TLS 1.3.
|
|
# The runtime suite proves the server FSM offline (loopback against the client
|
|
# driver); this proves interop with a real client — `openssl s_client`
|
|
# validating our hand-rolled server handshake and exchanging application data,
|
|
# for both an ECDSA-P256 and an RSA server certificate. Log: /tmp/tls-server.log.
|
|
set -uo pipefail
|
|
|
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
WOC="$ROOT/compiler/_build/default/bin/woc"
|
|
WOVM="$ROOT/runtime/wovm"
|
|
APP="$ROOT/docs/examples/tls-server"
|
|
PORT="${TLS_SERVER_PORT:-18553}"
|
|
LOG=/tmp/tls-server.log
|
|
|
|
pass=0; fail=0
|
|
ok() { echo "ok $1"; pass=$((pass + 1)); }
|
|
bad() { echo "FAIL $1"; fail=$((fail + 1)); }
|
|
|
|
WORK="$(mktemp -d)"
|
|
SRV_PID=""
|
|
cleanup() { [[ -n "$SRV_PID" ]] && kill -KILL "$SRV_PID" 2>/dev/null; rm -rf "$WORK"; }
|
|
trap cleanup EXIT
|
|
mkdir -p "$WORK/data"
|
|
|
|
[[ -x "$WOVM" ]] || { echo "tls-server: wovm not built — run: make -C runtime wovm" >&2; exit 1; }
|
|
[[ -x "$WOC" ]] || { echo "tls-server: woc not built — run: just woc-build" >&2; exit 1; }
|
|
command -v openssl >/dev/null || { echo "tls-server: needs openssl" >&2; exit 1; }
|
|
python3 -c 'import cryptography' 2>/dev/null || { echo "tls-server: needs python3 cryptography" >&2; exit 1; }
|
|
|
|
# ---- 1. a test CA + an EC leaf and an RSA leaf (SAN localhost) ------------
|
|
cat > "$WORK/mk.py" <<'PY'
|
|
import datetime, sys
|
|
from cryptography import x509
|
|
from cryptography.x509.oid import NameOID, ExtendedKeyUsageOID
|
|
from cryptography.hazmat.primitives import hashes, serialization as ser
|
|
from cryptography.hazmat.primitives.asymmetric import ec, rsa
|
|
d = sys.argv[1]
|
|
NB = datetime.datetime(2020,1,1); NA = datetime.datetime(2035,1,1)
|
|
def nm(cn): return x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, cn)])
|
|
cak = ec.generate_private_key(ec.SECP256R1())
|
|
ca = (x509.CertificateBuilder().subject_name(nm("wo-test-ca")).issuer_name(nm("wo-test-ca"))
|
|
.public_key(cak.public_key()).serial_number(x509.random_serial_number())
|
|
.not_valid_before(NB).not_valid_after(NA)
|
|
.add_extension(x509.BasicConstraints(ca=True, path_length=None), True).sign(cak, hashes.SHA256()))
|
|
open(d+"/ca.pem","wb").write(ca.public_bytes(ser.Encoding.PEM))
|
|
def leaf(key, tag):
|
|
c = (x509.CertificateBuilder().subject_name(nm("localhost")).issuer_name(ca.subject)
|
|
.public_key(key.public_key()).serial_number(x509.random_serial_number())
|
|
.not_valid_before(NB).not_valid_after(NA)
|
|
.add_extension(x509.SubjectAlternativeName([x509.DNSName("localhost")]), False)
|
|
.add_extension(x509.ExtendedKeyUsage([ExtendedKeyUsageOID.SERVER_AUTH]), False)
|
|
.sign(cak, hashes.SHA256()))
|
|
open(d+"/"+tag+".pem","wb").write(c.public_bytes(ser.Encoding.PEM))
|
|
open(d+"/"+tag+".key","wb").write(key.private_bytes(ser.Encoding.PEM,
|
|
ser.PrivateFormat.PKCS8, ser.NoEncryption()))
|
|
leaf(ec.generate_private_key(ec.SECP256R1()), "ec")
|
|
leaf(rsa.generate_private_key(public_exponent=65537, key_size=2048), "rsa")
|
|
PY
|
|
python3 "$WORK/mk.py" "$WORK" || { bad "cert generation"; echo "tls-server: $fail failures"; exit 1; }
|
|
ok "test CA + EC leaf + RSA leaf (SAN localhost, EKU serverAuth) generated"
|
|
|
|
# ---- 2. build the server -------------------------------------------------
|
|
{ echo; echo "== tls-server-accept $(date -Is) port $PORT =="; } >>"$LOG"
|
|
if "$WOC" --emit "$APP" -o "$WORK/srv.wob" 2>"$WORK/cerr"; then
|
|
ok "build: tls-server compiles"
|
|
else
|
|
bad "build: $(head -3 "$WORK/cerr")"; echo "tls-server: $fail failures"; exit 1
|
|
fi
|
|
|
|
# ---- 3. openssl s_client interop, per key type ---------------------------
|
|
probe() { # $1 = tag (ec|rsa) ; $2 = port (distinct per probe — avoids a bind race)
|
|
local p="$2"
|
|
kill -KILL "$SRV_PID" 2>/dev/null
|
|
WO_DATA="$WORK/data" "$WOVM" "$WORK/srv.wob" "$p" "$WORK/$1.pem" "$WORK/$1.key" >>"$LOG" 2>&1 &
|
|
SRV_PID=$!; disown "$SRV_PID" 2>/dev/null || true
|
|
for _ in $(seq 1 100); do
|
|
if ( exec 3<>"/dev/tcp/127.0.0.1/$p" ) 2>/dev/null; then break; fi
|
|
sleep 0.05
|
|
done
|
|
local out
|
|
out="$({ printf 'GET / HTTP/1.0\r\n\r\n'; sleep 1; } | \
|
|
timeout 12 openssl s_client -connect "127.0.0.1:$p" -CAfile "$WORK/ca.pem" \
|
|
-servername localhost -tls1_3 -verify_return_error -quiet 2>/dev/null)"
|
|
if [[ "$out" == *"hello-wo-tls"* ]]; then
|
|
ok "$1: openssl s_client validated the cert + got the reply"
|
|
else
|
|
bad "$1: no reply (out: ${out:0:80})"
|
|
fi
|
|
}
|
|
probe ec "$PORT"
|
|
probe rsa "$((PORT + 1))"
|
|
|
|
echo "tls-server: $((pass + fail)) checks, $fail failures"
|
|
[[ $fail -eq 0 ]]
|