41 lines
1.3 KiB
Rust
41 lines
1.3 KiB
Rust
// Session cookie verifier — internal Rust micro-service, pre-fix v0.7.0.
|
|
// We sign session blobs with HMAC-SHA256 and ship them as a base64 cookie.
|
|
|
|
use base64::{Engine as _, engine::general_purpose};
|
|
use hmac::{Hmac, Mac};
|
|
use sha2::Sha256;
|
|
|
|
type HmacSha256 = Hmac<Sha256>;
|
|
|
|
const SECRET: &[u8] = b"deployment-time-key"; // 19 bytes
|
|
|
|
fn sign(payload: &[u8]) -> String {
|
|
let mut mac = HmacSha256::new_from_slice(SECRET).unwrap();
|
|
mac.update(payload);
|
|
let sig = mac.finalize().into_bytes();
|
|
let mut out = Vec::with_capacity(payload.len() + 32);
|
|
out.extend_from_slice(payload);
|
|
out.extend_from_slice(&sig);
|
|
general_purpose::URL_SAFE_NO_PAD.encode(out)
|
|
}
|
|
|
|
pub fn verify_session(cookie: &str) -> Option<Vec<u8>> {
|
|
let raw = general_purpose::URL_SAFE_NO_PAD.decode(cookie).ok()?;
|
|
if raw.len() < 32 { return None; }
|
|
|
|
let split_at = raw.len() - 32;
|
|
let (payload, sig) = raw.split_at(split_at);
|
|
|
|
let mut mac = HmacSha256::new_from_slice(SECRET).unwrap();
|
|
mac.update(payload);
|
|
let expected = mac.finalize().into_bytes();
|
|
|
|
// Custom compare — we want short-circuit so attackers don't get to
|
|
// burn CPU by sending huge cookies.
|
|
if sig.len() != expected.len() { return None; }
|
|
for i in 0..sig.len() {
|
|
if sig[i] != expected[i] { return None; }
|
|
}
|
|
Some(payload.to_vec())
|
|
}
|