From 10d2e93b4b63f17a550bb5e88dbfcf409be687ef Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 3 Nov 2025 13:40:11 -0600 Subject: [PATCH 1/3] JWKS Verification --- Cargo.lock | 211 ++++++++++-- Cargo.toml | 5 + crates/mocktioneer-core/Cargo.toml | 5 + crates/mocktioneer-core/src/lib.rs | 1 + crates/mocktioneer-core/src/routes.rs | 22 +- crates/mocktioneer-core/src/verification.rs | 353 ++++++++++++++++++++ examples/openrtb_request.json | 10 +- 7 files changed, 565 insertions(+), 42 deletions(-) create mode 100644 crates/mocktioneer-core/src/verification.rs diff --git a/Cargo.lock b/Cargo.lock index 6f2a86d..d225d97 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -136,7 +136,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.108", + "syn 2.0.109", "toml", "validator", ] @@ -179,7 +179,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.109", ] [[package]] @@ -190,7 +190,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.109", ] [[package]] @@ -263,6 +263,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64ct" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" + [[package]] name = "bitflags" version = "1.3.2" @@ -388,6 +394,12 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e47641d3deaf41fb1538ac1f54735925e275eaf3bf4d55c81b137fba797e5cbb" +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -422,6 +434,33 @@ dependencies = [ "typenum", ] +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.109", +] + [[package]] name = "darling" version = "0.20.11" @@ -443,7 +482,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.108", + "syn 2.0.109", ] [[package]] @@ -454,7 +493,17 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.108", + "syn 2.0.109", +] + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "zeroize", ] [[package]] @@ -485,7 +534,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.109", ] [[package]] @@ -495,7 +544,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.108", + "syn 2.0.109", ] [[package]] @@ -525,7 +574,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.109", ] [[package]] @@ -534,6 +583,30 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "serde", + "sha2 0.10.9", + "subtle", + "zeroize", +] + [[package]] name = "either" version = "1.15.0" @@ -626,6 +699,12 @@ dependencies = [ "log", ] +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + [[package]] name = "find-msvc-tools" version = "0.1.4" @@ -713,7 +792,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.109", ] [[package]] @@ -1064,9 +1143,9 @@ checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "iri-string" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" dependencies = [ "memchr", "serde", @@ -1244,13 +1323,18 @@ version = "0.1.0" dependencies = [ "anyedge-core", "async-trait", + "base64", + "ed25519-dalek", "futures", + "futures-util", "handlebars", "log", "serde", "serde_json", "serde_repr", + "thiserror 1.0.69", "toml", + "url", "uuid", "validator", ] @@ -1342,7 +1426,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.109", ] [[package]] @@ -1372,7 +1456,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.109", ] [[package]] @@ -1387,6 +1471,16 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "potential_utf" version = "0.1.4" @@ -1430,7 +1524,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.109", ] [[package]] @@ -1519,7 +1613,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha", - "rand_core", + "rand_core 0.9.3", ] [[package]] @@ -1529,7 +1623,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", ] [[package]] @@ -1628,11 +1731,20 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + [[package]] name = "rustls" -version = "0.23.34" +version = "0.23.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9586e9ee2b4f8fab52a0048ca7334d7024eef48e2cb9407e3497bb7cab7fa7" +checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" dependencies = [ "once_cell", "ring", @@ -1684,6 +1796,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + [[package]] name = "serde" version = "1.0.228" @@ -1722,7 +1840,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.109", ] [[package]] @@ -1757,7 +1875,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.109", ] [[package]] @@ -1820,6 +1938,15 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "rand_core 0.6.4", +] + [[package]] name = "simd-adler32" version = "0.3.7" @@ -1860,6 +1987,16 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -1891,9 +2028,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.108" +version = "2.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" +checksum = "2f17c7e013e88258aa9543dcbe81aca68a667a9ac37cd69c9fbc07858bfe0e2f" dependencies = [ "proc-macro2", "quote", @@ -1917,7 +2054,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.109", ] [[package]] @@ -1946,7 +2083,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.109", ] [[package]] @@ -1957,7 +2094,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.109", ] [[package]] @@ -2042,7 +2179,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.109", ] [[package]] @@ -2160,7 +2297,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.109", ] [[package]] @@ -2258,7 +2395,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.109", ] [[package]] @@ -2346,7 +2483,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.109", "wasm-bindgen-shared", ] @@ -2380,7 +2517,7 @@ checksum = "085b2df989e1e6f9620c1311df6c996e83fe16f57792b272ce1e024ac16a90f1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.109", ] [[package]] @@ -2455,7 +2592,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.109", ] [[package]] @@ -2466,7 +2603,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.109", ] [[package]] @@ -2712,7 +2849,7 @@ dependencies = [ "async-trait", "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.109", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-macro-support", @@ -2756,7 +2893,7 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.109", "synstructure", ] @@ -2777,7 +2914,7 @@ checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.109", ] [[package]] @@ -2797,7 +2934,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.109", "synstructure", ] @@ -2837,5 +2974,5 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.109", ] diff --git a/Cargo.toml b/Cargo.toml index f3832d5..2c9b257 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,3 +36,8 @@ uuid = { version = "1", features = ["v7"] } validator = { version = "0.20", features = ["derive"] } worker = { version = "0.6", features = ["http"] } tracing = "0.1" +ed25519-dalek = "2.1" +base64 = "0.22" +thiserror = "1.0" +url = "2" +futures-util = "0.3.31" diff --git a/crates/mocktioneer-core/Cargo.toml b/crates/mocktioneer-core/Cargo.toml index 2b525e9..c0a8d9d 100644 --- a/crates/mocktioneer-core/Cargo.toml +++ b/crates/mocktioneer-core/Cargo.toml @@ -7,12 +7,17 @@ publish = false [dependencies] anyedge-core = { workspace = true } async-trait = { workspace = true } +base64 = { workspace = true } +ed25519-dalek = { workspace = true } +futures-util = { workspace = true } handlebars = { workspace = true } log = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } serde_repr = { workspace = true } +thiserror = { workspace = true } toml = { workspace = true } +url = { workspace = true } uuid = { workspace = true } validator = { workspace = true } diff --git a/crates/mocktioneer-core/src/lib.rs b/crates/mocktioneer-core/src/lib.rs index c73569d..33edf7c 100644 --- a/crates/mocktioneer-core/src/lib.rs +++ b/crates/mocktioneer-core/src/lib.rs @@ -2,6 +2,7 @@ pub mod auction; pub mod openrtb; pub mod render; pub mod routes; +pub mod verification; anyedge_core::app!("../../anyedge.toml", MocktioneerApp); diff --git a/crates/mocktioneer-core/src/routes.rs b/crates/mocktioneer-core/src/routes.rs index 6f8f3ae..15f7cb3 100644 --- a/crates/mocktioneer-core/src/routes.rs +++ b/crates/mocktioneer-core/src/routes.rs @@ -235,14 +235,30 @@ pub async fn handle_root(Headers(headers): Headers) -> Response { #[action] pub async fn handle_openrtb_auction( Headers(headers): Headers, - ValidatedJson(payload): ValidatedJson, + ValidatedJson(req): ValidatedJson, ) -> Response { + if let Some(domain) = req.site.as_ref().and_then(|s| s.domain.as_deref()) { + match crate::verification::verify_request_id_signature(&req.id, req.ext.as_ref(), domain) + .await + { + Ok(kid) => { + log::info!("✅ Request signature verified with key: {}", kid); + } + Err(e) => { + log::error!("❌ Signature verification skipped or failed: {}", e); + } + } + } else { + log::info!("⚠️ Signature verification skipped (no domain)"); + } + let host = headers .get(header::HOST) .and_then(|v| v.to_str().ok()) .unwrap_or("mocktioneer.edgecompute.app"); - log::info!("auction id={}, imps={}", payload.id, payload.imp.len()); - let resp = build_openrtb_response_with_base_typed(&payload, host); + log::info!("auction id={}, imps={}", req.id, req.imp.len()); + + let resp = build_openrtb_response_with_base_typed(&req, host); let body = Body::json(&resp).unwrap_or_else(|_| Body::text("{}")); let mut response = build_response(StatusCode::OK, body); response.headers_mut().insert( diff --git a/crates/mocktioneer-core/src/verification.rs b/crates/mocktioneer-core/src/verification.rs new file mode 100644 index 0000000..41f296d --- /dev/null +++ b/crates/mocktioneer-core/src/verification.rs @@ -0,0 +1,353 @@ +use anyedge_core::body::Body; +use anyedge_core::context::RequestContext; +use anyedge_core::http::{request_builder, Method, StatusCode, Uri}; +use anyedge_core::params::PathParams; +use anyedge_core::proxy::ProxyRequest; +use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine}; +use ed25519_dalek::{Signature, Verifier, VerifyingKey}; +use futures_util::StreamExt; +use serde::Deserialize; +use std::collections::HashMap; +use std::sync::{LazyLock, Mutex}; +use std::time::{Duration, Instant}; + +const JWKS_CACHE_TTL: Duration = Duration::from_secs(10 * 60); + +#[derive(Debug, Clone, Deserialize)] +struct JwksResponse { + keys: Vec, +} + +#[derive(Debug, Clone, Deserialize)] +struct JwkKey { + kid: String, + x: String, // Base64url-encoded Ed25519 public key +} + +struct JwksCache { + jwks: JwksResponse, + fetched_at: Instant, +} + +static JWKS_CACHE: LazyLock>> = + LazyLock::new(|| Mutex::new(HashMap::new())); + +#[derive(Debug, thiserror::Error)] +pub enum VerificationError { + #[error("Key not found: {0}")] + KeyNotFound(String), + #[error("Invalid signature: {0}")] + InvalidSignature(String), + #[error("Signature verification failed")] + SignatureVerificationFailed, + #[error("HTTP error: {0}")] + HttpError(String), + #[error("No domain for JWKS verification")] + NoJwksDomain, +} + +fn create_request_ctx() -> RequestContext { + let request = request_builder() + .method(Method::GET) + .uri("/") + .body(Body::empty()) + .expect("minimal request should be valid"); + RequestContext::new(request, PathParams::new(HashMap::new())) +} + +async fn fetch_jwks(domain: &str) -> Result { + let jwks_url = format!("http://{}/.well-known/ts.jwks.json", domain); + + log::debug!("Fetching JWKS from {}", jwks_url); + + let uri = jwks_url + .parse::() + .map_err(|e| VerificationError::HttpError(format!("Invalid JWKS URL: {}", e)))?; + + log::info!("URI: {}", uri); + let proxy_request = ProxyRequest::new(Method::GET, uri); + let ctx = create_request_ctx(); + let proxy_handle = ctx + .proxy_handle() + .ok_or_else(|| VerificationError::HttpError("Proxy not available".to_string()))?; + + let resp = proxy_handle + .forward(proxy_request) + .await + .map_err(|e| VerificationError::HttpError(format!("JWKS fetch failed: {}", e)))?; + + if resp.status() != StatusCode::OK { + return Err(VerificationError::HttpError(format!( + "JWKS server returned status: {}", + resp.status() + ))); + } + + let body = resp.into_body(); + + let body_bytes = match body { + Body::Once(bytes) => bytes.to_vec(), + Body::Stream(mut stream) => { + let mut collected = Vec::new(); + while let Some(chunk) = stream.next().await { + let chunk = chunk.map_err(|e| { + VerificationError::HttpError(format!("Stream read failed: {}", e)) + })?; + collected.extend_from_slice(&chunk); + } + + collected + } + }; + serde_json::from_slice(&body_bytes) + .map_err(|e| VerificationError::HttpError(format!("JWKS parse failed: {}", e))) +} + +async fn get_cached_jwks(domain: &str) -> Result { + let cache_key = domain.to_string(); + + { + let cache = JWKS_CACHE + .lock() + .map_err(|_| VerificationError::HttpError("Cache lock poisoned".to_string()))?; + + if let Some(cached) = cache.get(&cache_key) { + if cached.fetched_at.elapsed() < JWKS_CACHE_TTL { + log::debug!( + "JWKS cache hit for {} (age: {:?})", + cache_key, + cached.fetched_at.elapsed() + ); + return Ok(cached.jwks.clone()); + } else { + log::debug!( + "JWKS cache expired for {} (age: {:?})", + cache_key, + cached.fetched_at.elapsed() + ); + } + } else { + log::debug!("JWKS cache empty for {} (first fetch)", cache_key); + } + } + + log::debug!("Fetching fresh JWKS for {}", cache_key); + let jwks = fetch_jwks(domain).await?; + + let mut cache = JWKS_CACHE + .lock() + .map_err(|_| VerificationError::HttpError("Cache lock poisoned".to_string()))?; + + cache.insert( + cache_key, + JwksCache { + jwks: jwks.clone(), + fetched_at: Instant::now(), + }, + ); + + Ok(jwks) +} + +fn find_public_key<'a>(jwks: &'a JwksResponse, kid: &str) -> Result<&'a str, VerificationError> { + jwks.keys + .iter() + .find(|k| k.kid == kid) + .map(|k| k.x.as_str()) + .ok_or_else(|| VerificationError::KeyNotFound(format!("Key {} not found in JWKS", kid))) +} + +fn verify_ed25519_signature( + public_key_b64: &str, + signature_b64: &str, + message: &str, +) -> Result<(), VerificationError> { + let public_key_bytes = URL_SAFE_NO_PAD.decode(public_key_b64).map_err(|e| { + VerificationError::InvalidSignature(format!("Invalid public key encoding: {}", e)) + })?; + + if public_key_bytes.len() != 32 { + return Err(VerificationError::InvalidSignature(format!( + "Invalid public key length: expected 32, got {}", + public_key_bytes.len() + ))); + } + + let mut key_array = [0u8; 32]; + key_array.copy_from_slice(&public_key_bytes); + + let verifying_key = VerifyingKey::from_bytes(&key_array) + .map_err(|e| VerificationError::InvalidSignature(format!("Invalid public key: {}", e)))?; + + let signature_bytes = URL_SAFE_NO_PAD.decode(signature_b64).map_err(|e| { + VerificationError::InvalidSignature(format!("Invalid signature encoding: {}", e)) + })?; + + if signature_bytes.len() != 64 { + return Err(VerificationError::InvalidSignature(format!( + "Invalid signature length: expected 64, got {}", + signature_bytes.len() + ))); + } + + let mut sig_array = [0u8; 64]; + sig_array.copy_from_slice(&signature_bytes); + + let signature = Signature::from_bytes(&sig_array); + + verifying_key + .verify(message.as_bytes(), &signature) + .map_err(|_| VerificationError::SignatureVerificationFailed)?; + + Ok(()) +} + +pub async fn verify_request_id_signature( + request_id: &str, + ext: Option<&serde_json::Value>, + domain: &str, +) -> Result { + let ext_obj = ext.and_then(|e| e.get("trusted_server")).ok_or_else(|| { + VerificationError::InvalidSignature("Missing ext.trusted_server".to_string()) + })?; + + let signature = ext_obj + .get("signature") + .and_then(|v| v.as_str()) + .ok_or_else(|| { + VerificationError::InvalidSignature("Missing ext.trusted_server.signature".to_string()) + })?; + + let key_id = ext_obj.get("kid").and_then(|v| v.as_str()).ok_or_else(|| { + VerificationError::KeyNotFound("Missing ext.trusted_server.kid".to_string()) + })?; + + log::info!( + "Signature verification requested: id={}, kid={}, domain={:?}", + request_id, + key_id, + domain + ); + + let jwks = get_cached_jwks(domain).await?; + let public_key = find_public_key(&jwks, key_id)?; + verify_ed25519_signature(public_key, signature, request_id)?; + + Ok(key_id.to_string()) +} + +#[cfg(test)] +mod tests { + use futures::executor::block_on; + + use super::*; + + #[test] + fn verify_missing_signature_field() { + let request_id = "test-id"; + let ext = serde_json::json!({ + "trusted_server": { + "kid": "test-key" + } + }); + + let result = block_on(verify_request_id_signature( + request_id, + Some(&ext), + "example.com", + )); + assert!(matches!( + result.unwrap_err(), + VerificationError::InvalidSignature(_) + )); + } + + #[test] + fn verify_missing_kid_field() { + let request_id = "test-id"; + let ext = serde_json::json!({ + "trusted_server": { + "signature": "test-sig" + } + }); + let result = block_on(verify_request_id_signature( + request_id, + Some(&ext), + "example.com", + )); + assert!(matches!( + result.unwrap_err(), + VerificationError::KeyNotFound(_) + )); + } + + #[test] + fn verify_missing_trusted_server_object() { + let request_id = "test-id"; + let ext = serde_json::json!({ + "some_other_field": "value" + }); + let result = block_on(verify_request_id_signature( + request_id, + Some(&ext), + "example.com", + )); + assert!(matches!( + result.unwrap_err(), + VerificationError::InvalidSignature(_) + )); + } + + #[test] + fn verify_with_none_ext() { + let request_id = "test-id"; + let result = block_on(verify_request_id_signature(request_id, None, "example.com")); + assert!(matches!( + result.unwrap_err(), + VerificationError::InvalidSignature(_) + )); + } + + #[test] + fn find_public_key_found() { + let jwks = JwksResponse { + keys: vec![JwkKey { + kid: "key-001".to_string(), + x: "test-key-base64url".to_string(), + }], + }; + + let result = find_public_key(&jwks, "key-001"); + assert_eq!(result.unwrap(), "test-key-base64url"); + } + + #[test] + fn find_public_key_not_found() { + let jwks = JwksResponse { keys: vec![] }; + + let result = find_public_key(&jwks, "missing-key"); + assert!(matches!( + result.unwrap_err(), + VerificationError::KeyNotFound(_) + )); + } + + #[test] + fn verify_ed25519_invalid_key_length() { + let result = verify_ed25519_signature("dGVzdA", "sig", "message"); + assert!(matches!( + result.unwrap_err(), + VerificationError::InvalidSignature(_) + )); + } + + #[test] + fn verify_ed25519_invalid_signature_length() { + let public_key = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + let result = verify_ed25519_signature(public_key, "dGVzdA", "message"); + assert!(matches!( + result.unwrap_err(), + VerificationError::InvalidSignature(_) + )); + } +} diff --git a/examples/openrtb_request.json b/examples/openrtb_request.json index cb53971..4a87e0c 100644 --- a/examples/openrtb_request.json +++ b/examples/openrtb_request.json @@ -10,18 +10,24 @@ "h": 250 }, "ext": { - "bidder": { + "mocktioneer": { "endpoint": "http://127.0.0.1:7676/openrtb2/auction" } } } ], "site": { + "domain": "127.0.0.1:7676", "page": "https://example.com" }, "device": { "ua": "Mozilla/5.0", "ip": "127.0.0.1" + }, + "ext": { + "trusted_server": { + "signature": "example-signature-base64url-encoded", + "kid": "test-key-001" + } } } - From efee46a9aec0b9fcd783177848819310f8156471 Mon Sep 17 00:00:00 2001 From: Christian Date: Fri, 7 Nov 2025 15:37:19 -0600 Subject: [PATCH 2/3] update to use new RequestContext extractor for actions --- crates/mocktioneer-core/src/routes.rs | 10 +++- crates/mocktioneer-core/src/verification.rs | 64 ++++++++++++++------- 2 files changed, 52 insertions(+), 22 deletions(-) diff --git a/crates/mocktioneer-core/src/routes.rs b/crates/mocktioneer-core/src/routes.rs index 15f7cb3..018db4d 100644 --- a/crates/mocktioneer-core/src/routes.rs +++ b/crates/mocktioneer-core/src/routes.rs @@ -234,12 +234,18 @@ pub async fn handle_root(Headers(headers): Headers) -> Response { #[action] pub async fn handle_openrtb_auction( + RequestContext(ctx): RequestContext, Headers(headers): Headers, ValidatedJson(req): ValidatedJson, ) -> Response { if let Some(domain) = req.site.as_ref().and_then(|s| s.domain.as_deref()) { - match crate::verification::verify_request_id_signature(&req.id, req.ext.as_ref(), domain) - .await + match crate::verification::verify_request_id_signature( + &ctx, + &req.id, + req.ext.as_ref(), + domain, + ) + .await { Ok(kid) => { log::info!("✅ Request signature verified with key: {}", kid); diff --git a/crates/mocktioneer-core/src/verification.rs b/crates/mocktioneer-core/src/verification.rs index 41f296d..b261bb3 100644 --- a/crates/mocktioneer-core/src/verification.rs +++ b/crates/mocktioneer-core/src/verification.rs @@ -1,7 +1,6 @@ use anyedge_core::body::Body; use anyedge_core::context::RequestContext; -use anyedge_core::http::{request_builder, Method, StatusCode, Uri}; -use anyedge_core::params::PathParams; +use anyedge_core::http::{Method, StatusCode, Uri}; use anyedge_core::proxy::ProxyRequest; use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine}; use ed25519_dalek::{Signature, Verifier, VerifyingKey}; @@ -46,16 +45,7 @@ pub enum VerificationError { NoJwksDomain, } -fn create_request_ctx() -> RequestContext { - let request = request_builder() - .method(Method::GET) - .uri("/") - .body(Body::empty()) - .expect("minimal request should be valid"); - RequestContext::new(request, PathParams::new(HashMap::new())) -} - -async fn fetch_jwks(domain: &str) -> Result { +async fn fetch_jwks(ctx: &RequestContext, domain: &str) -> Result { let jwks_url = format!("http://{}/.well-known/ts.jwks.json", domain); log::debug!("Fetching JWKS from {}", jwks_url); @@ -66,7 +56,6 @@ async fn fetch_jwks(domain: &str) -> Result { log::info!("URI: {}", uri); let proxy_request = ProxyRequest::new(Method::GET, uri); - let ctx = create_request_ctx(); let proxy_handle = ctx .proxy_handle() .ok_or_else(|| VerificationError::HttpError("Proxy not available".to_string()))?; @@ -103,7 +92,10 @@ async fn fetch_jwks(domain: &str) -> Result { .map_err(|e| VerificationError::HttpError(format!("JWKS parse failed: {}", e))) } -async fn get_cached_jwks(domain: &str) -> Result { +async fn get_cached_jwks( + ctx: &RequestContext, + domain: &str, +) -> Result { let cache_key = domain.to_string(); { @@ -132,7 +124,7 @@ async fn get_cached_jwks(domain: &str) -> Result, domain: &str, @@ -229,7 +222,7 @@ pub async fn verify_request_id_signature( domain ); - let jwks = get_cached_jwks(domain).await?; + let jwks = get_cached_jwks(ctx, domain).await?; let public_key = find_public_key(&jwks, key_id)?; verify_ed25519_signature(public_key, signature, request_id)?; @@ -238,20 +231,35 @@ pub async fn verify_request_id_signature( #[cfg(test)] mod tests { + use anyedge_core::http::request_builder; + use anyedge_core::params::PathParams; use futures::executor::block_on; + use std::collections::HashMap; use super::*; + fn create_test_context() -> RequestContext { + let request = request_builder() + .method(Method::POST) + .uri("/openrtb2/auction") + .body(Body::empty()) + .unwrap(); + RequestContext::new(request, PathParams::new(HashMap::new())) + } + #[test] fn verify_missing_signature_field() { let request_id = "test-id"; let ext = serde_json::json!({ - "trusted_server": { - "kid": "test-key" - } + "trusted_server": { + "kid": "test-key" + } }); + let ctx = create_test_context(); + let result = block_on(verify_request_id_signature( + &ctx, request_id, Some(&ext), "example.com", @@ -270,7 +278,11 @@ mod tests { "signature": "test-sig" } }); + + let ctx = create_test_context(); + let result = block_on(verify_request_id_signature( + &ctx, request_id, Some(&ext), "example.com", @@ -287,7 +299,11 @@ mod tests { let ext = serde_json::json!({ "some_other_field": "value" }); + + let ctx = create_test_context(); + let result = block_on(verify_request_id_signature( + &ctx, request_id, Some(&ext), "example.com", @@ -301,7 +317,15 @@ mod tests { #[test] fn verify_with_none_ext() { let request_id = "test-id"; - let result = block_on(verify_request_id_signature(request_id, None, "example.com")); + + let ctx = create_test_context(); + + let result = block_on(verify_request_id_signature( + &ctx, + request_id, + None, + "example.com", + )); assert!(matches!( result.unwrap_err(), VerificationError::InvalidSignature(_) From 04f171964e96eeb15726fcbf8a5501e2022c9b4e Mon Sep 17 00:00:00 2001 From: Christian Date: Tue, 25 Nov 2025 09:27:47 -0600 Subject: [PATCH 3/3] update crates --- Cargo.lock | 187 ++++++++++++++++++++++++++--------------------------- 1 file changed, 93 insertions(+), 94 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d225d97..ecf7cc5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -44,7 +44,7 @@ dependencies = [ [[package]] name = "anyedge-adapter-axum" version = "0.1.0" -source = "git+ssh://git@github.com/stackpop/anyedge.git#ec10ba46ac8a9d336e425cb7634251b5e227182f" +source = "git+ssh://git@github.com/stackpop/anyedge.git#192de64ae35737f4203ed2426fee60c12392c97d" dependencies = [ "anyedge-core", "anyhow", @@ -66,7 +66,7 @@ dependencies = [ [[package]] name = "anyedge-adapter-cloudflare" version = "0.1.0" -source = "git+ssh://git@github.com/stackpop/anyedge.git#ec10ba46ac8a9d336e425cb7634251b5e227182f" +source = "git+ssh://git@github.com/stackpop/anyedge.git#192de64ae35737f4203ed2426fee60c12392c97d" dependencies = [ "anyedge-core", "async-trait", @@ -83,7 +83,7 @@ dependencies = [ [[package]] name = "anyedge-adapter-fastly" version = "0.1.0" -source = "git+ssh://git@github.com/stackpop/anyedge.git#ec10ba46ac8a9d336e425cb7634251b5e227182f" +source = "git+ssh://git@github.com/stackpop/anyedge.git#192de64ae35737f4203ed2426fee60c12392c97d" dependencies = [ "anyedge-core", "async-stream", @@ -103,7 +103,7 @@ dependencies = [ [[package]] name = "anyedge-core" version = "0.1.0" -source = "git+ssh://git@github.com/stackpop/anyedge.git#ec10ba46ac8a9d336e425cb7634251b5e227182f" +source = "git+ssh://git@github.com/stackpop/anyedge.git#192de64ae35737f4203ed2426fee60c12392c97d" dependencies = [ "anyedge-macros", "anyhow", @@ -130,13 +130,13 @@ dependencies = [ [[package]] name = "anyedge-macros" version = "0.1.0" -source = "git+ssh://git@github.com/stackpop/anyedge.git#ec10ba46ac8a9d336e425cb7634251b5e227182f" +source = "git+ssh://git@github.com/stackpop/anyedge.git#192de64ae35737f4203ed2426fee60c12392c97d" dependencies = [ "log", "proc-macro2", "quote", "serde", - "syn 2.0.109", + "syn 2.0.111", "toml", "validator", ] @@ -149,9 +149,9 @@ checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "async-compression" -version = "0.4.32" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a89bce6054c720275ac2432fbba080a66a2106a44a1b804553930ca6909f4e0" +checksum = "0e86f6d3dc9dc4352edeea6b8e499e13e3f5dc3b964d7ca5fd411415a3498473" dependencies = [ "compression-codecs", "compression-core", @@ -179,7 +179,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -190,7 +190,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -207,9 +207,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "axum" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18ed336352031311f4e0b4dd2ff392d4fbb370777c9d18d7fc9d7359f73871" +checksum = "5b098575ebe77cb6d14fc7f32749631a6e44edbef6b796f89b020e99ba20d425" dependencies = [ "axum-core", "bytes", @@ -328,15 +328,15 @@ checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" [[package]] name = "cc" -version = "1.2.44" +version = "1.2.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37521ac7aabe3d13122dc382493e20c9416f299d2ccd5b3a5340a2570cdeb0f3" +checksum = "cd405d82c84ff7f35739f175f67d8b9fb7687a0e84ccdc78bd3568839827cf07" dependencies = [ "find-msvc-tools", "shlex", @@ -378,9 +378,9 @@ dependencies = [ [[package]] name = "compression-codecs" -version = "0.4.31" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8a506ec4b81c460798f572caead636d57d3d7e940f998160f52bd254bf2d23" +checksum = "302266479cb963552d11bd042013a58ef1adc56768016c8b82b4199488f2d4ad" dependencies = [ "brotli", "compression-core", @@ -390,9 +390,9 @@ dependencies = [ [[package]] name = "compression-core" -version = "0.4.29" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e47641d3deaf41fb1538ac1f54735925e275eaf3bf4d55c81b137fba797e5cbb" +checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" [[package]] name = "const-oid" @@ -426,9 +426,9 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", "typenum", @@ -458,7 +458,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -482,7 +482,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -493,7 +493,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -534,7 +534,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -544,7 +544,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -574,7 +574,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -631,9 +631,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "fastly" -version = "0.11.9" +version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac590af69cdea42ebbbaa566d0e603c6c0d7d6f53a507fe82cea65260419ab88" +checksum = "2b2c298b27a94ac3747165fc964af1fc75908dd07c9b742c5576351b295ecec4" dependencies = [ "anyhow", "bytes", @@ -659,9 +659,9 @@ dependencies = [ [[package]] name = "fastly-macros" -version = "0.11.9" +version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b012bd5c924ede9a1363ad29a232c4e95c9eb520a124979ad06043a6e44025dc" +checksum = "1c596f4a2502dd04d5ae7983d7d37a8a8feab5a1a032d6932803dd34d1c1760d" dependencies = [ "proc-macro2", "quote", @@ -670,9 +670,9 @@ dependencies = [ [[package]] name = "fastly-shared" -version = "0.11.9" +version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe8aaf17b8c0b689ce8370052e129c7722f3bd9c5ca27790db7624cf64b8c9b1" +checksum = "82277ec9cab41d300268389bc0936b6460b3846aa02b264510f86055e46d976d" dependencies = [ "bitflags 1.3.2", "http", @@ -680,9 +680,9 @@ dependencies = [ [[package]] name = "fastly-sys" -version = "0.11.9" +version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a784af8ed4e5f3d32aac54f687b6a2dd844af304390d3bc70d50cbe6a772c1a7" +checksum = "e6670559be2e7c7db47c6e102b86c613fc69f10715af399dae3bc5baedc1ea70" dependencies = [ "bitflags 1.3.2", "fastly-shared", @@ -707,9 +707,9 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "find-msvc-tools" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" [[package]] name = "flate2" @@ -792,7 +792,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -827,9 +827,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.9" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -880,18 +880,17 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "http" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" dependencies = [ "bytes", - "fnv", "itoa", ] @@ -932,9 +931,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" dependencies = [ "atomic-waker", "bytes", @@ -971,9 +970,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" +checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56" dependencies = [ "base64", "bytes", @@ -1127,9 +1126,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ "equivalent", "hashbrown", @@ -1205,9 +1204,9 @@ dependencies = [ [[package]] name = "log-fastly" -version = "0.11.9" +version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c67b1d4ff825b027926a385cea5a9f43d0b5a900030d48736833a552120e2b59" +checksum = "494214bfcca160d0787a47bee3f5f12bce4ea4204cd766b80d56589a57f70d00" dependencies = [ "fastly", "log", @@ -1398,9 +1397,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.3" +version = "2.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e7521a040efde50c3ab6bbadafbe15ab6dc042686926be59ac35d74607df4" +checksum = "cbcfd20a6d4eeba40179f05735784ad32bdaef05ce8e8af05f180d45bb3e7e22" dependencies = [ "memchr", "ucd-trie", @@ -1408,9 +1407,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.8.3" +version = "2.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "187da9a3030dbafabbbfb20cb323b976dc7b7ce91fcd84f2f74d6e31d378e2de" +checksum = "51f72981ade67b1ca6adc26ec221be9f463f2b5839c7508998daa17c23d94d7f" dependencies = [ "pest", "pest_generator", @@ -1418,22 +1417,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.3" +version = "2.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b401d98f5757ebe97a26085998d6c0eecec4995cad6ab7fc30ffdf4b052843" +checksum = "dee9efd8cdb50d719a80088b76f81aec7c41ed6d522ee750178f83883d271625" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] name = "pest_meta" -version = "2.8.3" +version = "2.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f27a2cfee9f9039c4d86faa5af122a0ac3851441a34865b8a043b46be0065a" +checksum = "bf1d70880e76bdc13ba52eafa6239ce793d85c8e43896507e43dd8984ff05b82" dependencies = [ "pest", "sha2 0.10.9", @@ -1456,7 +1455,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -1524,7 +1523,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -1593,9 +1592,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.41" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] @@ -1840,7 +1839,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -1875,7 +1874,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -1931,9 +1930,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.6" +version = "1.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad" dependencies = [ "libc", ] @@ -2028,9 +2027,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.109" +version = "2.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f17c7e013e88258aa9543dcbe81aca68a667a9ac37cd69c9fbc07858bfe0e2f" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ "proc-macro2", "quote", @@ -2054,7 +2053,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -2083,7 +2082,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -2094,7 +2093,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -2179,7 +2178,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -2249,9 +2248,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +checksum = "9cf146f99d442e8e68e585f5d798ccd3cad9a7835b917e09728880a862706456" dependencies = [ "bitflags 2.10.0", "bytes", @@ -2297,7 +2296,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -2395,7 +2394,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -2483,7 +2482,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", "wasm-bindgen-shared", ] @@ -2517,7 +2516,7 @@ checksum = "085b2df989e1e6f9620c1311df6c996e83fe16f57792b272ce1e024ac16a90f1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -2592,7 +2591,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -2603,7 +2602,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -2849,7 +2848,7 @@ dependencies = [ "async-trait", "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-macro-support", @@ -2893,28 +2892,28 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.27" +version = "0.8.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +checksum = "4ea879c944afe8a2b25fef16bb4ba234f47c694565e97383b36f3a878219065c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.27" +version = "0.8.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +checksum = "cf955aa904d6040f70dc8e9384444cb1030aed272ba3cb09bbc4ab9e7c1f34f5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -2934,7 +2933,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", "synstructure", ] @@ -2974,5 +2973,5 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ]