mirror of
https://github.com/pomerium/pomerium.git
synced 2025-04-29 18:36:30 +02:00
* cryptutil: add envelope encryption w/key encryption key and data encryption key * use randomBytes, derive kek id, add tests * add comment about lru error
107 lines
3.5 KiB
Go
107 lines
3.5 KiB
Go
package cryptutil
|
|
|
|
import (
|
|
"crypto/cipher"
|
|
"encoding/base64"
|
|
"fmt"
|
|
|
|
lru "github.com/hashicorp/golang-lru"
|
|
"golang.org/x/crypto/chacha20poly1305"
|
|
)
|
|
|
|
const (
|
|
// DataEncryptionKeySize is the size of a data encryption key.
|
|
DataEncryptionKeySize = chacha20poly1305.KeySize
|
|
// DataEncryptionKeyCacheSize is the number of DEKs to keep in the LRU cache.
|
|
DataEncryptionKeyCacheSize = 20
|
|
)
|
|
|
|
// A DataEncryptionKey is an XChaCha20Poly1305 symmetric encryption key. For more details
|
|
// see the documentation on KeyEncryptionKeys.
|
|
type DataEncryptionKey struct {
|
|
data [DataEncryptionKeySize]byte
|
|
cipher cipher.AEAD
|
|
}
|
|
|
|
// NewDataEncryptionKey returns a new DataEncryptionKey from existing bytes.
|
|
func NewDataEncryptionKey(raw []byte) (*DataEncryptionKey, error) {
|
|
if len(raw) != DataEncryptionKeySize {
|
|
return nil, fmt.Errorf("cryptutil: invalid data encryption key, expected %d bytes, got %d",
|
|
DataEncryptionKeySize, len(raw))
|
|
}
|
|
dek := new(DataEncryptionKey)
|
|
copy(dek.data[:], raw)
|
|
dek.cipher, _ = chacha20poly1305.NewX(raw) // only errors on invalid size
|
|
return dek, nil
|
|
}
|
|
|
|
// GenerateDataEncryptionKey generates a new random data encryption key.
|
|
func GenerateDataEncryptionKey() (*DataEncryptionKey, error) {
|
|
raw := randomBytes(DataEncryptionKeySize)
|
|
return NewDataEncryptionKey(raw)
|
|
}
|
|
|
|
// Decrypt decrypts encrypted data using the data encryption key.
|
|
func (dek *DataEncryptionKey) Decrypt(ciphertext []byte) ([]byte, error) {
|
|
return Decrypt(dek.cipher, ciphertext, nil)
|
|
}
|
|
|
|
// DecryptString decrypts an encrypted string using the data encryption key and base64 encoding.
|
|
func (dek *DataEncryptionKey) DecryptString(ciphertext string) (string, error) {
|
|
ciphertextBytes, err := base64.StdEncoding.DecodeString(ciphertext)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
plaintextBytes, err := dek.Decrypt(ciphertextBytes)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(plaintextBytes), nil
|
|
}
|
|
|
|
// Encrypt encrypts data using the data encryption key.
|
|
func (dek *DataEncryptionKey) Encrypt(plaintext []byte) []byte {
|
|
return Encrypt(dek.cipher, plaintext, nil)
|
|
}
|
|
|
|
// EncryptString encrypts a string using the data encryption key and base64 encoding.
|
|
func (dek *DataEncryptionKey) EncryptString(plaintext string) string {
|
|
bs := dek.Encrypt([]byte(plaintext))
|
|
return base64.StdEncoding.EncodeToString(bs)
|
|
}
|
|
|
|
// KeyBytes returns the private key encryption key's raw bytes.
|
|
func (dek *DataEncryptionKey) KeyBytes() []byte {
|
|
data := make([]byte, DataEncryptionKeySize)
|
|
copy(data, dek.data[:])
|
|
return data
|
|
}
|
|
|
|
// A DataEncryptionKeyCache caches recently used data encryption keys based on their
|
|
// encrypted representation. The cache is safe for concurrent read and write access.
|
|
//
|
|
// Internally an LRU cache is used and the encrypted DEK bytes are converted to strings
|
|
// to allow usage as hash map keys.
|
|
type DataEncryptionKeyCache struct {
|
|
lru *lru.Cache
|
|
}
|
|
|
|
// NewDataEncryptionKeyCache creates a new DataEncryptionKeyCache.
|
|
func NewDataEncryptionKeyCache() *DataEncryptionKeyCache {
|
|
c, _ := lru.New(DataEncryptionKeyCacheSize) // only errors if size <= 0
|
|
return &DataEncryptionKeyCache{lru: c}
|
|
}
|
|
|
|
// Get returns a data encryption key if available.
|
|
func (cache *DataEncryptionKeyCache) Get(encryptedDEK []byte) (*DataEncryptionKey, bool) {
|
|
obj, ok := cache.lru.Get(string(encryptedDEK))
|
|
if ok {
|
|
return obj.(*DataEncryptionKey), true
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
// Put stores a data encryption key by its encrypted representation.
|
|
func (cache *DataEncryptionKeyCache) Put(encryptedDEK []byte, dek *DataEncryptionKey) {
|
|
cache.lru.Add(string(encryptedDEK), dek)
|
|
}
|