Skip to content
Closed
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: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ cargo build --release -p enclave-runtime --features testing --bin passkey-client
| `scripts/ci-check.sh` | Formatting, Clippy with warnings denied, workspace library tests, boot-origin tests |
| `scripts/ci-guests.sh` | Builds the guests; tests tasks, held connections, dispatch, TLS binding, HTTP/2, gRPC, authentication and logging |
| `scripts/ci-storage.sh` | Real MinIO/S3: retention, remount, corruption, rollback and competing claims (needs Docker) |
| `scripts/ci-e2e.sh` | The stages above, then the full QEMU stack (needs KVM, Docker and Nix) |
| `scripts/ci-e2e.sh` | The stages above, then the full QEMU stack (needs KVM, Docker and Nix), once with the example guest and once with the released [cosigner](https://github.com/vtxos/cosigner) pinned in [`cosigner.lock`](cosigner.lock) |
| `scripts/wit-drift.sh` | Vendored guest WIT matches [`wit/`](wit/) |
| `scripts/ci-bench.sh` | Criterion storage and instance-cost benchmarks |

Expand Down
4 changes: 4 additions & 0 deletions cosigner.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# The cosigner this runtime's CI serves: a release of vtxos/cosigner, by name and the sha256 of its
# cosigner.wasm. scripts/cosigner.sh fetches it and refuses any other bytes. Shell syntax.
RELEASE=v0.1.0
SHA256=ba4976e0b547e9e342fca273e1cd78d2426d0df55e2ea5d9761b6428b86889cf
27 changes: 27 additions & 0 deletions deploy/qemu-nitro/run-e2e.sh
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ PREFIX=e2e
# A second guest, altered, for leg 8. Nothing else here needs one, so lib.sh
# does not stage it unless asked.
WITH_SUBSTITUTE=1
# Any guest, not the example one (with GUEST_WASM): only the legs that hold for
# every guest run — the gate, the attestation, the PCRs, a restart and a
# substitution. The rest drive guest-http's own routes.
GUEST_AGNOSTIC="${GUEST_AGNOSTIC:-}"
# shellcheck source=lib.sh
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib.sh"

Expand Down Expand Up @@ -91,6 +95,7 @@ code="$(curl -sk -o /dev/null -w '%{http_code}' --max-time 20 \
|| fail "a request with no nonce answered $code; it should be refused before routing"
echo "un-nonced requests refused: 400"

if [[ -z "$GUEST_AGNOSTIC" ]]; then # guest-http's routes, to the end of 2b
say "2/8 a passkey enrols and its signed requests reach the guest"
rm -f "$RUNDIR/alice.json" "$RUNDIR/bob.json"
signed enrol >/dev/null || fail "registration failed"
Expand Down Expand Up @@ -134,6 +139,7 @@ for i in $(seq 1 "$FILES"); do
|| fail "/files/work-$i.txt kept a stale value \"$got\" after being overwritten"
done
echo "$FILES files written, read, overwritten and re-read through the gate"
fi

say "3/8 the attestation binds this connection's certificate, runtime and guest"
# There is no attestation endpoint: the document rides on the `/auth/` exchange,
Expand All @@ -151,6 +157,7 @@ say "3/8 the attestation binds this connection's certificate, runtime and guest
grep -q "binding the attested certificate" "$RUNDIR/attest.log" \
|| fail "the document did not bind the certificate this connection was served"

if [[ -z "$GUEST_AGNOSTIC" ]]; then # signs for guest-http's /counter
say "3b/8 a signed interaction verifies the enclave before it trusts it"
# The proof comes from the `/auth/` exchange, not from the guest's response:
# that exchange is where a client identifies the enclave, before it hands over
Expand Down Expand Up @@ -179,6 +186,7 @@ signed --dump-proof "$RUNDIR/proof" get --path /counter >/dev/null \

grep -q "binding the attested certificate" "$RUNDIR/attest-guest.log" \
|| fail "the document did not bind that connection's certificate"
fi

say "4/8 the attested PCR0 and PCR16 are the ones the builds produced"
# `nitro-attest --pcr0 --guest` already enforced both, so reaching here means
Expand All @@ -192,6 +200,7 @@ echo " attested: $(grep -oE '^PCR16 +[0-9a-f]+' "$RUNDIR/attest.log" | awk
# ---------------------------------------------------------------------------
# 5/8 — one instance per request, and one approval per interaction.
# ---------------------------------------------------------------------------
if [[ -z "$GUEST_AGNOSTIC" ]]; then # guest-http's routes, to the end of 6b
say "5/8 every request gets a fresh instance, and no two tenants share one"

# `/memory` counts in the guest's linear memory and writes nowhere. What it
Expand Down Expand Up @@ -409,6 +418,7 @@ echo "$wake" | jq -e '.message.notification == null' >/dev/null \
keys="$(echo "$wake" | jq -r '.message.data | keys | join(",")')"
[[ "$keys" == "category,ref,v" ]] || fail "the wake carried more than its labels: $keys"
echo "a data-only wake reached the enrolled device: category=task-done ref=e2e-job"
fi

# ---------------------------------------------------------------------------
# 7/8 — the boot machine, across a restart.
Expand Down Expand Up @@ -515,6 +525,23 @@ echo "it attests the substitute it is running: PCR16 $SUBSTITUTE_PCR16"

docker rm -f "$PREFIX-qemu-substitute" >/dev/null 2>&1 || true

if [[ -n "$GUEST_AGNOSTIC" ]]; then
cat <<EOF

== PASS (any guest: $GUEST_WASM) ==
the guest booted from the store, measured into PCR16, behind real ACME TLS
the guest was unreachable without a passkey assertion
the document bound this connection's certificate, PCR0 and PCR16
genesis wrote an attested state origin, and a restart resumed it
a substituted guest was measured and recorded as an upgrade, and a client
pinning the approved guest refused it

The legs that drive guest-http's routes did not run. A guest's own behaviour
is its own suite's to prove.
EOF
exit 0
fi

cat <<EOF

== PASS ==
Expand Down
9 changes: 8 additions & 1 deletion scripts/ci-e2e.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,11 @@ cd "$REPO"
# KVM, vsock, the QEMU image and vhost-device-vsock — see scripts/lib.sh.
prepare_enclave_host

exec deploy/qemu-nitro/run-e2e.sh
deploy/qemu-nitro/run-e2e.sh

# The same stack again, serving the cosigner as released (cosigner.lock): only
# the legs that hold for any guest, since the rest drive the example's routes.
# The image is built by now, so this costs its boots.
"$REPO/scripts/cosigner.sh"
GUEST_WASM="$REPO/target/cosigner/cosigner.wasm" GUEST_AGNOSTIC=1 \
exec deploy/qemu-nitro/run-e2e.sh
22 changes: 22 additions & 0 deletions scripts/cosigner.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env bash
# Put the cosigner release cosigner.lock pins at target/cosigner/cosigner.wasm,
# refusing any other bytes. ci-e2e.sh boots it as the e2e's second guest.

. "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
# shellcheck source=../cosigner.lock
. "$REPO/cosigner.lock"

out="$REPO/target/cosigner/cosigner.wasm"
if echo "$SHA256 $out" | sha256sum -c --status - 2>/dev/null; then
echo "cosigner $RELEASE already at $out"
exit 0
fi

say "fetching cosigner $RELEASE"
mkdir -p "$(dirname "$out")"
url="https://github.com/vtxos/cosigner/releases/download/$RELEASE/cosigner.wasm"
curl -fL --retry 3 --retry-all-errors --max-time 300 -o "$out.part" "$url" \
|| fail "could not download $url"
echo "$SHA256 $out.part" | sha256sum -c - \
|| fail "cosigner $RELEASE does not match cosigner.lock"
mv "$out.part" "$out"
Loading