package jwe import ( "context" "crypto/ecdsa" "fmt" "github.com/lestrrat-go/jwx/internal/json" "github.com/lestrrat-go/jwx/internal/pool" "github.com/lestrrat-go/jwx/jwk" "github.com/lestrrat-go/jwx/internal/base64" "github.com/lestrrat-go/jwx/jwa" "github.com/pkg/errors" ) // NewRecipient creates a Recipient object func NewRecipient() Recipient { return &stdRecipient{ headers: NewHeaders(), } } func (r *stdRecipient) SetHeaders(h Headers) error { r.headers = h return nil } func (r *stdRecipient) SetEncryptedKey(v []byte) error { r.encryptedKey = v return nil } func (r *stdRecipient) Headers() Headers { return r.headers } func (r *stdRecipient) EncryptedKey() []byte { return r.encryptedKey } type recipientMarshalProxy struct { Headers Headers `json:"header"` EncryptedKey string `json:"encrypted_key"` } func (r *stdRecipient) UnmarshalJSON(buf []byte) error { var proxy recipientMarshalProxy proxy.Headers = NewHeaders() if err := json.Unmarshal(buf, &proxy); err != nil { return errors.Wrap(err, `failed to unmarshal json into recipient`) } r.headers = proxy.Headers decoded, err := base64.DecodeString(proxy.EncryptedKey) if err != nil { return errors.Wrap(err, `failed to decode "encrypted_key"`) } r.encryptedKey = decoded return nil } func (r *stdRecipient) MarshalJSON() ([]byte, error) { buf := pool.GetBytesBuffer() defer pool.ReleaseBytesBuffer(buf) buf.WriteString(`{"header":`) hdrbuf, err := r.headers.MarshalJSON() if err != nil { return nil, errors.Wrap(err, `failed to marshal recipient header`) } buf.Write(hdrbuf) buf.WriteString(`,"encrypted_key":"`) buf.WriteString(base64.EncodeToString(r.encryptedKey)) buf.WriteString(`"}`) ret := make([]byte, buf.Len()) copy(ret, buf.Bytes()) return ret, nil } // NewMessage creates a new message func NewMessage() *Message { return &Message{} } func (m *Message) AuthenticatedData() []byte { return m.authenticatedData } func (m *Message) CipherText() []byte { return m.cipherText } func (m *Message) InitializationVector() []byte { return m.initializationVector } func (m *Message) Tag() []byte { return m.tag } func (m *Message) ProtectedHeaders() Headers { return m.protectedHeaders } func (m *Message) Recipients() []Recipient { return m.recipients } func (m *Message) UnprotectedHeaders() Headers { return m.unprotectedHeaders } const ( AuthenticatedDataKey = "aad" CipherTextKey = "ciphertext" CountKey = "p2c" InitializationVectorKey = "iv" ProtectedHeadersKey = "protected" RecipientsKey = "recipients" SaltKey = "p2s" TagKey = "tag" UnprotectedHeadersKey = "unprotected" HeadersKey = "header" EncryptedKeyKey = "encrypted_key" ) func (m *Message) Set(k string, v interface{}) error { switch k { case AuthenticatedDataKey: buf, ok := v.([]byte) if !ok { return errors.Errorf(`invalid value %T for %s key`, v, AuthenticatedDataKey) } m.authenticatedData = buf case CipherTextKey: buf, ok := v.([]byte) if !ok { return errors.Errorf(`invalid value %T for %s key`, v, CipherTextKey) } m.cipherText = buf case InitializationVectorKey: buf, ok := v.([]byte) if !ok { return errors.Errorf(`invalid value %T for %s key`, v, InitializationVectorKey) } m.initializationVector = buf case ProtectedHeadersKey: cv, ok := v.(Headers) if !ok { return errors.Errorf(`invalid value %T for %s key`, v, ProtectedHeadersKey) } m.protectedHeaders = cv case RecipientsKey: cv, ok := v.([]Recipient) if !ok { return errors.Errorf(`invalid value %T for %s key`, v, RecipientsKey) } m.recipients = cv case TagKey: buf, ok := v.([]byte) if !ok { return errors.Errorf(`invalid value %T for %s key`, v, TagKey) } m.tag = buf case UnprotectedHeadersKey: cv, ok := v.(Headers) if !ok { return errors.Errorf(`invalid value %T for %s key`, v, UnprotectedHeadersKey) } m.unprotectedHeaders = cv default: if m.unprotectedHeaders == nil { m.unprotectedHeaders = NewHeaders() } return m.unprotectedHeaders.Set(k, v) } return nil } type messageMarshalProxy struct { AuthenticatedData string `json:"aad,omitempty"` CipherText string `json:"ciphertext"` InitializationVector string `json:"iv,omitempty"` ProtectedHeaders json.RawMessage `json:"protected"` Recipients []json.RawMessage `json:"recipients,omitempty"` Tag string `json:"tag,omitempty"` UnprotectedHeaders Headers `json:"unprotected,omitempty"` // For flattened structure. Headers is NOT a Headers type, // so that we can detect its presence by checking proxy.Headers != nil Headers json.RawMessage `json:"header,omitempty"` EncryptedKey string `json:"encrypted_key,omitempty"` } func (m *Message) MarshalJSON() ([]byte, error) { // This is slightly convoluted, but we need to encode the // protected headers, so we do it by hand buf := pool.GetBytesBuffer() defer pool.ReleaseBytesBuffer(buf) enc := json.NewEncoder(buf) fmt.Fprintf(buf, `{`) var wrote bool if aad := m.AuthenticatedData(); len(aad) > 0 { wrote = true fmt.Fprintf(buf, `%#v:`, AuthenticatedDataKey) if err := enc.Encode(base64.EncodeToString(aad)); err != nil { return nil, errors.Wrapf(err, `failed to encode %s field`, AuthenticatedDataKey) } } if cipherText := m.CipherText(); len(cipherText) > 0 { if wrote { fmt.Fprintf(buf, `,`) } wrote = true fmt.Fprintf(buf, `%#v:`, CipherTextKey) if err := enc.Encode(base64.EncodeToString(cipherText)); err != nil { return nil, errors.Wrapf(err, `failed to encode %s field`, CipherTextKey) } } if iv := m.InitializationVector(); len(iv) > 0 { if wrote { fmt.Fprintf(buf, `,`) } wrote = true fmt.Fprintf(buf, `%#v:`, InitializationVectorKey) if err := enc.Encode(base64.EncodeToString(iv)); err != nil { return nil, errors.Wrapf(err, `failed to encode %s field`, InitializationVectorKey) } } if h := m.ProtectedHeaders(); h != nil { encodedHeaders, err := h.Encode() if err != nil { return nil, errors.Wrap(err, `failed to encode protected headers`) } if len(encodedHeaders) > 2 { if wrote { fmt.Fprintf(buf, `,`) } wrote = true fmt.Fprintf(buf, `%#v:%#v`, ProtectedHeadersKey, string(encodedHeaders)) } } if recipients := m.Recipients(); len(recipients) > 0 { if wrote { fmt.Fprintf(buf, `,`) } if len(recipients) == 1 { // Use flattened format fmt.Fprintf(buf, `%#v:`, HeadersKey) if err := enc.Encode(recipients[0].Headers()); err != nil { return nil, errors.Wrapf(err, `failed to encode %s field`, HeadersKey) } if ek := recipients[0].EncryptedKey(); len(ek) > 0 { fmt.Fprintf(buf, `,%#v:`, EncryptedKeyKey) if err := enc.Encode(base64.EncodeToString(ek)); err != nil { return nil, errors.Wrapf(err, `failed to encode %s field`, EncryptedKeyKey) } } } else { fmt.Fprintf(buf, `%#v:`, RecipientsKey) if err := enc.Encode(recipients); err != nil { return nil, errors.Wrapf(err, `failed to encode %s field`, RecipientsKey) } } } if tag := m.Tag(); len(tag) > 0 { if wrote { fmt.Fprintf(buf, `,`) } fmt.Fprintf(buf, `%#v:`, TagKey) if err := enc.Encode(base64.EncodeToString(tag)); err != nil { return nil, errors.Wrapf(err, `failed to encode %s field`, TagKey) } } if h := m.UnprotectedHeaders(); h != nil { unprotected, err := json.Marshal(h) if err != nil { return nil, errors.Wrap(err, `failed to encode unprotected headers`) } if len(unprotected) > 2 { fmt.Fprintf(buf, `,%#v:%#v`, UnprotectedHeadersKey, string(unprotected)) } } fmt.Fprintf(buf, `}`) ret := make([]byte, buf.Len()) copy(ret, buf.Bytes()) return ret, nil } func (m *Message) UnmarshalJSON(buf []byte) error { var proxy messageMarshalProxy proxy.UnprotectedHeaders = NewHeaders() if err := json.Unmarshal(buf, &proxy); err != nil { return errors.Wrap(err, `failed to unmashal JSON into message`) } // Get the string value var protectedHeadersStr string if err := json.Unmarshal(proxy.ProtectedHeaders, &protectedHeadersStr); err != nil { return errors.Wrap(err, `failed to decode protected headers (1)`) } // It's now in _quoted_ base64 string. Decode it protectedHeadersRaw, err := base64.DecodeString(protectedHeadersStr) if err != nil { return errors.Wrap(err, "failed to base64 decoded protected headers buffer") } h := NewHeaders() if err := json.Unmarshal(protectedHeadersRaw, h); err != nil { return errors.Wrap(err, `failed to decode protected headers (2)`) } // if this were a flattened message, we would see a "header" and "ciphertext" // field. TODO: do both of these conditions need to meet, or just one? if proxy.Headers != nil || len(proxy.EncryptedKey) > 0 { recipient := NewRecipient() hdrs := NewHeaders() if err := json.Unmarshal(proxy.Headers, hdrs); err != nil { return errors.Wrap(err, `failed to decode headers field`) } if err := recipient.SetHeaders(hdrs); err != nil { return errors.Wrap(err, `failed to set new headers`) } if v := proxy.EncryptedKey; len(v) > 0 { buf, err := base64.DecodeString(v) if err != nil { return errors.Wrap(err, `failed to decode encrypted key`) } if err := recipient.SetEncryptedKey(buf); err != nil { return errors.Wrap(err, `failed to set encrypted key`) } } m.recipients = append(m.recipients, recipient) } else { for i, recipientbuf := range proxy.Recipients { recipient := NewRecipient() if err := json.Unmarshal(recipientbuf, recipient); err != nil { return errors.Wrapf(err, `failed to decode recipient at index %d`, i) } m.recipients = append(m.recipients, recipient) } } if src := proxy.AuthenticatedData; len(src) > 0 { v, err := base64.DecodeString(src) if err != nil { return errors.Wrap(err, `failed to decode "aad"`) } m.authenticatedData = v } if src := proxy.CipherText; len(src) > 0 { v, err := base64.DecodeString(src) if err != nil { return errors.Wrap(err, `failed to decode "ciphertext"`) } m.cipherText = v } if src := proxy.InitializationVector; len(src) > 0 { v, err := base64.DecodeString(src) if err != nil { return errors.Wrap(err, `failed to decode "iv"`) } m.initializationVector = v } if src := proxy.Tag; len(src) > 0 { v, err := base64.DecodeString(src) if err != nil { return errors.Wrap(err, `failed to decode "tag"`) } m.tag = v } m.protectedHeaders = h if m.storeProtectedHeaders { // this is later used for decryption m.rawProtectedHeaders = base64.Encode(protectedHeadersRaw) } if !proxy.UnprotectedHeaders.(isZeroer).isZero() { m.unprotectedHeaders = proxy.UnprotectedHeaders } if len(m.recipients) == 0 { if err := m.makeDummyRecipient(proxy.EncryptedKey, m.protectedHeaders); err != nil { return errors.Wrap(err, `failed to setup recipient`) } } return nil } func (m *Message) makeDummyRecipient(enckeybuf string, protected Headers) error { // Recipients in this case should not contain the content encryption key, // so move that out hdrs, err := protected.Clone(context.TODO()) if err != nil { return errors.Wrap(err, `failed to clone headers`) } if err := hdrs.Remove(ContentEncryptionKey); err != nil { return errors.Wrapf(err, "failed to remove %#v from public header", ContentEncryptionKey) } enckey, err := base64.DecodeString(enckeybuf) if err != nil { return errors.Wrap(err, `failed to decode encrypted key`) } if err := m.Set(RecipientsKey, []Recipient{ &stdRecipient{ headers: hdrs, encryptedKey: enckey, }, }); err != nil { return errors.Wrapf(err, `failed to set %s`, RecipientsKey) } return nil } // Decrypt decrypts the message using the specified algorithm and key. // // `key` must be a private key in its "raw" format (i.e. something like // *rsa.PrivateKey, instead of jwk.Key) // // This method is marked for deprecation. It will be removed from the API // in the next major release. You should not rely on this method // to work 100% of the time, especially when it was obtained via jwe.Parse // instead of being constructed from scratch by this library. func (m *Message) Decrypt(alg jwa.KeyEncryptionAlgorithm, key interface{}) ([]byte, error) { var ctx decryptCtx ctx.alg = alg ctx.key = key ctx.msg = m return doDecryptCtx(&ctx) } func doDecryptCtx(dctx *decryptCtx) ([]byte, error) { m := dctx.msg alg := dctx.alg key := dctx.key if jwkKey, ok := key.(jwk.Key); ok { var raw interface{} if err := jwkKey.Raw(&raw); err != nil { return nil, errors.Wrapf(err, `failed to retrieve raw key from %T`, key) } key = raw } var err error ctx := context.TODO() h, err := m.protectedHeaders.Clone(ctx) if err != nil { return nil, errors.Wrap(err, `failed to copy protected headers`) } h, err = h.Merge(ctx, m.unprotectedHeaders) if err != nil { return nil, errors.Wrap(err, "failed to merge headers for message decryption") } enc := m.protectedHeaders.ContentEncryption() var aad []byte if aadContainer := m.authenticatedData; aadContainer != nil { aad = base64.Encode(aadContainer) } var computedAad []byte if len(m.rawProtectedHeaders) > 0 { computedAad = m.rawProtectedHeaders } else { // this is probably not required once msg.Decrypt is deprecated var err error computedAad, err = m.protectedHeaders.Encode() if err != nil { return nil, errors.Wrap(err, "failed to encode protected headers") } } dec := NewDecrypter(alg, enc, key). AuthenticatedData(aad). ComputedAuthenticatedData(computedAad). InitializationVector(m.initializationVector). Tag(m.tag) var plaintext []byte var lastError error // if we have no recipients, pretend like we only have one recipients := m.recipients if len(recipients) == 0 { r := NewRecipient() if err := r.SetHeaders(m.protectedHeaders); err != nil { return nil, errors.Wrap(err, `failed to set headers to recipient`) } recipients = append(recipients, r) } for _, recipient := range recipients { // strategy: try each recipient. If we fail in one of the steps, // keep looping because there might be another key with the same algo if recipient.Headers().Algorithm() != alg { // algorithms don't match continue } h2, err := h.Clone(ctx) if err != nil { lastError = errors.Wrap(err, `failed to copy headers (1)`) continue } h2, err = h2.Merge(ctx, recipient.Headers()) if err != nil { lastError = errors.Wrap(err, `failed to copy headers (2)`) continue } 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, errors.New("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, errors.Wrap(err, "failed to get public key") } dec.PublicKey(&pubkey) case jwk.OKPPublicKey: var pubkey interface{} if err := epk.Raw(&pubkey); err != nil { return nil, errors.Wrap(err, "failed to get public key") } dec.PublicKey(pubkey) default: return nil, errors.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, errors.New("failed to get 'iv' field") } ivB64Str, ok := ivB64.(string) if !ok { return nil, errors.Errorf("unexpected type for 'iv': %T", ivB64) } tagB64, ok := h2.Get(TagKey) if !ok { return nil, errors.New("failed to get 'tag' field") } tagB64Str, ok := tagB64.(string) if !ok { return nil, errors.Errorf("unexpected type for 'tag': %T", tagB64) } iv, err := base64.DecodeString(ivB64Str) if err != nil { return nil, errors.Wrap(err, "failed to b64-decode 'iv'") } tag, err := base64.DecodeString(tagB64Str) if err != nil { return nil, errors.Wrap(err, "failed to b64-decode 'tag'") } 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, errors.New("failed to get 'p2s' field") } saltB64Str, ok := saltB64.(string) if !ok { return nil, errors.Errorf("unexpected type for 'p2s': %T", saltB64) } count, ok := h2.Get(CountKey) if !ok { return nil, errors.New("failed to get 'p2c' field") } countFlt, ok := count.(float64) if !ok { return nil, errors.Errorf("unexpected type for 'p2c': %T", count) } salt, err := base64.DecodeString(saltB64Str) if err != nil { return nil, errors.Wrap(err, "failed to b64-decode 'salt'") } dec.KeySalt(salt) dec.KeyCount(int(countFlt)) } plaintext, err = dec.Decrypt(recipient.EncryptedKey(), m.cipherText) if err != nil { lastError = errors.Wrap(err, `failed to decrypt`) continue } if h2.Compression() == jwa.Deflate { buf, err := uncompress(plaintext) if err != nil { lastError = errors.Wrap(err, `failed to uncompress payload`) continue } plaintext = buf } break } if plaintext == nil { if lastError != nil { return nil, errors.Errorf(`failed to find matching recipient to decrypt key (last error = %s)`, lastError) } return nil, errors.New("failed to find matching recipient") } return plaintext, nil }