09 / EDGE
signature
The X-Content-Security request signature: an RSA-sealed envelope carrying the MAC key, the time and the payload type; a MAC over the timestamp, method, path, query and body digest; a tolerance on the timestamp; and an AES-ECB payload when the envelope says the body is sealed.
WHERE IT SITS
INTERFACE
suberror RefuseditemA content-security failure. Each variant names the one check that refused the request, so a caller can tell a replay from a wrong key.
pub(open) trait DecrypteritemTurns the sealed secret field of the header into the envelope it carries. Implemented by the RSA decrypter below and open to any transport a caller prefers — an HSM, a KMS, a key in an environment variable.
struct SignatureKeystructOne verification key: the fingerprint a caller names in the header, and the decrypter that opens envelopes sealed for it.
struct SignatureVerifierstructThe verification policy: the keys, how far a timestamp may drift, and whether a request that fails verification is refused or merely ignored.
fn SignatureVerifier::new( keys~ : Array[SignatureKey], tolerance_ms~ : Int64 = 3600000L, strict~ : Bool = false) -> SignatureVerifierfnA verifier over keys. tolerance_ms defaults to one hour, the expiry SignatureConf carries. A strict verifier refuses a request that fails; a lenient one serves it unsigned, which is how a service rolls the check out before every client signs.
fn SignatureVerifier::strict(self : SignatureVerifier) -> BoolfnWhether this verifier refuses a request it cannot verify.
fn parse_pairs(value : String) -> Map[String, String]fnSplit key=value pairs out of a header value: fields separated by ,, name and value by the **first** =, because a base64 value carries padding =. Both sides of a field are trimmed, and a field with no name is skipped.
fn constant_time_eq(a : BytesView, b : BytesView) -> BoolfnCompare two byte strings without leaking where they differ: a MAC check that returns early lets an attacker count matched octets.
fn parse_i64(text : String) -> Int64?fnParse a decimal integer, optionally signed. A value that is empty, carries a non-digit, or overflows is None — the caller reports it as a malformed field rather than reading a truncated number.
struct SignatureInputstructA request as the signature check sees it.
struct VerifiedContentstructThe plain content a verified request carries: the HMAC key the envelope named, whether the body was sealed, and the body itself — already decrypted when it was.
fn sign_content( timestamp : String, verb : String, path : String, query : String, body : Bytes) -> BytesfnThe MAC preimage: timestamp, method, path, query and the hex SHA-256 of the body, newline separated.
fn verify_signature( verifier : SignatureVerifier, input : SignatureInput, now_ms : Int64) -> VerifiedContent raise RefusedfnVerify one request against verifier at now_ms. On success the returned VerifiedContent carries the body to serve — the plaintext when the request declared an encrypted payload. A failure is the single check that refused it; the caller decides whether that is a 403 or a pass-through, per strict.
fn content_security( verifier : SignatureVerifier, clock : @clock.Clock, header~ : String = "x-content-security") -> MiddlewarefnContent-security middleware: verify the X-Content-Security signature on DELETE, GET, POST and PUT requests, the methods the reference implementation checks. A strict verifier answers 403 Forbidden when the check fails. A lenient one serves the request anyway, which is what lets a fleet roll signing out one client at a time. When the envelope declares an encrypted payload, the body served to the application is the decrypted one.