feat(auth): bring opaque (RFC 9807) auth
OPAQUE (RFC 9807) implementation (using `opaque-ke` crate)
with opaque authentfication, server will never receive the password (in the auth=password mode)
this is a must have to create trust with users to permit end to end encryption in the future
(we cannot know if user use the same password/passphrase for his asymetric key or his oxicloud auth,
this is why server must never have the password)
pass1: prepare server
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
# =============================================================
|
||||
# OxiCloud — OPAQUE aPAKE (Phase 0 substrate) — inertness smoke
|
||||
# =============================================================
|
||||
# The full OPAQUE handshake is NOT testable in Hurl (every message
|
||||
# contains session-random OPRF blinding + AKE nonces that can't be
|
||||
# hardcoded in a .hurl body). Full-flow assertions belong in a Rust
|
||||
# integration test using `opaque-ke` client-side against a real
|
||||
# server. That lands with the Phase 1 endpoints.
|
||||
#
|
||||
# What THIS file asserts is the substrate-level contract for Phase 0:
|
||||
#
|
||||
# 1. The server booted with the OPAQUE substrate loaded — proved
|
||||
# transitively by the fact that this suite reached the
|
||||
# `--test-report` stage at all. `tests/common/server.env` sets
|
||||
# `OXICLOUD_OPAQUE_MODE=migrate` + a persisted `SERVER_SETUP`;
|
||||
# a boot failure (bad base64, missing setup, ciphersuite drift)
|
||||
# would 500 every request or refuse to bind the port.
|
||||
#
|
||||
# 2. The Phase 1 endpoints are not yet routed. An unauthenticated
|
||||
# POST to any `/api/*` path returns **401** (not 404) — the
|
||||
# `/api` namespace is behind the auth middleware, so a missing
|
||||
# route is indistinguishable from "route exists but needs
|
||||
# auth". That's deliberate anti-enumeration: attackers can't
|
||||
# probe which endpoints exist.
|
||||
#
|
||||
# When Phase 1 ships:
|
||||
# - Register endpoints stay 401 unauth (they'll be
|
||||
# session-required — anti-enum still applies).
|
||||
# - Login KE1 / KE3 will flip to **400** because they'll be
|
||||
# public and reject the placeholder payloads below as
|
||||
# malformed. That's the natural regression signal: update
|
||||
# this file to hit the endpoints with a valid handshake
|
||||
# driven from a Rust integration test.
|
||||
#
|
||||
# 3. The legacy `POST /api/auth/login` continues to work under
|
||||
# Migrate mode. `auth_login.hurl` asserts this thoroughly; we
|
||||
# don't duplicate it here.
|
||||
# =============================================================
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Case 1 — Register-start endpoint not routed (401 anti-enum).
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
POST {{base_url}}/api/auth/opaque/register/start
|
||||
Content-Type: application/json
|
||||
{ "registrationRequest": "unused-phase-0" }
|
||||
|
||||
HTTP 401
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Case 2 — Register-finish endpoint not routed (401 anti-enum).
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
POST {{base_url}}/api/auth/opaque/register/finish
|
||||
Content-Type: application/json
|
||||
{ "registrationRecord": "unused-phase-0", "ciphersuiteVersion": 1 }
|
||||
|
||||
HTTP 401
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Case 3 — Login KE1 endpoint not routed (401 anti-enum).
|
||||
# Will flip to 400 in Phase 1 (public + malformed body).
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
POST {{base_url}}/api/auth/opaque/login/ke1
|
||||
Content-Type: application/json
|
||||
{ "userIdentifier": "{{username}}", "startLoginRequest": "unused-phase-0" }
|
||||
|
||||
HTTP 401
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Case 4 — Login KE3 endpoint not routed (401 anti-enum).
|
||||
# Will flip to 400 in Phase 1 (public + malformed body).
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
POST {{base_url}}/api/auth/opaque/login/ke3
|
||||
Content-Type: application/json
|
||||
{ "exchangeId": "unused-phase-0", "finishLoginRequest": "unused-phase-0" }
|
||||
|
||||
HTTP 401
|
||||
@@ -146,6 +146,7 @@ log "Running Hurl tests..."
|
||||
hurl --variables-file "$API_DIR/test.env" --file-root "$REPO_ROOT/tests" --test --jobs 1 \
|
||||
"$API_DIR/setup.hurl" \
|
||||
"$API_DIR/auth_login.hurl" \
|
||||
"$API_DIR/opaque_substrate.hurl" \
|
||||
"$API_DIR/user_ui_preferences.hurl" \
|
||||
"$API_DIR/auth_session_lifecycle.hurl" \
|
||||
"$API_DIR/auth_magic_link_login.hurl" \
|
||||
|
||||
@@ -117,3 +117,31 @@ OXICLOUD_MAGIC_LINK_SEND_PER_IP_PER_HOUR=50
|
||||
# permits IP spoofing for tests
|
||||
OXICLOUD_TRUST_PROXY_CIDR=0.0.0.0/0
|
||||
|
||||
# ── OPAQUE aPAKE (Phase 0 substrate) ────────────────────────────────
|
||||
# Boot the OPAQUE service in Migrate mode so every Hurl run exercises:
|
||||
# 1. OpaqueConfig::from_env parsing all five OPAQUE env vars.
|
||||
# 2. effective_mode(&auth) permitting Migrate because password IS in
|
||||
# OXICLOUD_AUTH_METHODS above (would auto-downgrade to Off if we
|
||||
# had disabled it — that path is unit-tested in
|
||||
# common::config::tests::effective_mode_downgrades_to_off_...).
|
||||
# 3. OpaqueService::from_config accepting a valid serialised setup.
|
||||
# 4. AppState wiring populating `opaque_service = Some(_)`.
|
||||
#
|
||||
# Phase 0 has no wire-facing endpoints, so this only proves the
|
||||
# substrate loads cleanly; opaque_substrate.hurl asserts the legacy
|
||||
# /api/auth/login path remains intact and the future OPAQUE endpoints
|
||||
# still 404 (they flip to 200 when Phase 1 lands).
|
||||
#
|
||||
# The SERVER_SETUP below is a throwaway keypair generated once for the
|
||||
# test env — real deployments call `opaque-setup` and paste the output.
|
||||
# Never reuse this value outside CI. Regenerate any time with:
|
||||
# cargo run --bin opaque-setup
|
||||
OXICLOUD_OPAQUE_MODE=migrate
|
||||
OXICLOUD_OPAQUE_SERVER_SETUP="ZY4hAGa1MNyE7Ht+8ksLcyMmi/K2iJvxQly+DdfllUxjiH0+CjCt4hG6+9Y68jGet2L213dV0hajCbr4fXnekkWtUxqLr+butVHEksZ9NJRuZTvS6SMC73yf/yku4WUHT1NSRB2yHurAFmYn75D9wdA1VaXTuwgO/u5i1pvcsQs="
|
||||
# Fast Argon2id — CI machines are underpowered vs production (256 MiB
|
||||
# default would drag every test-scaffold future OPAQUE handshake in
|
||||
# Phase 1+). Matches the params used in the round-trip unit test.
|
||||
OXICLOUD_OPAQUE_KSF_MEMORY_KIB=8
|
||||
OXICLOUD_OPAQUE_KSF_ITERATIONS=1
|
||||
OXICLOUD_OPAQUE_KSF_PARALLELISM=1
|
||||
|
||||
|
||||
Reference in New Issue
Block a user