From cfd087b155ac436103c879c27475d62f74760468 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Sat, 19 May 2018 23:43:48 -0400 Subject: [PATCH] Update rekey methods to indicate proper error codes in responses --- api/sys_rekey.go | 54 +++++--- api/sys_rekey_ext_test.go | 38 ++++++ http/sys_rekey.go | 40 +++--- vault/rekey.go | 258 ++++++++++++++++++++------------------ vault/seal.go | 9 +- 5 files changed, 234 insertions(+), 165 deletions(-) create mode 100644 api/sys_rekey_ext_test.go diff --git a/api/sys_rekey.go b/api/sys_rekey.go index 8b2d0435d0..8067217dfd 100644 --- a/api/sys_rekey.go +++ b/api/sys_rekey.go @@ -169,31 +169,35 @@ func (c *Sys) RekeyDeleteRecoveryBackup() error { } type RekeyInitRequest struct { - SecretShares int `json:"secret_shares"` - SecretThreshold int `json:"secret_threshold"` - StoredShares int `json:"stored_shares"` - PGPKeys []string `json:"pgp_keys"` - Backup bool + SecretShares int `json:"secret_shares"` + SecretThreshold int `json:"secret_threshold"` + StoredShares int `json:"stored_shares"` + PGPKeys []string `json:"pgp_keys"` + Backup bool + RequireVerification bool `json:"require_verification"` } type RekeyStatusResponse struct { - Nonce string `json:"nonce"` - Started bool `json:"started"` - T int `json:"t"` - N int `json:"n"` - Progress int `json:"progress"` - Required int `json:"required"` - PGPFingerprints []string `json:"pgp_fingerprints"` - Backup bool `json:"backup"` + Nonce string `json:"nonce"` + Started bool `json:"started"` + T int `json:"t"` + N int `json:"n"` + Progress int `json:"progress"` + Required int `json:"required"` + PGPFingerprints []string `json:"pgp_fingerprints"` + Backup bool `json:"backup"` + VerificationRequired bool `json:"verification_required"` } type RekeyUpdateResponse struct { - Nonce string `json:"nonce"` - Complete bool `json:"complete"` - Keys []string `json:"keys"` - KeysB64 []string `json:"keys_base64"` - PGPFingerprints []string `json:"pgp_fingerprints"` - Backup bool `json:"backup"` + Nonce string `json:"nonce"` + Complete bool `json:"complete"` + Keys []string `json:"keys"` + KeysB64 []string `json:"keys_base64"` + PGPFingerprints []string `json:"pgp_fingerprints"` + Backup bool `json:"backup"` + VerificationRequired bool `json:"verification_required"` + VerificationNonce string `json:"verification_nonce,omitempty"` } type RekeyRetrieveResponse struct { @@ -201,3 +205,15 @@ type RekeyRetrieveResponse struct { Keys map[string][]string `json:"keys"` KeysB64 map[string][]string `json:"keys_base64"` } + +type RekeyVerificationStatusResponse struct { + Nonce string `json:"nonce"` + T int `json:"t"` + N int `json:"n"` + Progress int `json:"progress"` +} + +type RekeyVerificationUpdateResponse struct { + Nonce string `json:"nonce"` + Complete bool `json:"complete"` +} diff --git a/api/sys_rekey_ext_test.go b/api/sys_rekey_ext_test.go new file mode 100644 index 0000000000..c3952d7aad --- /dev/null +++ b/api/sys_rekey_ext_test.go @@ -0,0 +1,38 @@ +package api_test + +import ( + "testing" + + "github.com/hashicorp/vault/api" + vaulthttp "github.com/hashicorp/vault/http" + "github.com/hashicorp/vault/vault" +) + +func TestSysRekey_Verification(t *testing.T) { + cluster := vault.NewTestCluster(t, nil, &vault.TestClusterOptions{ + HandlerFunc: vaulthttp.Handler, + }) + cluster.Start() + defer cluster.Cleanup() + + vault.TestWaitActive(t, cluster.Cores[0].Core) + client := cluster.Cores[0].Client + + vault.DefaultSealPretendsToAllowRecoveryKeys = true + vault.DefaultSealPretendsToAllowStoredShares = true + status, err := client.Sys().RekeyInit(&api.RekeyInitRequest{ + StoredShares: 1, + RequireVerification: true, + }) + if err != nil { + t.Fatal(err) + } + if status == nil { + t.Fatal("empty status") + } + + /* + cluster.EnsureCoresSealed(t) + cluster.UnsealCores(t) + */ +} diff --git a/http/sys_rekey.go b/http/sys_rekey.go index 4eb733a414..ea58862328 100644 --- a/http/sys_rekey.go +++ b/http/sys_rekey.go @@ -47,9 +47,9 @@ func handleSysRekeyInit(core *vault.Core, recovery bool) http.Handler { } func handleSysRekeyInitGet(ctx context.Context, core *vault.Core, recovery bool, w http.ResponseWriter, r *http.Request) { - barrierConfig, err := core.SealAccess().BarrierConfig(ctx) - if err != nil { - respondError(w, http.StatusInternalServerError, err) + barrierConfig, barrierConfErr := core.SealAccess().BarrierConfig(ctx) + if barrierConfErr != nil { + respondError(w, http.StatusInternalServerError, barrierConfErr) return } if barrierConfig == nil { @@ -60,20 +60,20 @@ func handleSysRekeyInitGet(ctx context.Context, core *vault.Core, recovery bool, // Get the rekey configuration rekeyConf, err := core.RekeyConfig(recovery) if err != nil { - respondError(w, http.StatusInternalServerError, err) + respondError(w, err.Code(), err) return } // Get the progress progress, err := core.RekeyProgress(recovery) if err != nil { - respondError(w, http.StatusInternalServerError, err) + respondError(w, err.Code(), err) return } sealThreshold, err := core.RekeyThreshold(ctx, recovery) if err != nil { - respondError(w, http.StatusInternalServerError, err) + respondError(w, err.Code(), err) return } @@ -146,7 +146,7 @@ func handleSysRekeyInitPut(ctx context.Context, core *vault.Core, recovery bool, VerificationRequired: req.RequireVerification, }, recovery) if err != nil { - respondError(w, http.StatusBadRequest, err) + respondError(w, err.Code(), err) return } @@ -156,7 +156,7 @@ func handleSysRekeyInitPut(ctx context.Context, core *vault.Core, recovery bool, func handleSysRekeyInitDelete(core *vault.Core, recovery bool, w http.ResponseWriter, r *http.Request) { err := core.RekeyCancel(recovery) if err != nil { - respondError(w, http.StatusInternalServerError, err) + respondError(w, err.Code(), err) return } respondOk(w, nil) @@ -203,9 +203,9 @@ func handleSysRekeyUpdate(core *vault.Core, recovery bool) http.Handler { defer cancel() // Use the key to make progress on rekey - result, err := core.RekeyUpdate(ctx, key, req.Nonce, recovery) - if err != nil { - respondError(w, http.StatusBadRequest, err) + result, rekeyErr := core.RekeyUpdate(ctx, key, req.Nonce, recovery) + if rekeyErr != nil { + respondError(w, rekeyErr.Code(), err) return } @@ -269,9 +269,9 @@ func handleSysRekeyVerify(core *vault.Core, recovery bool) http.Handler { } func handleSysRekeyVerifyGet(ctx context.Context, core *vault.Core, recovery bool, w http.ResponseWriter, r *http.Request) { - barrierConfig, err := core.SealAccess().BarrierConfig(ctx) - if err != nil { - respondError(w, http.StatusInternalServerError, err) + barrierConfig, barrierConfErr := core.SealAccess().BarrierConfig(ctx) + if barrierConfErr != nil { + respondError(w, http.StatusInternalServerError, barrierConfErr) return } if barrierConfig == nil { @@ -282,7 +282,7 @@ func handleSysRekeyVerifyGet(ctx context.Context, core *vault.Core, recovery boo // Get the rekey configuration rekeyConf, err := core.RekeyConfig(recovery) if err != nil { - respondError(w, http.StatusInternalServerError, err) + respondError(w, err.Code(), err) return } if rekeyConf == nil { @@ -293,7 +293,7 @@ func handleSysRekeyVerifyGet(ctx context.Context, core *vault.Core, recovery boo // Get the progress progress, err := core.RekeyVerifyProgress(recovery) if err != nil { - respondError(w, http.StatusInternalServerError, err) + respondError(w, err.Code(), err) return } @@ -310,7 +310,7 @@ func handleSysRekeyVerifyGet(ctx context.Context, core *vault.Core, recovery boo func handleSysRekeyVerifyDelete(core *vault.Core, recovery bool, w http.ResponseWriter, r *http.Request) { err := core.RekeyVerifyRestart(recovery) if err != nil { - respondError(w, http.StatusInternalServerError, err) + respondError(w, err.Code(), err) return } @@ -354,9 +354,9 @@ func handleSysRekeyVerifyPut(ctx context.Context, core *vault.Core, recovery boo defer cancel() // Use the key to make progress on rekey - result, err := core.RekeyVerify(ctx, key, recovery) - if err != nil { - respondError(w, http.StatusBadRequest, err) + result, rekeyErr := core.RekeyVerify(ctx, key, recovery) + if rekeyErr != nil { + respondError(w, rekeyErr.Code(), err) return } diff --git a/vault/rekey.go b/vault/rekey.go index 687ce2df8a..d90767c0d9 100644 --- a/vault/rekey.go +++ b/vault/rekey.go @@ -6,14 +6,15 @@ import ( "crypto/subtle" "encoding/hex" "encoding/json" - "errors" "fmt" + "net/http" "github.com/hashicorp/errwrap" "github.com/hashicorp/go-uuid" "github.com/hashicorp/vault/helper/consts" "github.com/hashicorp/vault/helper/jsonutil" "github.com/hashicorp/vault/helper/pgpkeys" + "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/physical" "github.com/hashicorp/vault/shamir" ) @@ -56,14 +57,14 @@ type RekeyBackup struct { // the recovery key threshold, depending on whether rekey is being // performed on the recovery key, or whether the seal supports // recovery keys. -func (c *Core) RekeyThreshold(ctx context.Context, recovery bool) (int, error) { +func (c *Core) RekeyThreshold(ctx context.Context, recovery bool) (int, logical.HTTPCodedError) { c.stateLock.RLock() defer c.stateLock.RUnlock() if c.sealed { - return 0, consts.ErrSealed + return 0, logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error()) } if c.standby { - return 0, consts.ErrStandby + return 0, logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error()) } c.rekeyLock.RLock() @@ -80,21 +81,24 @@ func (c *Core) RekeyThreshold(ctx context.Context, recovery bool) (int, error) { config, err = c.seal.BarrierConfig(ctx) } if err != nil { - return 0, err + return 0, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("unable to look up config: {{err}}", err).Error()) + } + if config == nil { + return 0, logical.CodedError(http.StatusBadRequest, ErrNotInit.Error()) } return config.SecretThreshold, nil } // RekeyProgress is used to return the rekey progress (num shares). -func (c *Core) RekeyProgress(recovery bool) (int, error) { +func (c *Core) RekeyProgress(recovery bool) (int, logical.HTTPCodedError) { c.stateLock.RLock() defer c.stateLock.RUnlock() if c.sealed { - return 0, consts.ErrSealed + return 0, logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error()) } if c.standby { - return 0, consts.ErrStandby + return 0, logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error()) } c.rekeyLock.RLock() @@ -108,14 +112,14 @@ func (c *Core) RekeyProgress(recovery bool) (int, error) { // RekeyVerifyProgress is used to return the rekey progress (num shares) during // verification. -func (c *Core) RekeyVerifyProgress(recovery bool) (int, error) { +func (c *Core) RekeyVerifyProgress(recovery bool) (int, logical.HTTPCodedError) { c.stateLock.RLock() defer c.stateLock.RUnlock() if c.sealed { - return 0, consts.ErrSealed + return 0, logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error()) } if c.standby { - return 0, consts.ErrStandby + return 0, logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error()) } c.rekeyLock.RLock() @@ -128,14 +132,14 @@ func (c *Core) RekeyVerifyProgress(recovery bool) (int, error) { } // RekeyConfig is used to read the rekey configuration -func (c *Core) RekeyConfig(recovery bool) (*SealConfig, error) { +func (c *Core) RekeyConfig(recovery bool) (*SealConfig, logical.HTTPCodedError) { c.stateLock.RLock() defer c.stateLock.RUnlock() if c.sealed { - return nil, consts.ErrSealed + return nil, logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error()) } if c.standby { - return nil, consts.ErrStandby + return nil, logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error()) } c.rekeyLock.Lock() @@ -158,7 +162,7 @@ func (c *Core) RekeyConfig(recovery bool) (*SealConfig, error) { // RekeyInit will either initialize the rekey of barrier or recovery key. // recovery determines whether this is a rekey on the barrier or recovery key. -func (c *Core) RekeyInit(config *SealConfig, recovery bool) error { +func (c *Core) RekeyInit(config *SealConfig, recovery bool) logical.HTTPCodedError { if recovery { return c.RecoveryRekeyInit(config) } @@ -166,39 +170,39 @@ func (c *Core) RekeyInit(config *SealConfig, recovery bool) error { } // BarrierRekeyInit is used to initialize the rekey settings for the barrier key -func (c *Core) BarrierRekeyInit(config *SealConfig) error { +func (c *Core) BarrierRekeyInit(config *SealConfig) logical.HTTPCodedError { if config.StoredShares > 0 { if !c.seal.StoredKeysSupported() { - return fmt.Errorf("storing keys not supported by barrier seal") + return logical.CodedError(http.StatusBadRequest, "storing keys not supported by barrier seal") } if len(config.PGPKeys) > 0 { - return fmt.Errorf("PGP key encryption not supported when using stored keys") + return logical.CodedError(http.StatusBadRequest, "PGP key encryption not supported when using stored keys") } if config.Backup { - return fmt.Errorf("key backup not supported when using stored keys") + return logical.CodedError(http.StatusBadRequest, "key backup not supported when using stored keys") } } if c.seal.RecoveryKeySupported() && c.seal.RecoveryType() == config.Type { c.logger.Debug("using recovery seal configuration to rekey barrier key") if config.VerificationRequired { - return fmt.Errorf("requiring verification not supported when rekeying the barrier key with recovery keys") + return logical.CodedError(http.StatusBadRequest, "requiring verification not supported when rekeying the barrier key with recovery keys") } } // Check if the seal configuration is valid if err := config.Validate(); err != nil { c.logger.Error("invalid rekey seal configuration", "error", err) - return errwrap.Wrapf("invalid rekey seal configuration: {{err}}", err) + return logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("invalid rekey seal configuration: {{err}}", err).Error()) } c.stateLock.RLock() defer c.stateLock.RUnlock() if c.sealed { - return consts.ErrSealed + return logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error()) } if c.standby { - return consts.ErrStandby + return logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error()) } c.rekeyLock.Lock() @@ -206,7 +210,7 @@ func (c *Core) BarrierRekeyInit(config *SealConfig) error { // Prevent multiple concurrent re-keys if c.barrierRekeyConfig != nil { - return fmt.Errorf("rekey already in progress") + return logical.CodedError(http.StatusBadRequest, "rekey already in progress") } // Copy the configuration @@ -216,7 +220,7 @@ func (c *Core) BarrierRekeyInit(config *SealConfig) error { nonce, err := uuid.GenerateUUID() if err != nil { c.barrierRekeyConfig = nil - return err + return logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("error generating nonce for procedure: {{err}}", err).Error()) } c.barrierRekeyConfig.Nonce = nonce @@ -227,28 +231,28 @@ func (c *Core) BarrierRekeyInit(config *SealConfig) error { } // RecoveryRekeyInit is used to initialize the rekey settings for the recovery key -func (c *Core) RecoveryRekeyInit(config *SealConfig) error { +func (c *Core) RecoveryRekeyInit(config *SealConfig) logical.HTTPCodedError { if config.StoredShares > 0 { - return fmt.Errorf("stored shares not supported by recovery key") + return logical.CodedError(http.StatusBadRequest, "stored shares not supported by recovery key") } // Check if the seal configuration is valid if err := config.Validate(); err != nil { c.logger.Error("invalid recovery configuration", "error", err) - return errwrap.Wrapf("invalid recovery configuration: {{err}}", err) + return logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("invalid recovery configuration: {{err}}", err).Error()) } if !c.seal.RecoveryKeySupported() { - return fmt.Errorf("recovery keys not supported") + return logical.CodedError(http.StatusBadRequest, "recovery keys not supported") } c.stateLock.RLock() defer c.stateLock.RUnlock() if c.sealed { - return consts.ErrSealed + return logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error()) } if c.standby { - return consts.ErrStandby + return logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error()) } c.rekeyLock.Lock() @@ -256,7 +260,7 @@ func (c *Core) RecoveryRekeyInit(config *SealConfig) error { // Prevent multiple concurrent re-keys if c.recoveryRekeyConfig != nil { - return fmt.Errorf("rekey already in progress") + return logical.CodedError(http.StatusBadRequest, "rekey already in progress") } // Copy the configuration @@ -266,7 +270,7 @@ func (c *Core) RecoveryRekeyInit(config *SealConfig) error { nonce, err := uuid.GenerateUUID() if err != nil { c.recoveryRekeyConfig = nil - return err + return logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("error generating nonce for procedure: {{err}}", err).Error()) } c.recoveryRekeyConfig.Nonce = nonce @@ -277,7 +281,7 @@ func (c *Core) RecoveryRekeyInit(config *SealConfig) error { } // RekeyUpdate is used to provide a new key part for the barrier or recovery key. -func (c *Core) RekeyUpdate(ctx context.Context, key []byte, nonce string, recovery bool) (*RekeyResult, error) { +func (c *Core) RekeyUpdate(ctx context.Context, key []byte, nonce string, recovery bool) (*RekeyResult, logical.HTTPCodedError) { if recovery { return c.RecoveryRekeyUpdate(ctx, key, nonce) } @@ -289,25 +293,25 @@ func (c *Core) RekeyUpdate(ctx context.Context, key []byte, nonce string, recove // key. // // N.B.: If recovery keys are used to rekey, the new barrier key shares are not returned. -func (c *Core) BarrierRekeyUpdate(ctx context.Context, key []byte, nonce string) (*RekeyResult, error) { +func (c *Core) BarrierRekeyUpdate(ctx context.Context, key []byte, nonce string) (*RekeyResult, logical.HTTPCodedError) { // Ensure we are already unsealed c.stateLock.RLock() defer c.stateLock.RUnlock() if c.sealed { - return nil, consts.ErrSealed + return nil, logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error()) } if c.standby { - return nil, consts.ErrStandby + return nil, logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error()) } // Verify the key length min, max := c.barrier.KeyLength() max += shamir.ShareOverhead if len(key) < min { - return nil, &ErrInvalidKey{fmt.Sprintf("key is shorter than minimum %d bytes", min)} + return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("key is shorter than minimum %d bytes", min)) } if len(key) > max { - return nil, &ErrInvalidKey{fmt.Sprintf("key is longer than maximum %d bytes", max)} + return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("key is longer than maximum %d bytes", max)) } c.rekeyLock.Lock() @@ -324,27 +328,26 @@ func (c *Core) BarrierRekeyUpdate(ctx context.Context, key []byte, nonce string) existingConfig, err = c.seal.BarrierConfig(ctx) } if err != nil { - return nil, err + return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to fetch existing config: {{err}}", err).Error()) } - // Ensure the barrier is initialized if existingConfig == nil { - return nil, ErrNotInit + return nil, logical.CodedError(http.StatusBadRequest, ErrNotInit.Error()) } // Ensure a rekey is in progress if c.barrierRekeyConfig == nil { - return nil, fmt.Errorf("no barrier rekey in progress") + return nil, logical.CodedError(http.StatusBadRequest, "no barrier rekey in progress") } if nonce != c.barrierRekeyConfig.Nonce { - return nil, fmt.Errorf("incorrect nonce supplied; nonce for this rekey operation is %q", c.barrierRekeyConfig.Nonce) + return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("incorrect nonce supplied; nonce for this rekey operation is %q", c.barrierRekeyConfig.Nonce)) } // Check if we already have this piece for _, existing := range c.barrierRekeyProgress { if subtle.ConstantTimeCompare(existing, key) == 1 { - return nil, fmt.Errorf("given key has already been provided during this generation operation") + return nil, logical.CodedError(http.StatusBadRequest, "given key has already been provided during this generation operation") } } @@ -371,19 +374,19 @@ func (c *Core) BarrierRekeyUpdate(ctx context.Context, key []byte, nonce string) } else { recoveredKey, err = shamir.Combine(c.barrierRekeyProgress) if err != nil { - return nil, errwrap.Wrapf("failed to compute master key: {{err}}", err) + return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to compute master key: {{err}}", err).Error()) } } if useRecovery { if err := c.seal.VerifyRecoveryKey(ctx, recoveredKey); err != nil { - c.logger.Error("rekey aborted, recovery key verification failed", "error", err) - return nil, err + c.logger.Error("rekey recovery key verification failed", "error", err) + return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("recovery key verification failed: {{err}}", err).Error()) } } else { if err := c.barrier.VerifyMaster(recoveredKey); err != nil { - c.logger.Error("rekey aborted, master key verification failed", "error", err) - return nil, err + c.logger.Error("master key verification failed", "error", err) + return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("master key verification failed: {{err}}", err).Error()) } } @@ -391,7 +394,7 @@ func (c *Core) BarrierRekeyUpdate(ctx context.Context, key []byte, nonce string) newMasterKey, err := c.barrier.GenerateKey() if err != nil { c.logger.Error("failed to generate master key", "error", err) - return nil, errwrap.Wrapf("master key generation failed: {{err}}", err) + return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("master key generation failed: {{err}}", err).Error()) } results := &RekeyResult{ @@ -406,7 +409,7 @@ func (c *Core) BarrierRekeyUpdate(ctx context.Context, key []byte, nonce string) shares, err := shamir.Split(newMasterKey, c.barrierRekeyConfig.SecretShares, c.barrierRekeyConfig.SecretThreshold) if err != nil { c.logger.Error("failed to generate shares", "error", err) - return nil, errwrap.Wrapf("failed to generate shares: {{err}}", err) + return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to generate shares: {{err}}", err).Error()) } results.SecretShares = shares } @@ -429,7 +432,7 @@ func (c *Core) BarrierRekeyUpdate(ctx context.Context, key []byte, nonce string) } results.PGPFingerprints, results.SecretShares, err = pgpkeys.EncryptShares(hexEncodedShares, c.barrierRekeyConfig.PGPKeys) if err != nil { - return nil, err + return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to encrypt shares: {{err}}", err).Error()) } // If backup is enabled, store backup info in vault.coreBarrierUnsealKeysBackupPath @@ -451,7 +454,7 @@ func (c *Core) BarrierRekeyUpdate(ctx context.Context, key []byte, nonce string) buf, err := json.Marshal(backupVals) if err != nil { c.logger.Error("failed to marshal unseal key backup", "error", err) - return nil, errwrap.Wrapf("failed to marshal unseal key backup: {{err}}", err) + return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to marshal unseal key backup: {{err}}", err).Error()) } pe := &physical.Entry{ Key: coreBarrierUnsealKeysBackupPath, @@ -459,7 +462,7 @@ func (c *Core) BarrierRekeyUpdate(ctx context.Context, key []byte, nonce string) } if err = c.physical.Put(ctx, pe); err != nil { c.logger.Error("failed to save unseal key backup", "error", err) - return nil, errwrap.Wrapf("failed to save unseal key backup: {{err}}", err) + return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to save unseal key backup: {{err}}", err).Error()) } } } @@ -467,7 +470,7 @@ func (c *Core) BarrierRekeyUpdate(ctx context.Context, key []byte, nonce string) if keysToStore != nil { if err := c.seal.SetStoredKeys(ctx, keysToStore); err != nil { c.logger.Error("failed to store keys", "error", err) - return nil, errwrap.Wrapf("failed to store keys: {{err}}", err) + return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to store keys: {{err}}", err).Error()) } } @@ -476,7 +479,7 @@ func (c *Core) BarrierRekeyUpdate(ctx context.Context, key []byte, nonce string) nonce, err := uuid.GenerateUUID() if err != nil { c.barrierRekeyConfig = nil - return nil, err + return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to generate verification nonce: {{err}}", err).Error()) } c.barrierRekeyConfig.VerificationNonce = nonce c.barrierRekeyConfig.VerificationKey = newMasterKey @@ -487,18 +490,18 @@ func (c *Core) BarrierRekeyUpdate(ctx context.Context, key []byte, nonce string) } if err := c.performBarrierRekey(ctx, newMasterKey); err != nil { - return nil, err + return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to perform barrier rekey: {{err}}", err).Error()) } c.barrierRekeyConfig = nil return results, nil } -func (c *Core) performBarrierRekey(ctx context.Context, newMasterKey []byte) error { +func (c *Core) performBarrierRekey(ctx context.Context, newMasterKey []byte) logical.HTTPCodedError { // Rekey the barrier if err := c.barrier.Rekey(ctx, newMasterKey); err != nil { c.logger.Error("failed to rekey barrier", "error", err) - return errwrap.Wrapf("failed to rekey barrier: {{err}}", err) + return logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to rekey barrier: {{err}}", err).Error()) } if c.logger.IsInfo() { c.logger.Info("security barrier rekeyed", "shares", c.barrierRekeyConfig.SecretShares, "threshold", c.barrierRekeyConfig.SecretThreshold) @@ -508,7 +511,7 @@ func (c *Core) performBarrierRekey(ctx context.Context, newMasterKey []byte) err if err := c.seal.SetBarrierConfig(ctx, c.barrierRekeyConfig); err != nil { c.logger.Error("error saving rekey seal configuration", "error", err) - return errwrap.Wrapf("failed to save rekey seal configuration: {{err}}", err) + return logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to save rekey seal configuration: {{err}}", err).Error()) } // Write to the canary path, which will force a synchronous truing during @@ -518,32 +521,32 @@ func (c *Core) performBarrierRekey(ctx context.Context, newMasterKey []byte) err Value: []byte(c.barrierRekeyConfig.Nonce), }); err != nil { c.logger.Error("error saving keyring canary", "error", err) - return errwrap.Wrapf("failed to save keyring canary: {{err}}", err) + return logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to save keyring canary: {{err}}", err).Error()) } return nil } // RecoveryRekeyUpdate is used to provide a new key part -func (c *Core) RecoveryRekeyUpdate(ctx context.Context, key []byte, nonce string) (*RekeyResult, error) { +func (c *Core) RecoveryRekeyUpdate(ctx context.Context, key []byte, nonce string) (*RekeyResult, logical.HTTPCodedError) { // Ensure we are already unsealed c.stateLock.RLock() defer c.stateLock.RUnlock() if c.sealed { - return nil, consts.ErrSealed + return nil, logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error()) } if c.standby { - return nil, consts.ErrStandby + return nil, logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error()) } // Verify the key length min, max := c.barrier.KeyLength() max += shamir.ShareOverhead if len(key) < min { - return nil, &ErrInvalidKey{fmt.Sprintf("key is shorter than minimum %d bytes", min)} + return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("key is shorter than minimum %d bytes", min)) } if len(key) > max { - return nil, &ErrInvalidKey{fmt.Sprintf("key is longer than maximum %d bytes", max)} + return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("key is longer than maximum %d bytes", max)) } c.rekeyLock.Lock() @@ -552,27 +555,26 @@ func (c *Core) RecoveryRekeyUpdate(ctx context.Context, key []byte, nonce string // Get the seal configuration existingConfig, err := c.seal.RecoveryConfig(ctx) if err != nil { - return nil, err + return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to fetch existing recovery config: {{err}}", err).Error()) } - // Ensure the seal is initialized if existingConfig == nil { - return nil, ErrNotInit + return nil, logical.CodedError(http.StatusBadRequest, ErrNotInit.Error()) } // Ensure a rekey is in progress if c.recoveryRekeyConfig == nil { - return nil, fmt.Errorf("no recovery rekey in progress") + return nil, logical.CodedError(http.StatusBadRequest, "no recovery rekey in progress") } if nonce != c.recoveryRekeyConfig.Nonce { - return nil, fmt.Errorf("incorrect nonce supplied; nonce for this rekey operation is %q", c.recoveryRekeyConfig.Nonce) + return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("incorrect nonce supplied; nonce for this rekey operation is %q", c.recoveryRekeyConfig.Nonce)) } // Check if we already have this piece for _, existing := range c.recoveryRekeyProgress { if subtle.ConstantTimeCompare(existing, key) == 1 { - return nil, fmt.Errorf("given key has already been provided during this rekey operation") + return nil, logical.CodedError(http.StatusBadRequest, "given key has already been provided during this rekey operation") } } @@ -599,21 +601,21 @@ func (c *Core) RecoveryRekeyUpdate(ctx context.Context, key []byte, nonce string } else { recoveryKey, err = shamir.Combine(c.recoveryRekeyProgress) if err != nil { - return nil, errwrap.Wrapf("failed to compute recovery key: {{err}}", err) + return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to compute recovery key: {{err}}", err).Error()) } } // Verify the recovery key if err := c.seal.VerifyRecoveryKey(ctx, recoveryKey); err != nil { - c.logger.Error("rekey aborted, recovery key verification failed", "error", err) - return nil, err + c.logger.Error("recovery key verification failed", "error", err) + return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("recovery key verification failed: {{err}}", err).Error()) } // Generate a new master key newMasterKey, err := c.barrier.GenerateKey() if err != nil { c.logger.Error("failed to generate recovery key", "error", err) - return nil, errwrap.Wrapf("recovery key generation failed: {{err}}", err) + return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("recovery key generation failed: {{err}}", err).Error()) } // Return the master key if only a single key part is used @@ -628,7 +630,7 @@ func (c *Core) RecoveryRekeyUpdate(ctx context.Context, key []byte, nonce string shares, err := shamir.Split(newMasterKey, c.recoveryRekeyConfig.SecretShares, c.recoveryRekeyConfig.SecretThreshold) if err != nil { c.logger.Error("failed to generate shares", "error", err) - return nil, errwrap.Wrapf("failed to generate shares: {{err}}", err) + return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to generate shares: {{err}}", err).Error()) } results.SecretShares = shares } @@ -640,7 +642,7 @@ func (c *Core) RecoveryRekeyUpdate(ctx context.Context, key []byte, nonce string } results.PGPFingerprints, results.SecretShares, err = pgpkeys.EncryptShares(hexEncodedShares, c.recoveryRekeyConfig.PGPKeys) if err != nil { - return nil, err + return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to encrypt shares: {{err}}", err).Error()) } if c.recoveryRekeyConfig.Backup { @@ -661,7 +663,7 @@ func (c *Core) RecoveryRekeyUpdate(ctx context.Context, key []byte, nonce string buf, err := json.Marshal(backupVals) if err != nil { c.logger.Error("failed to marshal recovery key backup", "error", err) - return nil, errwrap.Wrapf("failed to marshal recovery key backup: {{err}}", err) + return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to marshal recovery key backup: {{err}}", err).Error()) } pe := &physical.Entry{ Key: coreRecoveryUnsealKeysBackupPath, @@ -669,7 +671,7 @@ func (c *Core) RecoveryRekeyUpdate(ctx context.Context, key []byte, nonce string } if err = c.physical.Put(ctx, pe); err != nil { c.logger.Error("failed to save unseal key backup", "error", err) - return nil, errwrap.Wrapf("failed to save unseal key backup: {{err}}", err) + return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to save unseal key backup: {{err}}", err).Error()) } } } @@ -680,7 +682,7 @@ func (c *Core) RecoveryRekeyUpdate(ctx context.Context, key []byte, nonce string nonce, err := uuid.GenerateUUID() if err != nil { c.recoveryRekeyConfig = nil - return nil, err + return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to generate verification nonce: {{err}}", err).Error()) } c.recoveryRekeyConfig.VerificationNonce = nonce c.recoveryRekeyConfig.VerificationKey = newMasterKey @@ -691,24 +693,24 @@ func (c *Core) RecoveryRekeyUpdate(ctx context.Context, key []byte, nonce string } if err := c.performRecoveryRekey(ctx, newMasterKey); err != nil { - return nil, err + return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to perform recovery rekey: {{err}}", err).Error()) } c.recoveryRekeyConfig = nil return results, nil } -func (c *Core) performRecoveryRekey(ctx context.Context, newMasterKey []byte) error { +func (c *Core) performRecoveryRekey(ctx context.Context, newMasterKey []byte) logical.HTTPCodedError { if err := c.seal.SetRecoveryKey(ctx, newMasterKey); err != nil { c.logger.Error("failed to set recovery key", "error", err) - return errwrap.Wrapf("failed to set recovery key: {{err}}", err) + return logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to set recovery key: {{err}}", err).Error()) } c.recoveryRekeyConfig.VerificationKey = nil if err := c.seal.SetRecoveryConfig(ctx, c.recoveryRekeyConfig); err != nil { c.logger.Error("error saving rekey seal configuration", "error", err) - return errwrap.Wrapf("failed to save rekey seal configuration: {{err}}", err) + return logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to save rekey seal configuration: {{err}}", err).Error()) } // Write to the canary path, which will force a synchronous truing during @@ -718,38 +720,38 @@ func (c *Core) performRecoveryRekey(ctx context.Context, newMasterKey []byte) er Value: []byte(c.recoveryRekeyConfig.Nonce), }); err != nil { c.logger.Error("error saving keyring canary", "error", err) - return errwrap.Wrapf("failed to save keyring canary: {{err}}", err) + return logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to save keyring canary: {{err}}", err).Error()) } return nil } -func (c *Core) RekeyVerify(ctx context.Context, key []byte, recovery bool) (*RekeyVerifyResult, error) { +func (c *Core) RekeyVerify(ctx context.Context, key []byte, recovery bool) (*RekeyVerifyResult, logical.HTTPCodedError) { if recovery { return c.RecoveryRekeyVerify(ctx, key) } return c.BarrierRekeyVerify(ctx, key) } -func (c *Core) BarrierRekeyVerify(ctx context.Context, key []byte) (*RekeyVerifyResult, error) { +func (c *Core) BarrierRekeyVerify(ctx context.Context, key []byte) (*RekeyVerifyResult, logical.HTTPCodedError) { // Ensure we are already unsealed c.stateLock.RLock() defer c.stateLock.RUnlock() if c.sealed { - return nil, consts.ErrSealed + return nil, logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error()) } if c.standby { - return nil, consts.ErrStandby + return nil, logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error()) } // Verify the key length min, max := c.barrier.KeyLength() max += shamir.ShareOverhead if len(key) < min { - return nil, &ErrInvalidKey{fmt.Sprintf("key is shorter than minimum %d bytes", min)} + return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("key is shorter than minimum %d bytes", min)) } if len(key) > max { - return nil, &ErrInvalidKey{fmt.Sprintf("key is longer than maximum %d bytes", max)} + return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("key is longer than maximum %d bytes", max)) } c.rekeyLock.Lock() @@ -757,13 +759,13 @@ func (c *Core) BarrierRekeyVerify(ctx context.Context, key []byte) (*RekeyVerify // Ensure a rekey is in progress if c.barrierRekeyConfig == nil { - return nil, fmt.Errorf("no barrier rekey in progress") + return nil, logical.CodedError(http.StatusBadRequest, "no barrier rekey in progress") } // Check if we already have this piece for _, existing := range c.barrierRekeyVerifyProgress { if subtle.ConstantTimeCompare(existing, key) == 1 { - return nil, fmt.Errorf("given key has already been provided during this verify operation") + return nil, logical.CodedError(http.StatusBadRequest, "given key has already been provided during this verify operation") } } @@ -797,17 +799,17 @@ func (c *Core) BarrierRekeyVerify(ctx context.Context, key []byte) (*RekeyVerify } else { recoveredKey, err = shamir.Combine(c.barrierRekeyVerifyProgress) if err != nil { - return nil, errwrap.Wrapf("failed to compute master key for verification: {{err}}", err) + return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to compute master key for verification: {{err}}", err).Error()) } } if subtle.ConstantTimeCompare(recoveredKey, c.barrierRekeyConfig.VerificationKey) != 1 { c.logger.Error("rekey verification failed") - return nil, errors.New("rekey verification failed") + return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("verification for barrier rekey failed: {{err}}", err).Error()) } if err := c.performBarrierRekey(ctx, recoveredKey); err != nil { - return nil, err + return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to perform barrier rekey: {{err}}", err).Error()) } res := &RekeyVerifyResult{ @@ -818,25 +820,25 @@ func (c *Core) BarrierRekeyVerify(ctx context.Context, key []byte) (*RekeyVerify return res, nil } -func (c *Core) RecoveryRekeyVerify(ctx context.Context, key []byte) (*RekeyVerifyResult, error) { +func (c *Core) RecoveryRekeyVerify(ctx context.Context, key []byte) (*RekeyVerifyResult, logical.HTTPCodedError) { // Ensure we are already unsealed c.stateLock.RLock() defer c.stateLock.RUnlock() if c.sealed { - return nil, consts.ErrSealed + return nil, logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error()) } if c.standby { - return nil, consts.ErrStandby + return nil, logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error()) } // Verify the key length min, max := c.barrier.KeyLength() max += shamir.ShareOverhead if len(key) < min { - return nil, &ErrInvalidKey{fmt.Sprintf("key is shorter than minimum %d bytes", min)} + return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("key is shorter than minimum %d bytes", min)) } if len(key) > max { - return nil, &ErrInvalidKey{fmt.Sprintf("key is longer than maximum %d bytes", max)} + return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("key is longer than maximum %d bytes", max)) } c.rekeyLock.Lock() @@ -844,13 +846,13 @@ func (c *Core) RecoveryRekeyVerify(ctx context.Context, key []byte) (*RekeyVerif // Ensure a rekey is in progress if c.recoveryRekeyConfig == nil { - return nil, fmt.Errorf("no recovery rekey in progress") + return nil, logical.CodedError(http.StatusBadRequest, "no recovery rekey in progress") } // Check if we already have this piece for _, existing := range c.recoveryRekeyVerifyProgress { if subtle.ConstantTimeCompare(existing, key) == 1 { - return nil, fmt.Errorf("given key has already been provided during this verify operation") + return nil, logical.CodedError(http.StatusBadRequest, "given key has already been provided during this verify operation") } } @@ -884,18 +886,18 @@ func (c *Core) RecoveryRekeyVerify(ctx context.Context, key []byte) (*RekeyVerif } else { recoveryKey, err = shamir.Combine(c.recoveryRekeyVerifyProgress) if err != nil { - return nil, errwrap.Wrapf("failed to compute recovery key: {{err}}", err) + return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to compute recovery key: {{err}}", err).Error()) } } // Verify the recovery key if subtle.ConstantTimeCompare(recoveryKey, c.recoveryRekeyConfig.VerificationKey) != 1 { c.logger.Error("rekey verification failed", "error", err) - return nil, errors.New("rekey verification failed") + return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("verification for recovery rekey failed: {{err}}", err).Error()) } if err := c.performRecoveryRekey(ctx, recoveryKey); err != nil { - return nil, err + return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to perform recovery key rekey: {{err}}", err).Error()) } res := &RekeyVerifyResult{ @@ -907,14 +909,14 @@ func (c *Core) RecoveryRekeyVerify(ctx context.Context, key []byte) (*RekeyVerif } // RekeyCancel is used to cancel an in-progress rekey -func (c *Core) RekeyCancel(recovery bool) error { +func (c *Core) RekeyCancel(recovery bool) logical.HTTPCodedError { c.stateLock.RLock() defer c.stateLock.RUnlock() if c.sealed { - return consts.ErrSealed + return logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error()) } if c.standby { - return consts.ErrStandby + return logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error()) } c.rekeyLock.Lock() @@ -932,14 +934,14 @@ func (c *Core) RekeyCancel(recovery bool) error { } // RekeyVerifyCancel is used to start the verification process over -func (c *Core) RekeyVerifyRestart(recovery bool) error { +func (c *Core) RekeyVerifyRestart(recovery bool) logical.HTTPCodedError { c.stateLock.RLock() defer c.stateLock.RUnlock() if c.sealed { - return consts.ErrSealed + return logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error()) } if c.standby { - return consts.ErrStandby + return logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error()) } c.rekeyLock.Lock() @@ -967,14 +969,14 @@ func (c *Core) RekeyVerifyRestart(recovery bool) error { // RekeyRetrieveBackup is used to retrieve any backed-up PGP-encrypted unseal // keys -func (c *Core) RekeyRetrieveBackup(ctx context.Context, recovery bool) (*RekeyBackup, error) { +func (c *Core) RekeyRetrieveBackup(ctx context.Context, recovery bool) (*RekeyBackup, logical.HTTPCodedError) { c.stateLock.RLock() defer c.stateLock.RUnlock() if c.sealed { - return nil, consts.ErrSealed + return nil, logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error()) } if c.standby { - return nil, consts.ErrStandby + return nil, logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error()) } c.rekeyLock.RLock() @@ -988,7 +990,7 @@ func (c *Core) RekeyRetrieveBackup(ctx context.Context, recovery bool) (*RekeyBa entry, err = c.physical.Get(ctx, coreBarrierUnsealKeysBackupPath) } if err != nil { - return nil, err + return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("error getting keys from backup: {{err}}", err).Error()) } if entry == nil { return nil, nil @@ -997,28 +999,36 @@ func (c *Core) RekeyRetrieveBackup(ctx context.Context, recovery bool) (*RekeyBa ret := &RekeyBackup{} err = jsonutil.DecodeJSON(entry.Value, ret) if err != nil { - return nil, err + return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("error decoding backup keys: {{err}}", err).Error()) } return ret, nil } // RekeyDeleteBackup is used to delete any backed-up PGP-encrypted unseal keys -func (c *Core) RekeyDeleteBackup(ctx context.Context, recovery bool) error { +func (c *Core) RekeyDeleteBackup(ctx context.Context, recovery bool) logical.HTTPCodedError { c.stateLock.RLock() defer c.stateLock.RUnlock() if c.sealed { - return consts.ErrSealed + return logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error()) } if c.standby { - return consts.ErrStandby + return logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error()) } c.rekeyLock.Lock() defer c.rekeyLock.Unlock() if recovery { - return c.physical.Delete(ctx, coreRecoveryUnsealKeysBackupPath) + err := c.physical.Delete(ctx, coreRecoveryUnsealKeysBackupPath) + if err != nil { + return logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("error deleting backup keys: {{err}}", err).Error()) + } + return nil } - return c.physical.Delete(ctx, coreBarrierUnsealKeysBackupPath) + err := c.physical.Delete(ctx, coreBarrierUnsealKeysBackupPath) + if err != nil { + return logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("error deleting backup keys: {{err}}", err).Error()) + } + return nil } diff --git a/vault/seal.go b/vault/seal.go index ac7ffd7dce..70839361bc 100644 --- a/vault/seal.go +++ b/vault/seal.go @@ -86,6 +86,11 @@ type Seal interface { VerifyRecoveryKey(context.Context, []byte) error } +var ( + DefaultSealPretendsToAllowRecoveryKeys bool + DefaultSealPretendsToAllowStoredShares bool +) + type defaultSeal struct { config atomic.Value core *Core @@ -121,11 +126,11 @@ func (d *defaultSeal) BarrierType() string { } func (d *defaultSeal) StoredKeysSupported() bool { - return false + return DefaultSealPretendsToAllowStoredShares } func (d *defaultSeal) RecoveryKeySupported() bool { - return false + return DefaultSealPretendsToAllowRecoveryKeys } func (d *defaultSeal) SetStoredKeys(ctx context.Context, keys [][]byte) error {