jose

package
v0.30.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 3, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Index

Constants

View Source
const (
	DefaultSigAlg = jose.RS256
	DefaultKeyAlg = jose.RSA_OAEP_256
	DefaultEnc    = jose.A256GCM
	DefaultCty    = "application/json"
)
View Source
const ContentType = "application/jose"

ContentType is the IANA-registered media type for compact JOSE serializations. Used as the request and response Content-Type for JOSE-protected HTTP traffic.

View Source
const SentinelFieldName = "_"

SentinelFieldName is the conventional field name applications use for the jose-tagged sentinel field. Any blank-named field with a jose tag also matches; this constant documents the canonical choice.

View Source
const TagName = "jose"

Variables

View Source
var (
	ErrBodyRequired        = errors.New("jose: request body required")
	ErrUnsupportedMedia    = errors.New("jose: unsupported media type")
	ErrMalformed           = errors.New("jose: malformed compact serialization")
	ErrAlgorithmDisallowed = errors.New("jose: algorithm not in allowlist")
	ErrNoneAlgRejected     = errors.New("jose: alg=none rejected")
	ErrKidMissing          = errors.New("jose: header missing kid")
	ErrCritUnsupported     = errors.New("jose: unrecognized crit header value")
	ErrCtyRejected         = errors.New("jose: inner cty does not match policy")
	ErrKidUnknown          = errors.New("jose: kid not registered")
	ErrDecryptFailed       = errors.New("jose: decryption failed")
	ErrInnerNotJWS         = errors.New("jose: inner payload is not a JWS")
	ErrSignatureInvalid    = errors.New("jose: signature verification failed")
	ErrPlaintextRejected   = errors.New("jose: plaintext request rejected by policy")
	ErrOutboundFailed      = errors.New("jose: outbound seal failed")
	ErrPolicyMismatch      = errors.New("jose: policy mismatch")
	ErrPolicyAsymmetric    = errors.New("jose: bidirectional policy required (request and response must both declare jose tags or neither)")
	ErrTagInvalid          = errors.New("jose: invalid jose struct tag")
	ErrKeyResolution       = errors.New("jose: key resolution failed")
)

Functions

func AllowedContentEncs

func AllowedContentEncs() []jose.ContentEncryption

func AllowedKeyAlgs

func AllowedKeyAlgs() []jose.KeyAlgorithm

func AllowedSigAlgs

func AllowedSigAlgs() []jose.SignatureAlgorithm

AllowedSigAlgs returns a copy of the signature-algorithm allowlist for callers that need to pass it to go-jose primitives (e.g., jose.ParseSigned). Returning a copy prevents external mutation.

func IsAllowedEnc

func IsAllowedEnc(enc jose.ContentEncryption) bool

func IsAllowedKeyAlg

func IsAllowedKeyAlg(alg jose.KeyAlgorithm) bool

func IsAllowedSigAlg

func IsAllowedSigAlg(alg jose.SignatureAlgorithm) bool

func IsContentType

func IsContentType(ct string) bool

IsContentType reports whether ct (typically a Content-Type header value) names the JOSE compact-serialization media type. Matches application/jose with optional parameters (e.g., "application/jose; charset=utf-8") case-insensitively per RFC 7231 §3.1.1.1.

func IsError

func IsError(err error) bool

IsError reports whether err is (or wraps) a *jose.Error — useful for callers that need to distinguish JOSE crypto failures (signature invalid, decrypt failed, kid unknown, etc.) from network/transport errors. Equivalent to manually doing `var jerr *jose.Error; errors.As(err, &jerr)` but reads as a single intent at the call site.

func IsInboundVerified

func IsInboundVerified(ctx context.Context) bool

IsInboundVerified reports whether the context was marked verified by a successful inbound JOSE decode.

func Open

func Open(compact string, p *Policy, r KeyResolver) (plaintext []byte, claims *Claims, hdr OpenHeader, err error)

Open performs the inbound transformation: decrypt the compact JWE with our private key, verify the inner JWS with the peer's public key, and parse standard JWT claims out of the verified payload.

Returns the verified plaintext payload, the extracted Claims, and the JWE+JWS Headers for diagnostic logging by the caller. On any failure, returns an *Error with the appropriate Code/Status (mostly 401 for crypto failures, 400 for malformed input).

The middleware MUST set inbound-verified state on the context only when this returns nil error — that's the gate for the encrypt-on-response security invariant.

func ResolvePolicy

func ResolvePolicy(r KeyResolver, p *Policy) error

ResolvePolicy verifies that every kid named in the policy resolves to a key of the correct role via the resolver. Called once at registration time per route — failures must fail process startup (Fail Fast principle).

func Seal

func Seal(payload []byte, p *Policy, r KeyResolver) (string, error)

Seal performs the outbound transformation: sign payload as a compact JWS with our private key, then encrypt that JWS as a compact JWE to the peer's public key. Returns the compact JWE string.

On any failure, returns an *Error with Code/Status mapped to JOSE_OUTBOUND_FAILED (500) — outbound failures are framework/operator errors (missing keys, bad config), never peer-induced. The Cause field carries the underlying detail for logging.

func WithClaims

func WithClaims(ctx context.Context, c *Claims) context.Context

WithClaims attaches the verified claim set extracted from the inbound JWS payload. Applications retrieve it via ClaimsFromContext to enforce iat/exp/jti policies.

func WithInboundVerified

func WithInboundVerified(ctx context.Context) context.Context

WithInboundVerified marks the context as having passed JOSE inbound decryption + signature verification. The presence of this marker (not its value) gates outbound encryption per the security invariant: a response is JOSE-encrypted iff this is set AND the route has an outbound policy.

func WithPolicy

func WithPolicy(ctx context.Context, p *Policy) context.Context

WithPolicy attaches the active outbound Policy for this request so the response formatter can find it after the handler returns. Set by the inbound middleware branch (or by the route-registration scaffolding for outbound-only routes).

Types

type Claims

type Claims struct {
	Issuer    string
	Subject   string
	Audience  []string
	IssuedAt  time.Time
	ExpiresAt time.Time
	NotBefore time.Time
	JTI       string
	// Raw is the full decoded claim map for fields not promoted above (vendor extensions,
	// custom claims, etc.). Apps cast values themselves.
	Raw map[string]any
}

Claims is the verified claim set extracted from a successfully decrypted-and-verified inbound JOSE payload. The middleware sets it on the request context so application handlers can enforce iat/exp/jti policies (per the v1 decision: framework verifies the signature, applications enforce timing).

All fields are zero-valued if absent from the JWS payload; nothing here is required at the framework layer — apps that don't care about a particular claim simply ignore it.

func ClaimsFromContext

func ClaimsFromContext(ctx context.Context) *Claims

ClaimsFromContext returns the verified Claims attached by the inbound middleware, or nil if no JOSE verification ran for this request.

type Direction

type Direction int

Direction indicates which side of the request/response pipeline a Policy applies to.

const (
	DirectionInbound  Direction = iota // request body: decrypt + verify
	DirectionOutbound                  // response body: sign + encrypt
)

func (Direction) String

func (d Direction) String() string

type Error

type Error struct {
	Sentinel error
	Code     string
	Status   int
	Message  string
	Kid      string
	Alg      string
	Enc      string
	Cause    error
}

Error is the structured value returned by every jose package operation that fails. It carries diagnostic fields (Kid, Alg, Enc) for logging and an HTTP-mapped Code/Status for response shaping. Use errors.Is(err, ErrDecryptFailed) for sentinel comparisons.

The Cause field MUST NOT be exposed to peers — it can leak information about which key was tried or which library detected the failure. Server middleware logs Cause; only Code and the constant-time generic Message reach the wire.

func (*Error) Error

func (e *Error) Error() string

func (*Error) Is

func (e *Error) Is(target error) bool

func (*Error) Unwrap

func (e *Error) Unwrap() error
type Header struct {
	Kid string
	Alg string
	Enc string
	Cty string
}

Header (jose-package level) is the diagnostic header shape exposed to callers, distinct from the internal cryptoadapter.Header to insulate consumers from library churn.

type KeyResolver

type KeyResolver interface {
	PrivateKey(kid string) (*rsa.PrivateKey, error)
	PublicKey(kid string) (*rsa.PublicKey, error)
}

KeyResolver abstracts key lookup so the jose package does not depend directly on app.KeyStore. This keeps the door open for future JWKS-URL backed resolvers without breaking callers, and makes testing trivial (NewTestResolver in jose/testing/).

Public/private semantics map to JOSE roles:

  • PrivateKey: used to decrypt inbound JWE and sign outbound JWS.
  • PublicKey: used to verify inbound JWS and encrypt outbound JWE.

Implementations MUST return the registered ErrKidUnknown sentinel (wrapped in *Error) when a kid is not configured, so callers can distinguish unknown-key from other failures via errors.Is.

type KeyStoreLike

type KeyStoreLike interface {
	PrivateKey(name string) (*rsa.PrivateKey, error)
	PublicKey(name string) (*rsa.PublicKey, error)
}

KeyStoreLike is the minimal subset of app.KeyStore that the resolver consumes. Defining it locally lets jose/ wrap any compatible store without importing app/, which would create an app → server → jose → app cycle once the server module wires the resolver via app/module_registry.go.

type KeyStoreResolver

type KeyStoreResolver struct {
	// contains filtered or unexported fields
}

KeyStoreResolver adapts a KeyStoreLike (typically an app.KeyStore) to KeyResolver. It is the default resolver wired into the server when a keystore module is registered.

func NewKeyStoreResolver

func NewKeyStoreResolver(ks KeyStoreLike) *KeyStoreResolver

func (*KeyStoreResolver) PrivateKey

func (r *KeyStoreResolver) PrivateKey(kid string) (*rsa.PrivateKey, error)

func (*KeyStoreResolver) PublicKey

func (r *KeyStoreResolver) PublicKey(kid string) (*rsa.PublicKey, error)

type OpenHeader

type OpenHeader struct {
	JWE Header
	JWS Header
}

OpenHeader holds the diagnostic headers from both JOSE layers, surfaced to the caller so the middleware can log them. Never includes plaintext.

type Policy

type Policy struct {
	Direction Direction

	// Inbound (DirectionInbound) — both required.
	DecryptKid string // our private key kid (jose: decrypt=...)
	VerifyKid  string // peer public key kid (jose: verify=...)

	// Outbound (DirectionOutbound) — both required.
	SignKid    string // our private key kid (jose: sign=...)
	EncryptKid string // peer public key kid (jose: encrypt=...)

	// Algorithms — defaults applied by the parser if tag omits them.
	SigAlg jose.SignatureAlgorithm
	KeyAlg jose.KeyAlgorithm
	Enc    jose.ContentEncryption
	Cty    string
}

Policy captures the JOSE configuration declared by a request or response struct's jose: tag. Inbound policies populate DecryptKid/VerifyKid; outbound populate SignKid/EncryptKid. SigAlg/KeyAlg/Enc/Cty fall back to package defaults when unset.

A Policy is constructed once at registration time by the scanner, validated against the KeyResolver, and cached in the registry — never re-parsed per request.

func ParseTag

func ParseTag(tagValue string, dir Direction) (*Policy, error)

ParseTag parses a `jose:` struct tag value into a Policy with the given direction. Direction is supplied by the caller (the scanner knows whether the type is request or response from its position in HandlerFunc[T, R]). Returns an *Error wrapping ErrTagInvalid on any parse failure; the caller should treat this as a registration failure (panic at startup).

func PolicyFromContext

func PolicyFromContext(ctx context.Context) *Policy

PolicyFromContext returns the outbound Policy attached for this request, or nil.

func ScanType

func ScanType(t reflect.Type, dir Direction) (*Policy, error)

ScanType inspects a Go type for a jose: sentinel field. Returns (nil, nil) if the type has no jose tag (i.e., the route is not JOSE-protected). Returns (*Policy, nil) on a valid declaration. Returns (nil, *Error) on any parse or policy-validation failure.

The caller is responsible for resolving the Policy's kid references against a KeyResolver — ScanType is purely structural.

Pointer types are unwrapped: ScanType(reflect.TypeOf(*Foo)) and ScanType(reflect.TypeOf(Foo)) return the same result.

func (*Policy) Validate

func (p *Policy) Validate() error

Validate checks the Policy for internal consistency (correct kids set for the direction, algorithms in the allowlist). It does NOT resolve kids against a KeyResolver — that happens separately at registration time.

type PolicyRegistry

type PolicyRegistry struct {
	// contains filtered or unexported fields
}

PolicyRegistry caches scanned + resolved Policies keyed by (reflect.Type, Direction). Including direction is essential: the same struct type can legitimately be scanned for both DirectionInbound and DirectionOutbound (e.g., a shared envelope used as both a request and response on different routes), and the resulting policies have different required keys. A type-only key would return the wrong policy on the second scan.

Mirrors the pattern in database/internal/columns/registry.go:24,89 (sync.Map + LoadOrStore) which is proven thread-safe and lock-free on the hot path.

The cache stores nil to mean "this type was scanned and has no JOSE policy" — distinct from "this type has not been scanned yet" (cache miss). This avoids re-scanning untagged types on every request, which would otherwise dominate the request hot path for non-JOSE routes.

func NewPolicyRegistry

func NewPolicyRegistry() *PolicyRegistry

func (*PolicyRegistry) LoadOrScan

func (r *PolicyRegistry) LoadOrScan(t reflect.Type, dir Direction) (*Policy, error)

LoadOrScan returns the cached Policy for (t, dir), or scans t with the given direction and caches the result. If scan fails, returns the error without caching.

func (*PolicyRegistry) Store

func (r *PolicyRegistry) Store(t reflect.Type, p *Policy)

Store explicitly caches a (possibly resolved) policy for (t, dir). Used when the registration site has already done extra validation (kid resolution) and wants to memoize the result.

Directories

Path Synopsis
internal
cryptoadapter
Package cryptoadapter wraps go-jose/v4 with strict allowlist enforcement and a constant-time generic error surface.
Package cryptoadapter wraps go-jose/v4 with strict allowlist enforcement and a constant-time generic error surface.
Package testing provides utilities for testing JOSE-protected handlers in go-bricks applications without requiring real counterparty credentials.
Package testing provides utilities for testing JOSE-protected handlers in go-bricks applications without requiring real counterparty credentials.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL