mirror of
https://github.com/pomerium/pomerium.git
synced 2025-04-28 09:56:31 +02:00
174 lines
5.8 KiB
Go
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)
|
|
}
|