pomerium/pkg/cryptutil/kek.go
Caleb Doxsey 500405512f
dependencies: vendor base58, remove shortuuid (#2739)
* vendor base58

* remove shortuuid
2021-11-02 09:23:15 -06:00

174 lines
5.8 KiB
Go

package cryptutil
import (
"crypto/rand"
"fmt"
"golang.org/x/crypto/curve25519"
"golang.org/x/crypto/nacl/box"
"github.com/pomerium/pomerium/pkg/encoding/base58"
)
// A KeyEncryptionKey (KEK) is used to implement *envelope encryption*, similar to how data is stored at rest with
// AWS or Google Cloud:
//
// - AWS: https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#enveloping
// - Google Cloud: https://cloud.google.com/kms/docs/envelope-encryption
//
// Data is encrypted with a data encryption key (DEK) and that key is stored next to the data encrypted with the KEK.
// Finally the KEK id is also stored with the data.
//
// To decrypt the data you first retrieve the KEK, second decrypt the DEK, and finally decrypt the data using the DEK.
//
// - Our KEKs are asymmetric Curve25519 keys. We use the *public* key to encrypt the DEK so only the *private* key can
// decrypt it.
// - Our DEKs are symmetric XChaCha20Poly1305 keys.
//
type KeyEncryptionKey interface {
ID() string
KeyBytes() []byte
isKeyEncryptionKey()
}
// KeyEncryptionKeySize is the size of a key encryption key.
const KeyEncryptionKeySize = curve25519.ScalarSize
// PrivateKeyEncryptionKey is a Curve25519 asymmetric private encryption key used to decrypt data encryption keys.
type PrivateKeyEncryptionKey struct {
data [KeyEncryptionKeySize]byte
}
func (*PrivateKeyEncryptionKey) isKeyEncryptionKey() {}
// NewPrivateKeyEncryptionKey creates a new encryption key from existing bytes.
func NewPrivateKeyEncryptionKey(raw []byte) (*PrivateKeyEncryptionKey, error) {
if len(raw) != KeyEncryptionKeySize {
return nil, fmt.Errorf("cryptutil: invalid key encryption key, expected %d bytes, got %d",
KeyEncryptionKeySize, len(raw))
}
kek := new(PrivateKeyEncryptionKey)
copy(kek.data[:], raw)
return kek, nil
}
// GenerateKeyEncryptionKey generates a new random key encryption key.
func GenerateKeyEncryptionKey() (*PrivateKeyEncryptionKey, error) {
raw := randomBytes(KeyEncryptionKeySize)
return NewPrivateKeyEncryptionKey(raw)
}
// GetKeyEncryptionKeyID derives an id from the key encryption key data itself.
func GetKeyEncryptionKeyID(raw []byte) string {
return base58.Encode(Hash("KeyEncryptionKey", raw))
}
// Decrypt decrypts data from a NACL anonymous box.
func (kek *PrivateKeyEncryptionKey) Decrypt(ciphertext []byte) ([]byte, error) {
private := kek
public := kek.Public()
opened, ok := box.OpenAnonymous(nil, ciphertext, &public.data, &private.data)
if !ok {
return nil, fmt.Errorf("cryptutil: anonymous box decrypt failed")
}
return opened, nil
}
// DecryptDataEncryptionKey decrypts a data encryption key.
func (kek *PrivateKeyEncryptionKey) DecryptDataEncryptionKey(ciphertext []byte) (*DataEncryptionKey, error) {
raw, err := kek.Decrypt(ciphertext)
if err != nil {
return nil, err
}
return NewDataEncryptionKey(raw)
}
// ID returns the private key's id.
func (kek *PrivateKeyEncryptionKey) ID() string {
return kek.Public().id
}
// KeyBytes returns the private key encryption key's raw bytes.
func (kek *PrivateKeyEncryptionKey) KeyBytes() []byte {
data := make([]byte, KeyEncryptionKeySize)
copy(data, kek.data[:])
return data
}
// Public returns the private key's public key.
func (kek *PrivateKeyEncryptionKey) Public() *PublicKeyEncryptionKey {
// taken from NACL box.GenerateKey
var publicKey [32]byte
curve25519.ScalarBaseMult(&publicKey, &kek.data)
return &PublicKeyEncryptionKey{
id: GetKeyEncryptionKeyID(kek.data[:]),
data: publicKey,
}
}
// PublicKeyEncryptionKey is a Curve25519 asymmetric public encryption key used to encrypt data encryption keys.
type PublicKeyEncryptionKey struct {
id string
data [KeyEncryptionKeySize]byte
}
func (*PublicKeyEncryptionKey) isKeyEncryptionKey() {}
// NewPublicKeyEncryptionKey creates a new encryption key from existing bytes.
func NewPublicKeyEncryptionKey(raw []byte) (*PublicKeyEncryptionKey, error) {
return NewPublicKeyEncryptionKeyWithID(GetKeyEncryptionKeyID(raw), raw)
}
// NewPublicKeyEncryptionKeyWithID creates a new encryption key from an existing id and bytes.
func NewPublicKeyEncryptionKeyWithID(id string, raw []byte) (*PublicKeyEncryptionKey, error) {
if len(raw) != KeyEncryptionKeySize {
return nil, fmt.Errorf("cryptutil: invalid key encryption key, expected %d bytes, got %d",
KeyEncryptionKeySize, len(raw))
}
kek := &PublicKeyEncryptionKey{
id: id,
}
copy(kek.data[:], raw)
return kek, nil
}
// ID returns the public key's id.
func (kek *PublicKeyEncryptionKey) ID() string {
return kek.id
}
// KeyBytes returns the public key's raw bytes.
func (kek *PublicKeyEncryptionKey) KeyBytes() []byte {
data := make([]byte, KeyEncryptionKeySize)
copy(data, kek.data[:])
return data
}
// Encrypt encrypts data using a NACL anonymous box.
func (kek *PublicKeyEncryptionKey) Encrypt(plaintext []byte) ([]byte, error) {
sealed, err := box.SealAnonymous(nil, plaintext, &kek.data, rand.Reader)
if err != nil { // only fails on rand.Read errors
return nil, fmt.Errorf("cryptutil: anonymous box encrypt failed: %w", err)
}
return sealed, nil
}
// EncryptDataEncryptionKey encrypts a DataEncryptionKey.
func (kek *PublicKeyEncryptionKey) EncryptDataEncryptionKey(dek *DataEncryptionKey) ([]byte, error) {
return kek.Encrypt(dek.data[:])
}
// A KeyEncryptionKeySource gets private key encryption keys based on their id.
type KeyEncryptionKeySource interface {
GetKeyEncryptionKey(id string) (*PrivateKeyEncryptionKey, error)
}
// A KeyEncryptionKeySourceFunc implements the KeyEncryptionKeySource interface using a function.
type KeyEncryptionKeySourceFunc func(id string) (*PrivateKeyEncryptionKey, error)
// GetKeyEncryptionKey gets the key encryption key by calling the underlying function.
func (src KeyEncryptionKeySourceFunc) GetKeyEncryptionKey(id string) (*PrivateKeyEncryptionKey, error) {
return src(id)
}