diff --git a/Cargo.lock b/Cargo.lock index fac439d..b701b52 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -256,6 +256,12 @@ dependencies = [ "syn 2.0.39", ] +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + [[package]] name = "arrayvec" version = "0.7.4" @@ -349,6 +355,20 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02bef9e74b5908bed0360844109a55b62b07cc973274c11d3a577bda8cc1cf60" +[[package]] +name = "blake3" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "rayon", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -465,6 +485,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "constant_time_eq" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" + [[package]] name = "convert_case" version = "0.4.0" @@ -1380,6 +1406,7 @@ name = "piped-proxy" version = "0.1.0" dependencies = [ "actix-web", + "blake3", "image", "libwebp-sys", "mimalloc", diff --git a/Cargo.toml b/Cargo.toml index 9eb7f35..4b9c254 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,9 +23,10 @@ rgb = { version = "0.8.37", optional = true } once_cell = "1.18.0" regex = "1.10.2" +blake3 = { version = "1.5.0", features = ["rayon"], optional = true } [features] -default = ["webp", "mimalloc", "reqwest-rustls"] +default = ["webp", "mimalloc", "reqwest-rustls", "qhash"] reqwest-rustls = ["reqwest/rustls-tls"] reqwest-native-tls = ["reqwest/default-tls"] @@ -37,5 +38,7 @@ mimalloc = ["dep:mimalloc"] optimized = ["libwebp-sys?/sse41", "libwebp-sys?/avx2", "libwebp-sys?/neon"] +qhash = ["blake3"] + [profile.release] lto = true diff --git a/src/main.rs b/src/main.rs index 3e50b9e..e6c879a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -137,6 +137,53 @@ async fn index(req: HttpRequest) -> Result> { // parse query string let query = QString::from(req.query_string()); + #[cfg(feature = "qhash")] + { + use std::collections::BTreeSet; + + let secret = env::var("HASH_SECRET"); + if let Ok(secret) = secret { + let qhash = query.get("qhash"); + + if qhash.is_none() { + return Err("No qhash provided".into()); + } + + let qhash = qhash.unwrap(); + + // check that qhash is valid + if qhash.len() != 8 { + return Err("Invalid qhash provided".into()); + } + + // store sorted key-value pairs + let mut set = BTreeSet::new(); + + query.to_pairs().iter().for_each(|(key, value)| { + if matches!(*key, "qhash" | "range" | "rewrite") { + return; + } + set.insert((key.as_bytes(), value.as_bytes())); + }); + + let mut hasher = blake3::Hasher::new(); + + for (key, value) in set { + hasher.update(key); + hasher.update(value); + } + + hasher.update(secret.as_bytes()); + + let hash = hasher.finalize().to_hex(); + let hash = &hash[..8]; + + if hash != qhash { + return Err("Invalid qhash provided".into()); + } + } + } + let res = query.get("host"); let res = res.map(|s| s.to_string());