auth/vendor/github.com/lestrrat-go/jwx/v2/jwe/jwe.go

794 lines
23 KiB
Go

//go:generate ../tools/cmd/genjwe.sh
// Package jwe implements JWE as described in https://tools.ietf.org/html/rfc7516
package jwe
import (
"bytes"
"context"
"crypto/ecdsa"
"crypto/rsa"
"fmt"
"io"
"github.com/lestrrat-go/blackmagic"
"github.com/lestrrat-go/jwx/v2/internal/base64"
"github.com/lestrrat-go/jwx/v2/internal/json"
"github.com/lestrrat-go/jwx/v2/internal/keyconv"
"github.com/lestrrat-go/jwx/v2/jwk"
"github.com/lestrrat-go/jwx/v2/jwa"
"github.com/lestrrat-go/jwx/v2/jwe/internal/content_crypt"
"github.com/lestrrat-go/jwx/v2/jwe/internal/keyenc"
"github.com/lestrrat-go/jwx/v2/jwe/internal/keygen"
"github.com/lestrrat-go/jwx/v2/x25519"
)
const (
fmtInvalid = iota
fmtCompact
fmtJSON
fmtJSONPretty
fmtMax
)
var _ = fmtInvalid
var _ = fmtMax
var registry = json.NewRegistry()
type recipientBuilder struct {
alg jwa.KeyEncryptionAlgorithm
key interface{}
headers Headers
}
func (b *recipientBuilder) Build(cek []byte, calg jwa.ContentEncryptionAlgorithm, cc *content_crypt.Generic) (Recipient, []byte, error) {
// we need the raw key
rawKey := b.key
var keyID string
if jwkKey, ok := b.key.(jwk.Key); ok {
// Meanwhile, grab the kid as well
keyID = jwkKey.KeyID()
var raw interface{}
if err := jwkKey.Raw(&raw); err != nil {
return nil, nil, fmt.Errorf(`failed to retrieve raw key out of %T: %w`, b.key, err)
}
rawKey = raw
}
// First, create a key encryptor
var enc keyenc.Encrypter
switch b.alg {
case jwa.RSA1_5:
var pubkey rsa.PublicKey
if err := keyconv.RSAPublicKey(&pubkey, rawKey); err != nil {
return nil, nil, fmt.Errorf(`failed to generate public key from key (%T): %w`, rawKey, err)
}
v, err := keyenc.NewRSAPKCSEncrypt(b.alg, &pubkey)
if err != nil {
return nil, nil, fmt.Errorf(`failed to create RSA PKCS encrypter: %w`, err)
}
enc = v
case jwa.RSA_OAEP, jwa.RSA_OAEP_256:
var pubkey rsa.PublicKey
if err := keyconv.RSAPublicKey(&pubkey, rawKey); err != nil {
return nil, nil, fmt.Errorf(`failed to generate public key from key (%T): %w`, rawKey, err)
}
v, err := keyenc.NewRSAOAEPEncrypt(b.alg, &pubkey)
if err != nil {
return nil, nil, fmt.Errorf(`failed to create RSA OAEP encrypter: %w`, err)
}
enc = v
case jwa.A128KW, jwa.A192KW, jwa.A256KW,
jwa.A128GCMKW, jwa.A192GCMKW, jwa.A256GCMKW,
jwa.PBES2_HS256_A128KW, jwa.PBES2_HS384_A192KW, jwa.PBES2_HS512_A256KW:
sharedkey, ok := rawKey.([]byte)
if !ok {
return nil, nil, fmt.Errorf(`invalid key: []byte required (%T)`, rawKey)
}
var err error
switch b.alg {
case jwa.A128KW, jwa.A192KW, jwa.A256KW:
enc, err = keyenc.NewAES(b.alg, sharedkey)
case jwa.PBES2_HS256_A128KW, jwa.PBES2_HS384_A192KW, jwa.PBES2_HS512_A256KW:
enc, err = keyenc.NewPBES2Encrypt(b.alg, sharedkey)
default:
enc, err = keyenc.NewAESGCMEncrypt(b.alg, sharedkey)
}
if err != nil {
return nil, nil, fmt.Errorf(`failed to create key wrap encrypter: %w`, err)
}
// NOTE: there was formerly a restriction, introduced
// in PR #26, which disallowed certain key/content
// algorithm combinations. This seemed bogus, and
// interop with the jose tool demonstrates it.
case jwa.ECDH_ES, jwa.ECDH_ES_A128KW, jwa.ECDH_ES_A192KW, jwa.ECDH_ES_A256KW:
var keysize int
switch b.alg {
case jwa.ECDH_ES:
// https://tools.ietf.org/html/rfc7518#page-15
// In Direct Key Agreement mode, the output of the Concat KDF MUST be a
// key of the same length as that used by the "enc" algorithm.
keysize = cc.KeySize()
case jwa.ECDH_ES_A128KW:
keysize = 16
case jwa.ECDH_ES_A192KW:
keysize = 24
case jwa.ECDH_ES_A256KW:
keysize = 32
}
switch key := rawKey.(type) {
case x25519.PublicKey:
v, err := keyenc.NewECDHESEncrypt(b.alg, calg, keysize, rawKey)
if err != nil {
return nil, nil, fmt.Errorf(`failed to create ECDHS key wrap encrypter: %w`, err)
}
enc = v
default:
var pubkey ecdsa.PublicKey
if err := keyconv.ECDSAPublicKey(&pubkey, rawKey); err != nil {
return nil, nil, fmt.Errorf(`failed to generate public key from key (%T): %w`, key, err)
}
v, err := keyenc.NewECDHESEncrypt(b.alg, calg, keysize, &pubkey)
if err != nil {
return nil, nil, fmt.Errorf(`failed to create ECDHS key wrap encrypter: %w`, err)
}
enc = v
}
case jwa.DIRECT:
sharedkey, ok := rawKey.([]byte)
if !ok {
return nil, nil, fmt.Errorf("invalid key: []byte required")
}
enc, _ = keyenc.NewNoop(b.alg, sharedkey)
default:
return nil, nil, fmt.Errorf(`invalid key encryption algorithm (%s)`, b.alg)
}
if keyID != "" {
enc.SetKeyID(keyID)
}
r := NewRecipient()
if hdrs := b.headers; hdrs != nil {
_ = r.SetHeaders(hdrs)
}
if err := r.Headers().Set(AlgorithmKey, b.alg); err != nil {
return nil, nil, fmt.Errorf(`failed to set header: %w`, err)
}
if v := enc.KeyID(); v != "" {
if err := r.Headers().Set(KeyIDKey, v); err != nil {
return nil, nil, fmt.Errorf(`failed to set header: %w`, err)
}
}
var rawCEK []byte
enckey, err := enc.Encrypt(cek)
if err != nil {
return nil, nil, fmt.Errorf(`failed to encrypt key: %w`, err)
}
if enc.Algorithm() == jwa.ECDH_ES || enc.Algorithm() == jwa.DIRECT {
rawCEK = enckey.Bytes()
} else {
if err := r.SetEncryptedKey(enckey.Bytes()); err != nil {
return nil, nil, fmt.Errorf(`failed to set encrypted key: %w`, err)
}
}
if hp, ok := enckey.(populater); ok {
if err := hp.Populate(r.Headers()); err != nil {
return nil, nil, fmt.Errorf(`failed to populate: %w`, err)
}
}
return r, rawCEK, nil
}
// Encrypt generates a JWE message for the given payload and returns
// it in serialized form, which can be in either compact or
// JSON format. Default is compact.
//
// You must pass at least one key to `jwe.Encrypt()` by using `jwe.WithKey()`
// option.
//
// jwe.Encrypt(payload, jwe.WithKey(alg, key))
// jwe.Encrypt(payload, jws.WithJSON(), jws.WithKey(alg1, key1), jws.WithKey(alg2, key2))
//
// Note that in the second example the `jws.WithJSON()` option is
// specified as well. This is because the compact serialization
// format does not support multiple recipients, and users must
// specifically ask for the JSON serialization format.
//
// Read the documentation for `jwe.WithKey()` to learn more about the
// possible values that can be used for `alg` and `key`.
//
// Look for options that return `jwe.EncryptOption` or `jws.EncryptDecryptOption`
// for a complete list of options that can be passed to this function.
func Encrypt(payload []byte, options ...EncryptOption) ([]byte, error) {
// default content encryption algorithm
calg := jwa.A256GCM
// default compression is "none"
compression := jwa.NoCompress
format := fmtCompact
// builds each "recipient" with encrypted_key and headers
var builders []*recipientBuilder
var protected Headers
var mergeProtected bool
var useRawCEK bool
for _, option := range options {
//nolint:forcetypeassert
switch option.Ident() {
case identKey{}:
data := option.Value().(*withKey)
v, ok := data.alg.(jwa.KeyEncryptionAlgorithm)
if !ok {
return nil, fmt.Errorf(`jwe.Encrypt: expected alg to be jwa.KeyEncryptionAlgorithm, but got %T`, data.alg)
}
switch v {
case jwa.DIRECT, jwa.ECDH_ES:
useRawCEK = true
}
builders = append(builders, &recipientBuilder{
alg: v,
key: data.key,
headers: data.headers,
})
case identContentEncryptionAlgorithm{}:
calg = option.Value().(jwa.ContentEncryptionAlgorithm)
case identCompress{}:
compression = option.Value().(jwa.CompressionAlgorithm)
case identMergeProtectedHeaders{}:
mergeProtected = option.Value().(bool)
case identProtectedHeaders{}:
v := option.Value().(Headers)
if !mergeProtected || protected == nil {
protected = v
} else {
ctx := context.TODO()
merged, err := protected.Merge(ctx, v)
if err != nil {
return nil, fmt.Errorf(`jwe.Encrypt: failed to merge headers: %w`, err)
}
protected = merged
}
case identSerialization{}:
format = option.Value().(int)
}
}
// We need to have at least one builder
switch l := len(builders); {
case l == 0:
return nil, fmt.Errorf(`jwe.Encrypt: missing key encryption builders: use jwe.WithKey() to specify one`)
case l > 1:
if format == fmtCompact {
return nil, fmt.Errorf(`jwe.Encrypt: cannot use compact serialization when multiple recipients exist (check the number of WithKey() argument, or use WithJSON())`)
}
}
if useRawCEK {
if len(builders) != 1 {
return nil, fmt.Errorf(`jwe.Encrypt: multiple recipients for ECDH-ES/DIRECT mode supported`)
}
}
// There is exactly one content encrypter.
contentcrypt, err := content_crypt.NewGeneric(calg)
if err != nil {
return nil, fmt.Errorf(`jwe.Encrypt: failed to create AES encrypter: %w`, err)
}
generator := keygen.NewRandom(contentcrypt.KeySize())
bk, err := generator.Generate()
if err != nil {
return nil, fmt.Errorf(`jwe.Encrypt: failed to generate key: %w`, err)
}
cek := bk.Bytes()
recipients := make([]Recipient, len(builders))
for i, builder := range builders {
// some builders require hint from the contentcrypt object
r, rawCEK, err := builder.Build(cek, calg, contentcrypt)
if err != nil {
return nil, fmt.Errorf(`jwe.Encrypt: failed to create recipient #%d: %w`, i, err)
}
recipients[i] = r
// Kinda feels weird, but if useRawCEK == true, we asserted earlier
// that len(builders) == 1, so this is OK
if useRawCEK {
cek = rawCEK
}
}
if protected == nil {
protected = NewHeaders()
}
if err := protected.Set(ContentEncryptionKey, calg); err != nil {
return nil, fmt.Errorf(`jwe.Encrypt: failed to set "enc" in protected header: %w`, err)
}
if compression != jwa.NoCompress {
payload, err = compress(payload)
if err != nil {
return nil, fmt.Errorf(`jwe.Encrypt: failed to compress payload before encryption: %w`, err)
}
if err := protected.Set(CompressionKey, compression); err != nil {
return nil, fmt.Errorf(`jwe.Encrypt: failed to set "zip" in protected header: %w`, err)
}
}
// If there's only one recipient, you want to include that in the
// protected header
if len(recipients) == 1 {
h, err := protected.Merge(context.TODO(), recipients[0].Headers())
if err != nil {
return nil, fmt.Errorf(`jwe.Encrypt: failed to merge protected headers: %w`, err)
}
protected = h
}
aad, err := protected.Encode()
if err != nil {
return nil, fmt.Errorf(`failed to base64 encode protected headers: %w`, err)
}
iv, ciphertext, tag, err := contentcrypt.Encrypt(cek, payload, aad)
if err != nil {
return nil, fmt.Errorf(`failed to encrypt payload: %w`, err)
}
msg := NewMessage()
if err := msg.Set(CipherTextKey, ciphertext); err != nil {
return nil, fmt.Errorf(`failed to set %s: %w`, CipherTextKey, err)
}
if err := msg.Set(InitializationVectorKey, iv); err != nil {
return nil, fmt.Errorf(`failed to set %s: %w`, InitializationVectorKey, err)
}
if err := msg.Set(ProtectedHeadersKey, protected); err != nil {
return nil, fmt.Errorf(`failed to set %s: %w`, ProtectedHeadersKey, err)
}
if err := msg.Set(RecipientsKey, recipients); err != nil {
return nil, fmt.Errorf(`failed to set %s: %w`, RecipientsKey, err)
}
if err := msg.Set(TagKey, tag); err != nil {
return nil, fmt.Errorf(`failed to set %s: %w`, TagKey, err)
}
switch format {
case fmtCompact:
return Compact(msg)
case fmtJSON:
return json.Marshal(msg)
case fmtJSONPretty:
return json.MarshalIndent(msg, "", " ")
default:
return nil, fmt.Errorf(`jwe.Encrypt: invalid serialization`)
}
}
type decryptCtx struct {
msg *Message
aad []byte
computedAad []byte
keyProviders []KeyProvider
protectedHeaders Headers
}
// Decrypt takes the key encryption algorithm and the corresponding
// key to decrypt the JWE message, and returns the decrypted payload.
// The JWE message can be either compact or full JSON format.
//
// `alg` accepts a `jwa.KeyAlgorithm` for convenience so you can directly pass
// the result of `(jwk.Key).Algorithm()`, but in practice it must be of type
// `jwa.KeyEncryptionAlgorithm` or otherwise it will cause an error.
//
// `key` must be a private key. It can be either in its raw format (e.g. *rsa.PrivateKey) or a jwk.Key
func Decrypt(buf []byte, options ...DecryptOption) ([]byte, error) {
var keyProviders []KeyProvider
var keyUsed interface{}
var dst *Message
//nolint:forcetypeassert
for _, option := range options {
switch option.Ident() {
case identMessage{}:
dst = option.Value().(*Message)
case identKeyProvider{}:
keyProviders = append(keyProviders, option.Value().(KeyProvider))
case identKeyUsed{}:
keyUsed = option.Value()
case identKey{}:
pair := option.Value().(*withKey)
alg, ok := pair.alg.(jwa.KeyEncryptionAlgorithm)
if !ok {
return nil, fmt.Errorf(`WithKey() option must be specified using jwa.KeyEncryptionAlgorithm (got %T)`, pair.alg)
}
keyProviders = append(keyProviders, &staticKeyProvider{
alg: alg,
key: pair.key,
})
}
}
if len(keyProviders) < 1 {
return nil, fmt.Errorf(`jwe.Decrypt: no key providers have been provided (see jwe.WithKey(), jwe.WithKeySet(), and jwe.WithKeyProvider()`)
}
msg, err := parseJSONOrCompact(buf, true)
if err != nil {
return nil, fmt.Errorf(`failed to parse buffer for Decrypt: %w`, err)
}
// Process things that are common to the message
ctx := context.TODO()
h, err := msg.protectedHeaders.Clone(ctx)
if err != nil {
return nil, fmt.Errorf(`failed to copy protected headers: %w`, err)
}
h, err = h.Merge(ctx, msg.unprotectedHeaders)
if err != nil {
return nil, fmt.Errorf(`failed to merge headers for message decryption: %w`, err)
}
var aad []byte
if aadContainer := msg.authenticatedData; aadContainer != nil {
aad = base64.Encode(aadContainer)
}
var computedAad []byte
if len(msg.rawProtectedHeaders) > 0 {
computedAad = msg.rawProtectedHeaders
} else {
// this is probably not required once msg.Decrypt is deprecated
var err error
computedAad, err = msg.protectedHeaders.Encode()
if err != nil {
return nil, fmt.Errorf(`failed to encode protected headers: %w`, err)
}
}
// for each recipient, attempt to match the key providers
// if we have no recipients, pretend like we only have one
recipients := msg.recipients
if len(recipients) == 0 {
r := NewRecipient()
if err := r.SetHeaders(msg.protectedHeaders); err != nil {
return nil, fmt.Errorf(`failed to set headers to recipient: %w`, err)
}
recipients = append(recipients, r)
}
var dctx decryptCtx
dctx.aad = aad
dctx.computedAad = computedAad
dctx.msg = msg
dctx.keyProviders = keyProviders
dctx.protectedHeaders = h
var lastError error
for _, recipient := range recipients {
decrypted, err := dctx.try(ctx, recipient, keyUsed)
if err != nil {
lastError = err
continue
}
if dst != nil {
*dst = *msg
dst.rawProtectedHeaders = nil
dst.storeProtectedHeaders = false
}
return decrypted, nil
}
return nil, fmt.Errorf(`jwe.Decrypt: failed to decrypt any of the recipients (last error = %w)`, lastError)
}
func (dctx *decryptCtx) try(ctx context.Context, recipient Recipient, keyUsed interface{}) ([]byte, error) {
var tried int
var lastError error
for i, kp := range dctx.keyProviders {
var sink algKeySink
if err := kp.FetchKeys(ctx, &sink, recipient, dctx.msg); err != nil {
return nil, fmt.Errorf(`key provider %d failed: %w`, i, err)
}
for _, pair := range sink.list {
tried++
// alg is converted here because pair.alg is of type jwa.KeyAlgorithm.
// this may seem ugly, but we're trying to avoid declaring separate
// structs for `alg jwa.KeyAlgorithm` and `alg jwa.SignatureAlgorithm`
//nolint:forcetypeassert
alg := pair.alg.(jwa.KeyEncryptionAlgorithm)
key := pair.key
decrypted, err := dctx.decryptKey(ctx, alg, key, recipient)
if err != nil {
lastError = err
continue
}
if keyUsed != nil {
if err := blackmagic.AssignIfCompatible(keyUsed, key); err != nil {
return nil, fmt.Errorf(`failed to assign used key (%T) to %T: %w`, key, keyUsed, err)
}
}
return decrypted, nil
}
}
return nil, fmt.Errorf(`jwe.Decrypt: tried %d keys, but failed to match any of the keys with recipient (last error = %s)`, tried, lastError)
}
func (dctx *decryptCtx) decryptKey(ctx context.Context, alg jwa.KeyEncryptionAlgorithm, key interface{}, recipient Recipient) ([]byte, error) {
if jwkKey, ok := key.(jwk.Key); ok {
var raw interface{}
if err := jwkKey.Raw(&raw); err != nil {
return nil, fmt.Errorf(`failed to retrieve raw key from %T: %w`, key, err)
}
key = raw
}
dec := newDecrypter(alg, dctx.msg.protectedHeaders.ContentEncryption(), key).
AuthenticatedData(dctx.aad).
ComputedAuthenticatedData(dctx.computedAad).
InitializationVector(dctx.msg.initializationVector).
Tag(dctx.msg.tag)
if recipient.Headers().Algorithm() != alg {
// algorithms don't match
return nil, fmt.Errorf(`jwe.Decrypt: key and recipient algorithms do not match`)
}
h2, err := dctx.protectedHeaders.Clone(ctx)
if err != nil {
return nil, fmt.Errorf(`jwe.Decrypt: failed to copy headers (1): %w`, err)
}
h2, err = h2.Merge(ctx, recipient.Headers())
if err != nil {
return nil, fmt.Errorf(`failed to copy headers (2): %w`, err)
}
switch alg {
case jwa.ECDH_ES, jwa.ECDH_ES_A128KW, jwa.ECDH_ES_A192KW, jwa.ECDH_ES_A256KW:
epkif, ok := h2.Get(EphemeralPublicKeyKey)
if !ok {
return nil, fmt.Errorf(`failed to get 'epk' field`)
}
switch epk := epkif.(type) {
case jwk.ECDSAPublicKey:
var pubkey ecdsa.PublicKey
if err := epk.Raw(&pubkey); err != nil {
return nil, fmt.Errorf(`failed to get public key: %w`, err)
}
dec.PublicKey(&pubkey)
case jwk.OKPPublicKey:
var pubkey interface{}
if err := epk.Raw(&pubkey); err != nil {
return nil, fmt.Errorf(`failed to get public key: %w`, err)
}
dec.PublicKey(pubkey)
default:
return nil, fmt.Errorf("unexpected 'epk' type %T for alg %s", epkif, alg)
}
if apu := h2.AgreementPartyUInfo(); len(apu) > 0 {
dec.AgreementPartyUInfo(apu)
}
if apv := h2.AgreementPartyVInfo(); len(apv) > 0 {
dec.AgreementPartyVInfo(apv)
}
case jwa.A128GCMKW, jwa.A192GCMKW, jwa.A256GCMKW:
ivB64, ok := h2.Get(InitializationVectorKey)
if !ok {
return nil, fmt.Errorf(`failed to get 'iv' field`)
}
ivB64Str, ok := ivB64.(string)
if !ok {
return nil, fmt.Errorf("unexpected type for 'iv': %T", ivB64)
}
tagB64, ok := h2.Get(TagKey)
if !ok {
return nil, fmt.Errorf(`failed to get 'tag' field`)
}
tagB64Str, ok := tagB64.(string)
if !ok {
return nil, fmt.Errorf("unexpected type for 'tag': %T", tagB64)
}
iv, err := base64.DecodeString(ivB64Str)
if err != nil {
return nil, fmt.Errorf(`failed to b64-decode 'iv': %w`, err)
}
tag, err := base64.DecodeString(tagB64Str)
if err != nil {
return nil, fmt.Errorf(`failed to b64-decode 'tag': %w`, err)
}
dec.KeyInitializationVector(iv)
dec.KeyTag(tag)
case jwa.PBES2_HS256_A128KW, jwa.PBES2_HS384_A192KW, jwa.PBES2_HS512_A256KW:
saltB64, ok := h2.Get(SaltKey)
if !ok {
return nil, fmt.Errorf(`failed to get 'p2s' field`)
}
saltB64Str, ok := saltB64.(string)
if !ok {
return nil, fmt.Errorf("unexpected type for 'p2s': %T", saltB64)
}
count, ok := h2.Get(CountKey)
if !ok {
return nil, fmt.Errorf(`failed to get 'p2c' field`)
}
countFlt, ok := count.(float64)
if !ok {
return nil, fmt.Errorf("unexpected type for 'p2c': %T", count)
}
salt, err := base64.DecodeString(saltB64Str)
if err != nil {
return nil, fmt.Errorf(`failed to b64-decode 'salt': %w`, err)
}
dec.KeySalt(salt)
dec.KeyCount(int(countFlt))
}
plaintext, err := dec.Decrypt(recipient.EncryptedKey(), dctx.msg.cipherText)
if err != nil {
return nil, fmt.Errorf(`jwe.Decrypt: decryption failed: %w`, err)
}
if h2.Compression() == jwa.Deflate {
buf, err := uncompress(plaintext)
if err != nil {
return nil, fmt.Errorf(`jwe.Derypt: failed to uncompress payload: %w`, err)
}
plaintext = buf
}
if plaintext == nil {
return nil, fmt.Errorf(`failed to find matching recipient`)
}
return plaintext, nil
}
// Parse parses the JWE message into a Message object. The JWE message
// can be either compact or full JSON format.
//
// Parse() currently does not take any options, but the API accepts it
// in anticipation of future addition.
func Parse(buf []byte, _ ...ParseOption) (*Message, error) {
return parseJSONOrCompact(buf, false)
}
func parseJSONOrCompact(buf []byte, storeProtectedHeaders bool) (*Message, error) {
buf = bytes.TrimSpace(buf)
if len(buf) == 0 {
return nil, fmt.Errorf(`empty buffer`)
}
if buf[0] == '{' {
return parseJSON(buf, storeProtectedHeaders)
}
return parseCompact(buf, storeProtectedHeaders)
}
// ParseString is the same as Parse, but takes a string.
func ParseString(s string) (*Message, error) {
return Parse([]byte(s))
}
// ParseReader is the same as Parse, but takes an io.Reader.
func ParseReader(src io.Reader) (*Message, error) {
buf, err := io.ReadAll(src)
if err != nil {
return nil, fmt.Errorf(`failed to read from io.Reader: %w`, err)
}
return Parse(buf)
}
func parseJSON(buf []byte, storeProtectedHeaders bool) (*Message, error) {
m := NewMessage()
m.storeProtectedHeaders = storeProtectedHeaders
if err := json.Unmarshal(buf, &m); err != nil {
return nil, fmt.Errorf(`failed to parse JSON: %w`, err)
}
return m, nil
}
func parseCompact(buf []byte, storeProtectedHeaders bool) (*Message, error) {
parts := bytes.Split(buf, []byte{'.'})
if len(parts) != 5 {
return nil, fmt.Errorf(`compact JWE format must have five parts (%d)`, len(parts))
}
hdrbuf, err := base64.Decode(parts[0])
if err != nil {
return nil, fmt.Errorf(`failed to parse first part of compact form: %w`, err)
}
protected := NewHeaders()
if err := json.Unmarshal(hdrbuf, protected); err != nil {
return nil, fmt.Errorf(`failed to parse header JSON: %w`, err)
}
ivbuf, err := base64.Decode(parts[2])
if err != nil {
return nil, fmt.Errorf(`failed to base64 decode iv: %w`, err)
}
ctbuf, err := base64.Decode(parts[3])
if err != nil {
return nil, fmt.Errorf(`failed to base64 decode content: %w`, err)
}
tagbuf, err := base64.Decode(parts[4])
if err != nil {
return nil, fmt.Errorf(`failed to base64 decode tag: %w`, err)
}
m := NewMessage()
if err := m.Set(CipherTextKey, ctbuf); err != nil {
return nil, fmt.Errorf(`failed to set %s: %w`, CipherTextKey, err)
}
if err := m.Set(InitializationVectorKey, ivbuf); err != nil {
return nil, fmt.Errorf(`failed to set %s: %w`, InitializationVectorKey, err)
}
if err := m.Set(ProtectedHeadersKey, protected); err != nil {
return nil, fmt.Errorf(`failed to set %s: %w`, ProtectedHeadersKey, err)
}
if err := m.makeDummyRecipient(string(parts[1]), protected); err != nil {
return nil, fmt.Errorf(`failed to setup recipient: %w`, err)
}
if err := m.Set(TagKey, tagbuf); err != nil {
return nil, fmt.Errorf(`failed to set %s: %w`, TagKey, err)
}
if storeProtectedHeaders {
// This is later used for decryption.
m.rawProtectedHeaders = parts[0]
}
return m, nil
}
// RegisterCustomField allows users to specify that a private field
// be decoded as an instance of the specified type. This option has
// a global effect.
//
// For example, suppose you have a custom field `x-birthday`, which
// you want to represent as a string formatted in RFC3339 in JSON,
// but want it back as `time.Time`.
//
// In that case you would register a custom field as follows
//
// jwe.RegisterCustomField(`x-birthday`, timeT)
//
// Then `hdr.Get("x-birthday")` will still return an `interface{}`,
// but you can convert its type to `time.Time`
//
// bdayif, _ := hdr.Get(`x-birthday`)
// bday := bdayif.(time.Time)
func RegisterCustomField(name string, object interface{}) {
registry.Register(name, object)
}