Skip to main content

Crate broadcast_auth

Crate broadcast_auth 

Source
Expand description

§broadcast-auth

crates.io docs.rs

Shared multi-scheme HTTP and RTSP authentication: a client that answers WWW-Authenticate challenges, and a server that issues and verifies them (Basic, Digest, Bearer, reverse-proxy Forwarded, signed URLs).

§What it does / what it does not

Does: one Credentials model (Basic/Digest user and password, or a Bearer token); a client side (Authenticator, respond) that turns a server challenge into an Authorization value and keeps Digest’s nonce count advancing across a session; and a server side (server::Verifier) that renders the WWW-Authenticate challenge for a 401 and verifies an incoming RequestContext (method, URI, body, headers, peer address). Secrets are wiped on drop and compared in constant time.

Does not: do any IO (sockets, header framing, TLS), store credentials or parse config, or implement OAuth flows. Digest offers qop=auth with MD5 only (the shape every camera and player in the workspace uses); auth-int and SHA-256 Digest are not offered by the verifier. Each client or server owns its transport: rtsp-runtime, hls-runtime and multimux call into this crate.

§Standards supported

StandardEditionScopeStatusNotes
RFC 7617 HTTP BasicRFC 7617 (not vendored, unverified here)Client response, server verify (constant-time)FullChallenge parsing delegated to http-auth
RFC 7616 HTTP DigestRFC 7616 (not vendored, unverified here)Client response (MD5, qop=auth, advancing nc), server verify incl. §3.4.4 username* and claimed-uri checkPartialMD5 and qop=auth only. Server nonces are signed (HMAC-SHA256), expire after DIGEST_NONCE_LIFETIME (3600 s) and carry a 64-value anti-replay window per (nonce, cnonce), capped at DIGEST_NC_TRACK_CAP
RFC 6750 Bearer tokenRFC 6750 (not vendored, unverified here)Authorization: Bearer <token>FullNo challenge round-trip
RFC 7235 HTTP authentication frameworkRFC 7235 (not vendored, unverified here)WWW-Authenticate / Authorization header handlingFullVia http-auth’s ChallengeParser
RFC 2326 RTSP 1.0 §14 and §16RFC 2326 (vendored specs/rfc2326_rtsp.txt)RTSP reuses Basic and Digest; only the request URI differsFullUsed by rtsp-runtime
Forwarded (reverse-proxy trust)Workspace scheme, not a standardTrusts a configured header (default convention X-Forwarded-User)Full, server onlySafe only behind a proxy that strips client-supplied copies
Signed URLs (HMAC-SHA256 exp/kid/sig, optional ip)Workspace scheme, CDN-style, not a standardMint and verify, key rotation across kids, optional IP scope, constant-time compareFull, server onlysigned_url::SignedUrlKeySet

§Features

None. The crate has no Cargo features, is std, and has #![forbid(unsafe_code)].

§Quick start

A loopback of the whole flow: an origin issues a Digest challenge, a client answers it, the origin verifies.

use broadcast_auth::{AuthResult, Authenticator, Credentials, RequestContext, Verifier};

let creds = Credentials::new("admin", "12345");
let verifier = Verifier::new(creds.clone(), "cameras");

// 401 response: the server renders the challenge.
let challenge = verifier.challenge();
assert!(challenge.starts_with("Digest "));

// Client answers it and keeps the Authenticator for later requests.
let mut client = Authenticator::from_challenge(&challenge, creds).unwrap();
let authorization = client
    .authorization(&RequestContext::new("DESCRIBE", "rtsp://cam/stream"))
    .unwrap();

// Server verifies the retried request.
let headers = [("Authorization", authorization.as_str())];
let ctx = RequestContext::new("DESCRIBE", "rtsp://cam/stream").with_headers(&headers);
assert_eq!(verifier.verify(&ctx), AuthResult::Ok);

// A wrong password is refused.
let mut bad = Authenticator::from_challenge(&challenge, Credentials::new("admin", "nope")).unwrap();
let wrong = bad.authorization(&RequestContext::new("DESCRIBE", "rtsp://cam/stream")).unwrap();
let headers = [("Authorization", wrong.as_str())];
let ctx = RequestContext::new("DESCRIBE", "rtsp://cam/stream").with_headers(&headers);
assert_eq!(verifier.verify(&ctx), AuthResult::Unauthorized);

Signed URLs, with no Authorization header at all:

use broadcast_auth::{AuthResult, RequestContext, SignedUrlKeySet, Verifier};

let keys = SignedUrlKeySet::new([("key-1".to_string(), vec![0u8; 32])]).unwrap();
let query = keys.sign("key-1", "/stream/media.m3u8", 4_000_000_000, None).unwrap();
let verifier = Verifier::signed_url(keys);
let uri = format!("/stream/media.m3u8?{query}");
assert_eq!(verifier.verify(&RequestContext::new("GET", &uri)), AuthResult::Ok);

Runnable programs: cargo run -p broadcast-auth --example client_respond and --example server_verify.

§What is implemented

  • Client: Credentials::new (answers whichever of Basic or Digest the challenge advertises) and Credentials::bearer; Authenticator::from_challenge plus authorization for session reuse; one-shot respond.
  • Server: Verifier::new (scheme chosen by the Credentials variant: Basic, Digest or Bearer), Verifier::forwarded, Verifier::signed_url, challenge / challenge_for, verify returning AuthResult, with_clock and with_digest_nc_capacity for tests and tuning.
  • RequestContext: method, URI, optional body and headers (case-insensitive header lookup), peer address.
  • Security properties: Credentials, Verifier, nonce keys and SignedUrlKeySet zeroize on drop; comparisons use subtle::ConstantTimeEq; Digest nonces are stateless-signed and replay-windowed; a secret_debug_guard test forbids derived Debug over secrets.

§Design notes

Delegates challenge parsing and Digest hashing to http-auth; types are #[non_exhaustive] where they may grow; errors are structured (Error); a lexical dehandroll_guard test keeps hand-rolled HTTP or base64 out of the source.

Consumed by rtsp-runtime (re-exports Credentials), hls-runtime (client adapter) and multimux (input adapters and the shared output-auth gate). It depends on no other workspace crate.

§MSRV and licence

MSRV 1.95.0. Licensed under MIT OR Apache-2.0.

Re-exports§

pub use signed_url::SignedUrlKeySet;

Modules§

signed_url
HMAC-signed URL access control (issue #747) — CDN-style, short-lived, tamper-proof query-string tokens that gate a media egress route without the caller carrying a credential header at all.

Structs§

Authenticator
Negotiates a challenge once, then answers every subsequent request in the session (RFC 7235 WWW-Authenticate/Authorization; RFC 2326 §14 for RTSP; RFC 6750 for Bearer).
RequestContext
The request fields the Digest response hash covers (RFC 7616 §3.4.1 / RFC 2326 §14): the method, the request URI, and — for qop=auth-int — the body. Also carries the request’s headers and transport peer address, so a server-side crate::Verifier scheme can see beyond the Authorization header — e.g. a reverse-proxy forwarded-auth scheme reading X-Forwarded-User/X-Forwarded-For (issue #663 extensibility wave part 1). Client-side use (crate::respond/crate::Authenticator) needs neither field; Self::new defaults both to empty/None.
Verifier
Challenges + verifies incoming requests against one configured Credentials (RFC 7235 origin-side auth) — see the module docs.

Enums§

AuthResult
The outcome of Verifier::verify.
Credentials
Credentials for one of the supported auth schemes.
Error
Errors produced by challenge parsing / response computation.

Constants§

DIGEST_NC_TRACK_CAP
Default number of (nonce, cnonce) pairs a Digest Verifier tracks (see Verifier::with_digest_nc_capacity). Each tracked pair costs about 128 bytes (a 32-byte key hash stored twice, 28 bytes of counters, and hash-map/B-tree overhead), so the default is roughly 8 MiB at most.
DIGEST_NONCE_LIFETIME
How long an issued Digest nonce is accepted, measured from issue (RFC 7616 §5.4 leaves the lifetime to the server). An older nonce is refused, and Verifier::challenge_for answers it with stale=true so the client retries silently with a fresh nonce.
NC_WINDOW
Width of the per-pair nc anti-replay window: how far below the highest accepted nc a not-yet-seen nc is still accepted (RFC 4303 §3.4.3).

Functions§

respond
One-shot challenge->response: computes the Authorization value for a single request without keeping an Authenticator around.

Type Aliases§

Result
Result alias for the crate’s fallible operations.