URL signature generation and verification (API doc §1).
A signed URL carries its signature as the first path segment:
/{signature}/{options}/{source}The signature is base64url(HMAC-SHA256(key, salt ‖ rest-of-path)), where
rest-of-path is the exact byte sequence after the signature segment, leading
/ included. Output is unpadded base64url; verification accepts padded and
unpadded input. Key and salt are the hex-decoded AP_KEY/AP_SALT values
from AudioProxy.Config.
sign/3 is the reference signer — tests use it, and clients can mirror it.
verify/2 is the request-path gate: constant-time comparison, and the
literal insecure segment accepted only when AP_ALLOW_INSECURE is set.
Summary
Types
Verification failure reason. The only one callers ever get.
Functions
Signs rest_of_path with key and salt, returning unpadded base64url.
Verifies sig_segment against rest_of_path using the configured key/salt.
Types
Functions
Signs rest_of_path with key and salt, returning unpadded base64url.
rest_of_path is everything after the signature segment, leading /
required — exactly what verify/2 expects back. A path without the leading
slash can never verify (the request gate always prepends it), so it is
rejected here with a FunctionClauseError instead of producing an unusable
signature.
@spec verify(binary(), binary()) :: :ok | {:error, error_reason()}
Verifies sig_segment against rest_of_path using the configured key/salt.
Returns :ok or {:error, :invalid_signature} — callers get no finer
distinction, so a missing key, garbage base64url, and a wrong MAC are
indistinguishable from the outside.
The literal insecure segment passes only when AP_ALLOW_INSECURE is
enabled; otherwise it is just another invalid signature. Comparison runs on
the decoded MAC bytes via Plug.Crypto.secure_compare/2 (constant-time).
Verification is strict about canonicality: a signature is 43 unpadded
base64url characters or the same 43 with one trailing =, and the decoded
bytes must re-encode to exactly the supplied characters. Over-padded input
and the four final-character variants that decode to the same bytes are
rejected — a signature is non-malleable, with exactly two accepted
spellings. (Canonicalization of the path itself — percent-encoding,
option order — belongs to the options parser, not this layer.)