228 lines
6.1 KiB
Go
228 lines
6.1 KiB
Go
package jwe
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/lestrrat-go/jwx/v3/internal/tokens"
|
|
"github.com/lestrrat-go/jwx/v3/jwa"
|
|
"github.com/lestrrat-go/jwx/v3/jwe/internal/content_crypt"
|
|
"github.com/lestrrat-go/jwx/v3/jwe/jwebb"
|
|
)
|
|
|
|
// decrypter is responsible for taking various components to decrypt a message.
|
|
// its operation is not concurrency safe. You must provide locking yourself
|
|
//
|
|
//nolint:govet
|
|
type decrypter struct {
|
|
aad []byte
|
|
apu []byte
|
|
apv []byte
|
|
cek *[]byte
|
|
computedAad []byte
|
|
iv []byte
|
|
keyiv []byte
|
|
keysalt []byte
|
|
keytag []byte
|
|
tag []byte
|
|
privkey any
|
|
pubkey any
|
|
ctalg jwa.ContentEncryptionAlgorithm
|
|
keyalg jwa.KeyEncryptionAlgorithm
|
|
cipher content_crypt.Cipher
|
|
keycount int
|
|
}
|
|
|
|
// newDecrypter Creates a new Decrypter instance. You must supply the
|
|
// rest of parameters via their respective setter methods before
|
|
// calling Decrypt().
|
|
//
|
|
// privkey must be a private key in its "raw" format (i.e. something like
|
|
// *rsa.PrivateKey, instead of jwk.Key)
|
|
//
|
|
// You should consider this object immutable once you assign values to it.
|
|
func newDecrypter(keyalg jwa.KeyEncryptionAlgorithm, ctalg jwa.ContentEncryptionAlgorithm, privkey any) *decrypter {
|
|
return &decrypter{
|
|
ctalg: ctalg,
|
|
keyalg: keyalg,
|
|
privkey: privkey,
|
|
}
|
|
}
|
|
|
|
func (d *decrypter) AgreementPartyUInfo(apu []byte) *decrypter {
|
|
d.apu = apu
|
|
return d
|
|
}
|
|
|
|
func (d *decrypter) AgreementPartyVInfo(apv []byte) *decrypter {
|
|
d.apv = apv
|
|
return d
|
|
}
|
|
|
|
func (d *decrypter) AuthenticatedData(aad []byte) *decrypter {
|
|
d.aad = aad
|
|
return d
|
|
}
|
|
|
|
func (d *decrypter) ComputedAuthenticatedData(aad []byte) *decrypter {
|
|
d.computedAad = aad
|
|
return d
|
|
}
|
|
|
|
func (d *decrypter) ContentEncryptionAlgorithm(ctalg jwa.ContentEncryptionAlgorithm) *decrypter {
|
|
d.ctalg = ctalg
|
|
return d
|
|
}
|
|
|
|
func (d *decrypter) InitializationVector(iv []byte) *decrypter {
|
|
d.iv = iv
|
|
return d
|
|
}
|
|
|
|
func (d *decrypter) KeyCount(keycount int) *decrypter {
|
|
d.keycount = keycount
|
|
return d
|
|
}
|
|
|
|
func (d *decrypter) KeyInitializationVector(keyiv []byte) *decrypter {
|
|
d.keyiv = keyiv
|
|
return d
|
|
}
|
|
|
|
func (d *decrypter) KeySalt(keysalt []byte) *decrypter {
|
|
d.keysalt = keysalt
|
|
return d
|
|
}
|
|
|
|
func (d *decrypter) KeyTag(keytag []byte) *decrypter {
|
|
d.keytag = keytag
|
|
return d
|
|
}
|
|
|
|
// PublicKey sets the public key to be used in decoding EC based encryptions.
|
|
// The key must be in its "raw" format (i.e. *ecdsa.PublicKey, instead of jwk.Key)
|
|
func (d *decrypter) PublicKey(pubkey any) *decrypter {
|
|
d.pubkey = pubkey
|
|
return d
|
|
}
|
|
|
|
func (d *decrypter) Tag(tag []byte) *decrypter {
|
|
d.tag = tag
|
|
return d
|
|
}
|
|
|
|
func (d *decrypter) CEK(ptr *[]byte) *decrypter {
|
|
d.cek = ptr
|
|
return d
|
|
}
|
|
|
|
func (d *decrypter) ContentCipher() (content_crypt.Cipher, error) {
|
|
if d.cipher == nil {
|
|
cipher, err := jwebb.CreateContentCipher(d.ctalg.String())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
d.cipher = cipher
|
|
}
|
|
|
|
return d.cipher, nil
|
|
}
|
|
|
|
func (d *decrypter) Decrypt(recipient Recipient, ciphertext []byte, msg *Message) (plaintext []byte, err error) {
|
|
cek, keyerr := d.DecryptKey(recipient, msg)
|
|
if keyerr != nil {
|
|
err = fmt.Errorf(`failed to decrypt key: %w`, keyerr)
|
|
return
|
|
}
|
|
|
|
cipher, ciphererr := d.ContentCipher()
|
|
if ciphererr != nil {
|
|
err = fmt.Errorf(`failed to fetch content crypt cipher: %w`, ciphererr)
|
|
return
|
|
}
|
|
|
|
computedAad := d.computedAad
|
|
if d.aad != nil {
|
|
computedAad = append(append(computedAad, tokens.Period), d.aad...)
|
|
}
|
|
|
|
plaintext, err = cipher.Decrypt(cek, d.iv, ciphertext, d.tag, computedAad)
|
|
if err != nil {
|
|
err = fmt.Errorf(`failed to decrypt payload: %w`, err)
|
|
return
|
|
}
|
|
|
|
if d.cek != nil {
|
|
*d.cek = cek
|
|
}
|
|
return plaintext, nil
|
|
}
|
|
|
|
func (d *decrypter) DecryptKey(recipient Recipient, msg *Message) (cek []byte, err error) {
|
|
recipientKey := recipient.EncryptedKey()
|
|
if kd, ok := d.privkey.(KeyDecrypter); ok {
|
|
return kd.DecryptKey(d.keyalg, recipientKey, recipient, msg)
|
|
}
|
|
|
|
if jwebb.IsDirect(d.keyalg.String()) {
|
|
cek, ok := d.privkey.([]byte)
|
|
if !ok {
|
|
return nil, fmt.Errorf("decrypt key: []byte is required as the key for %s (got %T)", d.keyalg, d.privkey)
|
|
}
|
|
return jwebb.KeyDecryptDirect(recipientKey, recipientKey, d.keyalg.String(), cek)
|
|
}
|
|
|
|
if jwebb.IsPBES2(d.keyalg.String()) {
|
|
password, ok := d.privkey.([]byte)
|
|
if !ok {
|
|
return nil, fmt.Errorf("decrypt key: []byte is required as the password for %s (got %T)", d.keyalg, d.privkey)
|
|
}
|
|
salt := []byte(d.keyalg.String())
|
|
salt = append(salt, byte(0))
|
|
salt = append(salt, d.keysalt...)
|
|
return jwebb.KeyDecryptPBES2(recipientKey, recipientKey, d.keyalg.String(), password, salt, d.keycount)
|
|
}
|
|
|
|
if jwebb.IsAESGCMKW(d.keyalg.String()) {
|
|
sharedkey, ok := d.privkey.([]byte)
|
|
if !ok {
|
|
return nil, fmt.Errorf("decrypt key: []byte is required as the key for %s (got %T)", d.keyalg, d.privkey)
|
|
}
|
|
return jwebb.KeyDecryptAESGCMKW(recipientKey, recipientKey, d.keyalg.String(), sharedkey, d.keyiv, d.keytag)
|
|
}
|
|
|
|
if jwebb.IsECDHES(d.keyalg.String()) {
|
|
alg, keysize, keywrap, err := jwebb.KeyEncryptionECDHESKeySize(d.keyalg.String(), d.ctalg.String())
|
|
if err != nil {
|
|
return nil, fmt.Errorf(`failed to determine ECDH-ES key size: %w`, err)
|
|
}
|
|
|
|
if !keywrap {
|
|
return jwebb.KeyDecryptECDHES(recipientKey, cek, alg, d.apu, d.apv, d.privkey, d.pubkey, keysize)
|
|
}
|
|
return jwebb.KeyDecryptECDHESKeyWrap(recipientKey, recipientKey, d.keyalg.String(), d.apu, d.apv, d.privkey, d.pubkey, keysize)
|
|
}
|
|
|
|
if jwebb.IsRSA15(d.keyalg.String()) {
|
|
cipher, err := d.ContentCipher()
|
|
if err != nil {
|
|
return nil, fmt.Errorf(`failed to fetch content crypt cipher: %w`, err)
|
|
}
|
|
keysize := cipher.KeySize() / 2
|
|
return jwebb.KeyDecryptRSA15(recipientKey, recipientKey, d.privkey, keysize)
|
|
}
|
|
|
|
if jwebb.IsRSAOAEP(d.keyalg.String()) {
|
|
return jwebb.KeyDecryptRSAOAEP(recipientKey, recipientKey, d.keyalg.String(), d.privkey)
|
|
}
|
|
|
|
if jwebb.IsAESKW(d.keyalg.String()) {
|
|
sharedkey, ok := d.privkey.([]byte)
|
|
if !ok {
|
|
return nil, fmt.Errorf("[]byte is required as the key to decrypt %s", d.keyalg.String())
|
|
}
|
|
return jwebb.KeyDecryptAESKW(recipientKey, recipientKey, d.keyalg.String(), sharedkey)
|
|
}
|
|
|
|
return nil, fmt.Errorf(`unsupported algorithm for key decryption (%s)`, d.keyalg)
|
|
}
|