1// +build !enterprise
2
3package vault
4
5import (
6	"bytes"
7	"context"
8	"sync"
9	"testing"
10
11	proto "github.com/golang/protobuf/proto"
12	log "github.com/hashicorp/go-hclog"
13	wrapping "github.com/hashicorp/go-kms-wrapping"
14	"github.com/hashicorp/vault/sdk/physical"
15	"github.com/hashicorp/vault/sdk/physical/inmem"
16)
17
18func TestSealUnwrapper(t *testing.T) {
19	logger := log.New(&log.LoggerOptions{
20		Mutex: &sync.Mutex{},
21	})
22
23	// Test without transactions
24	phys, err := inmem.NewInmemHA(nil, logger)
25	if err != nil {
26		t.Fatal(err)
27	}
28	performTestSealUnwrapper(t, phys, logger)
29
30	// Test with transactions
31	tPhys, err := inmem.NewTransactionalInmemHA(nil, logger)
32	if err != nil {
33		t.Fatal(err)
34	}
35	performTestSealUnwrapper(t, tPhys, logger)
36}
37
38func performTestSealUnwrapper(t *testing.T, phys physical.Backend, logger log.Logger) {
39	ctx := context.Background()
40	base := &CoreConfig{
41		Physical: phys,
42	}
43	cluster := NewTestCluster(t, base, &TestClusterOptions{
44		Logger: logger,
45	})
46	cluster.Start()
47	defer cluster.Cleanup()
48
49	// Read a value and then save it back in a proto message
50	entry, err := phys.Get(ctx, "core/master")
51	if err != nil {
52		t.Fatal(err)
53	}
54	if len(entry.Value) == 0 {
55		t.Fatal("got no value for master")
56	}
57	// Save the original for comparison later
58	origBytes := make([]byte, len(entry.Value))
59	copy(origBytes, entry.Value)
60	se := &wrapping.EncryptedBlobInfo{
61		Ciphertext: entry.Value,
62	}
63	seb, err := proto.Marshal(se)
64	if err != nil {
65		t.Fatal(err)
66	}
67	// Write the canary
68	entry.Value = append(seb, 's')
69	// Save the protobuf value for comparison later
70	pBytes := make([]byte, len(entry.Value))
71	copy(pBytes, entry.Value)
72	if err = phys.Put(ctx, entry); err != nil {
73		t.Fatal(err)
74	}
75
76	// At this point we should be able to read through the standby cores,
77	// successfully decode it, but be able to unmarshal it when read back from
78	// the underlying physical store. When we read from active, it should both
79	// successfully decode it and persist it back.
80	checkValue := func(core *Core, wrapped bool) {
81		entry, err := core.physical.Get(ctx, "core/master")
82		if err != nil {
83			t.Fatal(err)
84		}
85		if !bytes.Equal(entry.Value, origBytes) {
86			t.Fatalf("mismatched original bytes and unwrapped entry bytes:\ngot:\n%v\nexpected:\n%v", entry.Value, origBytes)
87		}
88		underlyingEntry, err := phys.Get(ctx, "core/master")
89		if err != nil {
90			t.Fatal(err)
91		}
92		switch wrapped {
93		case true:
94			if !bytes.Equal(underlyingEntry.Value, pBytes) {
95				t.Fatalf("mismatched original bytes and proto entry bytes:\ngot:\n%v\nexpected:\n%v", underlyingEntry.Value, pBytes)
96			}
97		default:
98			if !bytes.Equal(underlyingEntry.Value, origBytes) {
99				t.Fatalf("mismatched original bytes and unwrapped entry bytes:\ngot:\n%v\nexpected:\n%v", underlyingEntry.Value, origBytes)
100			}
101		}
102	}
103
104	TestWaitActive(t, cluster.Cores[0].Core)
105	checkValue(cluster.Cores[2].Core, true)
106	checkValue(cluster.Cores[1].Core, true)
107	checkValue(cluster.Cores[0].Core, false)
108}
109