1package vault
2
3import (
4	"bytes"
5	"context"
6	"encoding/base64"
7	"encoding/json"
8	"fmt"
9	"sync/atomic"
10
11	"github.com/golang/protobuf/proto"
12	"github.com/hashicorp/errwrap"
13	"github.com/hashicorp/vault/sdk/helper/jsonutil"
14	"github.com/hashicorp/vault/sdk/physical"
15	"github.com/hashicorp/vault/vault/seal"
16	"github.com/keybase/go-crypto/openpgp"
17	"github.com/keybase/go-crypto/openpgp/packet"
18)
19
20const (
21	// barrierSealConfigPath is the path used to store our seal configuration.
22	// This value is stored in plaintext, since we must be able to read it even
23	// with the Vault sealed. This is required so that we know how many secret
24	// parts must be used to reconstruct the master key.
25	barrierSealConfigPath = "core/seal-config"
26
27	// recoverySealConfigPath is the path to the recovery key seal
28	// configuration. It lives inside the barrier.
29	// DEPRECATED: Use recoverySealConfigPlaintextPath instead.
30	recoverySealConfigPath = "core/recovery-seal-config"
31
32	// recoverySealConfigPlaintextPath is the path to the recovery key seal
33	// configuration. This is stored in plaintext so that we can perform
34	// auto-unseal.
35	recoverySealConfigPlaintextPath = "core/recovery-config"
36
37	// recoveryKeyPath is the path to the recovery key
38	recoveryKeyPath = "core/recovery-key"
39
40	// StoredBarrierKeysPath is the path used for storing HSM-encrypted unseal keys
41	StoredBarrierKeysPath = "core/hsm/barrier-unseal-keys"
42
43	// hsmStoredIVPath is the path to the initialization vector for stored keys
44	hsmStoredIVPath = "core/hsm/iv"
45)
46
47const (
48	RecoveryTypeUnsupported = "unsupported"
49	RecoveryTypeShamir      = "shamir"
50)
51
52type StoredKeysSupport int
53
54const (
55	// The 0 value of StoredKeysSupport is an invalid option
56	StoredKeysInvalid StoredKeysSupport = iota
57	StoredKeysNotSupported
58	StoredKeysSupportedGeneric
59	StoredKeysSupportedShamirMaster
60)
61
62func (s StoredKeysSupport) String() string {
63	switch s {
64	case StoredKeysNotSupported:
65		return "Old-style Shamir"
66	case StoredKeysSupportedGeneric:
67		return "AutoUnseal"
68	case StoredKeysSupportedShamirMaster:
69		return "New-style Shamir"
70	default:
71		return "Invalid StoredKeys type"
72	}
73}
74
75type Seal interface {
76	SetCore(*Core)
77	Init(context.Context) error
78	Finalize(context.Context) error
79
80	StoredKeysSupported() StoredKeysSupport
81	SealWrapable() bool
82	SetStoredKeys(context.Context, [][]byte) error
83	GetStoredKeys(context.Context) ([][]byte, error)
84
85	BarrierType() string
86	BarrierConfig(context.Context) (*SealConfig, error)
87	SetBarrierConfig(context.Context, *SealConfig) error
88	SetCachedBarrierConfig(*SealConfig)
89
90	RecoveryKeySupported() bool
91	RecoveryType() string
92	RecoveryConfig(context.Context) (*SealConfig, error)
93	RecoveryKey(context.Context) ([]byte, error)
94	SetRecoveryConfig(context.Context, *SealConfig) error
95	SetCachedRecoveryConfig(*SealConfig)
96	SetRecoveryKey(context.Context, []byte) error
97	VerifyRecoveryKey(context.Context, []byte) error
98
99	GetAccess() seal.Access
100}
101
102type defaultSeal struct {
103	access seal.Access
104	config atomic.Value
105	core   *Core
106}
107
108func NewDefaultSeal(lowLevel seal.Access) Seal {
109	ret := &defaultSeal{
110		access: lowLevel,
111	}
112	ret.config.Store((*SealConfig)(nil))
113	return ret
114}
115
116func (d *defaultSeal) SealWrapable() bool {
117	return false
118}
119
120func (d *defaultSeal) checkCore() error {
121	if d.core == nil {
122		return fmt.Errorf("seal does not have a core set")
123	}
124	return nil
125}
126
127func (d *defaultSeal) GetAccess() seal.Access {
128	return d.access
129}
130
131func (d *defaultSeal) SetAccess(access seal.Access) {
132	d.access = access
133}
134
135func (d *defaultSeal) SetCore(core *Core) {
136	d.core = core
137}
138
139func (d *defaultSeal) Init(ctx context.Context) error {
140	return nil
141}
142
143func (d *defaultSeal) Finalize(ctx context.Context) error {
144	return nil
145}
146
147func (d *defaultSeal) BarrierType() string {
148	return seal.Shamir
149}
150
151func (d *defaultSeal) StoredKeysSupported() StoredKeysSupport {
152	isLegacy, err := d.LegacySeal()
153	if err != nil {
154		if d.core != nil && d.core.logger != nil {
155			d.core.logger.Error("no seal config found, can't determine if legacy or new-style shamir")
156		}
157		return StoredKeysInvalid
158	}
159	switch {
160	case isLegacy:
161		return StoredKeysNotSupported
162	default:
163		return StoredKeysSupportedShamirMaster
164	}
165}
166
167func (d *defaultSeal) RecoveryKeySupported() bool {
168	return false
169}
170
171func (d *defaultSeal) SetStoredKeys(ctx context.Context, keys [][]byte) error {
172	isLegacy, err := d.LegacySeal()
173	if err != nil {
174		return err
175	}
176	if isLegacy {
177		return fmt.Errorf("stored keys are not supported")
178	}
179	return writeStoredKeys(ctx, d.core.physical, d.access, keys)
180}
181
182func (d *defaultSeal) LegacySeal() (bool, error) {
183	cfg := d.config.Load().(*SealConfig)
184	if cfg == nil {
185		return false, fmt.Errorf("no seal config found, can't determine if legacy or new-style shamir")
186	}
187	return cfg.StoredShares == 0, nil
188}
189
190func (d *defaultSeal) GetStoredKeys(ctx context.Context) ([][]byte, error) {
191	isLegacy, err := d.LegacySeal()
192	if err != nil {
193		return nil, err
194	}
195	if isLegacy {
196		return nil, fmt.Errorf("stored keys are not supported")
197	}
198	keys, err := readStoredKeys(ctx, d.core.physical, d.access)
199	return keys, err
200}
201
202func (d *defaultSeal) BarrierConfig(ctx context.Context) (*SealConfig, error) {
203	cfg := d.config.Load().(*SealConfig)
204	if cfg != nil {
205		return cfg.Clone(), nil
206	}
207
208	if err := d.checkCore(); err != nil {
209		return nil, err
210	}
211
212	// Fetch the core configuration
213	pe, err := d.core.physical.Get(ctx, barrierSealConfigPath)
214	if err != nil {
215		d.core.logger.Error("failed to read seal configuration", "error", err)
216		return nil, errwrap.Wrapf("failed to check seal configuration: {{err}}", err)
217	}
218
219	// If the seal configuration is missing, we are not initialized
220	if pe == nil {
221		d.core.logger.Info("seal configuration missing, not initialized")
222		return nil, nil
223	}
224
225	var conf SealConfig
226
227	// Decode the barrier entry
228	if err := jsonutil.DecodeJSON(pe.Value, &conf); err != nil {
229		d.core.logger.Error("failed to decode seal configuration", "error", err)
230		return nil, errwrap.Wrapf("failed to decode seal configuration: {{err}}", err)
231	}
232
233	switch conf.Type {
234	// This case should not be valid for other types as only this is the default
235	case "":
236		conf.Type = d.BarrierType()
237	case d.BarrierType():
238	default:
239		d.core.logger.Error("barrier seal type does not match expected type", "barrier_seal_type", conf.Type, "loaded_seal_type", d.BarrierType())
240		return nil, fmt.Errorf("barrier seal type of %q does not match expected type of %q", conf.Type, d.BarrierType())
241	}
242
243	// Check for a valid seal configuration
244	if err := conf.Validate(); err != nil {
245		d.core.logger.Error("invalid seal configuration", "error", err)
246		return nil, errwrap.Wrapf("seal validation failed: {{err}}", err)
247	}
248
249	d.config.Store(&conf)
250	return conf.Clone(), nil
251}
252
253func (d *defaultSeal) SetBarrierConfig(ctx context.Context, config *SealConfig) error {
254	if err := d.checkCore(); err != nil {
255		return err
256	}
257
258	// Provide a way to wipe out the cached value (also prevents actually
259	// saving a nil config)
260	if config == nil {
261		d.config.Store((*SealConfig)(nil))
262		return nil
263	}
264
265	config.Type = d.BarrierType()
266
267	// If we are doing a raft unseal we do not want to persist the barrier config
268	// because storage isn't setup yet.
269	if d.core.isRaftUnseal() {
270		d.config.Store(config.Clone())
271		return nil
272	}
273
274	// Encode the seal configuration
275	buf, err := json.Marshal(config)
276	if err != nil {
277		return errwrap.Wrapf("failed to encode seal configuration: {{err}}", err)
278	}
279
280	// Store the seal configuration
281	pe := &physical.Entry{
282		Key:   barrierSealConfigPath,
283		Value: buf,
284	}
285
286	if err := d.core.physical.Put(ctx, pe); err != nil {
287		d.core.logger.Error("failed to write seal configuration", "error", err)
288		return errwrap.Wrapf("failed to write seal configuration: {{err}}", err)
289	}
290
291	d.config.Store(config.Clone())
292
293	return nil
294}
295
296func (d *defaultSeal) SetCachedBarrierConfig(config *SealConfig) {
297	d.config.Store(config)
298}
299
300func (d *defaultSeal) RecoveryType() string {
301	return RecoveryTypeUnsupported
302}
303
304func (d *defaultSeal) RecoveryConfig(ctx context.Context) (*SealConfig, error) {
305	return nil, fmt.Errorf("recovery not supported")
306}
307
308func (d *defaultSeal) RecoveryKey(ctx context.Context) ([]byte, error) {
309	return nil, fmt.Errorf("recovery not supported")
310}
311
312func (d *defaultSeal) SetRecoveryConfig(ctx context.Context, config *SealConfig) error {
313	return fmt.Errorf("recovery not supported")
314}
315
316func (d *defaultSeal) SetCachedRecoveryConfig(config *SealConfig) {
317}
318
319func (d *defaultSeal) VerifyRecoveryKey(ctx context.Context, key []byte) error {
320	return fmt.Errorf("recovery not supported")
321}
322
323func (d *defaultSeal) SetRecoveryKey(ctx context.Context, key []byte) error {
324	return fmt.Errorf("recovery not supported")
325}
326
327// SealConfig is used to describe the seal configuration
328type SealConfig struct {
329	// The type, for sanity checking
330	Type string `json:"type" mapstructure:"type"`
331
332	// SecretShares is the number of shares the secret is split into. This is
333	// the N value of Shamir.
334	SecretShares int `json:"secret_shares" mapstructure:"secret_shares"`
335
336	// SecretThreshold is the number of parts required to open the vault. This
337	// is the T value of Shamir.
338	SecretThreshold int `json:"secret_threshold" mapstructure:"secret_threshold"`
339
340	// PGPKeys is the array of public PGP keys used, if requested, to encrypt
341	// the output unseal tokens. If provided, it sets the value of
342	// SecretShares. Ordering is important.
343	PGPKeys []string `json:"pgp_keys" mapstructure:"pgp_keys"`
344
345	// Nonce is a nonce generated by Vault used to ensure that when unseal keys
346	// are submitted for a rekey operation, the rekey operation itself is the
347	// one intended. This prevents hijacking of the rekey operation, since it
348	// is unauthenticated.
349	Nonce string `json:"nonce" mapstructure:"nonce"`
350
351	// Backup indicates whether or not a backup of PGP-encrypted unseal keys
352	// should be stored at coreUnsealKeysBackupPath after successful rekeying.
353	Backup bool `json:"backup" mapstructure:"backup"`
354
355	// How many keys to store, for seals that support storage.  Always 0 or 1.
356	StoredShares int `json:"stored_shares" mapstructure:"stored_shares"`
357
358	// Stores the progress of the rekey operation (key shares)
359	RekeyProgress [][]byte `json:"-"`
360
361	// VerificationRequired indicates that after a rekey validation must be
362	// performed (via providing shares from the new key) before the new key is
363	// actually installed. This is omitted from JSON as we don't persist the
364	// new key, it lives only in memory.
365	VerificationRequired bool `json:"-"`
366
367	// VerificationKey is the new key that we will roll to after successful
368	// validation
369	VerificationKey []byte `json:"-"`
370
371	// VerificationNonce stores the current operation nonce for verification
372	VerificationNonce string `json:"-"`
373
374	// Stores the progress of the verification operation (key shares)
375	VerificationProgress [][]byte `json:"-"`
376}
377
378// Validate is used to sanity check the seal configuration
379func (s *SealConfig) Validate() error {
380	if s.SecretShares < 1 {
381		return fmt.Errorf("shares must be at least one")
382	}
383	if s.SecretThreshold < 1 {
384		return fmt.Errorf("threshold must be at least one")
385	}
386	if s.SecretShares > 1 && s.SecretThreshold == 1 {
387		return fmt.Errorf("threshold must be greater than one for multiple shares")
388	}
389	if s.SecretShares > 255 {
390		return fmt.Errorf("shares must be less than 256")
391	}
392	if s.SecretThreshold > 255 {
393		return fmt.Errorf("threshold must be less than 256")
394	}
395	if s.SecretThreshold > s.SecretShares {
396		return fmt.Errorf("threshold cannot be larger than shares")
397	}
398	if s.StoredShares > 1 {
399		return fmt.Errorf("stored keys cannot be larger than 1")
400	}
401	if len(s.PGPKeys) > 0 && len(s.PGPKeys) != s.SecretShares {
402		return fmt.Errorf("count mismatch between number of provided PGP keys and number of shares")
403	}
404	if len(s.PGPKeys) > 0 {
405		for _, keystring := range s.PGPKeys {
406			data, err := base64.StdEncoding.DecodeString(keystring)
407			if err != nil {
408				return errwrap.Wrapf("error decoding given PGP key: {{err}}", err)
409			}
410			_, err = openpgp.ReadEntity(packet.NewReader(bytes.NewBuffer(data)))
411			if err != nil {
412				return errwrap.Wrapf("error parsing given PGP key: {{err}}", err)
413			}
414		}
415	}
416	return nil
417}
418
419func (s *SealConfig) Clone() *SealConfig {
420	ret := &SealConfig{
421		Type:                 s.Type,
422		SecretShares:         s.SecretShares,
423		SecretThreshold:      s.SecretThreshold,
424		Nonce:                s.Nonce,
425		Backup:               s.Backup,
426		StoredShares:         s.StoredShares,
427		VerificationRequired: s.VerificationRequired,
428		VerificationNonce:    s.VerificationNonce,
429	}
430	if len(s.PGPKeys) > 0 {
431		ret.PGPKeys = make([]string, len(s.PGPKeys))
432		copy(ret.PGPKeys, s.PGPKeys)
433	}
434	if len(s.VerificationKey) > 0 {
435		ret.VerificationKey = make([]byte, len(s.VerificationKey))
436		copy(ret.VerificationKey, s.VerificationKey)
437	}
438	return ret
439}
440
441func writeStoredKeys(ctx context.Context, storage physical.Backend, encryptor seal.Encryptor, keys [][]byte) error {
442	if keys == nil {
443		return fmt.Errorf("keys were nil")
444	}
445	if len(keys) == 0 {
446		return fmt.Errorf("no keys provided")
447	}
448
449	buf, err := json.Marshal(keys)
450	if err != nil {
451		return errwrap.Wrapf("failed to encode keys for storage: {{err}}", err)
452	}
453
454	// Encrypt and marshal the keys
455	blobInfo, err := encryptor.Encrypt(ctx, buf)
456	if err != nil {
457		return errwrap.Wrapf("failed to encrypt keys for storage: {{err}}", err)
458	}
459
460	value, err := proto.Marshal(blobInfo)
461	if err != nil {
462		return errwrap.Wrapf("failed to marshal value for storage: {{err}}", err)
463	}
464
465	// Store the seal configuration.
466	pe := &physical.Entry{
467		Key:   StoredBarrierKeysPath,
468		Value: value,
469	}
470
471	if err := storage.Put(ctx, pe); err != nil {
472		return errwrap.Wrapf("failed to write keys to storage: {{err}}", err)
473	}
474
475	return nil
476}
477
478func readStoredKeys(ctx context.Context, storage physical.Backend, encryptor seal.Encryptor) ([][]byte, error) {
479	pe, err := storage.Get(ctx, StoredBarrierKeysPath)
480	if err != nil {
481		return nil, errwrap.Wrapf("failed to fetch stored keys: {{err}}", err)
482	}
483
484	// This is not strictly an error; we may not have any stored keys, for
485	// instance, if we're not initialized
486	if pe == nil {
487		return nil, nil
488	}
489
490	blobInfo := &physical.EncryptedBlobInfo{}
491	if err := proto.Unmarshal(pe.Value, blobInfo); err != nil {
492		return nil, errwrap.Wrapf("failed to proto decode stored keys: {{err}}", err)
493	}
494
495	pt, err := encryptor.Decrypt(ctx, blobInfo)
496	if err != nil {
497		return nil, errwrap.Wrapf("failed to decrypt encrypted stored keys: {{err}}", err)
498	}
499
500	// Decode the barrier entry
501	var keys [][]byte
502	if err := json.Unmarshal(pt, &keys); err != nil {
503		return nil, fmt.Errorf("failed to decode stored keys: %v", err)
504	}
505
506	return keys, nil
507}
508