Skip to main content

osproxy_server/
cursor.rs

1//! Concrete HMAC signer for the stateless scroll/PIT affinity envelope
2//! (`docs/03` ยง6). The cluster a cursor is pinned to travels *with* the cursor in
3//! a signed token, so any fleet instance can recover it with no shared store; the
4//! signature stops a client redirecting a cursor to another cluster.
5//!
6//! The MAC is computed through the build's **validated** crypto module (ring
7//! under `non-fips`, aws-lc-rs under `fips`, cfg-selected exactly like the
8//! directive verifier and the TLS cert fingerprint, ADR-009), so a FIPS artifact
9//! never signs with a non-validated primitive. The mutual-exclusion compile
10//! guards live in [`crate::directive`].
11
12use osproxy_core::CursorSigner;
13
14// Same cfg-select as `HmacDirectiveVerifier`: ring and aws-lc-rs share this
15// `hmac` API (`Key::new`, `sign`).
16#[cfg(feature = "fips")]
17use aws_lc_rs::hmac;
18#[cfg(feature = "non-fips")]
19use ring::hmac;
20
21/// Signs cursor-affinity envelopes with a shared `HMAC-SHA256` key. The same key
22/// must be configured on every proxy instance so a token wrapped on one verifies
23/// on another (the whole point of the stateless design).
24pub struct HmacCursorSigner {
25    key: hmac::Key,
26}
27
28impl std::fmt::Debug for HmacCursorSigner {
29    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30        // Never render the key.
31        f.debug_struct("HmacCursorSigner").finish_non_exhaustive()
32    }
33}
34
35impl HmacCursorSigner {
36    /// Builds a signer from the shared `secret`.
37    #[must_use]
38    pub fn new(secret: &[u8]) -> Self {
39        Self {
40            key: hmac::Key::new(hmac::HMAC_SHA256, secret),
41        }
42    }
43}
44
45impl CursorSigner for HmacCursorSigner {
46    fn tag(&self, msg: &[u8]) -> Vec<u8> {
47        hmac::sign(&self.key, msg).as_ref().to_vec()
48    }
49}
50
51#[cfg(test)]
52#[path = "cursor_tests.rs"]
53mod tests;