From de118371de742cfa4561f00c3f7c5f43a496b84d Mon Sep 17 00:00:00 2001 From: Siavash Sameni Date: Thu, 26 Mar 2026 22:37:41 +0400 Subject: [PATCH] Fix bundle lookup: normalize fingerprints, handle 404 gracefully Server: normalize fingerprints by stripping colons and lowercasing before storing/looking up in sled. Adds tracing for register/lookup. Client: check HTTP status before parsing JSON response body. Shows clear error when user is not registered instead of parse error. Co-Authored-By: Claude Opus 4.6 (1M context) --- warzone/crates/warzone-client/src/net.rs | 14 +++++++++-- .../crates/warzone-server/src/routes/keys.rs | 23 +++++++++++++++---- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/warzone/crates/warzone-client/src/net.rs b/warzone/crates/warzone-client/src/net.rs index 4b754ec..b724eaa 100644 --- a/warzone/crates/warzone-client/src/net.rs +++ b/warzone/crates/warzone-client/src/net.rs @@ -59,7 +59,7 @@ impl ServerClient { /// Fetch a user's pre-key bundle from the server. pub async fn fetch_bundle(&self, fingerprint: &str) -> Result { - let resp: BundleResponse = self + let response = self .client .get(format!( "{}/v1/keys/{}", @@ -67,7 +67,17 @@ impl ServerClient { )) .send() .await - .context("failed to fetch bundle")? + .context("failed to fetch bundle")?; + + if !response.status().is_success() { + anyhow::bail!( + "server returned {} — user {} may not be registered", + response.status(), + fingerprint + ); + } + + let resp: BundleResponse = response .json() .await .context("failed to parse bundle response")?; diff --git a/warzone/crates/warzone-server/src/routes/keys.rs b/warzone/crates/warzone-server/src/routes/keys.rs index ae0a1c8..6b8882a 100644 --- a/warzone/crates/warzone-server/src/routes/keys.rs +++ b/warzone/crates/warzone-server/src/routes/keys.rs @@ -13,10 +13,18 @@ pub fn routes() -> Router { .route("/keys/{fingerprint}", get(get_bundle)) } +/// Normalize fingerprint: strip colons, lowercase. +fn normalize_fp(fp: &str) -> String { + fp.chars() + .filter(|c| c.is_ascii_hexdigit()) + .collect::() + .to_lowercase() +} + #[derive(Deserialize)] struct RegisterRequest { fingerprint: String, - bundle: Vec, // bincode-serialized PreKeyBundle + bundle: Vec, } #[derive(Serialize)] @@ -28,7 +36,9 @@ async fn register_keys( State(state): State, Json(req): Json, ) -> Json { - let _ = state.db.keys.insert(req.fingerprint.as_bytes(), req.bundle); + let key = normalize_fp(&req.fingerprint); + tracing::info!("Registering bundle for {}", key); + let _ = state.db.keys.insert(key.as_bytes(), req.bundle); Json(RegisterResponse { ok: true }) } @@ -36,11 +46,16 @@ async fn get_bundle( State(state): State, Path(fingerprint): Path, ) -> Result, axum::http::StatusCode> { - match state.db.keys.get(fingerprint.as_bytes()) { + let key = normalize_fp(&fingerprint); + tracing::info!("Looking up bundle for {}", key); + match state.db.keys.get(key.as_bytes()) { Ok(Some(data)) => Ok(Json(serde_json::json!({ "fingerprint": fingerprint, "bundle": base64::Engine::encode(&base64::engine::general_purpose::STANDARD, &data), }))), - _ => Err(axum::http::StatusCode::NOT_FOUND), + _ => { + tracing::warn!("Bundle not found for {}", key); + Err(axum::http::StatusCode::NOT_FOUND) + } } }