Documentation
¶
Overview ¶
Package keys defines operation-based signing and verification contracts so that callers never need to hold or pass around a raw crypto.PrivateKey.
signing.go defines the KeyManager a server uses to request a signature or public key for a specific purpose (today: JARM response signing; ID token and access token signing will register their own SigningPurpose values once those endpoints are implemented); verification.go defines the corresponding public-key resolution used to check signatures against a known or discovered JWK. Production concrete implementations (HSM-backed, KMS-backed) live outside this module and only need to satisfy these interfaces. The one exception is keys/ephemeral, an in-tree, in-memory KeyManager/ClientKeySource pair for local development and testing only — never production — that exists so integrating server doesn't require writing key management from scratch just to get something running; see its own package doc comment.
KeyManager never returns a crypto.Signer — only a Sign operation and a public JWK — so an HSM- or remote-signing-service-backed implementation never has to hand private key material into this process. Resolving a remote party's verification keys (e.g. a registered client's JWKS, for request-object and client-assertion verification) is a distinct concern from KeyManager: prefer administratively pre-resolved or registered keys over a live fetch in the request-handling path, and where a live fetch is unavoidable it must go through the same SSRF, response-size, content-type, bounded-redirect and stale-key/duplicate-kid protections as any other outbound fetch (see fapihttp).
Index ¶
- type ClientEncryptionKey
- type ClientEncryptionKeyRequest
- type ClientEncryptionKeySet
- type ClientEncryptionKeySource
- type ClientEncryptionPurpose
- type ClientKeyRequest
- type ClientKeySource
- type Decrypter
- type DecryptionPurpose
- type IssuerKey
- type IssuerKeyRequest
- type IssuerKeySet
- type IssuerKeySource
- type IssuerVerificationPurpose
- type JWKSIssuerKeySource
- type JWKSOption
- type KeyManager
- type PublicKeyInfo
- type Signature
- type SigningPurpose
- type SigningRequest
- type UnwrapRequest
- type VerificationKey
- type VerificationKeySet
- type VerificationPurpose
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type ClientEncryptionKey ¶ added in v0.4.0
type ClientEncryptionKey struct {
KeyID string
Algorithm fapi.KeyManagementAlgorithm
PublicKey crypto.PublicKey
}
ClientEncryptionKey is one candidate encryption key for a client. It deliberately holds a crypto.PublicKey rather than any JOSE-specific type, so an external implementation of ClientEncryptionKeySource never needs to depend on this module's internal JWK representation — the same reasoning VerificationKey already applies.
type ClientEncryptionKeyRequest ¶ added in v0.4.0
type ClientEncryptionKeyRequest struct {
ClientID fapi.ClientID
Purpose ClientEncryptionPurpose
Algorithm fapi.KeyManagementAlgorithm
KeyID string // "" if the client did not pin a specific key
}
ClientEncryptionKeyRequest describes which of a client's encryption keys is needed.
type ClientEncryptionKeySet ¶ added in v0.4.0
type ClientEncryptionKeySet struct {
Keys []ClientEncryptionKey
}
ClientEncryptionKeySet is the set of keys ResolveEncryptionKeys returned. Ordinarily this holds exactly one key (selected by KeyID), but an implementation may return more than one when a client is mid-rotation.
type ClientEncryptionKeySource ¶ added in v0.4.0
type ClientEncryptionKeySource interface {
ResolveEncryptionKeys(ctx context.Context, req ClientEncryptionKeyRequest) (ClientEncryptionKeySet, error)
}
ClientEncryptionKeySource resolves a registered client's encryption keys — the encryption-side counterpart of ClientKeySource. Implementations should prefer administratively pre-resolved or registered keys over a live JWKS fetch in the request-handling path; see the package doc comment for the protections a live fetch must apply if one is unavoidable.
type ClientEncryptionPurpose ¶ added in v0.4.0
type ClientEncryptionPurpose uint8
ClientEncryptionPurpose is a closed set of reasons a client's encryption key might be resolved — the encryption-side counterpart of VerificationPurpose. Encrypting something *to* a client is a different operation from verifying something *from* it (a different algorithm type, a different key), so this is a sibling set, not an addition to VerificationPurpose.
const ( // IDTokenEncryption resolves a key to encrypt an ID token to the // client (OIDC Core §2). IDTokenEncryption ClientEncryptionPurpose )
type ClientKeyRequest ¶
type ClientKeyRequest struct {
ClientID fapi.ClientID
Purpose VerificationPurpose
Algorithm fapi.SignatureAlgorithm
KeyID string // "" if the token carried no kid
}
ClientKeyRequest describes which of a client's verification keys is needed. KeyID and Algorithm come from the unverified header of the assertion or request object being checked — see, for example, clientassertion.Assertion.KeyID, which is documented as safe to use only as a lookup key, never as something to trust.
type ClientKeySource ¶
type ClientKeySource interface {
ResolveVerificationKeys(ctx context.Context, req ClientKeyRequest) (VerificationKeySet, error)
}
ClientKeySource resolves a registered client's verification keys. Implementations should prefer administratively pre-resolved or registered keys over a live JWKS fetch in the request-handling path; see the package doc comment for the protections a live fetch must apply if one is unavoidable.
type Decrypter ¶ added in v0.4.0
type Decrypter interface {
// UnwrapContentEncryptionKey recovers the content-encryption key
// req describes, using the key currently designated for req.Purpose
// and req.Algorithm.
UnwrapContentEncryptionKey(ctx context.Context, req UnwrapRequest) ([]byte, error)
// EncryptionPublicKey returns the public key (and its kid) currently
// designated for purpose and algorithm — *rsa.PublicKey for
// RSAOAEP256, *ecdh.PublicKey for ECDHESA256KW — so an embedder can
// register it with an authorization server out of band (this module
// has no dynamic client registration flow of its own) without ever
// holding the private key itself.
EncryptionPublicKey(ctx context.Context, purpose DecryptionPurpose, algorithm fapi.KeyManagementAlgorithm) (PublicKeyInfo, error)
}
Decrypter performs the client's own content-encryption-key recovery. Like KeyManager, it never returns a private key — only the unwrapped CEK bytes and the corresponding public key — so an HSM- or remote- signing-service-backed implementation never has to hand private key material into this process. It is a separate interface from KeyManager, not an addition to it, so an embedder that never needs encrypted ID token support isn't forced to implement a method it will never call.
type DecryptionPurpose ¶ added in v0.4.0
type DecryptionPurpose uint8
DecryptionPurpose is a closed set of reasons a party might need to decrypt something with its own key, mirroring SigningPurpose's own per-use-case key selection but for the decryption side: a client receiving an encrypted (or encrypted-then-signed) ID token needs to recover the content-encryption key an authorization server wrapped to its public key, without this module — or its caller — ever holding the corresponding private key.
const ( // IDTokenDecryption unwraps the content-encryption key of an // encrypted ID token (OIDC Core §10.2). IDTokenDecryption DecryptionPurpose // UserInfoDecryption unwraps the content-encryption key of a // signed-then-encrypted UserInfo response (OIDC Core §5.3.2). UserInfoDecryption )
type IssuerKey ¶
type IssuerKey struct {
KeyID string
Algorithm fapi.SignatureAlgorithm
PublicKey crypto.PublicKey
}
IssuerKey is one candidate verification key for an authorization server. It deliberately holds a crypto.PublicKey rather than any JOSE-specific type, for the same reason as VerificationKey.
type IssuerKeyRequest ¶
type IssuerKeyRequest struct {
Issuer string
Purpose IssuerVerificationPurpose
Algorithm fapi.SignatureAlgorithm
KeyID string
}
IssuerKeyRequest describes which of an authorization server's signing keys the caller needs to verify against. Issuer and Algorithm must be values the caller already trusts and expects — the server's registered issuer identifier and signing algorithm — never values read from the unverified token being checked. KeyID comes from the token's own (unverified) header and is safe to use only as a lookup hint, exactly like ClientKeyRequest.KeyID.
type IssuerKeySet ¶
type IssuerKeySet struct {
Keys []IssuerKey
}
IssuerKeySet is the set of keys ResolveIssuerKeys returned. Ordinarily this holds exactly one key (selected by KeyID), but an implementation may return more than one when the issuer is mid-rotation.
type IssuerKeySource ¶
type IssuerKeySource interface {
ResolveIssuerKeys(ctx context.Context, req IssuerKeyRequest) (IssuerKeySet, error)
}
IssuerKeySource resolves an authorization server's public verification keys — used by resource to verify access tokens and, in future, by client to verify JARM responses and ID tokens. Implementations should prefer a cached or administratively pre-resolved key set over a live JWKS fetch in the request-handling path; where a live fetch is unavoidable it must apply the same SSRF, size-limit, content-type, bounded-redirect and stale-key protections as any other outbound fetch — see ARCHITECTURE.md, design rule 6.
type IssuerVerificationPurpose ¶
type IssuerVerificationPurpose uint8
IssuerVerificationPurpose is a closed set of reasons an authorization server's verification key might be resolved, so an implementation can return different keys (or apply different trust policy) for different uses of the same issuer's key material — mirroring VerificationPurpose's role for ClientKeyRequest.
const ( // AccessTokenVerification resolves a key to verify a JWT access token // (RFC 9068) — used by resource. AccessTokenVerification IssuerVerificationPurpose // JARMVerification resolves a key to verify a JWT Secured // Authorization Response — used by client. JARMVerification // IDTokenVerification resolves a key to verify an OIDC ID token — // used by client. IDTokenVerification // UserInfoVerification resolves a key to verify an issuer-signed // artifact via client.VerifyIssuerJWS — most commonly a signed (or // signed-then-encrypted) UserInfo response (OIDC Core §5.3.2). UserInfoVerification )
type JWKSIssuerKeySource ¶
type JWKSIssuerKeySource struct {
// contains filtered or unexported fields
}
JWKSIssuerKeySource resolves an authorization server's verification keys by fetching its published JWKS document live over fapihttp — the hardened building block ARCHITECTURE.md design rule 6 describes for exactly this case — and caching the result for CacheTTL so a verification-heavy workload doesn't refetch on every call.
Per design rule 5, a live JWKS fetch in the request-handling path is a last resort; prefer an administratively pre-resolved or cached IssuerKeySource where a deployment can arrange one. This type exists for the common case where that isn't practical, and implements IssuerKeySource so both client and resource can use it.
func NewJWKSIssuerKeySource ¶
func NewJWKSIssuerKeySource(fetcher *fapihttp.Client, jwksURI fapi.URL, cacheTTL time.Duration, opts ...JWKSOption) (*JWKSIssuerKeySource, error)
NewJWKSIssuerKeySource returns a JWKSIssuerKeySource fetching from jwksURI via fetcher, caching the result for cacheTTL.
func (*JWKSIssuerKeySource) ResolveIssuerKeys ¶
func (s *JWKSIssuerKeySource) ResolveIssuerKeys(ctx context.Context, req IssuerKeyRequest) (IssuerKeySet, error)
ResolveIssuerKeys returns every cached key matching req.Algorithm (and req.KeyID, if set). If a specific KeyID was requested and isn't found in the cache, it forces one refresh before giving up — the stale-key handling design rule 5 requires, covering the case where the authorization server has rotated keys since the last fetch. That forced refresh is rate-limited to at most one per minRefreshInterval and single-flighted, so an unauthenticated caller cannot force unbounded concurrent upstream fetches by sending requests with unknown kids (req.KeyID is taken from the unverified token header, so it is attacker-controlled).
type JWKSOption ¶ added in v0.2.0
type JWKSOption func(*JWKSIssuerKeySource)
JWKSOption configures a JWKSIssuerKeySource.
func WithMinRefreshInterval ¶ added in v0.2.0
func WithMinRefreshInterval(d time.Duration) JWKSOption
WithMinRefreshInterval bounds how often an unknown-kid miss may force a live refetch, independent of CacheTTL. Defaults to CacheTTL, which means an unknown kid can force at most one extra fetch per TTL window — a real key rotation is still picked up within one TTL, the same worst case as plain TTL expiry, while an attacker sending requests with distinct unknown kids cannot force more than one upstream fetch per interval.
func WithRefreshBackoff ¶ added in v0.2.2
func WithRefreshBackoff(d time.Duration) JWKSOption
WithRefreshBackoff bounds how soon the base TTL-refresh path (currentKeys) will re-attempt a fetch after one fails, so a sequential request stream against a failing upstream doesn't issue one outbound fetch per request. Defaults to min(cacheTTL, 5s) — short relative to a long cacheTTL, but never longer than it — so a cold-start recovery isn't blocked for a full TTL window. This is distinct from minRefreshInterval, which rate-limits only the unknown-kid forced-refresh path.
type KeyManager ¶
type KeyManager interface {
// Sign produces a signature over req.Digest or req.SigningInput
// (see SigningRequest's own doc comment for which one, and why)
// using the key currently designated for req.Purpose and
// req.Algorithm.
Sign(ctx context.Context, req SigningRequest) (Signature, error)
// PublicKey returns the public key (and its kid) currently
// designated for purpose and algorithm, so a caller can construct a
// crypto.Signer-shaped adapter for this module's JOSE plumbing
// without ever holding the private key itself.
PublicKey(ctx context.Context, purpose SigningPurpose, algorithm fapi.SignatureAlgorithm) (PublicKeyInfo, error)
}
KeyManager performs the server's own signing operations. It never returns a crypto.Signer or a private key — only a Sign operation and the corresponding public key — so an HSM- or remote-signing-service- backed implementation never has to hand private key material into this process.
type PublicKeyInfo ¶
PublicKeyInfo identifies the public half of the key a given purpose and algorithm currently sign with.
type Signature ¶
Signature is the result of a Sign call. Value must be in the format Go's crypto.Signer contract specifies for the algorithm's key type — in particular, ASN.1 DER for an ECDSA signature, not the fixed-width R||S concatenation JWS uses on the wire. This module's JOSE layer performs that conversion itself; an implementation of KeyManager should produce exactly what a stdlib *ecdsa.PrivateKey or *rsa.PrivateKey would from Sign, since that is the contract this module's internal crypto.Signer adapter relies on. For EdDSA, Value is simpler: an ed25519.PrivateKey's Sign output is already the 64-byte wire format RFC 8037 wants, with no equivalent DER-to-fixed-width conversion needed.
type SigningPurpose ¶
type SigningPurpose uint8
SigningPurpose is a closed set of reasons a party might need to sign something with its own key, so an implementation can select different keys (or apply different rotation/HSM policy) per purpose. The first three are server purposes; the last three are client purposes — both roles use the same KeyManager contract (see ARCHITECTURE.md design rule 5), never a crypto.Signer or raw private key.
const ( // JARMSigning signs a JWT Secured Authorization Response. JARMSigning SigningPurpose // AccessTokenSigning signs a JWT access token (RFC 9068). AccessTokenSigning // IDTokenSigning signs an OIDC ID token. IDTokenSigning // ClientAuthentication signs a private_key_jwt client assertion. ClientAuthentication // RequestObjectSigning signs a pushed authorization request object // (RFC 9101). RequestObjectSigning // DPoPProofSigning signs a DPoP proof (RFC 9449). DPoPProofSigning )
type SigningRequest ¶
type SigningRequest struct {
Purpose SigningPurpose
Algorithm fapi.SignatureAlgorithm
Digest []byte
SigningInput []byte
}
SigningRequest describes one signature to produce. Exactly one of Digest or SigningInput is populated, chosen by Algorithm — never both, and an implementation should treat the other as absent rather than guess:
- Digest, for ES256/PS256: the pre-hashed signing input. This module always hashes before calling Sign for these algorithms, so an HSM- or remote-signing-service-backed implementation never receives more of the plaintext than it needs.
- SigningInput, for EdDSA: the raw, unhashed JWS Signing Input. RFC 8037 §3.1 requires pure EdDSA over the actual message, not a digest of it — there is no equivalent "pre-hash so the implementation sees less" option for this algorithm, so an EdDSA-capable implementation necessarily receives the full plaintext being signed. An implementation that hasn't been updated to handle EdDSA sees an empty Digest for such a request (not a wrong or insecure signature over the wrong bytes) and should fail closed.
func NewSigningRequest ¶ added in v0.6.0
func NewSigningRequest(purpose SigningPurpose, algorithm fapi.SignatureAlgorithm, digestOrMessage []byte) SigningRequest
NewSigningRequest builds a SigningRequest for purpose/algorithm, routing digestOrMessage into Digest or SigningInput as SigningRequest's own doc comment requires. It exists so every crypto.Signer-over-KeyManager adapter (client and server each keep their own private one) shares this one dispatch rather than reimplementing it — the routing decision belongs with the type it populates, not with each adapter that happens to call Sign.
type UnwrapRequest ¶ added in v0.4.0
type UnwrapRequest struct {
Purpose DecryptionPurpose
Algorithm fapi.KeyManagementAlgorithm
// EncryptedKey is the JWE's "encrypted_key" — the wire-format
// wrapped content-encryption key.
EncryptedKey []byte
// EphemeralPublicKey is the JWE header's "epk", required for
// ECDHESA256KW and nil for RSAOAEP256.
EphemeralPublicKey *ecdh.PublicKey
}
UnwrapRequest describes one content-encryption key to recover. EncryptedKey and EphemeralPublicKey come from the unverified header of the JWE being opened — safe to use only as inputs to the unwrap operation itself, never as something to trust ahead of it.
type VerificationKey ¶
type VerificationKey struct {
KeyID string
Algorithm fapi.SignatureAlgorithm
PublicKey crypto.PublicKey
}
VerificationKey is one candidate verification key for a client. It deliberately holds a crypto.PublicKey rather than any JOSE-specific type, so an external implementation of ClientKeySource never needs to depend on this module's internal JWK representation.
type VerificationKeySet ¶
type VerificationKeySet struct {
Keys []VerificationKey
}
VerificationKeySet is the set of keys ResolveVerificationKeys returned. Ordinarily this holds exactly one key (selected by KeyID), but an implementation may return more than one when a client is mid-rotation.
type VerificationPurpose ¶
type VerificationPurpose uint8
VerificationPurpose is a closed set of reasons a client's verification key might be resolved, so an implementation can return different keys (or apply different trust policy) for different uses of the same client's key material.
const ( // ClientAssertionVerification resolves a key to verify a // private_key_jwt client assertion. ClientAssertionVerification VerificationPurpose // RequestObjectVerification resolves a key to verify a signed // request object. RequestObjectVerification )
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package ephemeral provides in-memory implementations of keys.KeyManager and keys.ClientKeySource — for local development and testing only.
|
Package ephemeral provides in-memory implementations of keys.KeyManager and keys.ClientKeySource — for local development and testing only. |