mirror of
https://github.com/pomerium/pomerium.git
synced 2025-06-06 12:52:53 +02:00
cryptutil: add envelope encryption w/key encryption key and data encryption key (#2020)
* cryptutil: add envelope encryption w/key encryption key and data encryption key * use randomBytes, derive kek id, add tests * add comment about lru error
This commit is contained in:
parent
4cc697ace4
commit
dda6a9af60
4 changed files with 442 additions and 0 deletions
107
pkg/cryptutil/dek.go
Normal file
107
pkg/cryptutil/dek.go
Normal file
|
@ -0,0 +1,107 @@
|
|||
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)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue