open-vault/vault/barrier_aes_gcm_test.go
ncabatoff 1c98152fa0
Shamir seals now come in two varieties: legacy and new-style. (#7694)
Shamir seals now come in two varieties: legacy and new-style. Legacy
Shamir is automatically converted to new-style when a rekey operation
is performed. All new Vault initializations using Shamir are new-style.

New-style Shamir writes an encrypted master key to storage, just like
AutoUnseal. The stored master key is encrypted using the shared key that
is split via Shamir's algorithm. Thus when unsealing, we take the key
fragments given, combine them into a Key-Encryption-Key, and use that
to decrypt the master key on disk. Then the master key is used to read
the keyring that decrypts the barrier.
2019-10-18 14:46:00 -04:00

599 lines
13 KiB
Go

package vault
import (
"bytes"
"context"
"crypto/rand"
"encoding/json"
"testing"
log "github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/sdk/helper/logging"
"github.com/hashicorp/vault/sdk/logical"
"github.com/hashicorp/vault/sdk/physical"
"github.com/hashicorp/vault/sdk/physical/inmem"
)
var (
logger = logging.NewVaultLogger(log.Trace)
)
// mockBarrier returns a physical backend, security barrier, and master key
func mockBarrier(t testing.TB) (physical.Backend, SecurityBarrier, []byte) {
inm, err := inmem.NewInmem(nil, logger)
if err != nil {
t.Fatalf("err: %v", err)
}
b, err := NewAESGCMBarrier(inm)
if err != nil {
t.Fatalf("err: %v", err)
}
// Initialize and unseal
key, _ := b.GenerateKey(rand.Reader)
b.Initialize(context.Background(), key, nil, rand.Reader)
b.Unseal(context.Background(), key)
return inm, b, key
}
func TestAESGCMBarrier_Basic(t *testing.T) {
inm, err := inmem.NewInmem(nil, logger)
if err != nil {
t.Fatalf("err: %v", err)
}
b, err := NewAESGCMBarrier(inm)
if err != nil {
t.Fatalf("err: %v", err)
}
testBarrier(t, b)
}
func TestAESGCMBarrier_Rotate(t *testing.T) {
inm, err := inmem.NewInmem(nil, logger)
if err != nil {
t.Fatalf("err: %v", err)
}
b, err := NewAESGCMBarrier(inm)
if err != nil {
t.Fatalf("err: %v", err)
}
testBarrier_Rotate(t, b)
}
func TestAESGCMBarrier_Upgrade(t *testing.T) {
inm, err := inmem.NewInmem(nil, logger)
if err != nil {
t.Fatalf("err: %v", err)
}
b1, err := NewAESGCMBarrier(inm)
if err != nil {
t.Fatalf("err: %v", err)
}
b2, err := NewAESGCMBarrier(inm)
if err != nil {
t.Fatalf("err: %v", err)
}
testBarrier_Upgrade(t, b1, b2)
}
func TestAESGCMBarrier_Upgrade_Rekey(t *testing.T) {
inm, err := inmem.NewInmem(nil, logger)
if err != nil {
t.Fatalf("err: %v", err)
}
b1, err := NewAESGCMBarrier(inm)
if err != nil {
t.Fatalf("err: %v", err)
}
b2, err := NewAESGCMBarrier(inm)
if err != nil {
t.Fatalf("err: %v", err)
}
testBarrier_Upgrade_Rekey(t, b1, b2)
}
func TestAESGCMBarrier_Rekey(t *testing.T) {
inm, err := inmem.NewInmem(nil, logger)
if err != nil {
t.Fatalf("err: %v", err)
}
b, err := NewAESGCMBarrier(inm)
if err != nil {
t.Fatalf("err: %v", err)
}
testBarrier_Rekey(t, b)
}
// Test an upgrade from the old (0.1) barrier/init to the new
// core/keyring style
func TestAESGCMBarrier_BackwardsCompatible(t *testing.T) {
inm, err := inmem.NewInmem(nil, logger)
if err != nil {
t.Fatalf("err: %v", err)
}
b, err := NewAESGCMBarrier(inm)
if err != nil {
t.Fatalf("err: %v", err)
}
// Generate a barrier/init entry
encrypt, _ := b.GenerateKey(rand.Reader)
init := &barrierInit{
Version: 1,
Key: encrypt,
}
buf, _ := json.Marshal(init)
// Protect with master key
master, _ := b.GenerateKey(rand.Reader)
gcm, _ := b.aeadFromKey(master)
value, err := b.encrypt(barrierInitPath, initialKeyTerm, gcm, buf)
if err != nil {
t.Fatal(err)
}
// Write to the physical backend
pe := &physical.Entry{
Key: barrierInitPath,
Value: value,
}
inm.Put(context.Background(), pe)
// Create a fake key
gcm, _ = b.aeadFromKey(encrypt)
value, err = b.encrypt("test/foo", initialKeyTerm, gcm, []byte("test"))
if err != nil {
t.Fatal(err)
}
pe = &physical.Entry{
Key: "test/foo",
Value: value,
}
inm.Put(context.Background(), pe)
// Should still be initialized
isInit, err := b.Initialized(context.Background())
if err != nil {
t.Fatalf("err: %v", err)
}
if !isInit {
t.Fatalf("should be initialized")
}
// Unseal should work and migrate online
err = b.Unseal(context.Background(), master)
if err != nil {
t.Fatalf("err: %v", err)
}
// Check for migration
out, err := inm.Get(context.Background(), barrierInitPath)
if err != nil {
t.Fatalf("err: %v", err)
}
if out != nil {
t.Fatalf("should delete old barrier init")
}
// Should have keyring
out, err = inm.Get(context.Background(), keyringPath)
if err != nil {
t.Fatalf("err: %v", err)
}
if out == nil {
t.Fatalf("should have keyring file")
}
// Attempt to read encrypted key
entry, err := b.Get(context.Background(), "test/foo")
if err != nil {
t.Fatalf("err: %v", err)
}
if string(entry.Value) != "test" {
t.Fatalf("bad: %#v", entry)
}
}
// Verify data sent through is encrypted
func TestAESGCMBarrier_Confidential(t *testing.T) {
inm, err := inmem.NewInmem(nil, logger)
if err != nil {
t.Fatalf("err: %v", err)
}
b, err := NewAESGCMBarrier(inm)
if err != nil {
t.Fatalf("err: %v", err)
}
// Initialize and unseal
key, _ := b.GenerateKey(rand.Reader)
b.Initialize(context.Background(), key, nil, rand.Reader)
b.Unseal(context.Background(), key)
// Put a logical entry
entry := &logical.StorageEntry{Key: "test", Value: []byte("test")}
err = b.Put(context.Background(), entry)
if err != nil {
t.Fatalf("err: %v", err)
}
// Check the physical entry
pe, err := inm.Get(context.Background(), "test")
if err != nil {
t.Fatalf("err: %v", err)
}
if pe == nil {
t.Fatalf("missing physical entry")
}
if pe.Key != "test" {
t.Fatalf("bad: %#v", pe)
}
if bytes.Equal(pe.Value, entry.Value) {
t.Fatalf("bad: %#v", pe)
}
}
// Verify data sent through cannot be tampered with
func TestAESGCMBarrier_Integrity(t *testing.T) {
inm, err := inmem.NewInmem(nil, logger)
if err != nil {
t.Fatalf("err: %v", err)
}
b, err := NewAESGCMBarrier(inm)
if err != nil {
t.Fatalf("err: %v", err)
}
// Initialize and unseal
key, _ := b.GenerateKey(rand.Reader)
b.Initialize(context.Background(), key, nil, rand.Reader)
b.Unseal(context.Background(), key)
// Put a logical entry
entry := &logical.StorageEntry{Key: "test", Value: []byte("test")}
err = b.Put(context.Background(), entry)
if err != nil {
t.Fatalf("err: %v", err)
}
// Change a byte in the underlying physical entry
pe, _ := inm.Get(context.Background(), "test")
pe.Value[15]++
err = inm.Put(context.Background(), pe)
if err != nil {
t.Fatalf("err: %v", err)
}
// Read from the barrier
_, err = b.Get(context.Background(), "test")
if err == nil {
t.Fatalf("should fail!")
}
}
// Verify data sent through cannot be moved
func TestAESGCMBarrier_MoveIntegrityV1(t *testing.T) {
inm, err := inmem.NewInmem(nil, logger)
if err != nil {
t.Fatalf("err: %v", err)
}
b, err := NewAESGCMBarrier(inm)
if err != nil {
t.Fatalf("err: %v", err)
}
b.currentAESGCMVersionByte = AESGCMVersion1
// Initialize and unseal
key, _ := b.GenerateKey(rand.Reader)
err = b.Initialize(context.Background(), key, nil, rand.Reader)
if err != nil {
t.Fatalf("err: %v", err)
}
err = b.Unseal(context.Background(), key)
if err != nil {
t.Fatalf("err: %v", err)
}
// Put a logical entry
entry := &logical.StorageEntry{Key: "test", Value: []byte("test")}
err = b.Put(context.Background(), entry)
if err != nil {
t.Fatalf("err: %v", err)
}
// Change the location of the underlying physical entry
pe, _ := inm.Get(context.Background(), "test")
pe.Key = "moved"
err = inm.Put(context.Background(), pe)
if err != nil {
t.Fatalf("err: %v", err)
}
// Read from the barrier
_, err = b.Get(context.Background(), "moved")
if err != nil {
t.Fatalf("should succeed with version 1!")
}
}
func TestAESGCMBarrier_MoveIntegrityV2(t *testing.T) {
inm, err := inmem.NewInmem(nil, logger)
if err != nil {
t.Fatalf("err: %v", err)
}
b, err := NewAESGCMBarrier(inm)
if err != nil {
t.Fatalf("err: %v", err)
}
b.currentAESGCMVersionByte = AESGCMVersion2
// Initialize and unseal
key, _ := b.GenerateKey(rand.Reader)
err = b.Initialize(context.Background(), key, nil, rand.Reader)
if err != nil {
t.Fatalf("err: %v", err)
}
err = b.Unseal(context.Background(), key)
if err != nil {
t.Fatalf("err: %v", err)
}
// Put a logical entry
entry := &logical.StorageEntry{Key: "test", Value: []byte("test")}
err = b.Put(context.Background(), entry)
if err != nil {
t.Fatalf("err: %v", err)
}
// Change the location of the underlying physical entry
pe, _ := inm.Get(context.Background(), "test")
pe.Key = "moved"
err = inm.Put(context.Background(), pe)
if err != nil {
t.Fatalf("err: %v", err)
}
// Read from the barrier
_, err = b.Get(context.Background(), "moved")
if err == nil {
t.Fatalf("should fail with version 2!")
}
}
func TestAESGCMBarrier_UpgradeV1toV2(t *testing.T) {
inm, err := inmem.NewInmem(nil, logger)
if err != nil {
t.Fatalf("err: %v", err)
}
b, err := NewAESGCMBarrier(inm)
if err != nil {
t.Fatalf("err: %v", err)
}
b.currentAESGCMVersionByte = AESGCMVersion1
// Initialize and unseal
key, _ := b.GenerateKey(rand.Reader)
err = b.Initialize(context.Background(), key, nil, rand.Reader)
if err != nil {
t.Fatalf("err: %v", err)
}
err = b.Unseal(context.Background(), key)
if err != nil {
t.Fatalf("err: %v", err)
}
// Put a logical entry
entry := &logical.StorageEntry{Key: "test", Value: []byte("test")}
err = b.Put(context.Background(), entry)
if err != nil {
t.Fatalf("err: %v", err)
}
// Seal
err = b.Seal()
if err != nil {
t.Fatalf("err: %v", err)
}
// Open again as version 2
b, err = NewAESGCMBarrier(inm)
if err != nil {
t.Fatalf("err: %v", err)
}
b.currentAESGCMVersionByte = AESGCMVersion2
// Unseal
err = b.Unseal(context.Background(), key)
if err != nil {
t.Fatalf("err: %v", err)
}
// Check successful decryption
_, err = b.Get(context.Background(), "test")
if err != nil {
t.Fatalf("Upgrade unsuccessful")
}
}
func TestEncrypt_Unique(t *testing.T) {
inm, err := inmem.NewInmem(nil, logger)
if err != nil {
t.Fatalf("err: %v", err)
}
b, err := NewAESGCMBarrier(inm)
if err != nil {
t.Fatalf("err: %v", err)
}
key, _ := b.GenerateKey(rand.Reader)
b.Initialize(context.Background(), key, nil, rand.Reader)
b.Unseal(context.Background(), key)
if b.keyring == nil {
t.Fatalf("barrier is sealed")
}
entry := &logical.StorageEntry{Key: "test", Value: []byte("test")}
term := b.keyring.ActiveTerm()
primary, _ := b.aeadForTerm(term)
first, err := b.encrypt("test", term, primary, entry.Value)
if err != nil {
t.Fatal(err)
}
second, err := b.encrypt("test", term, primary, entry.Value)
if err != nil {
t.Fatal(err)
}
if bytes.Equal(first, second) == true {
t.Fatalf("improper random seeding detected")
}
}
func TestInitialize_KeyLength(t *testing.T) {
inm, err := inmem.NewInmem(nil, logger)
if err != nil {
t.Fatalf("err: %v", err)
}
b, err := NewAESGCMBarrier(inm)
if err != nil {
t.Fatalf("err: %v", err)
}
long := []byte("ThisKeyDoesNotHaveTheRightLength!")
middle := []byte("ThisIsASecretKeyAndMore")
short := []byte("Key")
err = b.Initialize(context.Background(), long, nil, rand.Reader)
if err == nil {
t.Fatalf("key length protection failed")
}
err = b.Initialize(context.Background(), middle, nil, rand.Reader)
if err == nil {
t.Fatalf("key length protection failed")
}
err = b.Initialize(context.Background(), short, nil, rand.Reader)
if err == nil {
t.Fatalf("key length protection failed")
}
}
func TestEncrypt_BarrierEncryptor(t *testing.T) {
inm, err := inmem.NewInmem(nil, logger)
if err != nil {
t.Fatalf("err: %v", err)
}
if err != nil {
t.Fatalf("err: %v", err)
}
b, err := NewAESGCMBarrier(inm)
if err != nil {
t.Fatalf("err: %v", err)
}
// Initialize and unseal
key, _ := b.GenerateKey(rand.Reader)
b.Initialize(context.Background(), key, nil, rand.Reader)
b.Unseal(context.Background(), key)
cipher, err := b.Encrypt(context.Background(), "foo", []byte("quick brown fox"))
if err != nil {
t.Fatalf("err: %v", err)
}
plain, err := b.Decrypt(context.Background(), "foo", cipher)
if err != nil {
t.Fatalf("err: %v", err)
}
if string(plain) != "quick brown fox" {
t.Fatalf("bad: %s", plain)
}
}
func TestAESGCMBarrier_ReloadKeyring(t *testing.T) {
inm, err := inmem.NewInmem(nil, logger)
if err != nil {
t.Fatalf("err: %v", err)
}
b, err := NewAESGCMBarrier(inm)
if err != nil {
t.Fatalf("err: %v", err)
}
// Initialize and unseal
key, _ := b.GenerateKey(rand.Reader)
b.Initialize(context.Background(), key, nil, rand.Reader)
b.Unseal(context.Background(), key)
keyringRaw, err := inm.Get(context.Background(), keyringPath)
if err != nil {
t.Fatalf("err: %v", err)
}
// Encrypt something to test cache invalidation
_, err = b.Encrypt(context.Background(), "foo", []byte("quick brown fox"))
if err != nil {
t.Fatalf("err: %v", err)
}
{
// Create a second barrier and rotate the keyring
b2, err := NewAESGCMBarrier(inm)
if err != nil {
t.Fatalf("err: %v", err)
}
b2.Unseal(context.Background(), key)
_, err = b2.Rotate(context.Background(), rand.Reader)
if err != nil {
t.Fatalf("err: %v", err)
}
}
// Reload the keyring on the first
err = b.ReloadKeyring(context.Background())
if err != nil {
t.Fatalf("err: %v", err)
}
if b.keyring.ActiveTerm() != 2 {
t.Fatal("failed to reload keyring")
}
if len(b.cache) != 0 {
t.Fatal("failed to clear cache")
}
// Encrypt something to test cache invalidation
_, err = b.Encrypt(context.Background(), "foo", []byte("quick brown fox"))
if err != nil {
t.Fatalf("err: %v", err)
}
// Restore old keyring to test rolling back
err = inm.Put(context.Background(), keyringRaw)
if err != nil {
t.Fatalf("err: %v", err)
}
// Reload the keyring on the first
err = b.ReloadKeyring(context.Background())
if err != nil {
t.Fatalf("err: %v", err)
}
if b.keyring.ActiveTerm() != 1 {
t.Fatal("failed to reload keyring")
}
if len(b.cache) != 0 {
t.Fatal("failed to clear cache")
}
}