2021-08-12 19:03:24 +00:00
|
|
|
/*-
|
|
|
|
* Copyright 2014 Square Inc.
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package jose
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/ecdsa"
|
|
|
|
"crypto/rsa"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
|
2024-03-25 10:58:34 +00:00
|
|
|
"github.com/go-jose/go-jose/v4/json"
|
2021-08-12 19:03:24 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Encrypter represents an encrypter which produces an encrypted JWE object.
|
|
|
|
type Encrypter interface {
|
|
|
|
Encrypt(plaintext []byte) (*JSONWebEncryption, error)
|
|
|
|
EncryptWithAuthData(plaintext []byte, aad []byte) (*JSONWebEncryption, error)
|
|
|
|
Options() EncrypterOptions
|
|
|
|
}
|
|
|
|
|
|
|
|
// A generic content cipher
|
|
|
|
type contentCipher interface {
|
|
|
|
keySize() int
|
|
|
|
encrypt(cek []byte, aad, plaintext []byte) (*aeadParts, error)
|
|
|
|
decrypt(cek []byte, aad []byte, parts *aeadParts) ([]byte, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
// A key generator (for generating/getting a CEK)
|
|
|
|
type keyGenerator interface {
|
|
|
|
keySize() int
|
|
|
|
genKey() ([]byte, rawHeader, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
// A generic key encrypter
|
|
|
|
type keyEncrypter interface {
|
|
|
|
encryptKey(cek []byte, alg KeyAlgorithm) (recipientInfo, error) // Encrypt a key
|
|
|
|
}
|
|
|
|
|
|
|
|
// A generic key decrypter
|
|
|
|
type keyDecrypter interface {
|
|
|
|
decryptKey(headers rawHeader, recipient *recipientInfo, generator keyGenerator) ([]byte, error) // Decrypt a key
|
|
|
|
}
|
|
|
|
|
|
|
|
// A generic encrypter based on the given key encrypter and content cipher.
|
|
|
|
type genericEncrypter struct {
|
|
|
|
contentAlg ContentEncryption
|
|
|
|
compressionAlg CompressionAlgorithm
|
|
|
|
cipher contentCipher
|
|
|
|
recipients []recipientKeyInfo
|
|
|
|
keyGenerator keyGenerator
|
|
|
|
extraHeaders map[HeaderKey]interface{}
|
|
|
|
}
|
|
|
|
|
|
|
|
type recipientKeyInfo struct {
|
|
|
|
keyID string
|
|
|
|
keyAlg KeyAlgorithm
|
|
|
|
keyEncrypter keyEncrypter
|
|
|
|
}
|
|
|
|
|
|
|
|
// EncrypterOptions represents options that can be set on new encrypters.
|
|
|
|
type EncrypterOptions struct {
|
|
|
|
Compression CompressionAlgorithm
|
|
|
|
|
2024-03-11 10:36:13 +00:00
|
|
|
// Optional map of name/value pairs to be inserted into the protected
|
|
|
|
// header of a JWS object. Some specifications which make use of
|
|
|
|
// JWS require additional values here.
|
|
|
|
//
|
|
|
|
// Values will be serialized by [json.Marshal] and must be valid inputs to
|
|
|
|
// that function.
|
|
|
|
//
|
|
|
|
// [json.Marshal]: https://pkg.go.dev/encoding/json#Marshal
|
2021-08-12 19:03:24 +00:00
|
|
|
ExtraHeaders map[HeaderKey]interface{}
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithHeader adds an arbitrary value to the ExtraHeaders map, initializing it
|
2024-03-11 10:36:13 +00:00
|
|
|
// if necessary, and returns the updated EncrypterOptions.
|
|
|
|
//
|
|
|
|
// The v parameter will be serialized by [json.Marshal] and must be a valid
|
|
|
|
// input to that function.
|
|
|
|
//
|
|
|
|
// [json.Marshal]: https://pkg.go.dev/encoding/json#Marshal
|
2021-08-12 19:03:24 +00:00
|
|
|
func (eo *EncrypterOptions) WithHeader(k HeaderKey, v interface{}) *EncrypterOptions {
|
|
|
|
if eo.ExtraHeaders == nil {
|
|
|
|
eo.ExtraHeaders = map[HeaderKey]interface{}{}
|
|
|
|
}
|
|
|
|
eo.ExtraHeaders[k] = v
|
|
|
|
return eo
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithContentType adds a content type ("cty") header and returns the updated
|
|
|
|
// EncrypterOptions.
|
|
|
|
func (eo *EncrypterOptions) WithContentType(contentType ContentType) *EncrypterOptions {
|
|
|
|
return eo.WithHeader(HeaderContentType, contentType)
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithType adds a type ("typ") header and returns the updated EncrypterOptions.
|
|
|
|
func (eo *EncrypterOptions) WithType(typ ContentType) *EncrypterOptions {
|
|
|
|
return eo.WithHeader(HeaderType, typ)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Recipient represents an algorithm/key to encrypt messages to.
|
|
|
|
//
|
|
|
|
// PBES2Count and PBES2Salt correspond with the "p2c" and "p2s" headers used
|
|
|
|
// on the password-based encryption algorithms PBES2-HS256+A128KW,
|
|
|
|
// PBES2-HS384+A192KW, and PBES2-HS512+A256KW. If they are not provided a safe
|
|
|
|
// default of 100000 will be used for the count and a 128-bit random salt will
|
|
|
|
// be generated.
|
|
|
|
type Recipient struct {
|
2024-03-11 10:36:13 +00:00
|
|
|
Algorithm KeyAlgorithm
|
|
|
|
// Key must have one of these types:
|
|
|
|
// - ed25519.PublicKey
|
|
|
|
// - *ecdsa.PublicKey
|
|
|
|
// - *rsa.PublicKey
|
|
|
|
// - *JSONWebKey
|
|
|
|
// - JSONWebKey
|
|
|
|
// - []byte (a symmetric key)
|
|
|
|
// - Any type that satisfies the OpaqueKeyEncrypter interface
|
|
|
|
//
|
|
|
|
// The type of Key must match the value of Algorithm.
|
2021-08-12 19:03:24 +00:00
|
|
|
Key interface{}
|
|
|
|
KeyID string
|
|
|
|
PBES2Count int
|
|
|
|
PBES2Salt []byte
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewEncrypter creates an appropriate encrypter based on the key type
|
|
|
|
func NewEncrypter(enc ContentEncryption, rcpt Recipient, opts *EncrypterOptions) (Encrypter, error) {
|
|
|
|
encrypter := &genericEncrypter{
|
|
|
|
contentAlg: enc,
|
|
|
|
recipients: []recipientKeyInfo{},
|
|
|
|
cipher: getContentCipher(enc),
|
|
|
|
}
|
|
|
|
if opts != nil {
|
|
|
|
encrypter.compressionAlg = opts.Compression
|
|
|
|
encrypter.extraHeaders = opts.ExtraHeaders
|
|
|
|
}
|
|
|
|
|
|
|
|
if encrypter.cipher == nil {
|
|
|
|
return nil, ErrUnsupportedAlgorithm
|
|
|
|
}
|
|
|
|
|
|
|
|
var keyID string
|
|
|
|
var rawKey interface{}
|
|
|
|
switch encryptionKey := rcpt.Key.(type) {
|
|
|
|
case JSONWebKey:
|
|
|
|
keyID, rawKey = encryptionKey.KeyID, encryptionKey.Key
|
|
|
|
case *JSONWebKey:
|
|
|
|
keyID, rawKey = encryptionKey.KeyID, encryptionKey.Key
|
|
|
|
case OpaqueKeyEncrypter:
|
|
|
|
keyID, rawKey = encryptionKey.KeyID(), encryptionKey
|
|
|
|
default:
|
|
|
|
rawKey = encryptionKey
|
|
|
|
}
|
|
|
|
|
|
|
|
switch rcpt.Algorithm {
|
|
|
|
case DIRECT:
|
|
|
|
// Direct encryption mode must be treated differently
|
2024-03-11 10:36:13 +00:00
|
|
|
keyBytes, ok := rawKey.([]byte)
|
|
|
|
if !ok {
|
2021-08-12 19:03:24 +00:00
|
|
|
return nil, ErrUnsupportedKeyType
|
|
|
|
}
|
2024-03-11 10:36:13 +00:00
|
|
|
if encrypter.cipher.keySize() != len(keyBytes) {
|
2021-08-12 19:03:24 +00:00
|
|
|
return nil, ErrInvalidKeySize
|
|
|
|
}
|
|
|
|
encrypter.keyGenerator = staticKeyGenerator{
|
2024-03-11 10:36:13 +00:00
|
|
|
key: keyBytes,
|
2021-08-12 19:03:24 +00:00
|
|
|
}
|
2024-03-11 10:36:13 +00:00
|
|
|
recipientInfo, _ := newSymmetricRecipient(rcpt.Algorithm, keyBytes)
|
2021-08-12 19:03:24 +00:00
|
|
|
recipientInfo.keyID = keyID
|
|
|
|
if rcpt.KeyID != "" {
|
|
|
|
recipientInfo.keyID = rcpt.KeyID
|
|
|
|
}
|
|
|
|
encrypter.recipients = []recipientKeyInfo{recipientInfo}
|
|
|
|
return encrypter, nil
|
|
|
|
case ECDH_ES:
|
|
|
|
// ECDH-ES (w/o key wrapping) is similar to DIRECT mode
|
2024-03-11 10:36:13 +00:00
|
|
|
keyDSA, ok := rawKey.(*ecdsa.PublicKey)
|
|
|
|
if !ok {
|
2021-08-12 19:03:24 +00:00
|
|
|
return nil, ErrUnsupportedKeyType
|
|
|
|
}
|
|
|
|
encrypter.keyGenerator = ecKeyGenerator{
|
|
|
|
size: encrypter.cipher.keySize(),
|
|
|
|
algID: string(enc),
|
2024-03-11 10:36:13 +00:00
|
|
|
publicKey: keyDSA,
|
2021-08-12 19:03:24 +00:00
|
|
|
}
|
2024-03-11 10:36:13 +00:00
|
|
|
recipientInfo, _ := newECDHRecipient(rcpt.Algorithm, keyDSA)
|
2021-08-12 19:03:24 +00:00
|
|
|
recipientInfo.keyID = keyID
|
|
|
|
if rcpt.KeyID != "" {
|
|
|
|
recipientInfo.keyID = rcpt.KeyID
|
|
|
|
}
|
|
|
|
encrypter.recipients = []recipientKeyInfo{recipientInfo}
|
|
|
|
return encrypter, nil
|
|
|
|
default:
|
|
|
|
// Can just add a standard recipient
|
|
|
|
encrypter.keyGenerator = randomKeyGenerator{
|
|
|
|
size: encrypter.cipher.keySize(),
|
|
|
|
}
|
|
|
|
err := encrypter.addRecipient(rcpt)
|
|
|
|
return encrypter, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewMultiEncrypter creates a multi-encrypter based on the given parameters
|
|
|
|
func NewMultiEncrypter(enc ContentEncryption, rcpts []Recipient, opts *EncrypterOptions) (Encrypter, error) {
|
|
|
|
cipher := getContentCipher(enc)
|
|
|
|
|
|
|
|
if cipher == nil {
|
|
|
|
return nil, ErrUnsupportedAlgorithm
|
|
|
|
}
|
2023-01-09 08:23:49 +00:00
|
|
|
if len(rcpts) == 0 {
|
|
|
|
return nil, fmt.Errorf("go-jose/go-jose: recipients is nil or empty")
|
2021-08-12 19:03:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
encrypter := &genericEncrypter{
|
|
|
|
contentAlg: enc,
|
|
|
|
recipients: []recipientKeyInfo{},
|
|
|
|
cipher: cipher,
|
|
|
|
keyGenerator: randomKeyGenerator{
|
|
|
|
size: cipher.keySize(),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
if opts != nil {
|
|
|
|
encrypter.compressionAlg = opts.Compression
|
2021-09-10 12:42:14 +00:00
|
|
|
encrypter.extraHeaders = opts.ExtraHeaders
|
2021-08-12 19:03:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, recipient := range rcpts {
|
|
|
|
err := encrypter.addRecipient(recipient)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return encrypter, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *genericEncrypter) addRecipient(recipient Recipient) (err error) {
|
|
|
|
var recipientInfo recipientKeyInfo
|
|
|
|
|
|
|
|
switch recipient.Algorithm {
|
|
|
|
case DIRECT, ECDH_ES:
|
2023-01-09 08:23:49 +00:00
|
|
|
return fmt.Errorf("go-jose/go-jose: key algorithm '%s' not supported in multi-recipient mode", recipient.Algorithm)
|
2021-08-12 19:03:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
recipientInfo, err = makeJWERecipient(recipient.Algorithm, recipient.Key)
|
|
|
|
if recipient.KeyID != "" {
|
|
|
|
recipientInfo.keyID = recipient.KeyID
|
|
|
|
}
|
|
|
|
|
|
|
|
switch recipient.Algorithm {
|
|
|
|
case PBES2_HS256_A128KW, PBES2_HS384_A192KW, PBES2_HS512_A256KW:
|
|
|
|
if sr, ok := recipientInfo.keyEncrypter.(*symmetricKeyCipher); ok {
|
|
|
|
sr.p2c = recipient.PBES2Count
|
|
|
|
sr.p2s = recipient.PBES2Salt
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if err == nil {
|
|
|
|
ctx.recipients = append(ctx.recipients, recipientInfo)
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func makeJWERecipient(alg KeyAlgorithm, encryptionKey interface{}) (recipientKeyInfo, error) {
|
|
|
|
switch encryptionKey := encryptionKey.(type) {
|
|
|
|
case *rsa.PublicKey:
|
|
|
|
return newRSARecipient(alg, encryptionKey)
|
|
|
|
case *ecdsa.PublicKey:
|
|
|
|
return newECDHRecipient(alg, encryptionKey)
|
|
|
|
case []byte:
|
|
|
|
return newSymmetricRecipient(alg, encryptionKey)
|
|
|
|
case string:
|
|
|
|
return newSymmetricRecipient(alg, []byte(encryptionKey))
|
|
|
|
case *JSONWebKey:
|
|
|
|
recipient, err := makeJWERecipient(alg, encryptionKey.Key)
|
|
|
|
recipient.keyID = encryptionKey.KeyID
|
|
|
|
return recipient, err
|
2024-03-11 10:36:13 +00:00
|
|
|
case OpaqueKeyEncrypter:
|
|
|
|
return newOpaqueKeyEncrypter(alg, encryptionKey)
|
2021-08-12 19:03:24 +00:00
|
|
|
}
|
|
|
|
return recipientKeyInfo{}, ErrUnsupportedKeyType
|
|
|
|
}
|
|
|
|
|
|
|
|
// newDecrypter creates an appropriate decrypter based on the key type
|
|
|
|
func newDecrypter(decryptionKey interface{}) (keyDecrypter, error) {
|
|
|
|
switch decryptionKey := decryptionKey.(type) {
|
|
|
|
case *rsa.PrivateKey:
|
|
|
|
return &rsaDecrypterSigner{
|
|
|
|
privateKey: decryptionKey,
|
|
|
|
}, nil
|
|
|
|
case *ecdsa.PrivateKey:
|
|
|
|
return &ecDecrypterSigner{
|
|
|
|
privateKey: decryptionKey,
|
|
|
|
}, nil
|
|
|
|
case []byte:
|
|
|
|
return &symmetricKeyCipher{
|
|
|
|
key: decryptionKey,
|
|
|
|
}, nil
|
|
|
|
case string:
|
|
|
|
return &symmetricKeyCipher{
|
|
|
|
key: []byte(decryptionKey),
|
|
|
|
}, nil
|
|
|
|
case JSONWebKey:
|
|
|
|
return newDecrypter(decryptionKey.Key)
|
|
|
|
case *JSONWebKey:
|
|
|
|
return newDecrypter(decryptionKey.Key)
|
2024-03-11 10:36:13 +00:00
|
|
|
case OpaqueKeyDecrypter:
|
|
|
|
return &opaqueKeyDecrypter{decrypter: decryptionKey}, nil
|
|
|
|
default:
|
|
|
|
return nil, ErrUnsupportedKeyType
|
2021-08-12 19:03:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Implementation of encrypt method producing a JWE object.
|
|
|
|
func (ctx *genericEncrypter) Encrypt(plaintext []byte) (*JSONWebEncryption, error) {
|
|
|
|
return ctx.EncryptWithAuthData(plaintext, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Implementation of encrypt method producing a JWE object.
|
|
|
|
func (ctx *genericEncrypter) EncryptWithAuthData(plaintext, aad []byte) (*JSONWebEncryption, error) {
|
|
|
|
obj := &JSONWebEncryption{}
|
|
|
|
obj.aad = aad
|
|
|
|
|
|
|
|
obj.protected = &rawHeader{}
|
|
|
|
err := obj.protected.set(headerEncryption, ctx.contentAlg)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
obj.recipients = make([]recipientInfo, len(ctx.recipients))
|
|
|
|
|
|
|
|
if len(ctx.recipients) == 0 {
|
2023-01-09 08:23:49 +00:00
|
|
|
return nil, fmt.Errorf("go-jose/go-jose: no recipients to encrypt to")
|
2021-08-12 19:03:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
cek, headers, err := ctx.keyGenerator.genKey()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
obj.protected.merge(&headers)
|
|
|
|
|
|
|
|
for i, info := range ctx.recipients {
|
|
|
|
recipient, err := info.keyEncrypter.encryptKey(cek, info.keyAlg)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = recipient.header.set(headerAlgorithm, info.keyAlg)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if info.keyID != "" {
|
|
|
|
err = recipient.header.set(headerKeyID, info.keyID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
obj.recipients[i] = recipient
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(ctx.recipients) == 1 {
|
|
|
|
// Move per-recipient headers into main protected header if there's
|
|
|
|
// only a single recipient.
|
|
|
|
obj.protected.merge(obj.recipients[0].header)
|
|
|
|
obj.recipients[0].header = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if ctx.compressionAlg != NONE {
|
|
|
|
plaintext, err = compress(ctx.compressionAlg, plaintext)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = obj.protected.set(headerCompression, ctx.compressionAlg)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for k, v := range ctx.extraHeaders {
|
|
|
|
b, err := json.Marshal(v)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
(*obj.protected)[k] = makeRawMessage(b)
|
|
|
|
}
|
|
|
|
|
|
|
|
authData := obj.computeAuthData()
|
|
|
|
parts, err := ctx.cipher.encrypt(cek, authData, plaintext)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
obj.iv = parts.iv
|
|
|
|
obj.ciphertext = parts.ciphertext
|
|
|
|
obj.tag = parts.tag
|
|
|
|
|
|
|
|
return obj, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *genericEncrypter) Options() EncrypterOptions {
|
|
|
|
return EncrypterOptions{
|
|
|
|
Compression: ctx.compressionAlg,
|
|
|
|
ExtraHeaders: ctx.extraHeaders,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-11 10:36:13 +00:00
|
|
|
// Decrypt and validate the object and return the plaintext. This
|
|
|
|
// function does not support multi-recipient. If you desire multi-recipient
|
2021-08-12 19:03:24 +00:00
|
|
|
// decryption use DecryptMulti instead.
|
2024-03-11 10:36:13 +00:00
|
|
|
//
|
|
|
|
// The decryptionKey argument must contain a private or symmetric key
|
|
|
|
// and must have one of these types:
|
|
|
|
// - *ecdsa.PrivateKey
|
|
|
|
// - *rsa.PrivateKey
|
|
|
|
// - *JSONWebKey
|
|
|
|
// - JSONWebKey
|
|
|
|
// - *JSONWebKeySet
|
|
|
|
// - JSONWebKeySet
|
|
|
|
// - []byte (a symmetric key)
|
|
|
|
// - string (a symmetric key)
|
|
|
|
// - Any type that satisfies the OpaqueKeyDecrypter interface.
|
|
|
|
//
|
|
|
|
// Note that ed25519 is only available for signatures, not encryption, so is
|
|
|
|
// not an option here.
|
|
|
|
//
|
|
|
|
// Automatically decompresses plaintext, but returns an error if the decompressed
|
|
|
|
// data would be >250kB or >10x the size of the compressed data, whichever is larger.
|
2021-08-12 19:03:24 +00:00
|
|
|
func (obj JSONWebEncryption) Decrypt(decryptionKey interface{}) ([]byte, error) {
|
|
|
|
headers := obj.mergedHeaders(nil)
|
|
|
|
|
|
|
|
if len(obj.recipients) > 1 {
|
2023-01-09 08:23:49 +00:00
|
|
|
return nil, errors.New("go-jose/go-jose: too many recipients in payload; expecting only one")
|
2021-08-12 19:03:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
critical, err := headers.getCritical()
|
|
|
|
if err != nil {
|
2023-01-09 08:23:49 +00:00
|
|
|
return nil, fmt.Errorf("go-jose/go-jose: invalid crit header")
|
2021-08-12 19:03:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(critical) > 0 {
|
2023-01-09 08:23:49 +00:00
|
|
|
return nil, fmt.Errorf("go-jose/go-jose: unsupported crit header")
|
2021-08-12 19:03:24 +00:00
|
|
|
}
|
|
|
|
|
2023-01-09 08:23:49 +00:00
|
|
|
key := tryJWKS(decryptionKey, obj.Header)
|
|
|
|
decrypter, err := newDecrypter(key)
|
2021-08-12 19:03:24 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
cipher := getContentCipher(headers.getEncryption())
|
|
|
|
if cipher == nil {
|
2023-01-09 08:23:49 +00:00
|
|
|
return nil, fmt.Errorf("go-jose/go-jose: unsupported enc value '%s'", string(headers.getEncryption()))
|
2021-08-12 19:03:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
generator := randomKeyGenerator{
|
|
|
|
size: cipher.keySize(),
|
|
|
|
}
|
|
|
|
|
|
|
|
parts := &aeadParts{
|
|
|
|
iv: obj.iv,
|
|
|
|
ciphertext: obj.ciphertext,
|
|
|
|
tag: obj.tag,
|
|
|
|
}
|
|
|
|
|
|
|
|
authData := obj.computeAuthData()
|
|
|
|
|
|
|
|
var plaintext []byte
|
|
|
|
recipient := obj.recipients[0]
|
|
|
|
recipientHeaders := obj.mergedHeaders(&recipient)
|
|
|
|
|
|
|
|
cek, err := decrypter.decryptKey(recipientHeaders, &recipient, generator)
|
|
|
|
if err == nil {
|
|
|
|
// Found a valid CEK -- let's try to decrypt.
|
|
|
|
plaintext, err = cipher.decrypt(cek, authData, parts)
|
|
|
|
}
|
|
|
|
|
|
|
|
if plaintext == nil {
|
|
|
|
return nil, ErrCryptoFailure
|
|
|
|
}
|
|
|
|
|
|
|
|
// The "zip" header parameter may only be present in the protected header.
|
|
|
|
if comp := obj.protected.getCompression(); comp != "" {
|
|
|
|
plaintext, err = decompress(comp, plaintext)
|
2024-03-11 10:36:13 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("go-jose/go-jose: failed to decompress plaintext: %v", err)
|
|
|
|
}
|
2021-08-12 19:03:24 +00:00
|
|
|
}
|
|
|
|
|
2024-03-11 10:36:13 +00:00
|
|
|
return plaintext, nil
|
2021-08-12 19:03:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// DecryptMulti decrypts and validates the object and returns the plaintexts,
|
|
|
|
// with support for multiple recipients. It returns the index of the recipient
|
|
|
|
// for which the decryption was successful, the merged headers for that recipient,
|
|
|
|
// and the plaintext.
|
2024-03-11 10:36:13 +00:00
|
|
|
//
|
|
|
|
// The decryptionKey argument must have one of the types allowed for the
|
|
|
|
// decryptionKey argument of Decrypt().
|
|
|
|
//
|
|
|
|
// Automatically decompresses plaintext, but returns an error if the decompressed
|
|
|
|
// data would be >250kB or >3x the size of the compressed data, whichever is larger.
|
2021-08-12 19:03:24 +00:00
|
|
|
func (obj JSONWebEncryption) DecryptMulti(decryptionKey interface{}) (int, Header, []byte, error) {
|
|
|
|
globalHeaders := obj.mergedHeaders(nil)
|
|
|
|
|
|
|
|
critical, err := globalHeaders.getCritical()
|
|
|
|
if err != nil {
|
2023-01-09 08:23:49 +00:00
|
|
|
return -1, Header{}, nil, fmt.Errorf("go-jose/go-jose: invalid crit header")
|
2021-08-12 19:03:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(critical) > 0 {
|
2023-01-09 08:23:49 +00:00
|
|
|
return -1, Header{}, nil, fmt.Errorf("go-jose/go-jose: unsupported crit header")
|
2021-08-12 19:03:24 +00:00
|
|
|
}
|
|
|
|
|
2023-01-09 08:23:49 +00:00
|
|
|
key := tryJWKS(decryptionKey, obj.Header)
|
|
|
|
decrypter, err := newDecrypter(key)
|
2021-08-12 19:03:24 +00:00
|
|
|
if err != nil {
|
|
|
|
return -1, Header{}, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
encryption := globalHeaders.getEncryption()
|
|
|
|
cipher := getContentCipher(encryption)
|
|
|
|
if cipher == nil {
|
2023-01-09 08:23:49 +00:00
|
|
|
return -1, Header{}, nil, fmt.Errorf("go-jose/go-jose: unsupported enc value '%s'", string(encryption))
|
2021-08-12 19:03:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
generator := randomKeyGenerator{
|
|
|
|
size: cipher.keySize(),
|
|
|
|
}
|
|
|
|
|
|
|
|
parts := &aeadParts{
|
|
|
|
iv: obj.iv,
|
|
|
|
ciphertext: obj.ciphertext,
|
|
|
|
tag: obj.tag,
|
|
|
|
}
|
|
|
|
|
|
|
|
authData := obj.computeAuthData()
|
|
|
|
|
|
|
|
index := -1
|
|
|
|
var plaintext []byte
|
|
|
|
var headers rawHeader
|
|
|
|
|
|
|
|
for i, recipient := range obj.recipients {
|
|
|
|
recipientHeaders := obj.mergedHeaders(&recipient)
|
|
|
|
|
|
|
|
cek, err := decrypter.decryptKey(recipientHeaders, &recipient, generator)
|
|
|
|
if err == nil {
|
|
|
|
// Found a valid CEK -- let's try to decrypt.
|
|
|
|
plaintext, err = cipher.decrypt(cek, authData, parts)
|
|
|
|
if err == nil {
|
|
|
|
index = i
|
|
|
|
headers = recipientHeaders
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-09 08:23:49 +00:00
|
|
|
if plaintext == nil {
|
2021-08-12 19:03:24 +00:00
|
|
|
return -1, Header{}, nil, ErrCryptoFailure
|
|
|
|
}
|
|
|
|
|
|
|
|
// The "zip" header parameter may only be present in the protected header.
|
|
|
|
if comp := obj.protected.getCompression(); comp != "" {
|
2024-03-11 10:36:13 +00:00
|
|
|
plaintext, err = decompress(comp, plaintext)
|
|
|
|
if err != nil {
|
|
|
|
return -1, Header{}, nil, fmt.Errorf("go-jose/go-jose: failed to decompress plaintext: %v", err)
|
|
|
|
}
|
2021-08-12 19:03:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sanitized, err := headers.sanitized()
|
|
|
|
if err != nil {
|
2023-01-09 08:23:49 +00:00
|
|
|
return -1, Header{}, nil, fmt.Errorf("go-jose/go-jose: failed to sanitize header: %v", err)
|
2021-08-12 19:03:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return index, sanitized, plaintext, err
|
|
|
|
}
|