open-vault/vault/barrier_aes_gcm.go
Jeff Mitchell 27c960d8df
Split SubView functionality into logical.StorageView (#6141)
This lets other parts of Vault that can't depend on the vault package
take advantage of the subview functionality.

This also allows getting rid of BarrierStorage and vault.Entry, two
totally redundant abstractions.
2019-01-31 09:25:18 -05:00

951 lines
23 KiB
Go

package vault
import (
"context"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/subtle"
"encoding/binary"
"errors"
"fmt"
"strings"
"sync"
"time"
metrics "github.com/armon/go-metrics"
"github.com/hashicorp/errwrap"
"github.com/hashicorp/vault/helper/jsonutil"
"github.com/hashicorp/vault/helper/strutil"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/physical"
)
const (
// initialKeyTerm is the hard coded initial key term. This is
// used only for values that are not encrypted with the keyring.
initialKeyTerm = 1
// termSize the number of bytes used for the key term.
termSize = 4
)
// Versions of the AESGCM storage methodology
const (
AESGCMVersion1 = 0x1
AESGCMVersion2 = 0x2
)
// barrierInit is the JSON encoded value stored
type barrierInit struct {
Version int // Version is the current format version
Key []byte // Key is the primary encryption key
}
// Validate AESGCMBarrier satisfies SecurityBarrier interface
var _ SecurityBarrier = &AESGCMBarrier{}
// AESGCMBarrier is a SecurityBarrier implementation that uses the AES
// cipher core and the Galois Counter Mode block mode. It defaults to
// the golang NONCE default value of 12 and a key size of 256
// bit. AES-GCM is high performance, and provides both confidentiality
// and integrity.
type AESGCMBarrier struct {
backend physical.Backend
l sync.RWMutex
sealed bool
// keyring is used to maintain all of the encryption keys, including
// the active key used for encryption, but also prior keys to allow
// decryption of keys encrypted under previous terms.
keyring *Keyring
// cache is used to reduce the number of AEAD constructions we do
cache map[uint32]cipher.AEAD
cacheLock sync.RWMutex
// currentAESGCMVersionByte is prefixed to a message to allow for
// future versioning of barrier implementations. It's var instead
// of const to allow for testing
currentAESGCMVersionByte byte
}
// NewAESGCMBarrier is used to construct a new barrier that uses
// the provided physical backend for storage.
func NewAESGCMBarrier(physical physical.Backend) (*AESGCMBarrier, error) {
b := &AESGCMBarrier{
backend: physical,
sealed: true,
cache: make(map[uint32]cipher.AEAD),
currentAESGCMVersionByte: byte(AESGCMVersion2),
}
return b, nil
}
// Initialized checks if the barrier has been initialized
// and has a master key set.
func (b *AESGCMBarrier) Initialized(ctx context.Context) (bool, error) {
// Read the keyring file
keys, err := b.backend.List(ctx, keyringPrefix)
if err != nil {
return false, errwrap.Wrapf("failed to check for initialization: {{err}}", err)
}
if strutil.StrListContains(keys, "keyring") {
return true, nil
}
// Fallback, check for the old sentinel file
out, err := b.backend.Get(ctx, barrierInitPath)
if err != nil {
return false, errwrap.Wrapf("failed to check for initialization: {{err}}", err)
}
return out != nil, nil
}
// Initialize works only if the barrier has not been initialized
// and makes use of the given master key.
func (b *AESGCMBarrier) Initialize(ctx context.Context, key []byte) error {
// Verify the key size
min, max := b.KeyLength()
if len(key) < min || len(key) > max {
return fmt.Errorf("key size must be %d or %d", min, max)
}
// Check if already initialized
if alreadyInit, err := b.Initialized(ctx); err != nil {
return err
} else if alreadyInit {
return ErrBarrierAlreadyInit
}
// Generate encryption key
encrypt, err := b.GenerateKey()
if err != nil {
return errwrap.Wrapf("failed to generate encryption key: {{err}}", err)
}
// Create a new keyring, install the keys
keyring := NewKeyring()
keyring = keyring.SetMasterKey(key)
keyring, err = keyring.AddKey(&Key{
Term: 1,
Version: 1,
Value: encrypt,
})
if err != nil {
return errwrap.Wrapf("failed to create keyring: {{err}}", err)
}
return b.persistKeyring(ctx, keyring)
}
// persistKeyring is used to write out the keyring using the
// master key to encrypt it.
func (b *AESGCMBarrier) persistKeyring(ctx context.Context, keyring *Keyring) error {
// Create the keyring entry
keyringBuf, err := keyring.Serialize()
defer memzero(keyringBuf)
if err != nil {
return errwrap.Wrapf("failed to serialize keyring: {{err}}", err)
}
// Create the AES-GCM
gcm, err := b.aeadFromKey(keyring.MasterKey())
if err != nil {
return err
}
// Encrypt the barrier init value
value, err := b.encrypt(keyringPath, initialKeyTerm, gcm, keyringBuf)
if err != nil {
return err
}
// Create the keyring physical entry
pe := &physical.Entry{
Key: keyringPath,
Value: value,
}
if err := b.backend.Put(ctx, pe); err != nil {
return errwrap.Wrapf("failed to persist keyring: {{err}}", err)
}
// Serialize the master key value
key := &Key{
Term: 1,
Version: 1,
Value: keyring.MasterKey(),
}
keyBuf, err := key.Serialize()
defer memzero(keyBuf)
if err != nil {
return errwrap.Wrapf("failed to serialize master key: {{err}}", err)
}
// Encrypt the master key
activeKey := keyring.ActiveKey()
aead, err := b.aeadFromKey(activeKey.Value)
if err != nil {
return err
}
value, err = b.encrypt(masterKeyPath, activeKey.Term, aead, keyBuf)
if err != nil {
return err
}
// Update the masterKeyPath for standby instances
pe = &physical.Entry{
Key: masterKeyPath,
Value: value,
}
if err := b.backend.Put(ctx, pe); err != nil {
return errwrap.Wrapf("failed to persist master key: {{err}}", err)
}
return nil
}
// GenerateKey is used to generate a new key
func (b *AESGCMBarrier) GenerateKey() ([]byte, error) {
// Generate a 256bit key
buf := make([]byte, 2*aes.BlockSize)
_, err := rand.Read(buf)
return buf, err
}
// KeyLength is used to sanity check a key
func (b *AESGCMBarrier) KeyLength() (int, int) {
return aes.BlockSize, 2 * aes.BlockSize
}
// Sealed checks if the barrier has been unlocked yet. The Barrier
// is not expected to be able to perform any CRUD until it is unsealed.
func (b *AESGCMBarrier) Sealed() (bool, error) {
b.l.RLock()
sealed := b.sealed
b.l.RUnlock()
return sealed, nil
}
// VerifyMaster is used to check if the given key matches the master key
func (b *AESGCMBarrier) VerifyMaster(key []byte) error {
b.l.RLock()
defer b.l.RUnlock()
if b.sealed {
return ErrBarrierSealed
}
if subtle.ConstantTimeCompare(key, b.keyring.MasterKey()) != 1 {
return ErrBarrierInvalidKey
}
return nil
}
// ReloadKeyring is used to re-read the underlying keyring.
// This is used for HA deployments to ensure the latest keyring
// is present in the leader.
func (b *AESGCMBarrier) ReloadKeyring(ctx context.Context) error {
b.l.Lock()
defer b.l.Unlock()
// Create the AES-GCM
gcm, err := b.aeadFromKey(b.keyring.MasterKey())
if err != nil {
return err
}
// Read in the keyring
out, err := b.backend.Get(ctx, keyringPath)
if err != nil {
return errwrap.Wrapf("failed to check for keyring: {{err}}", err)
}
// Ensure that the keyring exists. This should never happen,
// and indicates something really bad has happened.
if out == nil {
return errors.New("keyring unexpectedly missing")
}
// Verify the term is always just one
term := binary.BigEndian.Uint32(out.Value[:4])
if term != initialKeyTerm {
return errors.New("term mis-match")
}
// Decrypt the barrier init key
plain, err := b.decrypt(keyringPath, gcm, out.Value)
defer memzero(plain)
if err != nil {
if strings.Contains(err.Error(), "message authentication failed") {
return ErrBarrierInvalidKey
}
return err
}
// Recover the keyring
keyring, err := DeserializeKeyring(plain)
if err != nil {
return errwrap.Wrapf("keyring deserialization failed: {{err}}", err)
}
// Setup the keyring and finish
b.keyring = keyring
return nil
}
// ReloadMasterKey is used to re-read the underlying masterkey.
// This is used for HA deployments to ensure the latest master key
// is available for keyring reloading.
func (b *AESGCMBarrier) ReloadMasterKey(ctx context.Context) error {
// Read the masterKeyPath upgrade
out, err := b.Get(ctx, masterKeyPath)
if err != nil {
return errwrap.Wrapf("failed to read master key path: {{err}}", err)
}
// The masterKeyPath could be missing (backwards incompatible),
// we can ignore this and attempt to make progress with the current
// master key.
if out == nil {
return nil
}
defer memzero(out.Value)
// Deserialize the master key
key, err := DeserializeKey(out.Value)
if err != nil {
return errwrap.Wrapf("failed to deserialize key: {{err}}", err)
}
b.l.Lock()
defer b.l.Unlock()
// Check if the master key is the same
if subtle.ConstantTimeCompare(b.keyring.MasterKey(), key.Value) == 1 {
return nil
}
// Update the master key
oldKeyring := b.keyring
b.keyring = b.keyring.SetMasterKey(key.Value)
oldKeyring.Zeroize(false)
return nil
}
// Unseal is used to provide the master key which permits the barrier
// to be unsealed. If the key is not correct, the barrier remains sealed.
func (b *AESGCMBarrier) Unseal(ctx context.Context, key []byte) error {
b.l.Lock()
defer b.l.Unlock()
// Do nothing if already unsealed
if !b.sealed {
return nil
}
// Create the AES-GCM
gcm, err := b.aeadFromKey(key)
if err != nil {
return err
}
// Read in the keyring
out, err := b.backend.Get(ctx, keyringPath)
if err != nil {
return errwrap.Wrapf("failed to check for keyring: {{err}}", err)
}
if out != nil {
// Verify the term is always just one
term := binary.BigEndian.Uint32(out.Value[:4])
if term != initialKeyTerm {
return errors.New("term mis-match")
}
// Decrypt the barrier init key
plain, err := b.decrypt(keyringPath, gcm, out.Value)
defer memzero(plain)
if err != nil {
if strings.Contains(err.Error(), "message authentication failed") {
return ErrBarrierInvalidKey
}
return err
}
// Recover the keyring
keyring, err := DeserializeKeyring(plain)
if err != nil {
return errwrap.Wrapf("keyring deserialization failed: {{err}}", err)
}
// Setup the keyring and finish
b.keyring = keyring
b.sealed = false
return nil
}
// Read the barrier initialization key
out, err = b.backend.Get(ctx, barrierInitPath)
if err != nil {
return errwrap.Wrapf("failed to check for initialization: {{err}}", err)
}
if out == nil {
return ErrBarrierNotInit
}
// Verify the term is always just one
term := binary.BigEndian.Uint32(out.Value[:4])
if term != initialKeyTerm {
return errors.New("term mis-match")
}
// Decrypt the barrier init key
plain, err := b.decrypt(barrierInitPath, gcm, out.Value)
if err != nil {
if strings.Contains(err.Error(), "message authentication failed") {
return ErrBarrierInvalidKey
}
return err
}
defer memzero(plain)
// Unmarshal the barrier init
var init barrierInit
if err := jsonutil.DecodeJSON(plain, &init); err != nil {
return fmt.Errorf("failed to unmarshal barrier init file")
}
// Setup a new keyring, this is for backwards compatibility
keyringNew := NewKeyring()
keyring := keyringNew.SetMasterKey(key)
// AddKey reuses the master, so we are only zeroizing after this call
defer keyringNew.Zeroize(false)
keyring, err = keyring.AddKey(&Key{
Term: 1,
Version: 1,
Value: init.Key,
})
if err != nil {
return errwrap.Wrapf("failed to create keyring: {{err}}", err)
}
if err := b.persistKeyring(ctx, keyring); err != nil {
return err
}
// Delete the old barrier entry
if err := b.backend.Delete(ctx, barrierInitPath); err != nil {
return errwrap.Wrapf("failed to delete barrier init file: {{err}}", err)
}
// Set the vault as unsealed
b.keyring = keyring
b.sealed = false
return nil
}
// Seal is used to re-seal the barrier. This requires the barrier to
// be unsealed again to perform any further operations.
func (b *AESGCMBarrier) Seal() error {
b.l.Lock()
defer b.l.Unlock()
// Remove the primary key, and seal the vault
b.cache = make(map[uint32]cipher.AEAD)
b.keyring.Zeroize(true)
b.keyring = nil
b.sealed = true
return nil
}
// Rotate is used to create a new encryption key. All future writes
// should use the new key, while old values should still be decryptable.
func (b *AESGCMBarrier) Rotate(ctx context.Context) (uint32, error) {
b.l.Lock()
defer b.l.Unlock()
if b.sealed {
return 0, ErrBarrierSealed
}
// Generate a new key
encrypt, err := b.GenerateKey()
if err != nil {
return 0, errwrap.Wrapf("failed to generate encryption key: {{err}}", err)
}
// Get the next term
term := b.keyring.ActiveTerm()
newTerm := term + 1
// Add a new encryption key
newKeyring, err := b.keyring.AddKey(&Key{
Term: newTerm,
Version: 1,
Value: encrypt,
})
if err != nil {
return 0, errwrap.Wrapf("failed to add new encryption key: {{err}}", err)
}
// Persist the new keyring
if err := b.persistKeyring(ctx, newKeyring); err != nil {
return 0, err
}
// Swap the keyrings
b.keyring = newKeyring
return newTerm, nil
}
// CreateUpgrade creates an upgrade path key to the given term from the previous term
func (b *AESGCMBarrier) CreateUpgrade(ctx context.Context, term uint32) error {
b.l.RLock()
defer b.l.RUnlock()
if b.sealed {
return ErrBarrierSealed
}
// Get the key for this term
termKey := b.keyring.TermKey(term)
buf, err := termKey.Serialize()
defer memzero(buf)
if err != nil {
return err
}
// Get the AEAD for the previous term
prevTerm := term - 1
primary, err := b.aeadForTerm(prevTerm)
if err != nil {
return err
}
key := fmt.Sprintf("%s%d", keyringUpgradePrefix, prevTerm)
value, err := b.encrypt(key, prevTerm, primary, buf)
if err != nil {
return err
}
// Create upgrade key
pe := &physical.Entry{
Key: key,
Value: value,
}
return b.backend.Put(ctx, pe)
}
// DestroyUpgrade destroys the upgrade path key to the given term
func (b *AESGCMBarrier) DestroyUpgrade(ctx context.Context, term uint32) error {
path := fmt.Sprintf("%s%d", keyringUpgradePrefix, term-1)
return b.Delete(ctx, path)
}
// CheckUpgrade looks for an upgrade to the current term and installs it
func (b *AESGCMBarrier) CheckUpgrade(ctx context.Context) (bool, uint32, error) {
b.l.RLock()
defer b.l.RUnlock()
if b.sealed {
return false, 0, ErrBarrierSealed
}
// Get the current term
activeTerm := b.keyring.ActiveTerm()
// Check for an upgrade key
upgrade := fmt.Sprintf("%s%d", keyringUpgradePrefix, activeTerm)
entry, err := b.Get(ctx, upgrade)
if err != nil {
return false, 0, err
}
// Nothing to do if no upgrade
if entry == nil {
return false, 0, nil
}
defer memzero(entry.Value)
// Deserialize the key
key, err := DeserializeKey(entry.Value)
if err != nil {
return false, 0, err
}
// Upgrade from read lock to write lock
b.l.RUnlock()
defer b.l.RLock()
b.l.Lock()
defer b.l.Unlock()
// Update the keyring
newKeyring, err := b.keyring.AddKey(key)
if err != nil {
return false, 0, errwrap.Wrapf("failed to add new encryption key: {{err}}", err)
}
b.keyring = newKeyring
// Done!
return true, key.Term, nil
}
// ActiveKeyInfo is used to inform details about the active key
func (b *AESGCMBarrier) ActiveKeyInfo() (*KeyInfo, error) {
b.l.RLock()
defer b.l.RUnlock()
if b.sealed {
return nil, ErrBarrierSealed
}
// Determine the key install time
term := b.keyring.ActiveTerm()
key := b.keyring.TermKey(term)
// Return the key info
info := &KeyInfo{
Term: int(term),
InstallTime: key.InstallTime,
}
return info, nil
}
// Rekey is used to change the master key used to protect the keyring
func (b *AESGCMBarrier) Rekey(ctx context.Context, key []byte) error {
b.l.Lock()
defer b.l.Unlock()
newKeyring, err := b.updateMasterKeyCommon(key)
if err != nil {
return err
}
// Persist the new keyring
if err := b.persistKeyring(ctx, newKeyring); err != nil {
return err
}
// Swap the keyrings
oldKeyring := b.keyring
b.keyring = newKeyring
oldKeyring.Zeroize(false)
return nil
}
// SetMasterKey updates the keyring's in-memory master key but does not persist
// anything to storage
func (b *AESGCMBarrier) SetMasterKey(key []byte) error {
b.l.Lock()
defer b.l.Unlock()
newKeyring, err := b.updateMasterKeyCommon(key)
if err != nil {
return err
}
// Swap the keyrings
oldKeyring := b.keyring
b.keyring = newKeyring
oldKeyring.Zeroize(false)
return nil
}
// Performs common tasks related to updating the master key; note that the lock
// must be held before calling this function
func (b *AESGCMBarrier) updateMasterKeyCommon(key []byte) (*Keyring, error) {
if b.sealed {
return nil, ErrBarrierSealed
}
// Verify the key size
min, max := b.KeyLength()
if len(key) < min || len(key) > max {
return nil, fmt.Errorf("key size must be %d or %d", min, max)
}
return b.keyring.SetMasterKey(key), nil
}
// Put is used to insert or update an entry
func (b *AESGCMBarrier) Put(ctx context.Context, entry *logical.StorageEntry) error {
defer metrics.MeasureSince([]string{"barrier", "put"}, time.Now())
b.l.RLock()
if b.sealed {
b.l.RUnlock()
return ErrBarrierSealed
}
term := b.keyring.ActiveTerm()
primary, err := b.aeadForTerm(term)
b.l.RUnlock()
if err != nil {
return err
}
value, err := b.encrypt(entry.Key, term, primary, entry.Value)
if err != nil {
return err
}
pe := &physical.Entry{
Key: entry.Key,
Value: value,
SealWrap: entry.SealWrap,
}
return b.backend.Put(ctx, pe)
}
// Get is used to fetch an entry
func (b *AESGCMBarrier) Get(ctx context.Context, key string) (*logical.StorageEntry, error) {
defer metrics.MeasureSince([]string{"barrier", "get"}, time.Now())
b.l.RLock()
if b.sealed {
b.l.RUnlock()
return nil, ErrBarrierSealed
}
// Read the key from the backend
pe, err := b.backend.Get(ctx, key)
if err != nil {
b.l.RUnlock()
return nil, err
} else if pe == nil {
b.l.RUnlock()
return nil, nil
}
if len(pe.Value) < 4 {
b.l.RUnlock()
return nil, errors.New("invalid value")
}
// Verify the term
term := binary.BigEndian.Uint32(pe.Value[:4])
// Get the GCM by term
// It is expensive to do this first but it is not a
// normal case that this won't match
gcm, err := b.aeadForTerm(term)
b.l.RUnlock()
if err != nil {
return nil, err
}
if gcm == nil {
return nil, fmt.Errorf("no decryption key available for term %d", term)
}
// Decrypt the ciphertext
plain, err := b.decrypt(key, gcm, pe.Value)
if err != nil {
return nil, errwrap.Wrapf("decryption failed: {{err}}", err)
}
// Wrap in a logical entry
entry := &logical.StorageEntry{
Key: key,
Value: plain,
SealWrap: pe.SealWrap,
}
return entry, nil
}
// Delete is used to permanently delete an entry
func (b *AESGCMBarrier) Delete(ctx context.Context, key string) error {
defer metrics.MeasureSince([]string{"barrier", "delete"}, time.Now())
b.l.RLock()
sealed := b.sealed
b.l.RUnlock()
if sealed {
return ErrBarrierSealed
}
return b.backend.Delete(ctx, key)
}
// List is used ot list all the keys under a given
// prefix, up to the next prefix.
func (b *AESGCMBarrier) List(ctx context.Context, prefix string) ([]string, error) {
defer metrics.MeasureSince([]string{"barrier", "list"}, time.Now())
b.l.RLock()
sealed := b.sealed
b.l.RUnlock()
if sealed {
return nil, ErrBarrierSealed
}
return b.backend.List(ctx, prefix)
}
// aeadForTerm returns the AES-GCM AEAD for the given term
func (b *AESGCMBarrier) aeadForTerm(term uint32) (cipher.AEAD, error) {
// Check for the keyring
keyring := b.keyring
if keyring == nil {
return nil, nil
}
// Check the cache for the aead
b.cacheLock.RLock()
aead, ok := b.cache[term]
b.cacheLock.RUnlock()
if ok {
return aead, nil
}
// Read the underlying key
key := keyring.TermKey(term)
if key == nil {
return nil, nil
}
// Create a new aead
aead, err := b.aeadFromKey(key.Value)
if err != nil {
return nil, err
}
// Update the cache
b.cacheLock.Lock()
b.cache[term] = aead
b.cacheLock.Unlock()
return aead, nil
}
// aeadFromKey returns an AES-GCM AEAD using the given key.
func (b *AESGCMBarrier) aeadFromKey(key []byte) (cipher.AEAD, error) {
// Create the AES cipher
aesCipher, err := aes.NewCipher(key)
if err != nil {
return nil, errwrap.Wrapf("failed to create cipher: {{err}}", err)
}
// Create the GCM mode AEAD
gcm, err := cipher.NewGCM(aesCipher)
if err != nil {
return nil, fmt.Errorf("failed to initialize GCM mode")
}
return gcm, nil
}
// encrypt is used to encrypt a value
func (b *AESGCMBarrier) encrypt(path string, term uint32, gcm cipher.AEAD, plain []byte) ([]byte, error) {
// Allocate the output buffer with room for tern, version byte,
// nonce, GCM tag and the plaintext
capacity := termSize + 1 + gcm.NonceSize() + gcm.Overhead() + len(plain)
size := termSize + 1 + gcm.NonceSize()
out := make([]byte, size, capacity)
// Set the key term
binary.BigEndian.PutUint32(out[:4], term)
// Set the version byte
out[4] = b.currentAESGCMVersionByte
// Generate a random nonce
nonce := out[5 : 5+gcm.NonceSize()]
n, err := rand.Read(nonce)
if err != nil {
return nil, err
}
if n != len(nonce) {
return nil, errors.New("unable to read enough random bytes to fill gcm nonce")
}
// Seal the output
switch b.currentAESGCMVersionByte {
case AESGCMVersion1:
out = gcm.Seal(out, nonce, plain, nil)
case AESGCMVersion2:
aad := []byte(nil)
if path != "" {
aad = []byte(path)
}
out = gcm.Seal(out, nonce, plain, aad)
default:
panic("Unknown AESGCM version")
}
return out, nil
}
// decrypt is used to decrypt a value using the keyring
func (b *AESGCMBarrier) decrypt(path string, gcm cipher.AEAD, cipher []byte) ([]byte, error) {
// Capture the parts
nonce := cipher[5 : 5+gcm.NonceSize()]
raw := cipher[5+gcm.NonceSize():]
out := make([]byte, 0, len(raw)-gcm.NonceSize())
// Attempt to open
switch cipher[4] {
case AESGCMVersion1:
return gcm.Open(out, nonce, raw, nil)
case AESGCMVersion2:
aad := []byte(nil)
if path != "" {
aad = []byte(path)
}
return gcm.Open(out, nonce, raw, aad)
default:
return nil, fmt.Errorf("version bytes mis-match")
}
}
// Encrypt is used to encrypt in-memory for the BarrierEncryptor interface
func (b *AESGCMBarrier) Encrypt(ctx context.Context, key string, plaintext []byte) ([]byte, error) {
b.l.RLock()
if b.sealed {
b.l.RUnlock()
return nil, ErrBarrierSealed
}
term := b.keyring.ActiveTerm()
primary, err := b.aeadForTerm(term)
b.l.RUnlock()
if err != nil {
return nil, err
}
ciphertext, err := b.encrypt(key, term, primary, plaintext)
if err != nil {
return nil, err
}
return ciphertext, nil
}
// Decrypt is used to decrypt in-memory for the BarrierEncryptor interface
func (b *AESGCMBarrier) Decrypt(ctx context.Context, key string, ciphertext []byte) ([]byte, error) {
b.l.RLock()
if b.sealed {
b.l.RUnlock()
return nil, ErrBarrierSealed
}
// Verify the term
term := binary.BigEndian.Uint32(ciphertext[:4])
// Get the GCM by term
// It is expensive to do this first but it is not a
// normal case that this won't match
gcm, err := b.aeadForTerm(term)
b.l.RUnlock()
if err != nil {
return nil, err
}
if gcm == nil {
return nil, fmt.Errorf("no decryption key available for term %d", term)
}
// Decrypt the ciphertext
plain, err := b.decrypt(key, gcm, ciphertext)
if err != nil {
return nil, errwrap.Wrapf("decryption failed: {{err}}", err)
}
return plain, nil
}
func (b *AESGCMBarrier) Keyring() (*Keyring, error) {
b.l.RLock()
defer b.l.RUnlock()
if b.sealed {
return nil, ErrBarrierSealed
}
return b.keyring.Clone(), nil
}