Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement jwe.KeyEncrypter and jwe.KeyDecrypter #925

Merged
merged 2 commits into from
Jun 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Changes
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@ v2 has many incompatibilities with v1. To see the full list of differences betwe
v1 and v2, please read the Changes-v2.md file (https://github.com/lestrrat-go/jwx/blob/develop/v2/Changes-v2.md)

v2.0.10 - UNRELEASED
[New Features]
* [jwe] (EXPERIMENTAL) Added `jwe.KeyEncrypter` and `jwe.KeyDecrypter` interfaces
that works in similar ways as how `crypto.Signer` works for signature
generation and verification. It can act as the interface for your encryption/decryption
keys that are for example stored in an hardware device.

This feature is labeled experimental because the API for the above interfaces have not
been battle tested, and may need to changed yet. Please be aware that until the API
is deemed stable, you may have to adapat our code to these possible changes,
_even_ during minor version upgrades of this library.

[Bug fixes]
* Registering JWS signers/verifiers did not work since v2.0.0, because the
way we handle algorithm names changed in 2aa98ce6884187180a7145b73da78c859dd46c84.
Expand Down
11 changes: 8 additions & 3 deletions jwe/decrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,8 @@ func (d *decrypter) ContentCipher() (content_crypt.Cipher, error) {
return d.cipher, nil
}

func (d *decrypter) Decrypt(recipientKey, ciphertext []byte) (plaintext []byte, err error) {
cek, keyerr := d.DecryptKey(recipientKey)
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
Expand Down Expand Up @@ -226,7 +226,12 @@ func (d *decrypter) decryptSymmetricKey(recipientKey, cek []byte) ([]byte, error
}
}

func (d *decrypter) DecryptKey(recipientKey []byte) (cek []byte, err error) {
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 d.keyalg.IsSymmetric() {
var ok bool
cek, ok = d.privkey.([]byte)
Expand Down
54 changes: 54 additions & 0 deletions jwe/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,63 @@ package jwe
import (
"github.com/lestrrat-go/iter/mapiter"
"github.com/lestrrat-go/jwx/v2/internal/iter"
"github.com/lestrrat-go/jwx/v2/jwa"
"github.com/lestrrat-go/jwx/v2/jwe/internal/keygen"
)

// KeyEncrypter is an interface for object that can encrypt a
// content encryption key.
//
// You can use this in place of a regular key (i.e. in jwe.WithKey())
// to encrypt the content encryption key in a JWE message without
// having to expose the secret key in memory, for example, when you
// want to use hardware security modules (HSMs) to encrypt the key.
//
// This API is experimental and may change without notice, even
// in minor releases.
type KeyEncrypter interface {
// Algorithm returns the algorithm used to encrypt the key.
Algorithm() jwa.KeyEncryptionAlgorithm

// EncryptKey encrypts the given content encryption key.
EncryptKey([]byte) ([]byte, error)
}

// KeyIDer is an interface for things that can return a key ID.
//
// As of this writing, this is solely used to identify KeyEncrypter
// objects that also carry a key ID on its own.
type KeyIDer interface {
KeyID() string
}

// KeyDecrypter is an interface for objects that can decrypt a content
// encryption key.
//
// You can use this in place of a regular key (i.e. in jwe.WithKey())
// to decrypt the encrypted key in a JWE message without having to
// expose the secret key in memory, for example, when you want to use
// hardware security modules (HSMs) to decrypt the key.
//
// This API is experimental and may change without notice, even
// in minor releases.
type KeyDecrypter interface {
// Decrypt decrypts the encrypted key of a JWE message.
//
// Make sure you understand how JWE messages are structured.
//
// For example, while in most circumstances a JWE message will only have one recipient,
// a JWE message may contain multiple recipients, each with their own
// encrypted key. This method will be called for each recipient, instead of
// just once for a message.
//
// Also, header values could be found in either protected/unprotected headers
// of a JWE message, as well as in protected/unprotected headers for each recipient.
// When checking a header value, you can decide to use either one, or both, but you
// must be aware that there are multiple places to look for.
DecryptKey(alg jwa.KeyEncryptionAlgorithm, encryptedKey []byte, recipient Recipient, message *Message) ([]byte, error)
}

// Recipient holds the encrypted key and hints to decrypt the key
type Recipient interface {
Headers() Headers
Expand Down
8 changes: 1 addition & 7 deletions jwe/internal/keyenc/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,7 @@ import (
// Encrypter is an interface for things that can encrypt keys
type Encrypter interface {
Algorithm() jwa.KeyEncryptionAlgorithm
Encrypt([]byte) (keygen.ByteSource, error)
// KeyID returns the key id for this Encrypter. This exists so that
// you can pass in a Encrypter to MultiEncrypt, you can rest assured
// that the generated key will have the proper key ID.
KeyID() string

SetKeyID(string)
EncryptKey([]byte) (keygen.ByteSource, error)
}

// Decrypter is an interface for things that can decrypt keys
Expand Down
14 changes: 7 additions & 7 deletions jwe/internal/keyenc/keyenc.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func (kw *Noop) KeyID() string {
return kw.keyID
}

func (kw *Noop) Encrypt(cek []byte) (keygen.ByteSource, error) {
func (kw *Noop) EncryptKey(cek []byte) (keygen.ByteSource, error) {
return keygen.ByteKey(kw.sharedkey), nil
}

Expand Down Expand Up @@ -88,7 +88,7 @@ func (kw *AES) Decrypt(enckey []byte) ([]byte, error) {
}

// KeyEncrypt encrypts the given content encryption key
func (kw *AES) Encrypt(cek []byte) (keygen.ByteSource, error) {
func (kw *AES) EncryptKey(cek []byte) (keygen.ByteSource, error) {
block, err := aes.NewCipher(kw.sharedkey)
if err != nil {
return nil, fmt.Errorf(`failed to create cipher from shared key: %w`, err)
Expand Down Expand Up @@ -119,7 +119,7 @@ func (kw AESGCMEncrypt) KeyID() string {
return kw.keyID
}

func (kw AESGCMEncrypt) Encrypt(cek []byte) (keygen.ByteSource, error) {
func (kw AESGCMEncrypt) EncryptKey(cek []byte) (keygen.ByteSource, error) {
block, err := aes.NewCipher(kw.sharedkey)
if err != nil {
return nil, fmt.Errorf(`failed to create cipher from shared key: %w`, err)
Expand Down Expand Up @@ -181,7 +181,7 @@ func (kw PBES2Encrypt) KeyID() string {
return kw.keyID
}

func (kw PBES2Encrypt) Encrypt(cek []byte) (keygen.ByteSource, error) {
func (kw PBES2Encrypt) EncryptKey(cek []byte) (keygen.ByteSource, error) {
count := 10000
salt := make([]byte, kw.keylen)
_, err := io.ReadFull(rand.Reader, salt)
Expand Down Expand Up @@ -245,7 +245,7 @@ func (kw ECDHESEncrypt) KeyID() string {
}

// KeyEncrypt encrypts the content encryption key using ECDH-ES
func (kw ECDHESEncrypt) Encrypt(cek []byte) (keygen.ByteSource, error) {
func (kw ECDHESEncrypt) EncryptKey(cek []byte) (keygen.ByteSource, error) {
kg, err := kw.generator.Generate()
if err != nil {
return nil, fmt.Errorf(`failed to create key generator: %w`, err)
Expand Down Expand Up @@ -443,7 +443,7 @@ func (e RSAOAEPEncrypt) KeyID() string {
}

// KeyEncrypt encrypts the content encryption key using RSA PKCS1v15
func (e RSAPKCSEncrypt) Encrypt(cek []byte) (keygen.ByteSource, error) {
func (e RSAPKCSEncrypt) EncryptKey(cek []byte) (keygen.ByteSource, error) {
if e.alg != jwa.RSA1_5 {
return nil, fmt.Errorf("invalid RSA PKCS encrypt algorithm (%s)", e.alg)
}
Expand All @@ -455,7 +455,7 @@ func (e RSAPKCSEncrypt) Encrypt(cek []byte) (keygen.ByteSource, error) {
}

// KeyEncrypt encrypts the content encryption key using RSA OAEP
func (e RSAOAEPEncrypt) Encrypt(cek []byte) (keygen.ByteSource, error) {
func (e RSAOAEPEncrypt) EncryptKey(cek []byte) (keygen.ByteSource, error) {
var hash hash.Hash
switch e.alg {
case jwa.RSA_OAEP:
Expand Down
3 changes: 0 additions & 3 deletions jwe/internal/keygen/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ type Generator interface {
Generate() (ByteSource, error)
}

// StaticKeyGenerate uses a static byte buffer to provide keys.
type Static []byte

// RandomKeyGenerate generates random keys
type Random struct {
keysize int
Expand Down
12 changes: 0 additions & 12 deletions jwe/internal/keygen/keygen.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,6 @@ func (k ByteKey) Bytes() []byte {
return []byte(k)
}

// Size returns the size of the key
func (g Static) Size() int {
return len(g)
}

// Generate returns the key
func (g Static) Generate() (ByteSource, error) {
buf := make([]byte, g.Size())
copy(buf, g)
return ByteKey(buf), nil
}

// NewRandom creates a new Generator that returns
// random bytes
func NewRandom(n int) Random {
Expand Down
Loading