Documentation
¶
Overview ¶
Package httpsignature implements RFC 9421 HTTP Message Signatures and RFC 9530 digest fields for net/http using the pinned RFC 8941 Structured Fields dialect, not later dialect extensions. Start with explicit SigningProfile or VerificationProfile and parser limits; optional digest/body/HTTP adapters compose these without owning a server, client, key store or business effect. Compatibility isolates caller-implemented legacy protocols. No cgo is added.
Parsing and cryptographic validity do not authenticate or authorize an operation by themselves. Applications must construct explicit signing and verification profiles, resolve algorithm-bound keys, enforce replay policy, supply trusted external request context behind proxies, and map safe typed failures to their own protocol. Profiles copy policy collections but borrow clocks, providers, resolvers, replay stores and cryptographic key objects. Concurrent use requires safe immutable collaborators and message snapshots; bound callbacks and I/O cooperatively with context. No callback/crypto worker is detached to force cancellation. Buffered versus streaming body ownership is explicit; transport/body cleanup and authentication policy are not global defaults. Editions/local choices are in docs/specification-decisions.md; these comments do not qualify the pinned conformance or external corpora.
Example (ClientAndServerMiddleware) ¶
package main
import (
"context"
"fmt"
"io"
"net/http"
"net/http/httptest"
"strings"
"time"
httpsignature "github.com/faustbrian/go-http-signature"
)
type exampleProvider struct {
key httpsignature.SigningKey
}
func (provider exampleProvider) SigningKey(context.Context) (httpsignature.SigningKey, error) {
return provider.key, nil
}
type exampleResolver struct {
key httpsignature.ResolvedKey
}
func (resolver exampleResolver) Resolve(context.Context, string) (httpsignature.ResolvedKey, error) {
return resolver.key, nil
}
func main() {
now := time.Unix(1_700_000_000, 0)
key, _ := httpsignature.NewHMACKey([]byte("0123456789abcdef0123456789abcdef"))
components := []httpsignature.ComponentIdentifier{
{Name: "@method"}, {Name: "@authority"}, {Name: "content-digest"},
}
signingProfile, _ := httpsignature.NewSigningProfile(httpsignature.SigningProfileConfig{
AllowedAlgorithms: []httpsignature.Algorithm{httpsignature.HMACSHA256}, CoveredComponents: components,
Expires: httpsignature.ParameterForbidden, AlgorithmParameter: httpsignature.ParameterRequired,
Nonce: httpsignature.ParameterForbidden, Tag: httpsignature.ParameterForbidden,
ResolveTimeout: time.Second, Now: func() time.Time { return now },
Provider: exampleProvider{key: httpsignature.SigningKey{
KeyID: "key-2026", Algorithm: httpsignature.HMACSHA256, Key: key,
NotBefore: now.Add(-time.Hour), NotAfter: now.Add(time.Hour),
}},
})
verificationProfile, _ := httpsignature.NewVerificationProfile(httpsignature.VerificationProfileConfig{
AllowedAlgorithms: []httpsignature.Algorithm{httpsignature.HMACSHA256}, RequiredComponents: components,
Created: httpsignature.ParameterRequired, Expires: httpsignature.ParameterForbidden,
AlgorithmParameter: httpsignature.ParameterRequired, Nonce: httpsignature.ParameterForbidden,
Tag: httpsignature.ParameterForbidden, MaxAge: time.Minute, ClockSkew: time.Second,
ResolveTimeout: time.Second, Now: func() time.Time { return now },
Resolver: exampleResolver{key: httpsignature.ResolvedKey{
Algorithm: httpsignature.HMACSHA256, Key: key,
NotBefore: now.Add(-time.Hour), NotAfter: now.Add(time.Hour), FreshUntil: now.Add(time.Minute),
}},
})
mapError := func(writer http.ResponseWriter, _ *http.Request, _ error) {
http.Error(writer, "invalid signed request", http.StatusUnauthorized)
}
verifySignature, _ := httpsignature.NewRequestVerificationMiddleware(
httpsignature.RequestVerificationMiddlewareConfig{
Verifier: httpsignature.NewVerifier(verificationProfile),
SelectLabel: func(*http.Request, httpsignature.SignatureInputs, httpsignature.Signatures) (string, error) {
return "sig", nil
},
MapError: mapError,
},
)
verifyDigest, _ := httpsignature.NewBufferedContentDigestVerificationMiddleware(
httpsignature.BufferedContentDigestVerificationMiddlewareConfig{
RequiredAlgorithms: []httpsignature.DigestAlgorithm{httpsignature.SHA256},
MaxBytes: 1024, MapError: mapError,
},
)
server := httptest.NewServer(verifyDigest(verifySignature(http.HandlerFunc(
func(writer http.ResponseWriter, request *http.Request) {
body, _ := io.ReadAll(request.Body)
_, verified := httpsignature.VerifiedSignatureFromContext(request.Context())
_, _ = fmt.Fprintln(writer, verified, string(body))
},
))))
defer server.Close()
signingTransport, _ := httpsignature.NewSigningRoundTripper(httpsignature.SigningRoundTripperConfig{
Transport: server.Client().Transport, Signer: httpsignature.NewSigner(signingProfile), Label: "sig",
Existing: httpsignature.ExistingSignaturesReject,
Options: func(context.Context, *http.Request) (httpsignature.SigningOptions, error) {
return httpsignature.SigningOptions{}, nil
},
})
digestTransport, _ := httpsignature.NewBufferedContentDigestRoundTripper(
httpsignature.BufferedContentDigestRoundTripperConfig{
Transport: signingTransport, Algorithms: []httpsignature.DigestAlgorithm{httpsignature.SHA256}, MaxBytes: 1024,
},
)
client := &http.Client{Transport: digestTransport}
response, _ := client.Post(server.URL+"/messages", "text/plain", strings.NewReader("signed payload"))
responseBody, _ := io.ReadAll(response.Body)
_ = response.Body.Close()
fmt.Print(response.StatusCode, " ", string(responseBody))
}
Output: 200 true signed payload
Example (RequestSignAndVerify) ¶
package main
import (
"context"
"fmt"
"net/http"
"time"
httpsignature "github.com/faustbrian/go-http-signature"
)
type exampleProvider struct {
key httpsignature.SigningKey
}
func (provider exampleProvider) SigningKey(context.Context) (httpsignature.SigningKey, error) {
return provider.key, nil
}
type exampleResolver struct {
key httpsignature.ResolvedKey
}
func (resolver exampleResolver) Resolve(context.Context, string) (httpsignature.ResolvedKey, error) {
return resolver.key, nil
}
func main() {
now := time.Unix(1_700_000_000, 0)
key, _ := httpsignature.NewHMACKey([]byte("0123456789abcdef0123456789abcdef"))
signingProfile, _ := httpsignature.NewSigningProfile(httpsignature.SigningProfileConfig{
AllowedAlgorithms: []httpsignature.Algorithm{httpsignature.HMACSHA256},
CoveredComponents: []httpsignature.ComponentIdentifier{{Name: "@method"}, {Name: "@authority"}},
Expires: httpsignature.ParameterForbidden,
AlgorithmParameter: httpsignature.ParameterRequired,
Nonce: httpsignature.ParameterForbidden,
Tag: httpsignature.ParameterForbidden,
ResolveTimeout: time.Second,
Now: func() time.Time { return now },
Provider: exampleProvider{key: httpsignature.SigningKey{
KeyID: "key-2026", Algorithm: httpsignature.HMACSHA256, Key: key,
NotBefore: now.Add(-time.Hour), NotAfter: now.Add(time.Hour),
}},
})
request, _ := http.NewRequest(http.MethodPost, "https://api.example/pay", nil)
signed, _ := httpsignature.NewSigner(signingProfile).Sign(
context.Background(), httpsignature.MessageContext{Request: request}, "sig", httpsignature.SigningOptions{},
)
inputs, _ := httpsignature.ParseSignatureInputs([]string{signed.SignatureInputField()})
signatures, _ := httpsignature.ParseSignatures([]string{signed.SignatureField()})
verificationProfile, _ := httpsignature.NewVerificationProfile(httpsignature.VerificationProfileConfig{
AllowedAlgorithms: []httpsignature.Algorithm{httpsignature.HMACSHA256},
RequiredComponents: []httpsignature.ComponentIdentifier{{Name: "@method"}, {Name: "@authority"}},
Created: httpsignature.ParameterRequired,
Expires: httpsignature.ParameterForbidden,
AlgorithmParameter: httpsignature.ParameterRequired,
Nonce: httpsignature.ParameterForbidden,
Tag: httpsignature.ParameterForbidden,
MaxAge: time.Minute,
ClockSkew: time.Second,
ResolveTimeout: time.Second,
Now: func() time.Time { return now },
Resolver: exampleResolver{key: httpsignature.ResolvedKey{
Algorithm: httpsignature.HMACSHA256, Key: key,
NotBefore: now.Add(-time.Hour), NotAfter: now.Add(time.Hour), FreshUntil: now.Add(time.Minute),
}},
})
verified, err := httpsignature.NewVerifier(verificationProfile).Verify(
context.Background(), httpsignature.MessageContext{Request: request}, "sig", inputs, signatures,
)
fmt.Println(err == nil, verified.Label, verified.Algorithm)
}
Output: true sig hmac-sha256
Index ¶
- Constants
- Variables
- func CombineSignedFields(fields ...SignedFields) (SignatureInputs, Signatures, error)
- func CreateSignatureBase(context MessageContext, input SignatureInput) (string, error)
- func Sign(ctx context.Context, algorithm Algorithm, key any, signatureBase []byte, ...) ([]byte, error)
- func Verify(ctx context.Context, algorithm Algorithm, key any, ...) error
- type AcceptSignatures
- type Algorithm
- type BufferedContentDigestRoundTripper
- type BufferedContentDigestRoundTripperConfig
- type BufferedContentDigestVerificationMiddleware
- type BufferedContentDigestVerificationMiddlewareConfig
- type BufferedTrailerVerificationMiddleware
- type BufferedTrailerVerificationMiddlewareConfig
- type BufferedTrailerVerifyingRoundTripper
- type BufferedTrailerVerifyingRoundTripperConfig
- type ComponentIdentifier
- type Digest
- type DigestAlgorithm
- type DigestField
- type DigestPreference
- type DigestPreferences
- type ExistingSignaturesPolicy
- type ExternalRequestContext
- type HMACKey
- type KeyResolver
- type MemoryReplayConfig
- type MemoryReplayStore
- type MessageContext
- type Parameter
- type ParameterPolicy
- type ReplayRecord
- type ReplayStore
- type RequestVerificationMiddleware
- type RequestVerificationMiddlewareConfig
- type ResolvedKey
- type ResponseSigningMiddleware
- type ResponseSigningMiddlewareConfig
- type ResponseTransportMode
- type SFToken
- type SignatureInput
- type SignatureInputs
- type SignatureRequest
- type SignatureValue
- type Signatures
- type SignedFields
- type Signer
- type SigningKey
- type SigningKeyProvider
- type SigningOptions
- type SigningProfile
- type SigningProfileConfig
- type SigningRoundTripper
- type SigningRoundTripperConfig
- type StructuredFieldType
- type SyntaxLimits
- type TrailerResponseSigningMiddleware
- type TrailerResponseSigningMiddlewareConfig
- type TrailerSigningRoundTripper
- type TrailerSigningRoundTripperConfig
- type VerificationError
- type VerificationFailure
- type VerificationProfile
- type VerificationProfileConfig
- type VerifiedSignature
- type Verifier
- type VerifyingRoundTripper
- type VerifyingRoundTripperConfig
Examples ¶
Constants ¶
const DefaultMaxSignatureBaseBytes = 1 << 20
DefaultMaxSignatureBaseBytes is the bounded default for direct callers that do not select a stricter per-message limit.
Variables ¶
var ( // ErrUnsupportedSignatureAlgorithm reports an unavailable or unregistered algorithm. ErrUnsupportedSignatureAlgorithm = errors.New("http signature: unsupported signature algorithm") // ErrIncompatibleKey reports a wrong, malformed, or out-of-policy key type. ErrIncompatibleKey = errors.New("http signature: incompatible key") // ErrInvalidSignatureValue reports cryptographic verification failure. ErrInvalidSignatureValue = errors.New("http signature: invalid signature value") // ErrSignatureRandomness reports unavailable signing randomness. ErrSignatureRandomness = errors.New("http signature: signing randomness unavailable") )
var ( // ErrInvalidBodyIntegration reports incomplete buffered digest policy. ErrInvalidBodyIntegration = errors.New("http signature: invalid body digest integration") // ErrBodyTooLarge reports that content exceeded its explicit buffering limit. ErrBodyTooLarge = errors.New("http signature: body exceeds digest buffer") // ErrBodyRead reports a body read or closure failure without retaining // potentially sensitive backend details. ErrBodyRead = errors.New("http signature: body unavailable for digest") // ErrExistingDigest reports a caller-supplied Content-Digest field that this // fail-closed adapter refuses to replace. ErrExistingDigest = errors.New("http signature: existing Content-Digest rejected") )
var ( // ErrInvalidDigestField reports malformed, empty, or ambiguous digest data. ErrInvalidDigestField = errors.New("http signature: invalid digest field") // ErrUnsupportedDigestAlgorithm reports an algorithm unavailable for local computation. ErrUnsupportedDigestAlgorithm = errors.New("http signature: unsupported digest algorithm") // ErrMissingDigest reports that policy selected an algorithm absent from the field. ErrMissingDigest = errors.New("http signature: required digest is missing") // ErrDigestMismatch reports that content does not match a selected digest. ErrDigestMismatch = errors.New("http signature: digest mismatch") )
var ( // ErrInvalidHTTPIntegration reports incomplete adapter configuration. ErrInvalidHTTPIntegration = errors.New("http signature: invalid HTTP integration") // ErrHTTPIntegrationSigning reports request-signing failure without exposing // message or key material. ErrHTTPIntegrationSigning = errors.New("http signature: HTTP request signing failed") // ErrExistingSignatures reports fields forbidden by adapter policy. ErrExistingSignatures = errors.New("http signature: existing signature fields rejected") // ErrHTTPIntegrationVerification reports response-verification failure // without exposing message or key material. ErrHTTPIntegrationVerification = errors.New("http signature: HTTP response verification failed") // ErrResponseBodyTooLarge reports that buffered response signing reached its // explicit resource limit before any response bytes were emitted. ErrResponseBodyTooLarge = errors.New("http signature: response body exceeds signing buffer") // ErrAmbiguousProtectedField reports case-colliding map keys for a protected // HTTP field whose values cannot be interpreted unambiguously. ErrAmbiguousProtectedField = errors.New("http signature: ambiguous protected HTTP field") )
var ( // ErrSignatureBase reports that a covered component cannot be resolved safely. ErrSignatureBase = errors.New("http signature: cannot create signature base") // ErrSignatureBaseLimit reports that canonicalization exceeded its explicit // output bound. It wraps ErrSignatureBase so existing classification remains // fail closed. ErrSignatureBaseLimit = fmt.Errorf("%w: resource limit exceeded", ErrSignatureBase) )
var ( // ErrReplayDetected reports that the same key and nonce pair was already // consumed within its validity window. ErrReplayDetected = errors.New("http signature: replay detected") // ErrReplayCapacity reports that a bounded replay backend cannot accept a // new record. Callers must fail verification closed. ErrReplayCapacity = errors.New("http signature: replay store capacity exceeded") // ErrInvalidReplayRecord reports an incomplete or out-of-policy record. ErrInvalidReplayRecord = errors.New("http signature: invalid replay record") // ErrInvalidReplayConfig reports missing or non-positive resource bounds. ErrInvalidReplayConfig = errors.New("http signature: invalid replay store configuration") )
var ( // ErrInvalidSignatureInput reports a malformed or semantically invalid // Signature-Input field. ErrInvalidSignatureInput = errors.New("http signature: invalid Signature-Input field") // ErrInvalidSignature reports a malformed or semantically invalid Signature // field. ErrInvalidSignature = errors.New("http signature: invalid Signature field") // ErrInvalidAcceptSignature reports a malformed or semantically invalid // Accept-Signature field. ErrInvalidAcceptSignature = errors.New("http signature: invalid Accept-Signature field") )
var ( // ErrInvalidSigningProfile reports an incomplete or contradictory signing // policy. ErrInvalidSigningProfile = errors.New("http signature: invalid signing profile") // ErrSigningPolicy reports that a signing request violates its profile. ErrSigningPolicy = errors.New("http signature: signing policy rejected request") // ErrSigningKey reports unavailable, expired, revoked, mismatched, or // incompatible signing material. ErrSigningKey = errors.New("http signature: signing key unavailable") // ErrSigningProvider reports a provider failure without retaining backend // details in the public error chain. ErrSigningProvider = errors.New("http signature: signing key provider failed") // ErrSigningBase reports signature-base construction failure. ErrSigningBase = errors.New("http signature: signing base unavailable") // ErrSigningCryptographic reports failure of the selected signing primitive. ErrSigningCryptographic = errors.New("http signature: cryptographic signing failed") // ErrInvalidSignedFields reports an empty, malformed, mismatched, or // duplicate set supplied for deterministic field combination. ErrInvalidSignedFields = errors.New("http signature: invalid signed fields") )
var ( // ErrInvalidSyntaxLimits reports missing or contradictory parser bounds. ErrInvalidSyntaxLimits = errors.New("http signature: invalid syntax limits") // ErrSyntaxLimit reports input rejected by an explicit parser bound. ErrSyntaxLimit = errors.New("http signature: syntax resource limit exceeded") )
var ( // ErrInvalidVerificationProfile reports an incomplete or contradictory // application policy. ErrInvalidVerificationProfile = errors.New("http signature: invalid verification profile") // ErrKeyNotFound allows resolvers to distinguish an unknown identifier from // transient backend failure without disclosing the identifier. ErrKeyNotFound = errors.New("http signature: verification key not found") // ErrKeyResolutionFailure reports a resolver failure whose backend details // are deliberately not retained in the public error chain. ErrKeyResolutionFailure = errors.New("http signature: verification key resolution failed") // ErrReplayBackendFailure reports an unknown replay-backend outcome whose // details are deliberately not retained in the public error chain. ErrReplayBackendFailure = errors.New("http signature: replay backend failed") )
var ErrInvalidDigestPreferences = errors.New("http signature: invalid digest preferences")
ErrInvalidDigestPreferences reports a malformed RFC 9530 integrity preference dictionary.
Functions ¶
func CombineSignedFields ¶
func CombineSignedFields(fields ...SignedFields) (SignatureInputs, Signatures, error)
CombineSignedFields combines validated label pairs in caller order. It rejects duplicate labels and never uses map iteration to choose wire order. Returned containers deep-copy values; zero/incomplete input refuses with zero outputs. This combines fields, not verification, negotiation or signing of previously supplied pairs; direct count/output bounds are caller-owned.
func CreateSignatureBase ¶
func CreateSignatureBase(context MessageContext, input SignatureInput) (string, error)
CreateSignatureBase resolves the ordered covered components and produces the exact RFC 9421 signature base. It returns no partial base on error. Borrowed message/parameter/maps must stay immutable. No body is read; trailers must already be available, and owned output may expose covered application data. Limits bound canonicalization, not all caller-owned objects. Managed response fields require explicit identity when ambiguous; trusted related/proxy context is caller policy (HTTP-SIG-DEC-005/007/020).
func Sign ¶
func Sign(ctx context.Context, algorithm Algorithm, key any, signatureBase []byte, random io.Reader) ([]byte, error)
Sign applies the exact RFC 9421 algorithm to signatureBase. RSA moduli must contain 2048 through 8192 bits. Randomized algorithms use Go-managed cryptographically secure randomness. random is retained for source compatibility and ignored. Key objects and base bytes are borrowed for the call and must remain immutable; output bytes are owned. Context is checked before/after synchronous crypto, not during it. There is no base-length cap here: use CreateSignatureBase for bounded message canonicalization. Errors exclude key/base data but unsupported-algorithm errors include caller text.
func Verify ¶
func Verify(ctx context.Context, algorithm Algorithm, key any, signatureBase, signature []byte) error
Verify applies the configured RFC 9421 primitive over borrowed immutable base/signature/key data. Typed errors exclude key/base bytes; unsupported algorithm errors include that caller-supplied string. RSA moduli must contain 2048 through 8192 bits. Context is checked before/after synchronous work, not forcibly during it; there is no direct base-length limit or resolver policy. Verification keys must be public key types; RSA or ECDSA private keys are deliberately rejected.
Types ¶
type AcceptSignatures ¶
type AcceptSignatures struct {
// contains filtered or unexported fields
}
AcceptSignatures is an immutable ordered Accept-Signature field.
func ParseAcceptSignatures ¶
func ParseAcceptSignatures(values []string) (AcceptSignatures, error)
ParseAcceptSignatures parses requested covered components and metadata. The created and expires parameters are Boolean requests rather than timestamps.
func ParseAcceptSignaturesWithLimits ¶
func ParseAcceptSignaturesWithLimits(values []string, limits SyntaxLimits) (AcceptSignatures, error)
ParseAcceptSignaturesWithLimits parses Accept-Signature under explicit resource bounds.
func (AcceptSignatures) Entries ¶
func (requests AcceptSignatures) Entries() []SignatureRequest
Entries returns a deep copy in combined-field wire order.
func (AcceptSignatures) String ¶
func (requests AcceptSignatures) String() string
String returns the canonical Structured Fields serialization.
type Algorithm ¶
type Algorithm string
Algorithm names an HTTP Signature Algorithms registry entry. Sign/Verify support the constants below under the reviewed pinned registry, not arbitrary new names or automatic negotiation (HTTP-SIG-DEC-010).
const ( // RSAPSSSHA512 is RSASSA-PSS with SHA-512, MGF1-SHA-512, and 64-byte salt. RSAPSSSHA512 Algorithm = "rsa-pss-sha512" // RSAV15SHA256 is RSASSA-PKCS1-v1_5 with SHA-256. RSAV15SHA256 Algorithm = "rsa-v1_5-sha256" // HMACSHA256 is HMAC with SHA-256. HMACSHA256 Algorithm = "hmac-sha256" // ECDSAP256SHA256 is P-256 ECDSA with SHA-256 and IEEE P1363 encoding. ECDSAP256SHA256 Algorithm = "ecdsa-p256-sha256" // ECDSAP384SHA384 is P-384 ECDSA with SHA-384 and IEEE P1363 encoding. ECDSAP384SHA384 Algorithm = "ecdsa-p384-sha384" // Ed25519 is pure Ed25519 with no prehash. Ed25519 Algorithm = "ed25519" )
type BufferedContentDigestRoundTripper ¶
type BufferedContentDigestRoundTripper struct {
// contains filtered or unexported fields
}
BufferedContentDigestRoundTripper consumes and closes the caller request body, then delegates a cloned request with a replayable buffered body. It is intended to wrap a SigningRoundTripper when Content-Digest must be covered: digest transport outside, signing transport inside, network transport last. Construct with NewBufferedContentDigestRoundTripper; zero is not usable. Shared calls require safe borrowed transports/callbacks. No connection close, retry worker or callback panic containment is owned by the adapter.
func NewBufferedContentDigestRoundTripper ¶
func NewBufferedContentDigestRoundTripper(config BufferedContentDigestRoundTripperConfig) (*BufferedContentDigestRoundTripper, error)
NewBufferedContentDigestRoundTripper validates explicit algorithms and a positive resource limit. Algorithms are copied; transport remains borrowed. Typed nil is not detected. No worker or connection lifecycle is started.
type BufferedContentDigestRoundTripperConfig ¶
type BufferedContentDigestRoundTripperConfig struct {
// Transport is mandatory borrowed RoundTripper; callers own timeout, redirects,
// connections and concurrency.
Transport http.RoundTripper
// Algorithms is copied nonempty unique SHA256/SHA512 computation policy.
Algorithms []DigestAlgorithm
// MaxBytes is a positive per-body content-byte bound, not an exact heap/aggregate-
// throughput quota.
MaxBytes int64
}
BufferedContentDigestRoundTripperConfig defines eager Content-Digest generation. It hashes the HTTP content bytes presented to the transport, after any application-managed content coding.
type BufferedContentDigestVerificationMiddleware ¶
BufferedContentDigestVerificationMiddleware wraps an http.Handler.
func NewBufferedContentDigestVerificationMiddleware ¶
func NewBufferedContentDigestVerificationMiddleware(config BufferedContentDigestVerificationMiddlewareConfig) (BufferedContentDigestVerificationMiddleware, error)
NewBufferedContentDigestVerificationMiddleware validates an explicit, bounded inbound digest policy. Algorithms are copied; synchronous MapError stays borrowed. Digest comparison alone is not origin authentication: compose explicit signature coverage of Content-Digest. Read/Close is cooperative and success gives next an owned replayable body, not business authorization.
type BufferedContentDigestVerificationMiddlewareConfig ¶
type BufferedContentDigestVerificationMiddlewareConfig struct {
// RequiredAlgorithms is copied nonempty unique SHA256/SHA512 verification policy,
// not negotiated from untrusted fields.
RequiredAlgorithms []DigestAlgorithm
// MaxBytes is a positive per-body content-byte bound, not an exact heap/aggregate-
// throughput quota.
MaxBytes int64
// MapError is mandatory synchronous application failure rendering; it may receive
// nil request and must redact safely.
MapError func(http.ResponseWriter, *http.Request, error)
}
BufferedContentDigestVerificationMiddlewareConfig defines eager inbound Content-Digest verification. MapError owns application-specific HTTP status and disclosure behavior.
type BufferedTrailerVerificationMiddleware ¶
BufferedTrailerVerificationMiddleware wraps an http.Handler.
func NewBufferedTrailerVerificationMiddleware ¶
func NewBufferedTrailerVerificationMiddleware(config BufferedTrailerVerificationMiddlewareConfig) (BufferedTrailerVerificationMiddleware, error)
NewBufferedTrailerVerificationMiddleware validates a bounded trailer policy. It requires the verification profile to cover content-digest with tr so a successful result authenticates the digest that was checked.
type BufferedTrailerVerificationMiddlewareConfig ¶
type BufferedTrailerVerificationMiddlewareConfig struct {
// Verifier is mandatory borrowed constructed verifier; it verifies policy, not
// protected business effects.
Verifier *Verifier
// SelectLabel must explicitly select a valid actual label from parsed fields;
// snapshot inputs do not confer authority.
SelectLabel func(*http.Request, SignatureInputs, Signatures) (string, error)
// RequiredAlgorithms is copied nonempty unique SHA256/SHA512 verification policy,
// not negotiated from untrusted fields.
RequiredAlgorithms []DigestAlgorithm
// MaxBytes is a positive per-body content-byte bound, not an exact heap/aggregate-
// throughput quota.
MaxBytes int64
// ExternalContext is optional borrowed trusted origin/target reconstruction; never
// trust arbitrary forwarding values.
ExternalContext func(context.Context, *http.Request) (*ExternalRequestContext, error)
// MapError is mandatory synchronous application failure rendering; it may receive
// nil request and must redact safely.
MapError func(http.ResponseWriter, *http.Request, error)
}
BufferedTrailerVerificationMiddlewareConfig defines eager processing for a request whose Content-Digest and message signature arrive in trailers.
type BufferedTrailerVerifyingRoundTripper ¶
type BufferedTrailerVerifyingRoundTripper struct {
// contains filtered or unexported fields
}
BufferedTrailerVerifyingRoundTripper verifies response trailers after EOF and replaces the consumed body with a replayable in-memory copy. It closes the received body after successful upstream delegation, on adapter success or refusal. Upstream response-plus-error returns unchanged; body closure then remains with upstream/caller rather than this verification path. Construct with NewBufferedTrailerVerifyingRoundTripper; zero is not usable. Shared calls require safe borrowed transports/callbacks. No connection close, retry worker or callback panic containment is owned by the adapter.
func NewBufferedTrailerVerifyingRoundTripper ¶
func NewBufferedTrailerVerifyingRoundTripper(config BufferedTrailerVerifyingRoundTripperConfig) (*BufferedTrailerVerifyingRoundTripper, error)
NewBufferedTrailerVerifyingRoundTripper validates an explicit bounded response-trailer policy. The profile must authenticate Content-Digest with the tr parameter.
func (*BufferedTrailerVerifyingRoundTripper) RoundTrip ¶
func (transport *BufferedTrailerVerifyingRoundTripper) RoundTrip(request *http.Request) (*http.Response, error)
RoundTrip implements http.RoundTripper. Trailer loss, digest mismatch, and signature failure close the response body and return no response.
type BufferedTrailerVerifyingRoundTripperConfig ¶
type BufferedTrailerVerifyingRoundTripperConfig struct {
// Transport is mandatory borrowed RoundTripper; callers own timeout, redirects,
// connections and concurrency.
Transport http.RoundTripper
// Verifier is mandatory borrowed constructed verifier; it verifies policy, not
// protected business effects.
Verifier *Verifier
// SelectLabel must explicitly select a valid actual label from parsed fields;
// snapshot inputs do not confer authority.
SelectLabel func(*http.Request, *http.Response, SignatureInputs, Signatures) (string, error)
// RequiredAlgorithms is copied nonempty unique SHA256/SHA512 verification policy,
// not negotiated from untrusted fields.
RequiredAlgorithms []DigestAlgorithm
// MaxBytes is a positive per-body content-byte bound, not an exact heap/aggregate-
// throughput quota.
MaxBytes int64
// ExternalContext is optional borrowed trusted origin/target reconstruction; never
// trust arbitrary forwarding values.
ExternalContext func(context.Context, *http.Request, *http.Response) (*ExternalRequestContext, error)
}
BufferedTrailerVerifyingRoundTripperConfig defines eager response processing when Content-Digest and the message signature arrive in trailers. The body is not returned to the caller until its bounded digest and signature verify.
type ComponentIdentifier ¶
type ComponentIdentifier struct {
// Name is a lowercase covered field or derived @ identifier; known modes are
// enforced during base resolution.
Name string
// Parameters is borrowed ordered data; identity comparison sorts names while
// serialization retains order.
Parameters []Parameter
}
ComponentIdentifier identifies one ordered covered message component.
type Digest ¶
type Digest struct {
// Algorithm is a parsed dictionary key; unknown unselected names remain available to
// application policy.
Algorithm DigestAlgorithm
// Value is borrowed checksum bytes in constructed values; parsing/Entries returns
// owned copies.
Value []byte
// Parameters preserves ordered SF extensions, not an automatic algorithm negotiation
// policy.
Parameters []Parameter
}
Digest is one algorithm and checksum member of an RFC 9530 integrity field.
type DigestAlgorithm ¶
type DigestAlgorithm string
DigestAlgorithm is an RFC 9530 Hash Algorithms for HTTP Digest Fields key.
const ( // SHA256 identifies the active sha-256 digest algorithm. SHA256 DigestAlgorithm = "sha-256" // SHA512 identifies the active sha-512 digest algorithm. SHA512 DigestAlgorithm = "sha-512" )
type DigestField ¶
type DigestField struct {
// contains filtered or unexported fields
}
DigestField is an ordered, immutable RFC 9530 integrity-field dictionary. Parsed fields preserve wire order; locally computed fields use algorithm-key order so output does not depend on caller or map iteration order.
func ComputeDigests ¶
func ComputeDigests(algorithms []DigestAlgorithm, content []byte) (DigestField, error)
ComputeDigests calculates an integrity field over content using active algorithms from the RFC 9530 registry. At least one unique algorithm is required.
func ParseDigestField ¶
func ParseDigestField(value string) (DigestField, error)
ParseDigestField parses the RFC 9530 Dictionary form used by Content-Digest and Repr-Digest. It rejects duplicate keys and values other than Structured Fields Byte Sequences.
func ParseDigestFields ¶
func ParseDigestFields(values []string) (DigestField, error)
ParseDigestFields combines and parses all field lines for one Content-Digest or Repr-Digest field. Duplicate algorithm keys are rejected across lines.
func ParseDigestFieldsWithLimits ¶
func ParseDigestFieldsWithLimits(values []string, limits SyntaxLimits) (DigestField, error)
ParseDigestFieldsWithLimits combines and parses integrity field lines under explicit resource bounds.
func (DigestField) Entries ¶
func (field DigestField) Entries() []Digest
Entries returns a deep copy of the ordered digest members.
func (DigestField) String ¶
func (field DigestField) String() string
String serializes the digest dictionary using canonical Byte Sequence encoding and the field's stable member order.
func (DigestField) Verify ¶
func (field DigestField) Verify(content []byte, required []DigestAlgorithm) error
Verify computes and constant-time compares every policy-selected digest. Unknown, unselected field members are retained but ignored as RFC 9530 permits recipients to ignore any digest.
type DigestPreference ¶
type DigestPreference struct {
// Algorithm is a syntactically valid digest key; unknown names are retained rather
// than automatically computed.
Algorithm DigestAlgorithm
// Weight is inclusive 0–10 relative preference; zero refuses and equal weights add
// no tie-break rule.
Weight int64
}
DigestPreference is one algorithm and relative weight from a Want-Content-Digest or Want-Repr-Digest field. Weight is in the inclusive range 0 through 10; zero means not acceptable.
type DigestPreferences ¶
type DigestPreferences struct {
// contains filtered or unexported fields
}
DigestPreferences is an immutable ordered RFC 9530 integrity preference dictionary. Order is preserved because equal weights have no protocol-level tie-breaking semantics.
func NewDigestPreferences ¶
func NewDigestPreferences(entries []DigestPreference) (DigestPreferences, error)
NewDigestPreferences validates and copies an ordered preference dictionary.
func ParseDigestPreferences ¶
func ParseDigestPreferences(values []string) (DigestPreferences, error)
ParseDigestPreferences parses combined Want-Content-Digest or Want-Repr-Digest field lines. Unknown algorithm keys are retained for application policy and interoperability.
func ParseDigestPreferencesWithLimits ¶
func ParseDigestPreferencesWithLimits(values []string, limits SyntaxLimits) (DigestPreferences, error)
ParseDigestPreferencesWithLimits parses integrity preferences under explicit resource bounds.
func (DigestPreferences) Entries ¶
func (preferences DigestPreferences) Entries() []DigestPreference
Entries returns a copy in combined-field wire order.
func (DigestPreferences) String ¶
func (preferences DigestPreferences) String() string
String returns the canonical Structured Fields serialization.
type ExistingSignaturesPolicy ¶
type ExistingSignaturesPolicy uint8
ExistingSignaturesPolicy controls how a signing transport handles fields already present on a caller request. The zero value is invalid.
const ( // ExistingSignaturesReject prevents accidental overwrite or signature // confusion. ExistingSignaturesReject ExistingSignaturesPolicy = iota + 1 // ExistingSignaturesAppend parses, validates, and preserves existing label // order before appending the new signature. ExistingSignaturesAppend )
type ExternalRequestContext ¶
type ExternalRequestContext struct {
// Scheme is trusted external http/https context, not arbitrary forwarding data.
Scheme string
// Authority is trusted external authority, normalized consistently with any
// absolute/CONNECT target.
Authority string
// RequestTarget is the complete trusted target; all three fields must be nonempty
// when supplied.
RequestTarget string
}
ExternalRequestContext is trusted request-target information supplied by an application that terminates HTTP behind an intermediary. Values are never inferred from Forwarded or X-Forwarded-* fields.
type HMACKey ¶
type HMACKey struct {
// contains filtered or unexported fields
}
HMACKey owns copied HMAC key bytes. Keys contain between 256 and 512 bits. The lower bound matches the HMAC-SHA-256 output size; the upper bound matches its block size, beyond which HMAC hashes the key before use.
func NewHMACKey ¶
NewHMACKey copies 32–64 secret bytes; invalid size returns zero and ErrIncompatibleKey. The immutable value can be shared, but copied secrets are not securely erased. This bound is local registered-algorithm policy.
type KeyResolver ¶
type KeyResolver interface {
// Resolve supplies trusted algorithm/key/lifecycle/freshness for an opaque
// ID, not policy inferred from untrusted alg. Honor ctx and support concurrent
// use; late success refuses and backend diagnostic graphs are stripped.
Resolve(context.Context, string) (ResolvedKey, error)
}
KeyResolver resolves an opaque key identifier under the supplied bounded context. Implementations must return only context-aware, bounded operations.
type MemoryReplayConfig ¶
type MemoryReplayConfig struct {
// Capacity is the maximum number of retained key and nonce identities.
Capacity int
// MaxTTL is the longest accepted interval from Now to ExpiresAt.
MaxTTL time.Duration
// MaxKeyIDBytes and MaxNonceBytes bound caller-controlled identity storage.
MaxKeyIDBytes int
// MaxNonceBytes is a positive per-nonce retained-byte ceiling, not a global heap
// quota.
MaxNonceBytes int
// Now must be safe for concurrent use and must return promptly. The store
// never invokes it while holding replay state.
Now func() time.Time
}
MemoryReplayConfig defines mandatory resource bounds and the caller-owned clock for a process-local replay store. String limits count bytes, not Unicode code points. It is not a distributed replay guarantee.
type MemoryReplayStore ¶
type MemoryReplayStore struct {
// contains filtered or unexported fields
}
MemoryReplayStore is a bounded process-local ReplayStore. It starts no goroutines. Consume removes at most one unrelated expired identity and uses an expiration heap, so cleanup work is bounded and never scans all records. Concurrent clock observations are clamped so store time never moves backward. Copies refer to the same synchronized replay state. Construct with NewMemoryReplayStore; nil/zero refuses. Retained strings are cloned, not securely erased; per-record bounds/capacity are not exact heap limits. State is lost on restart, with no automatic rotation or cross-process ownership.
func NewMemoryReplayStore ¶
func NewMemoryReplayStore(config MemoryReplayConfig) (*MemoryReplayStore, error)
NewMemoryReplayStore constructs a process-local store only when all resource bounds and the clock are explicitly supplied.
func (*MemoryReplayStore) Consume ¶
func (store *MemoryReplayStore) Consume(ctx context.Context, record ReplayRecord) error
Consume atomically reserves record until its expiration. It performs a constant number of O(log Capacity) heap operations and never scans every retained identity. Caller-owned clock and context methods run without the replay lock held. Cancellation observed while waiting for replay state returns the context error without consuming the record; cancellation racing after lock acquisition may lose to a successful reservation.
type MessageContext ¶
type MessageContext struct {
// Request is the borrowed target request; exactly one of Request/Response must be
// non-nil.
Request *http.Request
// Response is the borrowed target response; managed fields require correct
// ResponseTransport selection.
Response *http.Response
// RelatedRequest is explicit borrowed actual-request context for response components
// carrying req.
RelatedRequest *http.Request
// ExternalRequest is optional borrowed complete trusted proxy reconstruction, never
// auto-derived from forwarding fields.
ExternalRequest *ExternalRequestContext
// StructuredFields is borrowed lowercase field-name/type policy required for sf
// canonicalization.
StructuredFields map[string]StructuredFieldType
// ResponseTransport selects received versus Response.Write wire identity;
// unspecified refuses ambiguous managed fields.
ResponseTransport ResponseTransportMode
// MaxSignatureBaseBytes bounds the complete canonical base. Zero selects
// DefaultMaxSignatureBaseBytes; negative values are invalid.
MaxSignatureBaseBytes int
}
MessageContext supplies the target message and optional related request. Exactly one of Request and Response must be set. RelatedRequest is required when a response signature covers a component carrying the req parameter.
type Parameter ¶
type Parameter struct {
// Name is an RFC 8941 lowercase key, preserved in supplied serialization order.
Name string
// Value is a supported SF scalar/token or borrowed bytes; copying accessors clone
// byte values.
Value any
}
Parameter is an ordered Structured Fields parameter. Value is one of bool, string, int64, float64, []byte, or SFToken. Directly constructed values borrow bytes; parsers, copying accessors and profile constructors clone them.
type ParameterPolicy ¶
type ParameterPolicy uint8
ParameterPolicy specifies whether one registered signature parameter is prohibited, accepted, or mandatory. The zero value is invalid so profiles cannot accidentally inherit a permissive default.
const ( // ParameterForbidden rejects presence of that registered parameter. ParameterForbidden ParameterPolicy = iota + 1 // ParameterOptional permits absence/presence with associated profile constraints. ParameterOptional // ParameterRequired requires presence; zero ParameterPolicy is always invalid. ParameterRequired )
type ReplayRecord ¶
type ReplayRecord struct {
// KeyID is the exact opaque resolver namespace; rotation namespaces and multi-tenant
// isolation are caller-defined.
KeyID string
// Nonce is exact opaque signed identity; equal key/nonce pairs share replay state.
Nonce string
// ExpiresAt is retention boundary selected from accepted validity windows, including
// configured skew.
ExpiresAt time.Time
}
ReplayRecord identifies one verification attempt until ExpiresAt. KeyID and Nonce are treated as opaque values and are never included in errors.
type ReplayStore ¶
type ReplayStore interface {
// Consume atomically reserves exact key/nonce until ExpiresAt, including
// accepted skew. Honor ctx and document bounded retention/durability and
// namespace scope; unknown outcomes fail closed, not coupled to business effects.
Consume(context.Context, ReplayRecord) error
}
ReplayStore atomically consumes a signature nonce. A durable implementation must make concurrent Consume calls for the same key and nonce linearizable: exactly one call succeeds before expiration and all others return ErrReplayDetected. Unknown backend outcomes must return an error, never nil.
type RequestVerificationMiddleware ¶
RequestVerificationMiddleware wraps an http.Handler.
func NewRequestVerificationMiddleware ¶
func NewRequestVerificationMiddleware(config RequestVerificationMiddlewareConfig) (RequestVerificationMiddleware, error)
NewRequestVerificationMiddleware validates an inbound adapter. It supplies no status-code or disclosure defaults; MapError owns application mapping.
type RequestVerificationMiddlewareConfig ¶
type RequestVerificationMiddlewareConfig struct {
// Verifier is mandatory borrowed constructed verifier; it verifies policy, not
// protected business effects.
Verifier *Verifier
// SelectLabel must explicitly select a valid actual label from parsed fields;
// snapshot inputs do not confer authority.
SelectLabel func(*http.Request, SignatureInputs, Signatures) (string, error)
// ExternalContext is optional borrowed trusted origin/target reconstruction; never
// trust arbitrary forwarding values.
ExternalContext func(context.Context, *http.Request) (*ExternalRequestContext, error)
// MapError is mandatory synchronous application failure rendering; it may receive
// nil request and must redact safely.
MapError func(http.ResponseWriter, *http.Request, error)
}
RequestVerificationMiddlewareConfig defines inbound selection, trusted external-origin reconstruction, and application-specific failure mapping.
type ResolvedKey ¶
type ResolvedKey struct {
// Algorithm is trusted resolver binding; it must be allowed and match any protected
// alg.
Algorithm Algorithm
// Key is borrowed verification material of the exact public/shared type; private
// asymmetric keys are refused.
Key any
// NotBefore is required inclusive activation; verification and signed creation apply
// configured skew.
NotBefore time.Time
// NotAfter is required exclusive key expiry after NotBefore, extended only by
// configured skew.
NotAfter time.Time
// FreshUntil is required exclusive resolver-cache freshness at processing time,
// without skew extension.
FreshUntil time.Time
// Revoked refuses use regardless of cached algorithm/time data.
Revoked bool
}
ResolvedKey binds verification material to exactly one algorithm and an explicit validity and cache-freshness interval. Resolver implementations own storage, rotation, revocation lookup, caching, and remote IO.
type ResponseSigningMiddleware ¶
ResponseSigningMiddleware wraps an http.Handler.
func NewResponseSigningMiddleware ¶
func NewResponseSigningMiddleware(config ResponseSigningMiddlewareConfig) (ResponseSigningMiddleware, error)
NewResponseSigningMiddleware validates an explicit response-signing policy. Configuration, mapping, and reporting callbacks and the signing profile must be safe for concurrent handler calls.
type ResponseSigningMiddlewareConfig ¶
type ResponseSigningMiddlewareConfig struct {
// Signer is mandatory borrowed constructed signer; shared use depends on safe
// profile collaborators.
Signer *Signer
// Label is an explicit valid SF dictionary key, never chosen by map iteration.
Label string
// Existing is explicit reject/append policy; append preserves labels but does not
// verify earlier signatures.
Existing ExistingSignaturesPolicy
// MaxBufferedBytes must be positive and bounds emitted content buffered before
// signing, not header/global memory.
MaxBufferedBytes int64
// ContentDigestAlgorithms optionally computes copied SHA256/SHA512 policy; the
// signing profile must cover Content-Digest.
ContentDigestAlgorithms []DigestAlgorithm
// Options is a mandatory synchronous borrowed per-message callback; support
// concurrency, bound work and avoid body/message mutation.
Options func(context.Context, *http.Request, *http.Response) (SigningOptions, error)
// ExternalContext is optional borrowed trusted origin/target reconstruction; never
// trust arbitrary forwarding values.
ExternalContext func(context.Context, *http.Request, *http.Response) (*ExternalRequestContext, error)
// MapError is mandatory synchronous application failure rendering; it may receive
// nil request and must redact safely.
MapError func(http.ResponseWriter, *http.Request, error)
// ReportError is mandatory synchronous diagnostic reporting; bound work and redact,
// including after response commitment.
ReportError func(*http.Request, error)
}
ResponseSigningMiddlewareConfig defines a fail-closed buffered response signing boundary. MaxBufferedBytes and ReportError are mandatory; the latter records redacted output failures after signed headers have committed. Handlers requiring streaming, flushing, hijacking, or full-duplex operation need a trailer-aware adapter instead.
type ResponseTransportMode ¶
type ResponseTransportMode uint8
ResponseTransportMode selects the net/http response representation whose transport-managed fields are covered by a signature.
const ( // ResponseTransportUnspecified accepts a managed response field only when // the received and Response.Write representations are provably identical. ResponseTransportUnspecified ResponseTransportMode = iota // ResponseTransportReceived covers a response parsed by net/http or returned // by a RoundTripper. Preserved Header values carry received field identity. ResponseTransportReceived // ResponseTransportWrite covers the deterministic output of Response.Write. ResponseTransportWrite )
type SignatureInput ¶
type SignatureInput struct {
// Label is a dictionary key selecting this member, not authentication by itself.
Label string
// Components is borrowed ordered coverage; full identity includes order-insensitive
// parameters.
Components []ComponentIdentifier
// Parameters is borrowed ordered metadata; unknown extensions parse, application
// policy selects registered fields.
Parameters []Parameter
}
SignatureInput is one labeled Signature-Input dictionary member.
type SignatureInputs ¶
type SignatureInputs struct {
// contains filtered or unexported fields
}
SignatureInputs is an immutable ordered Signature-Input field.
func ParseSignatureInputs ¶
func ParseSignatureInputs(values []string) (SignatureInputs, error)
ParseSignatureInputs parses and semantically validates combined Signature-Input field lines. It preserves dictionary, component, and parameter order and rejects duplicate labels before Structured Fields dictionary replacement semantics could hide them.
func ParseSignatureInputsWithLimits ¶
func ParseSignatureInputsWithLimits(values []string, limits SyntaxLimits) (SignatureInputs, error)
ParseSignatureInputsWithLimits parses Signature-Input under explicit resource bounds.
func (SignatureInputs) Entries ¶
func (inputs SignatureInputs) Entries() []SignatureInput
Entries returns a deep copy in combined-field wire order.
func (SignatureInputs) String ¶
func (inputs SignatureInputs) String() string
String returns the canonical Structured Fields serialization.
type SignatureRequest ¶
type SignatureRequest = SignatureInput
SignatureRequest is one requested signature from an Accept-Signature field.
type SignatureValue ¶
type SignatureValue struct {
// Label must match its Signature-Input dictionary label for verification.
Label string
// Value is borrowed signature bytes in direct constructed values; parser/accessors
// return owned copies.
Value []byte
// Parameters preserves ordered SF extensions; cryptographic verification operates on
// Value.
Parameters []Parameter
}
SignatureValue is one labeled Signature dictionary member.
type Signatures ¶
type Signatures struct {
// contains filtered or unexported fields
}
Signatures is an immutable ordered Signature field.
func ParseSignatures ¶
func ParseSignatures(values []string) (Signatures, error)
ParseSignatures parses and semantically validates combined Signature field lines, preserving label order and rejecting duplicate labels.
func ParseSignaturesWithLimits ¶
func ParseSignaturesWithLimits(values []string, limits SyntaxLimits) (Signatures, error)
ParseSignaturesWithLimits parses Signature under explicit resource bounds.
func (Signatures) Entries ¶
func (signatures Signatures) Entries() []SignatureValue
Entries returns a deep copy in combined-field wire order.
func (Signatures) String ¶
func (signatures Signatures) String() string
String returns the canonical Structured Fields serialization.
type SignedFields ¶
type SignedFields struct {
// contains filtered or unexported fields
}
SignedFields owns one matching Signature-Input and Signature label pair.
func (SignedFields) SignatureField ¶
func (signed SignedFields) SignatureField() string
SignatureField returns a complete canonical field value for this label.
func (SignedFields) SignatureInputField ¶
func (signed SignedFields) SignatureInputField() string
SignatureInputField returns a complete canonical field value for this label.
type Signer ¶
type Signer struct {
// contains filtered or unexported fields
}
Signer applies one immutable application signing profile.
func NewSigner ¶
func NewSigner(profile *SigningProfile) *Signer
NewSigner retains an explicit profile and starts no work. Nil profile is accepted at construction but Sign then returns ErrSigningPolicy. Shared use requires safe profile collaborators and immutable borrowed messages.
func (*Signer) Sign ¶
func (signer *Signer) Sign(ctx context.Context, message MessageContext, label string, options SigningOptions) (SignedFields, error)
Sign creates one matching signature field pair without mutating the HTTP message or consuming its body. Nil context returns context.Canceled; nil/zero signer refuses. Key window is inclusive/exclusive at sampled processing time; required expires cannot exceed key expiry. Nonce is caller-supplied, not generated. Provider deadline/diagnostic and crypto errors are classified; failure returns zero SignedFields but collaborator effects are not rolled back.
type SigningKey ¶
type SigningKey struct {
// KeyID is nonempty public key identity emitted in protected metadata, not
// application authorization.
KeyID string
// Algorithm must match the profile allowlist and the exact key type.
Algorithm Algorithm
// Key is borrowed private/shared material; keep it immutable during use and obey
// algorithm size rules.
Key any
// NotBefore is required inclusive activation at sampled signing time.
NotBefore time.Time
// NotAfter is required exclusive expiry after NotBefore; required signature expiry
// cannot extend beyond it.
NotAfter time.Time
// Revoked refuses use even when the time window and algorithm match.
Revoked bool
}
SigningKey binds private or shared signing material to an identifier, algorithm, validity interval, and revocation decision.
type SigningKeyProvider ¶
type SigningKeyProvider interface {
// SigningKey selects trusted current algorithm-bound material with explicit
// activation/expiry/revocation. Late success is rejected; raw provider errors
// are mapped to ErrSigningProvider rather than retained.
SigningKey(context.Context) (SigningKey, error)
}
SigningKeyProvider selects current signing material under a bounded context. Implementations own key storage/rotation and concurrent use; honor context, bound work and keep returned borrowed keys immutable during signing. The core makes one synchronous call without detaching a worker or containing panics.
type SigningOptions ¶
type SigningOptions struct {
// Nonce is supplied by the caller only when required; freshness/entropy is
// application-owned.
Nonce string
}
SigningOptions supplies per-message values that cannot be safely defaulted.
type SigningProfile ¶
type SigningProfile struct {
// contains filtered or unexported fields
}
SigningProfile copies immutable policy while borrowing its clock/provider. Construct with NewSigningProfile; shared use requires those collaborators to be safe and prompt. It owns no key erasure, rotation worker or close lifecycle.
func NewSigningProfile ¶
func NewSigningProfile(config SigningProfileConfig) (*SigningProfile, error)
NewSigningProfile validates/copies unique algorithms, ordered coverage and parameter bytes, returning nil/ErrInvalidSigningProfile on refusal. Clock and provider remain borrowed; typed nil and callback panics are not contained. Creation/keyid are emitted; each other registered parameter is explicitly required or forbidden, not implicitly optional (HTTP-SIG-DEC-008).
type SigningProfileConfig ¶
type SigningProfileConfig struct {
// AllowedAlgorithms is a nonempty unique supported allowlist copied at construction.
AllowedAlgorithms []Algorithm
// CoveredComponents is copied, including parameter byte values, in exact signing
// order.
CoveredComponents []ComponentIdentifier
// AllowEmptyCoverage must explicitly permit no covered components; it does not
// authorize any message effect.
AllowEmptyCoverage bool
// Expires must explicitly require or forbid expires; optional is not a signing
// policy.
Expires ParameterPolicy
// AlgorithmParameter must require or forbid emitting alg; resolved provider
// algorithm always binds the primitive.
AlgorithmParameter ParameterPolicy
// Nonce must require or forbid a caller-supplied nonce; no nonce generator is
// provided.
Nonce ParameterPolicy
// Tag must require or forbid the configured exact TagValue.
Tag ParameterPolicy
// TagValue is required valid SF text with required Tag, empty when forbidden.
TagValue string
// Lifetime is whole positive seconds with required Expires, zero when forbidden.
Lifetime time.Duration
// ResolveTimeout is positive cooperative key-provider deadline; late success is
// rejected.
ResolveTimeout time.Duration
// Now is mandatory borrowed concurrent-safe prompt clock, not context-preempted.
Now func() time.Time
// Provider is mandatory borrowed rotation/key owner; honor context and support
// concurrent calls.
Provider SigningKeyProvider
// Random is retained for source compatibility.
//
// Deprecated: Random is ignored. Randomized algorithms use Go-managed
// cryptographically secure randomness.
Random io.Reader
// RequireExternalRequestContext refuses signing without an explicit trusted external
// request context.
RequireExternalRequestContext bool
}
SigningProfileConfig defines all signing decisions. Creation time and keyid are always included as required by this signing API. Other registered parameters must be explicitly required or forbidden.
type SigningRoundTripper ¶
type SigningRoundTripper struct {
// contains filtered or unexported fields
}
SigningRoundTripper signs a cloned request immediately before delegation. It does not read or replace Body; the wrapped transport retains normal body consumption and closure ownership. Construct with NewSigningRoundTripper; zero is not usable. Shared calls require safe borrowed transports/callbacks. No connection close, retry worker or callback panic containment is owned by the adapter.
func NewSigningRoundTripper ¶
func NewSigningRoundTripper(config SigningRoundTripperConfig) (*SigningRoundTripper, error)
NewSigningRoundTripper validates an explicit outbound adapter configuration.
func (*SigningRoundTripper) RoundTrip ¶
RoundTrip signs a cloned header view, then transfers normal body ownership to the borrowed transport. Original-request callbacks remain caller code and can mutate if misused; pre-delegation failures close the original body. No redirect, proxy, retry or forced-timeout policy is installed.
type SigningRoundTripperConfig ¶
type SigningRoundTripperConfig struct {
// Transport is mandatory borrowed RoundTripper; callers own timeout, redirects,
// connections and concurrency.
Transport http.RoundTripper
// Signer is mandatory borrowed constructed signer; shared use depends on safe
// profile collaborators.
Signer *Signer
// Label is an explicit valid SF dictionary key, never chosen by map iteration.
Label string
// Existing is explicit reject/append policy; append preserves labels but does not
// verify earlier signatures.
Existing ExistingSignaturesPolicy
// Options runs synchronously on the original request; caller mutations/effects are
// not isolated by this adapter.
Options func(context.Context, *http.Request) (SigningOptions, error)
// ExternalContext is optional borrowed trusted origin/target reconstruction; never
// trust arbitrary forwarding values.
ExternalContext func(context.Context, *http.Request) (*ExternalRequestContext, error)
}
SigningRoundTripperConfig defines an outbound request-signing boundary. Transport, options, label, and existing-field policy are all explicit.
type StructuredFieldType ¶
type StructuredFieldType uint8
StructuredFieldType describes the application-known RFC 8941 field shape required by the sf component parameter.
const ( // StructuredFieldDictionary identifies an RFC 8941 Dictionary field. StructuredFieldDictionary StructuredFieldType = iota + 1 // StructuredFieldList identifies an RFC 8941 List field. StructuredFieldList // StructuredFieldItem identifies an RFC 8941 Item field. StructuredFieldItem )
type SyntaxLimits ¶
type SyntaxLimits struct {
// MaxFieldBytes caps combined encoded bytes, including comma-space line separators;
// positive.
MaxFieldBytes int
// MaxFieldLines caps input field-line count before parsing; positive.
MaxFieldLines int
// MaxDictionaryMembers caps decoded dictionary members after parsing; positive.
MaxDictionaryMembers int
// MaxComponentsPerSignature caps covered items per decoded signature inner list;
// positive.
MaxComponentsPerSignature int
// MaxParametersPerItem caps each decoded item/list parameter count; positive.
MaxParametersPerItem int
// MaxBinaryBytes caps decoded byte sequences; positive and no larger than
// MaxFieldBytes.
MaxBinaryBytes int
}
SyntaxLimits bounds untrusted Structured Fields before and after parsing. Limits are per logical combined field.
func DefaultSyntaxLimits ¶
func DefaultSyntaxLimits() SyntaxLimits
DefaultSyntaxLimits returns the restrictive bound used by convenience parsers. A fresh value prevents mutable package-global parser policy.
func (SyntaxLimits) Validate ¶
func (limits SyntaxLimits) Validate() error
Validate checks that every resource dimension has a positive bound.
type TrailerResponseSigningMiddleware ¶
TrailerResponseSigningMiddleware wraps an http.Handler. Recipients must wait for EOF and reject a response whose declared digest or signature trailers are absent. Configuration callbacks and ReportError must be concurrency-safe.
func NewTrailerResponseSigningMiddleware ¶
func NewTrailerResponseSigningMiddleware(config TrailerResponseSigningMiddlewareConfig) (TrailerResponseSigningMiddleware, error)
NewTrailerResponseSigningMiddleware validates an explicit streaming policy. The signing profile must cover Content-Digest with the tr parameter.
type TrailerResponseSigningMiddlewareConfig ¶
type TrailerResponseSigningMiddlewareConfig struct {
// Signer is mandatory borrowed constructed signer; shared use depends on safe
// profile collaborators.
Signer *Signer
// Label is an explicit valid SF dictionary key, never chosen by map iteration.
Label string
// Algorithms is copied nonempty unique SHA256/SHA512 computation policy.
Algorithms []DigestAlgorithm
// MaxBytes is a positive per-body content-byte bound, not an exact heap/aggregate-
// throughput quota.
MaxBytes int64
// Options is a mandatory synchronous borrowed per-message callback; support
// concurrency, bound work and avoid body/message mutation.
Options func(context.Context, *http.Request) (SigningOptions, error)
// ExternalContext is optional borrowed trusted origin/target reconstruction; never
// trust arbitrary forwarding values.
ExternalContext func(context.Context, *http.Request) (*ExternalRequestContext, error)
// ReportError is mandatory synchronous diagnostic reporting; bound work and redact,
// including after response commitment.
ReportError func(*http.Request, error)
}
TrailerResponseSigningMiddlewareConfig defines streaming response signing. ReportError is required because a size, handler-write, key, randomness, or signing failure can occur after response bytes have already been emitted.
type TrailerSigningRoundTripper ¶
type TrailerSigningRoundTripper struct {
// contains filtered or unexported fields
}
TrailerSigningRoundTripper streams one non-replayable request attempt. It does not pre-read the body. Size, hash, key, or signing failure during Read aborts the attempt; bytes returned by earlier reads may already be on wire. A response is released only after EOF finalization succeeds. Construct with NewTrailerSigningRoundTripper; zero is not usable. Shared calls require safe borrowed transports/callbacks. No connection close, retry worker or callback panic containment is owned by the adapter.
func NewTrailerSigningRoundTripper ¶
func NewTrailerSigningRoundTripper(config TrailerSigningRoundTripperConfig) (*TrailerSigningRoundTripper, error)
NewTrailerSigningRoundTripper validates a streaming trailer policy.
func (*TrailerSigningRoundTripper) RoundTrip ¶
func (transport *TrailerSigningRoundTripper) RoundTrip(request *http.Request) (*http.Response, error)
RoundTrip implements http.RoundTripper. The wrapped transport owns the body while active; an early successful response is closed and rejected, and the adapter closes the unfinished request body.
type TrailerSigningRoundTripperConfig ¶
type TrailerSigningRoundTripperConfig struct {
// Transport is mandatory borrowed RoundTripper; callers own timeout, redirects,
// connections and concurrency.
Transport http.RoundTripper
// Signer is mandatory borrowed constructed signer; shared use depends on safe
// profile collaborators.
Signer *Signer
// Label is an explicit valid SF dictionary key, never chosen by map iteration.
Label string
// Algorithms is copied nonempty unique SHA256/SHA512 computation policy.
Algorithms []DigestAlgorithm
// MaxBytes is a positive per-body content-byte bound, not an exact heap/aggregate-
// throughput quota.
MaxBytes int64
// Options runs on the original request at EOF; own nonce freshness and do not mutate
// covered state.
Options func(context.Context, *http.Request) (SigningOptions, error)
// ExternalContext is optional borrowed trusted origin/target reconstruction; never
// trust arbitrary forwarding values.
ExternalContext func(context.Context, *http.Request) (*ExternalRequestContext, error)
}
TrailerSigningRoundTripperConfig defines a streaming request adapter that emits Content-Digest, Signature-Input, and Signature in trailers after EOF. The signing profile must cover content-digest with the tr parameter.
type VerificationError ¶
type VerificationError struct {
// Failure is a fixed category for core-created errors; caller-constructed strings
// are not sanitized.
Failure VerificationFailure
// contains filtered or unexported fields
}
VerificationError separates a core-created fixed category from its cause. Core Error omits key/nonce/base/signature/message data; public Failure can contain arbitrary caller-created strings and is not a redaction boundary. Methods require non-nil receiver; Unwrap exposes classification causes.
func (*VerificationError) Error ¶
func (err *VerificationError) Error() string
Error renders the category only, including arbitrary caller-set Failure text.
func (*VerificationError) Unwrap ¶
func (err *VerificationError) Unwrap() error
Unwrap supports errors.Is and errors.As without including the cause in the rendered error string.
type VerificationFailure ¶
type VerificationFailure string
VerificationFailure is a stable, application-mappable failure category.
const ( // VerificationSelection identifies invalid context/profile or mismatched label sets. VerificationSelection VerificationFailure = "selection" // VerificationPolicy identifies parameter/coverage/trusted-context refusal. VerificationPolicy VerificationFailure = "policy" // VerificationTime identifies creation/expiration refusal. VerificationTime VerificationFailure = "time" // VerificationKeyResolution identifies bounded resolver failure before key use. VerificationKeyResolution VerificationFailure = "key-resolution" // VerificationKey identifies unusable material/lifecycle/freshness. VerificationKey VerificationFailure = "key" // VerificationAlgorithm identifies disallowed/mismatched algorithm binding. VerificationAlgorithm VerificationFailure = "algorithm" // VerificationBase identifies canonical base refusal. VerificationBase VerificationFailure = "signature-base" // VerificationCryptographic identifies failed primitive verification. VerificationCryptographic VerificationFailure = "cryptographic" // VerificationReplay identifies replay refusal, deadline or unknown outcome. VerificationReplay VerificationFailure = "replay" )
type VerificationProfile ¶
type VerificationProfile struct {
// contains filtered or unexported fields
}
VerificationProfile copies policy and borrows clock/resolver/replay owners. Construct with NewVerificationProfile; shared use requires safe prompt collaborators and immutable messages. No cache, worker or Close is owned.
func NewVerificationProfile ¶
func NewVerificationProfile(config VerificationProfileConfig) (*VerificationProfile, error)
NewVerificationProfile validates/copies algorithms, coverage parameter bytes and tags, returning nil/ErrInvalidVerificationProfile on refusal. Permitted nonce requires replay ownership/deadline; callbacks stay borrowed. No global input-count cap or typed-nil/panic detection is added (HTTP-SIG-DEC-008/011/012).
type VerificationProfileConfig ¶
type VerificationProfileConfig struct {
// AllowedAlgorithms is nonempty unique supported policy copied at construction;
// inbound alg cannot widen it.
AllowedAlgorithms []Algorithm
// RequiredComponents is copied coverage policy with order-insensitive parameter
// identity; extra covered components may remain.
RequiredComponents []ComponentIdentifier
// AllowEmptyCoverage must explicitly permit empty coverage and does not imply
// authorization.
AllowEmptyCoverage bool
// Created explicitly forbids, permits or requires signed creation; non-forbidden
// needs positive MaxAge.
Created ParameterPolicy
// Expires explicitly forbids, permits or requires exclusive signature expiration.
Expires ParameterPolicy
// AlgorithmParameter controls protected alg presence, not whether resolver/primitive
// binding is enforced.
AlgorithmParameter ParameterPolicy
// Nonce controls nonce presence; non-forbidden requires Replay and positive
// ReplayTimeout.
Nonce ParameterPolicy
// Tag controls tag presence; non-forbidden requires a nonempty allowed-tag set.
Tag ParameterPolicy
// AllowedTags is a unique valid SF-text allowlist copied at construction; empty only
// when Tag is forbidden.
AllowedTags []string
// MaxAge is zero with forbidden Created, positive otherwise; MaxAge+ClockSkew must
// not overflow.
MaxAge time.Duration
// ClockSkew is nonnegative payload/key tolerance; it does not extend FreshUntil.
ClockSkew time.Duration
// ResolveTimeout is positive cooperative resolver deadline; late successful results
// are rejected.
ResolveTimeout time.Duration
// ReplayTimeout is positive when nonce is not forbidden, otherwise zero; timeout may
// follow a committed reservation.
ReplayTimeout time.Duration
// Now is mandatory borrowed prompt concurrent-safe clock; input and key policy
// sample it separately.
Now func() time.Time
// Resolver is mandatory borrowed trusted key/lifecycle owner; honor context and
// support concurrent use.
Resolver KeyResolver
// Replay is mandatory when nonce is not forbidden; atomic backend
// lifetime/consistency remain caller-owned.
Replay ReplayStore
// RequireExternalRequestContext refuses verification without explicit trusted
// external request context.
RequireExternalRequestContext bool
}
VerificationProfileConfig defines the application decisions RFC 9421 leaves to a profile. Key identifiers are mandatory in this profile implementation; static keys can be exposed by a resolver that recognizes a fixed identifier.
type VerifiedSignature ¶
type VerifiedSignature struct {
// Label is the explicitly selected actual signature label.
Label string
// KeyID is resolved public identity, not principal authentication or authorization.
KeyID string
// Algorithm is trusted resolved primitive binding.
Algorithm Algorithm
// Created is signed creation time, zero if absent under policy.
Created time.Time
// Expires is signed expiration, zero if absent; replay retention may use MaxAge and
// skew instead.
Expires time.Time
}
VerifiedSignature reports the selected label and resolved public metadata. A successful result proves cryptographic validity and profile conformance; it does not grant authentication or authorization.
func VerifiedSignatureFromContext ¶
func VerifiedSignatureFromContext(ctx context.Context) (VerifiedSignature, bool)
VerifiedSignatureFromContext returns profile-conformant verification metadata stored by RequestVerificationMiddleware. It is not an authorization result.
func VerifiedSignatureFromResponse ¶
func VerifiedSignatureFromResponse(response *http.Response) (VerifiedSignature, bool)
VerifiedSignatureFromResponse returns profile-conformant verification metadata stored by VerifyingRoundTripper.
type Verifier ¶
type Verifier struct {
// contains filtered or unexported fields
}
Verifier borrows one immutable profile. Shared use requires safe prompt collaborators; resolver/replay shutdown and business transactions remain caller-owned. No background worker or implicit authorization is added.
func NewVerifier ¶
func NewVerifier(profile *VerificationProfile) *Verifier
NewVerifier retains an explicit profile without starting work. Nil profile is accepted here but Verify refuses; nil/zero Verifier also refuses. No default key, clock, replay or authorization policy is installed.
func (*Verifier) Verify ¶
func (verifier *Verifier) Verify( ctx context.Context, message MessageContext, label string, inputs SignatureInputs, signatures Signatures, ) (VerifiedSignature, error)
Verify selects one explicit label, enforces application policy, resolves an algorithm-bound key, verifies the reconstructed signature base, and only then atomically consumes the nonce. Borrowed message/storage must remain immutable; failure returns zero VerifiedSignature. Successful resolution is rechecked for deadline, skew-aware activation and strict FreshUntil. Replay deadline may follow a committed reservation; replay is not rolled back for deadline or downstream failure. Signature time is sampled before base/key resolution, while key policy samples separately afterward; there is no final signature-time or authorization recheck. Callback blocking/panics remain owner-bound.
type VerifyingRoundTripper ¶
type VerifyingRoundTripper struct {
// contains filtered or unexported fields
}
VerifyingRoundTripper verifies response signatures. A selected signature that covers a response Content-Digest is accepted only when an explicit bounded digest policy verifies and replaces the consumed body. Construct with NewVerifyingRoundTripper; zero is not usable. Shared calls require safe borrowed transports/callbacks. No connection close, retry worker or callback panic containment is owned by the adapter.
func NewVerifyingRoundTripper ¶
func NewVerifyingRoundTripper(config VerifyingRoundTripperConfig) (*VerifyingRoundTripper, error)
NewVerifyingRoundTripper validates an explicit response-verification adapter.
type VerifyingRoundTripperConfig ¶
type VerifyingRoundTripperConfig struct {
// Transport is mandatory borrowed RoundTripper; callers own timeout, redirects,
// connections and concurrency.
Transport http.RoundTripper
// Verifier is mandatory borrowed constructed verifier; it verifies policy, not
// protected business effects.
Verifier *Verifier
// SelectLabel must explicitly select a valid actual label from parsed fields;
// snapshot inputs do not confer authority.
SelectLabel func(*http.Request, *http.Response, SignatureInputs, Signatures) (string, error)
// ContentDigestAlgorithms is optional copied policy; required before accepting
// coverage of a response Content-Digest.
ContentDigestAlgorithms []DigestAlgorithm
// MaxBufferedBytes is positive with ContentDigestAlgorithms, zero when no digest
// buffering policy is configured.
MaxBufferedBytes int64
// ExternalContext is optional borrowed trusted origin/target reconstruction; never
// trust arbitrary forwarding values.
ExternalContext func(context.Context, *http.Request, *http.Response) (*ExternalRequestContext, error)
}
VerifyingRoundTripperConfig defines response signature selection and trusted external request context. The wrapped transport retains request-body ownership; this adapter closes a response body when verification fails.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package compatibility isolates non-RFC 9421 HTTP authentication schemes behind caller-supplied protocol implementations.
|
Package compatibility isolates non-RFC 9421 HTTP authentication schemes behind caller-supplied protocol implementations. |