Documentation
¶
Overview ¶
Package httpsig signs and verifies amber-store remote-protocol HTTP messages. A request signature covers a canonical CBOR map of {method, path+query, timestamp, nonce, blake3(body)}; a response signature covers {request nonce, status, blake3(body)}. Both are SSHSIG blobs in the amber-store-http namespace, base64 in Amber-* headers. The expensive part — hashing a multi-megabyte body — is blake3; SSHSIG's internal SHA-512 only covers the ~100-byte canonical payload.
Index ¶
- Constants
- func AppendSignatureTrailer(w io.Writer, sigB64 string) error
- func HashBody(body []byte) []byte
- func SignRequest(req *http.Request, signer ssh.Signer, timestamp int64, nonce, body []byte) error
- func SignRequestHash(req *http.Request, signer ssh.Signer, timestamp int64, nonce, bodyHash []byte) error
- func SignResponse(signer ssh.Signer, nonce []byte, status int, bodyHash []byte) (string, error)
- func SplitSignatureTrailer(body []byte) (prefix []byte, sigB64 string, ok bool)
- func VerifyRequest(r *http.Request, body []byte, now time.Time, window time.Duration) (ssh.PublicKey, []byte, error)
- func VerifyRequestHash(r *http.Request, bodyHash []byte, now time.Time, window time.Duration) (ssh.PublicKey, []byte, error)
- func VerifyResponse(serverPubWire, nonce []byte, status int, bodyHash []byte, sigB64 string) error
Constants ¶
const ( HeaderPublicKey = "Amber-Public-Key" // signer's public key, SSH wire format, base64 HeaderTimestamp = "Amber-Timestamp" // ns since the Unix epoch, decimal HeaderNonce = "Amber-Nonce" // random bytes, base64 HeaderSignature = "Amber-Signature" // raw SSHSIG blob, base64 )
Header names of the four signature components.
const DefaultWindow = 5 * time.Minute
DefaultWindow is the default timestamp validity window (each side of now).
const Namespace = "amber-store-http"
Namespace is the SSHSIG namespace for amber-store HTTP signatures.
Variables ¶
This section is empty.
Functions ¶
func AppendSignatureTrailer ¶
AppendSignatureTrailer writes an in-band response signature after an already-written body: the base64 signature followed by its big-endian uint32 length. Unlike an HTTP trailer (which proxies routinely drop) it is part of the body, so it survives any intermediary; unlike a header it can be emitted after a streamed body, when the body hash is finally known. SplitSignatureTrailer recovers it. The signature itself still covers {nonce, status, blake3(prefix)} via SignResponse, so a tampered or truncated prefix fails verification.
func SignRequest ¶
SignRequest signs req's method, path+query, timestamp, nonce and body, and sets the four Amber-* headers. body must be the exact bytes the request will send.
func SignRequestHash ¶
func SignRequestHash(req *http.Request, signer ssh.Signer, timestamp int64, nonce, bodyHash []byte) error
SignRequestHash is SignRequest for a body the caller has already hashed — needed when the body is streamed (so it is never held whole), since the signature header must be set before the body is sent. bodyHash must be the blake3-256 of the exact bytes the request will send.
func SignResponse ¶
SignResponse signs {nonce, status, bodyHash} with the server identity, returning the base64 header/trailer value. nonce is the request's nonce, binding the response to its request.
func SplitSignatureTrailer ¶
SplitSignatureTrailer splits a fully-buffered body into the signed prefix and the base64 signature appended by AppendSignatureTrailer. ok is false if the body is too short or its trailing length is inconsistent — both of which then fail closed at signature verification.
func VerifyRequest ¶
func VerifyRequest(r *http.Request, body []byte, now time.Time, window time.Duration) (ssh.PublicKey, []byte, error)
VerifyRequest checks r's Amber-* headers against body. See VerifyRequestHash.
func VerifyRequestHash ¶
func VerifyRequestHash(r *http.Request, bodyHash []byte, now time.Time, window time.Duration) (ssh.PublicKey, []byte, error)
VerifyRequestHash is VerifyRequest for callers that have already hashed the body (e.g. a streaming receiver). bodyHash must be blake3-256 of the exact request body bytes: the timestamp must be within window of now and the signature must verify over the reconstructed payload with the claimed public key. It returns the claimed key and the nonce; the caller still must check the nonce for replay and the key against an allowlist. Callers should map any non-nil error to a 401 response. The nonce is returned even on failure so error responses can be signed over it.
Types ¶
This section is empty.