Documentation
¶
Overview ¶
Package jwe implements JWE (RFC 7516) compact-serialization encryption and decryption for exactly the two key-management algorithms fapi.KeyManagementAlgorithm supports (RSA-OAEP-256 and ECDH-ES+A256KW), each of which may be paired with either content-encryption algorithm fapi.ContentEncryptionAlgorithm supports: A256GCM (a single AEAD primitive) or A256CBC-HS512 (encrypt-then-MAC — AES-256-CBC plus a separate HMAC-SHA-512 tag, RFC 7518 §5.2.3).
Like internal/jose, this package is intentionally low-level and generic: it knows nothing about ID tokens, nested JWTs, or any other FAPI-specific convention. A caller wanting a signed-then-encrypted nested JWT (OIDC Core §10.2) composes this package with internal/jose itself — sign first, then Encrypt the resulting compact JWS as this package's plaintext with ContentType "JWT" — rather than this package knowing about that convention on its own.
Index ¶
- Variables
- func Encrypt(req EncryptRequest) (string, error)
- func UnwrapCEK(alg fapi.KeyManagementAlgorithm, recipientKey any, encryptedKey []byte, ...) ([]byte, error)
- func UnwrapCEKFromSharedSecret(alg fapi.KeyManagementAlgorithm, z, encryptedKey []byte) ([]byte, error)
- type DecryptRequest
- type DecryptResult
- type EncryptRequest
- type Header
- type Unwrapper
Constants ¶
This section is empty.
Variables ¶
var ( // ErrMalformed indicates a compact serialization that is not // well-formed (wrong number of segments, empty segment, invalid // base64url). ErrMalformed = errors.New("jwe: malformed compact serialization") // ErrAlgorithmMismatch indicates the "alg" or "enc" recorded in a // JWE header does not match what the caller required. ErrAlgorithmMismatch = errors.New("jwe: algorithm mismatch") // ErrDecryptionFailed indicates key unwrapping or content decryption // failed — a wrong key, a tampered ciphertext/tag, or (for // ECDH-ES+A256KW) a key-unwrap integrity check failure. Deliberately // undifferentiated: which specific step failed is not safe to expose // to a caller processing untrusted input. ErrDecryptionFailed = errors.New("jwe: decryption failed") )
Functions ¶
func Encrypt ¶
func Encrypt(req EncryptRequest) (string, error)
Encrypt produces a JWE in compact serialization for req.
func UnwrapCEK ¶
func UnwrapCEK(alg fapi.KeyManagementAlgorithm, recipientKey any, encryptedKey []byte, epk *ecdh.PublicKey) ([]byte, error)
UnwrapCEK recovers the content-encryption key encryptedKey carries, using recipientKey directly (*rsa.PrivateKey for RSAOAEP256, *ecdh.PrivateKey for ECDHESA256KW — epk is required for the latter, ignored for the former). Exported so a KeyManager-backed Unwrapper implementation that does hold a concrete private key in memory (e.g. keys/ephemeral, for tests) can reuse this package's own already-verified ECDH-ES+A256KW unwrap logic rather than reimplementing the Concat KDF and AES Key Wrap itself.
func UnwrapCEKFromSharedSecret ¶ added in v0.11.0
func UnwrapCEKFromSharedSecret(alg fapi.KeyManagementAlgorithm, z, encryptedKey []byte) ([]byte, error)
UnwrapCEKFromSharedSecret recovers the content-encryption key encryptedKey carries given z, an ECDH shared secret already agreed with the epk embedded in the JWE header — the Concat-KDF (RFC 7518 Appendix B) and RFC-3394 AES-Unwrap steps of ECDHESA256KW, factored out of UnwrapCEK so a caller that only performs the ECDH agreement step itself (an HSM or KMS's raw key-agreement primitive, never handing this package a private key at all) can still recover the CEK through this package's own audited KDF/unwrap logic rather than reimplementing it.
Types ¶
type DecryptRequest ¶
type DecryptRequest struct {
Algorithm fapi.KeyManagementAlgorithm
Encryption fapi.ContentEncryptionAlgorithm
// RecipientKey is either a concrete private key this package unwraps
// with directly (*rsa.PrivateKey for RSAOAEP256, *ecdh.PrivateKey for
// ECDHESA256KW), or a value implementing Unwrapper — for a caller
// that never holds the raw private key itself (e.g. an HSM- or
// remote-signing-service-backed key manager), and so cannot hand one
// to this package at all.
RecipientKey any
Compact string
}
DecryptRequest describes one JWE to open. Algorithm and Encryption are what the caller requires — never derived from the token's own header — so a header claiming a different algorithm than expected is rejected outright, the same policy internal/jose applies to a JWS "alg" header.
type DecryptResult ¶
DecryptResult is a successfully decrypted and authenticated JWE.
func Decrypt ¶
func Decrypt(ctx context.Context, req DecryptRequest) (DecryptResult, error)
Decrypt opens req.Compact, returning an error if it is malformed, its header doesn't match what the caller required, or authentication fails for any reason (wrong key, tampered ciphertext or tag, or a failed ECDH-ES+A256KW key-unwrap integrity check). ctx is only used when req.RecipientKey is an Unwrapper.
type EncryptRequest ¶
type EncryptRequest struct {
// Algorithm selects the key-management algorithm (the "alg" header)
// and, with it, which concrete type RecipientKey must be:
// *rsa.PublicKey for RSAOAEP256, *ecdh.PublicKey for ECDHESA256KW.
Algorithm fapi.KeyManagementAlgorithm
// Encryption selects the content-encryption algorithm (the "enc"
// header): A256GCM or A256CBC-HS512.
Encryption fapi.ContentEncryptionAlgorithm
RecipientKey any
// KeyID, if non-empty, is embedded as the header's "kid" — which of
// the recipient's possibly several encryption keys this was
// encrypted to.
KeyID string
// ContentType, if non-empty, is embedded as the header's "cty" —
// e.g. "JWT" for a nested JWT (OIDC Core §10.2). This package
// attaches no meaning to it itself.
ContentType string
// Random is the source of randomness for the CEK, the IV, and (for
// ECDHESA256KW) the ephemeral key pair. crypto/rand.Reader if nil.
Random io.Reader
Plaintext []byte
}
EncryptRequest describes one JWE to produce.
type Header ¶
type Header struct {
Algorithm fapi.KeyManagementAlgorithm
Encryption fapi.ContentEncryptionAlgorithm
ContentType string // "cty", optional — e.g. "JWT" for a nested JWT
KeyID string // "kid", optional — identifies which recipient key was used
// EphemeralPublicKey is present only for ECDH-ES-family algorithms
// (RFC 7518 §4.6.1.1's "epk" member) — the sender's one-time public
// key, used with the recipient's static key to derive the shared
// secret. Always nil for RSAOAEP256.
EphemeralPublicKey *ecdh.PublicKey
}
Header is a JWE protected header. Only the members this package's supported operations need are represented, but that does not make this a closed allow-list: RFC 7516 §4.2/§4.3 requires any Public or Private Header Parameter Name a recipient doesn't act on (e.g. an issuer's own "iss"/"aud") to be ignored, not rejected — the header is authenticated as JWE AAD either way, so an unrecognized informational member can't weaken what Decrypt checks. The one member enforced beyond what's modeled here is "crit" (RFC 7516 §4.1.13, which inherits RFC 7515 §4.1.11's rule): every name it lists must be one this parser actually understands and processes, or parsing fails outright.
type Unwrapper ¶
type Unwrapper interface {
UnwrapCEK(ctx context.Context, alg fapi.KeyManagementAlgorithm, keyID string, encryptedKey []byte, ephemeralPublicKey *ecdh.PublicKey) ([]byte, error)
}
Unwrapper delivers the content-encryption key for one JWE without this package (or its caller) ever holding the recipient's private key — the decryption-side equivalent of how this module's signing path never hands a crypto.Signer or raw private key across a KeyManager boundary. ctx is threaded through unchanged, so an implementation backed by a remote call can honor cancellation the same way keys.KeyManager.Sign does. keyID is the header's own "kid", forwarded so an implementation holding more than one registered key (e.g. mid-rotation) can select the right one; like every other header value, it's a routing hint only — an implementation still must perform the actual unwrap/decrypt to confirm it, never trust keyID by itself as proof of which key was really used.