open-vault/vault/rekey.go

1031 lines
36 KiB
Go
Raw Normal View History

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package vault
import (
"bytes"
"context"
2018-05-18 01:17:52 +00:00
"crypto/subtle"
"encoding/hex"
"encoding/json"
"fmt"
"net/http"
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
aeadwrapper "github.com/hashicorp/go-kms-wrapping/wrappers/aead/v2"
"github.com/hashicorp/go-uuid"
"github.com/hashicorp/vault/helper/pgpkeys"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/sdk/helper/jsonutil"
"github.com/hashicorp/vault/sdk/logical"
"github.com/hashicorp/vault/sdk/physical"
"github.com/hashicorp/vault/shamir"
Raft Storage Backend (#6888) * Work on raft backend * Add logstore locally * Add encryptor and unsealable interfaces * Add clustering support to raft * Remove client and handler * Bootstrap raft on init * Cleanup raft logic a bit * More raft work * Work on TLS config * More work on bootstrapping * Fix build * More work on bootstrapping * More bootstrapping work * fix build * Remove consul dep * Fix build * merged oss/master into raft-storage * Work on bootstrapping * Get bootstrapping to work * Clean up FMS and node-id * Update local node ID logic * Cleanup node-id change * Work on snapshotting * Raft: Add remove peer API (#906) * Add remove peer API * Add some comments * Fix existing snapshotting (#909) * Raft get peers API (#912) * Read raft configuration * address review feedback * Use the Leadership Transfer API to step-down the active node (#918) * Raft join and unseal using Shamir keys (#917) * Raft join using shamir * Store AEAD instead of master key * Split the raft join process to answer the challenge after a successful unseal * get the follower to standby state * Make unseal work * minor changes * Some input checks * reuse the shamir seal access instead of new default seal access * refactor joinRaftSendAnswer function * Synchronously send answer in auto-unseal case * Address review feedback * Raft snapshots (#910) * Fix existing snapshotting * implement the noop snapshotting * Add comments and switch log libraries * add some snapshot tests * add snapshot test file * add TODO * More work on raft snapshotting * progress on the ConfigStore strategy * Don't use two buckets * Update the snapshot store logic to hide the file logic * Add more backend tests * Cleanup code a bit * [WIP] Raft recovery (#938) * Add recovery functionality * remove fmt.Printfs * Fix a few fsm bugs * Add max size value for raft backend (#942) * Add max size value for raft backend * Include physical.ErrValueTooLarge in the message * Raft snapshot Take/Restore API (#926) * Inital work on raft snapshot APIs * Always redirect snapshot install/download requests * More work on the snapshot APIs * Cleanup code a bit * On restore handle special cases * Use the seal to encrypt the sha sum file * Add sealer mechanism and fix some bugs * Call restore while state lock is held * Send restore cb trigger through raft log * Make error messages nicer * Add test helpers * Add snapshot test * Add shamir unseal test * Add more raft snapshot API tests * Fix locking * Change working to initalize * Add underlying raw object to test cluster core * Move leaderUUID to core * Add raft TLS rotation logic (#950) * Add TLS rotation logic * Cleanup logic a bit * Add/Remove from follower state on add/remove peer * add comments * Update more comments * Update request_forwarding_service.proto * Make sure we populate all nodes in the followerstate obj * Update times * Apply review feedback * Add more raft config setting (#947) * Add performance config setting * Add more config options and fix tests * Test Raft Recovery (#944) * Test raft recovery * Leave out a node during recovery * remove unused struct * Update physical/raft/snapshot_test.go * Update physical/raft/snapshot_test.go * fix vendoring * Switch to new raft interface * Remove unused files * Switch a gogo -> proto instance * Remove unneeded vault dep in go.sum * Update helper/testhelpers/testhelpers.go Co-Authored-By: Calvin Leung Huang <cleung2010@gmail.com> * Update vault/cluster/cluster.go * track active key within the keyring itself (#6915) * track active key within the keyring itself * lookup and store using the active key ID * update docstring * minor refactor * Small text fixes (#6912) * Update physical/raft/raft.go Co-Authored-By: Calvin Leung Huang <cleung2010@gmail.com> * review feedback * Move raft logical system into separate file * Update help text a bit * Enforce cluster addr is set and use it for raft bootstrapping * Fix tests * fix http test panic * Pull in latest raft-snapshot library * Add comment
2019-06-20 19:14:58 +00:00
"github.com/hashicorp/vault/vault/seal"
)
2016-04-04 14:44:22 +00:00
const (
2018-03-20 18:54:10 +00:00
// coreUnsealKeysBackupPath is the path used to backup encrypted unseal
2016-04-04 14:44:22 +00:00
// keys if specified during a rekey operation. This is outside of the
// barrier.
coreBarrierUnsealKeysBackupPath = "core/unseal-keys-backup"
2018-03-20 18:54:10 +00:00
// coreRecoveryUnsealKeysBackupPath is the path used to backup encrypted
2016-04-04 14:44:22 +00:00
// recovery keys if specified during a rekey operation. This is outside of
// the barrier.
coreRecoveryUnsealKeysBackupPath = "core/recovery-keys-backup"
)
// RekeyResult is used to provide the key parts back after
// they are generated as part of the rekey.
type RekeyResult struct {
2018-05-18 01:17:52 +00:00
SecretShares [][]byte
PGPFingerprints []string
Backup bool
RecoveryKey bool
VerificationRequired bool
VerificationNonce string
}
type RekeyVerifyResult struct {
2018-05-22 00:54:20 +00:00
Complete bool
Nonce string
}
// RekeyBackup stores the backup copy of PGP-encrypted keys
type RekeyBackup struct {
Nonce string
Keys map[string][]string
}
2017-10-23 18:59:37 +00:00
// RekeyThreshold returns the secret threshold for the current seal
// config. This threshold can either be the barrier key threshold or
// 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, logical.HTTPCodedError) {
2016-04-04 14:44:22 +00:00
c.stateLock.RLock()
defer c.stateLock.RUnlock()
if c.Sealed() {
return 0, logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error())
2016-04-04 14:44:22 +00:00
}
if c.standby {
return 0, logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error())
2016-04-04 14:44:22 +00:00
}
c.rekeyLock.RLock()
defer c.rekeyLock.RUnlock()
var config *SealConfig
var err error
2017-10-23 18:59:37 +00:00
// If we are rekeying the recovery key, or if the seal supports
// recovery keys and we are rekeying the barrier key, we use the
// recovery config as the threshold instead.
if recovery || c.seal.RecoveryKeySupported() {
config, err = c.seal.RecoveryConfig(ctx)
2016-04-04 14:44:22 +00:00
} else {
config, err = c.seal.BarrierConfig(ctx)
2016-04-04 14:44:22 +00:00
}
if err != nil {
return 0, logical.CodedError(http.StatusInternalServerError, fmt.Errorf("unable to look up config: %w", err).Error())
}
if config == nil {
return 0, logical.CodedError(http.StatusBadRequest, ErrNotInit.Error())
2016-04-04 14:44:22 +00:00
}
return config.SecretThreshold, nil
}
2017-10-23 18:59:37 +00:00
// RekeyProgress is used to return the rekey progress (num shares).
func (c *Core) RekeyProgress(recovery, verification bool) (bool, int, logical.HTTPCodedError) {
c.stateLock.RLock()
defer c.stateLock.RUnlock()
if c.Sealed() {
return false, 0, logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error())
}
if c.standby {
return false, 0, logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error())
}
2016-04-04 14:44:22 +00:00
c.rekeyLock.RLock()
defer c.rekeyLock.RUnlock()
2018-05-21 21:46:32 +00:00
var conf *SealConfig
2016-04-04 14:44:22 +00:00
if recovery {
2018-05-21 21:46:32 +00:00
conf = c.recoveryRekeyConfig
} else {
conf = c.barrierRekeyConfig
2016-04-04 14:44:22 +00:00
}
if conf == nil {
return false, 0, logical.CodedError(http.StatusBadRequest, "rekey operation not in progress")
}
2018-05-21 21:46:32 +00:00
if verification {
return len(conf.VerificationKey) > 0, len(conf.VerificationProgress), nil
2018-05-18 01:17:52 +00:00
}
return true, len(conf.RekeyProgress), nil
2018-05-18 01:17:52 +00:00
}
// RekeyConfig is used to read the rekey configuration
func (c *Core) RekeyConfig(recovery bool) (*SealConfig, logical.HTTPCodedError) {
c.stateLock.RLock()
defer c.stateLock.RUnlock()
if c.Sealed() {
return nil, logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error())
}
if c.standby {
return nil, logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error())
}
c.rekeyLock.Lock()
defer c.rekeyLock.Unlock()
// Copy the seal config if any
var conf *SealConfig
2016-04-04 14:44:22 +00:00
if recovery {
if c.recoveryRekeyConfig != nil {
conf = c.recoveryRekeyConfig.Clone()
}
} else {
if c.barrierRekeyConfig != nil {
conf = c.barrierRekeyConfig.Clone()
}
}
2016-04-04 14:44:22 +00:00
return conf, nil
}
2017-10-23 18:59:37 +00:00
// 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) logical.HTTPCodedError {
if config.SecretThreshold > config.SecretShares {
return logical.CodedError(http.StatusBadRequest, "provided threshold greater than the total shares")
}
2016-04-04 14:44:22 +00:00
if recovery {
return c.RecoveryRekeyInit(config)
2016-04-04 14:44:22 +00:00
}
return c.BarrierRekeyInit(config)
2016-04-04 14:44:22 +00:00
}
// BarrierRekeyInit is used to initialize the rekey settings for the barrier key
func (c *Core) BarrierRekeyInit(config *SealConfig) logical.HTTPCodedError {
switch c.seal.BarrierType() {
case wrapping.WrapperTypeShamir:
// As of Vault 1.3 all seals use StoredShares==1. The one exception is
// legacy shamir seals, which we can read but not write (by design).
// So if someone does a rekey, regardless of their intention, we're going
// to migrate them to a non-legacy Shamir seal.
if config.StoredShares != 1 {
c.logger.Warn("shamir stored keys supported, forcing rekey shares/threshold to 1")
config.StoredShares = 1
}
default:
if config.StoredShares != 1 {
c.logger.Warn("stored keys supported, forcing rekey shares/threshold to 1")
config.StoredShares = 1
}
2018-05-29 17:13:47 +00:00
config.SecretShares = 1
config.SecretThreshold = 1
2016-04-04 14:44:22 +00:00
if len(config.PGPKeys) > 0 {
return logical.CodedError(http.StatusBadRequest, "PGP key encryption not supported when using stored keys")
2016-04-04 14:44:22 +00:00
}
if config.Backup {
return logical.CodedError(http.StatusBadRequest, "key backup not supported when using stored keys")
2016-04-04 14:44:22 +00:00
}
}
2016-04-04 14:44:22 +00:00
if c.seal.RecoveryKeySupported() {
if config.VerificationRequired {
return logical.CodedError(http.StatusBadRequest, "requiring verification not supported when rekeying the barrier key with recovery keys")
2018-05-18 01:17:52 +00:00
}
c.logger.Debug("using recovery seal configuration to rekey barrier key")
2017-10-23 18:59:37 +00:00
}
2016-05-15 16:58:36 +00:00
// Check if the seal configuration is valid
if err := config.Validate(); err != nil {
c.logger.Error("invalid rekey seal configuration", "error", err)
return logical.CodedError(http.StatusInternalServerError, fmt.Errorf("invalid rekey seal configuration: %w", err).Error())
}
c.stateLock.RLock()
defer c.stateLock.RUnlock()
if c.Sealed() {
return logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error())
}
if c.standby {
return logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error())
}
2016-04-04 14:44:22 +00:00
c.rekeyLock.Lock()
defer c.rekeyLock.Unlock()
// Prevent multiple concurrent re-keys
2016-04-04 14:44:22 +00:00
if c.barrierRekeyConfig != nil {
return logical.CodedError(http.StatusBadRequest, "rekey already in progress")
}
// Copy the configuration
2016-04-04 14:44:22 +00:00
c.barrierRekeyConfig = config.Clone()
// Initialize the nonce
nonce, err := uuid.GenerateUUID()
if err != nil {
2016-04-04 14:44:22 +00:00
c.barrierRekeyConfig = nil
return logical.CodedError(http.StatusInternalServerError, fmt.Errorf("error generating nonce for procedure: %w", err).Error())
}
2016-04-04 14:44:22 +00:00
c.barrierRekeyConfig.Nonce = nonce
2016-08-19 20:45:17 +00:00
if c.logger.IsInfo() {
2018-05-18 01:17:52 +00:00
c.logger.Info("rekey initialized", "nonce", c.barrierRekeyConfig.Nonce, "shares", c.barrierRekeyConfig.SecretShares, "threshold", c.barrierRekeyConfig.SecretThreshold, "validation_required", c.barrierRekeyConfig.VerificationRequired)
2016-08-19 20:45:17 +00:00
}
return nil
}
2016-04-04 14:44:22 +00:00
// RecoveryRekeyInit is used to initialize the rekey settings for the recovery key
func (c *Core) RecoveryRekeyInit(config *SealConfig) logical.HTTPCodedError {
2016-04-04 14:44:22 +00:00
if config.StoredShares > 0 {
return logical.CodedError(http.StatusBadRequest, "stored shares not supported by recovery key")
}
2016-04-04 14:44:22 +00:00
2016-05-15 16:58:36 +00:00
// Check if the seal configuration is valid
2016-04-04 14:44:22 +00:00
if err := config.Validate(); err != nil {
c.logger.Error("invalid recovery configuration", "error", err)
return logical.CodedError(http.StatusInternalServerError, fmt.Errorf("invalid recovery configuration: %w", err).Error())
}
if !c.seal.RecoveryKeySupported() {
return logical.CodedError(http.StatusBadRequest, "recovery keys not supported")
2016-04-04 14:44:22 +00:00
}
c.stateLock.RLock()
defer c.stateLock.RUnlock()
if c.Sealed() {
return logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error())
2016-04-04 14:44:22 +00:00
}
if c.standby {
return logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error())
2016-04-04 14:44:22 +00:00
}
c.rekeyLock.Lock()
defer c.rekeyLock.Unlock()
// Prevent multiple concurrent re-keys
if c.recoveryRekeyConfig != nil {
return logical.CodedError(http.StatusBadRequest, "rekey already in progress")
2016-04-04 14:44:22 +00:00
}
// Copy the configuration
c.recoveryRekeyConfig = config.Clone()
// Initialize the nonce
nonce, err := uuid.GenerateUUID()
if err != nil {
2016-04-04 14:44:22 +00:00
c.recoveryRekeyConfig = nil
return logical.CodedError(http.StatusInternalServerError, fmt.Errorf("error generating nonce for procedure: %w", err).Error())
}
2016-04-04 14:44:22 +00:00
c.recoveryRekeyConfig.Nonce = nonce
2016-08-19 20:45:17 +00:00
if c.logger.IsInfo() {
2018-05-18 01:17:52 +00:00
c.logger.Info("rekey initialized", "nonce", c.recoveryRekeyConfig.Nonce, "shares", c.recoveryRekeyConfig.SecretShares, "threshold", c.recoveryRekeyConfig.SecretThreshold, "validation_required", c.recoveryRekeyConfig.VerificationRequired)
2016-08-19 20:45:17 +00:00
}
2016-04-04 14:44:22 +00:00
return nil
}
2017-10-23 18:59:37 +00:00
// 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, logical.HTTPCodedError) {
2016-04-04 14:44:22 +00:00
if recovery {
return c.RecoveryRekeyUpdate(ctx, key, nonce)
}
return c.BarrierRekeyUpdate(ctx, key, nonce)
2016-04-04 14:44:22 +00:00
}
2017-10-23 18:59:37 +00:00
// BarrierRekeyUpdate is used to provide a new key part. Barrier rekey can be done
// with unseal keys, or recovery keys if that's supported and we are storing the barrier
// 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, logical.HTTPCodedError) {
// Ensure we are already unsealed
c.stateLock.RLock()
defer c.stateLock.RUnlock()
if c.Sealed() {
return nil, logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error())
}
if c.standby {
return nil, logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error())
}
2016-04-04 14:44:22 +00:00
// Verify the key length
min, max := c.barrier.KeyLength()
max += shamir.ShareOverhead
if len(key) < min {
return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("key is shorter than minimum %d bytes", min))
2016-04-04 14:44:22 +00:00
}
if len(key) > max {
return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("key is longer than maximum %d bytes", max))
2016-04-04 14:44:22 +00:00
}
c.rekeyLock.Lock()
defer c.rekeyLock.Unlock()
2016-04-04 14:44:22 +00:00
// Get the seal configuration
2017-10-23 18:59:37 +00:00
var existingConfig *SealConfig
var err error
var useRecovery bool // Determines whether recovery key is being used to rekey the root key
if c.seal.StoredKeysSupported() == seal.StoredKeysSupportedGeneric && c.seal.RecoveryKeySupported() {
existingConfig, err = c.seal.RecoveryConfig(ctx)
2017-10-23 18:59:37 +00:00
useRecovery = true
} else {
existingConfig, err = c.seal.BarrierConfig(ctx)
2017-10-23 18:59:37 +00:00
}
2016-04-04 14:44:22 +00:00
if err != nil {
return nil, logical.CodedError(http.StatusInternalServerError, fmt.Errorf("failed to fetch existing config: %w", err).Error())
2016-04-04 14:44:22 +00:00
}
// Ensure the barrier is initialized
if existingConfig == nil {
return nil, logical.CodedError(http.StatusBadRequest, ErrNotInit.Error())
2016-04-04 14:44:22 +00:00
}
// Ensure a rekey is in progress
2016-04-04 14:44:22 +00:00
if c.barrierRekeyConfig == nil {
return nil, logical.CodedError(http.StatusBadRequest, "no barrier rekey in progress")
}
if len(c.barrierRekeyConfig.VerificationKey) > 0 {
return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("rekey operation already finished; verification must be performed; nonce for the verification operation is %q", c.barrierRekeyConfig.VerificationNonce))
}
2016-04-04 14:44:22 +00:00
if nonce != 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
2018-05-21 21:46:32 +00:00
for _, existing := range c.barrierRekeyConfig.RekeyProgress {
2018-05-18 01:30:39 +00:00
if subtle.ConstantTimeCompare(existing, key) == 1 {
return nil, logical.CodedError(http.StatusBadRequest, "given key has already been provided during this generation operation")
}
}
// Store this key
2018-05-21 21:46:32 +00:00
c.barrierRekeyConfig.RekeyProgress = append(c.barrierRekeyConfig.RekeyProgress, key)
// Check if we don't have enough keys to unlock
2018-05-21 21:46:32 +00:00
if len(c.barrierRekeyConfig.RekeyProgress) < existingConfig.SecretThreshold {
2016-08-19 20:45:17 +00:00
if c.logger.IsDebug() {
2018-05-21 21:46:32 +00:00
c.logger.Debug("cannot rekey yet, not enough keys", "keys", len(c.barrierRekeyConfig.RekeyProgress), "threshold", existingConfig.SecretThreshold)
2016-08-19 20:45:17 +00:00
}
return nil, nil
}
// Recover the root key or recovery key
2017-10-23 18:59:37 +00:00
var recoveredKey []byte
2016-04-04 14:44:22 +00:00
if existingConfig.SecretThreshold == 1 {
2018-05-21 21:46:32 +00:00
recoveredKey = c.barrierRekeyConfig.RekeyProgress[0]
c.barrierRekeyConfig.RekeyProgress = nil
} else {
2018-05-21 21:46:32 +00:00
recoveredKey, err = shamir.Combine(c.barrierRekeyConfig.RekeyProgress)
c.barrierRekeyConfig.RekeyProgress = nil
if err != nil {
return nil, logical.CodedError(http.StatusInternalServerError, fmt.Errorf("failed to compute root key: %w", err).Error())
}
}
switch {
case useRecovery:
if err := c.seal.VerifyRecoveryKey(ctx, recoveredKey); err != nil {
c.logger.Error("rekey recovery key verification failed", "error", err)
return nil, logical.CodedError(http.StatusBadRequest, fmt.Errorf("recovery key verification failed: %w", err).Error())
2017-10-23 18:59:37 +00:00
}
case c.seal.BarrierType() == wrapping.WrapperTypeShamir:
if c.seal.StoredKeysSupported() == seal.StoredKeysSupportedShamirRoot {
testseal := NewDefaultSeal(&seal.Access{
Wrapper: aeadwrapper.NewShamirWrapper(),
})
testseal.SetCore(c)
err = testseal.GetAccess().Wrapper.(*aeadwrapper.ShamirWrapper).SetAesGcmKeyBytes(recoveredKey)
if err != nil {
return nil, logical.CodedError(http.StatusInternalServerError, fmt.Errorf("failed to setup unseal key: %w", err).Error())
}
cfg, err := c.seal.BarrierConfig(ctx)
if err != nil {
return nil, logical.CodedError(http.StatusInternalServerError, fmt.Errorf("failed to setup test barrier config: %w", err).Error())
}
testseal.SetCachedBarrierConfig(cfg)
stored, err := testseal.GetStoredKeys(ctx)
if err != nil {
return nil, logical.CodedError(http.StatusInternalServerError, fmt.Errorf("failed to read root key: %w", err).Error())
}
recoveredKey = stored[0]
}
if err := c.barrier.VerifyRoot(recoveredKey); err != nil {
c.logger.Error("root key verification failed", "error", err)
return nil, logical.CodedError(http.StatusBadRequest, fmt.Errorf("rootter key verification failed: %w", err).Error())
2017-10-23 18:59:37 +00:00
}
}
// Generate a new key: for AutoUnseal, this is a new root key; for Shamir,
// this is a new unseal key, and performBarrierRekey will also generate a
// new root key.
newKey, err := c.barrier.GenerateKey(c.secureRandomReader)
if err != nil {
c.logger.Error("failed to generate root key", "error", err)
return nil, logical.CodedError(http.StatusInternalServerError, fmt.Errorf("root key generation failed: %w", err).Error())
}
results := &RekeyResult{
2016-04-04 14:44:22 +00:00
Backup: c.barrierRekeyConfig.Backup,
}
if c.seal.StoredKeysSupported() != seal.StoredKeysSupportedGeneric {
// Set result.SecretShares to the new key itself if only a single key
// part is used -- no Shamir split required.
if c.barrierRekeyConfig.SecretShares == 1 {
results.SecretShares = append(results.SecretShares, newKey)
} else {
// Split the new key using the Shamir algorithm
shares, err := shamir.Split(newKey, c.barrierRekeyConfig.SecretShares, c.barrierRekeyConfig.SecretThreshold)
if err != nil {
c.logger.Error("failed to generate shares", "error", err)
return nil, logical.CodedError(http.StatusInternalServerError, fmt.Errorf("failed to generate shares: %w", err).Error())
}
results.SecretShares = shares
2016-04-04 14:44:22 +00:00
}
}
2017-10-23 18:59:37 +00:00
// If PGP keys are passed in, encrypt shares with corresponding PGP keys.
2016-04-04 14:44:22 +00:00
if len(c.barrierRekeyConfig.PGPKeys) > 0 {
2016-01-20 00:19:07 +00:00
hexEncodedShares := make([][]byte, len(results.SecretShares))
for i := range results.SecretShares {
2016-01-20 00:19:07 +00:00
hexEncodedShares[i] = []byte(hex.EncodeToString(results.SecretShares[i]))
}
2016-04-04 14:44:22 +00:00
results.PGPFingerprints, results.SecretShares, err = pgpkeys.EncryptShares(hexEncodedShares, c.barrierRekeyConfig.PGPKeys)
if err != nil {
return nil, logical.CodedError(http.StatusInternalServerError, fmt.Errorf("failed to encrypt shares: %w", err).Error())
}
2017-10-23 18:59:37 +00:00
// If backup is enabled, store backup info in vault.coreBarrierUnsealKeysBackupPath
2016-04-04 14:44:22 +00:00
if c.barrierRekeyConfig.Backup {
backupInfo := map[string][]string{}
for i := 0; i < len(results.PGPFingerprints); i++ {
encShare := bytes.NewBuffer(results.SecretShares[i])
if backupInfo[results.PGPFingerprints[i]] == nil {
backupInfo[results.PGPFingerprints[i]] = []string{hex.EncodeToString(encShare.Bytes())}
} else {
backupInfo[results.PGPFingerprints[i]] = append(backupInfo[results.PGPFingerprints[i]], hex.EncodeToString(encShare.Bytes()))
}
}
backupVals := &RekeyBackup{
2016-04-04 14:44:22 +00:00
Nonce: c.barrierRekeyConfig.Nonce,
Keys: backupInfo,
}
buf, err := json.Marshal(backupVals)
if err != nil {
c.logger.Error("failed to marshal unseal key backup", "error", err)
return nil, logical.CodedError(http.StatusInternalServerError, fmt.Errorf("failed to marshal unseal key backup: %w", err).Error())
}
pe := &physical.Entry{
2016-04-04 14:44:22 +00:00
Key: coreBarrierUnsealKeysBackupPath,
Value: buf,
}
if err = c.physical.Put(ctx, pe); err != nil {
c.logger.Error("failed to save unseal key backup", "error", err)
return nil, logical.CodedError(http.StatusInternalServerError, fmt.Errorf("failed to save unseal key backup: %w", err).Error())
}
}
}
2018-05-18 01:17:52 +00:00
// If we are requiring validation, return now; otherwise rekey the barrier
if c.barrierRekeyConfig.VerificationRequired {
nonce, err := uuid.GenerateUUID()
if err != nil {
c.barrierRekeyConfig = nil
return nil, logical.CodedError(http.StatusInternalServerError, fmt.Errorf("failed to generate verification nonce: %w", err).Error())
2018-05-18 01:17:52 +00:00
}
c.barrierRekeyConfig.VerificationNonce = nonce
c.barrierRekeyConfig.VerificationKey = newKey
2018-05-18 01:17:52 +00:00
results.VerificationRequired = true
results.VerificationNonce = nonce
return results, nil
}
if err := c.performBarrierRekey(ctx, newKey); err != nil {
return nil, logical.CodedError(http.StatusInternalServerError, fmt.Errorf("failed to perform barrier rekey: %w", err).Error())
2018-05-18 01:17:52 +00:00
}
c.barrierRekeyConfig = nil
return results, nil
}
func (c *Core) performBarrierRekey(ctx context.Context, newSealKey []byte) logical.HTTPCodedError {
legacyUpgrade := c.seal.StoredKeysSupported() == seal.StoredKeysNotSupported
if legacyUpgrade {
// We won't be able to call SetStoredKeys without setting StoredShares=1.
existingConfig, err := c.seal.BarrierConfig(ctx)
if err != nil {
return logical.CodedError(http.StatusInternalServerError, fmt.Errorf("failed to fetch existing config: %w", err).Error())
}
existingConfig.StoredShares = 1
c.seal.SetCachedBarrierConfig(existingConfig)
}
if c.seal.StoredKeysSupported() != seal.StoredKeysSupportedGeneric {
err := c.seal.GetAccess().Wrapper.(*aeadwrapper.ShamirWrapper).SetAesGcmKeyBytes(newSealKey)
if err != nil {
return logical.CodedError(http.StatusInternalServerError, fmt.Errorf("failed to update barrier seal key: %w", err).Error())
}
}
newRootKey, err := c.barrier.GenerateKey(c.secureRandomReader)
if err != nil {
return logical.CodedError(http.StatusInternalServerError, fmt.Errorf("failed to perform rekey: %w", err).Error())
}
if err := c.seal.SetStoredKeys(ctx, [][]byte{newRootKey}); err != nil {
c.logger.Error("failed to store keys", "error", err)
return logical.CodedError(http.StatusInternalServerError, fmt.Errorf("failed to store keys: %w", err).Error())
}
// Rekey the barrier
if err := c.barrier.Rekey(ctx, newRootKey); err != nil {
c.logger.Error("failed to rekey barrier", "error", err)
return logical.CodedError(http.StatusInternalServerError, fmt.Errorf("failed to rekey barrier: %w", err).Error())
}
2016-08-19 20:45:17 +00:00
if c.logger.IsInfo() {
c.logger.Info("security barrier rekeyed", "stored", c.barrierRekeyConfig.StoredShares, "shares", c.barrierRekeyConfig.SecretShares, "threshold", c.barrierRekeyConfig.SecretThreshold)
}
if len(newSealKey) > 0 {
err := c.barrier.Put(ctx, &logical.StorageEntry{
Key: shamirKekPath,
Value: newSealKey,
})
if err != nil {
c.logger.Error("failed to store new seal key", "error", err)
return logical.CodedError(http.StatusInternalServerError, fmt.Errorf("failed to store new seal key: %w", err).Error())
}
2016-08-19 20:45:17 +00:00
}
2018-05-18 01:17:52 +00:00
c.barrierRekeyConfig.VerificationKey = nil
if err := c.seal.SetBarrierConfig(ctx, c.barrierRekeyConfig); err != nil {
c.logger.Error("error saving rekey seal configuration", "error", err)
return logical.CodedError(http.StatusInternalServerError, fmt.Errorf("failed to save rekey seal configuration: %w", err).Error())
2016-04-04 14:44:22 +00:00
}
2017-02-24 15:45:29 +00:00
// Write to the canary path, which will force a synchronous truing during
// replication
if err := c.barrier.Put(ctx, &logical.StorageEntry{
2017-02-24 15:45:29 +00:00
Key: coreKeyringCanaryPath,
Value: []byte(c.barrierRekeyConfig.Nonce),
}); err != nil {
c.logger.Error("error saving keyring canary", "error", err)
return logical.CodedError(http.StatusInternalServerError, fmt.Errorf("failed to save keyring canary: %w", err).Error())
2017-02-24 15:45:29 +00:00
}
2018-05-29 17:13:47 +00:00
c.barrierRekeyConfig.RekeyProgress = nil
2018-05-18 01:17:52 +00:00
return nil
2016-04-04 14:44:22 +00:00
}
2016-04-04 14:44:22 +00:00
// RecoveryRekeyUpdate is used to provide a new key part
func (c *Core) RecoveryRekeyUpdate(ctx context.Context, key []byte, nonce string) (*RekeyResult, logical.HTTPCodedError) {
2016-04-04 14:44:22 +00:00
// Ensure we are already unsealed
c.stateLock.RLock()
defer c.stateLock.RUnlock()
if c.Sealed() {
return nil, logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error())
}
2016-04-04 14:44:22 +00:00
if c.standby {
return nil, logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error())
2016-04-04 14:44:22 +00:00
}
// Verify the key length
min, max := c.barrier.KeyLength()
max += shamir.ShareOverhead
if len(key) < min {
return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("key is shorter than minimum %d bytes", min))
2016-04-04 14:44:22 +00:00
}
if len(key) > max {
return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("key is longer than maximum %d bytes", max))
2016-04-04 14:44:22 +00:00
}
c.rekeyLock.Lock()
defer c.rekeyLock.Unlock()
// Get the seal configuration
existingConfig, err := c.seal.RecoveryConfig(ctx)
2016-04-04 14:44:22 +00:00
if err != nil {
return nil, logical.CodedError(http.StatusInternalServerError, fmt.Errorf("failed to fetch existing recovery config: %w", err).Error())
2016-04-04 14:44:22 +00:00
}
2017-10-23 18:59:37 +00:00
// Ensure the seal is initialized
if existingConfig == nil {
return nil, logical.CodedError(http.StatusBadRequest, ErrNotInit.Error())
2016-04-04 14:44:22 +00:00
}
// Ensure a rekey is in progress
if c.recoveryRekeyConfig == nil {
return nil, logical.CodedError(http.StatusBadRequest, "no recovery rekey in progress")
2016-04-04 14:44:22 +00:00
}
if len(c.recoveryRekeyConfig.VerificationKey) > 0 {
return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("rekey operation already finished; verification must be performed; nonce for the verification operation is %q", c.recoveryRekeyConfig.VerificationNonce))
}
2016-04-04 14:44:22 +00:00
if nonce != c.recoveryRekeyConfig.Nonce {
return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("incorrect nonce supplied; nonce for this rekey operation is %q", c.recoveryRekeyConfig.Nonce))
2016-04-04 14:44:22 +00:00
}
// Check if we already have this piece
2018-05-21 21:46:32 +00:00
for _, existing := range c.recoveryRekeyConfig.RekeyProgress {
2018-05-18 01:30:39 +00:00
if subtle.ConstantTimeCompare(existing, key) == 1 {
return nil, logical.CodedError(http.StatusBadRequest, "given key has already been provided during this rekey operation")
2016-04-04 14:44:22 +00:00
}
}
// Store this key
2018-05-21 21:46:32 +00:00
c.recoveryRekeyConfig.RekeyProgress = append(c.recoveryRekeyConfig.RekeyProgress, key)
2016-04-04 14:44:22 +00:00
// Check if we don't have enough keys to unlock
2018-05-21 21:46:32 +00:00
if len(c.recoveryRekeyConfig.RekeyProgress) < existingConfig.SecretThreshold {
2016-08-19 20:45:17 +00:00
if c.logger.IsDebug() {
2018-05-21 21:46:32 +00:00
c.logger.Debug("cannot rekey yet, not enough keys", "keys", len(c.recoveryRekeyConfig.RekeyProgress), "threshold", existingConfig.SecretThreshold)
2016-08-19 20:45:17 +00:00
}
2016-04-04 14:44:22 +00:00
return nil, nil
}
// Recover the root key
2017-10-23 18:59:37 +00:00
var recoveryKey []byte
2016-04-04 14:44:22 +00:00
if existingConfig.SecretThreshold == 1 {
2018-05-21 21:46:32 +00:00
recoveryKey = c.recoveryRekeyConfig.RekeyProgress[0]
c.recoveryRekeyConfig.RekeyProgress = nil
2016-04-04 14:44:22 +00:00
} else {
2018-05-21 21:46:32 +00:00
recoveryKey, err = shamir.Combine(c.recoveryRekeyConfig.RekeyProgress)
c.recoveryRekeyConfig.RekeyProgress = nil
2016-04-04 14:44:22 +00:00
if err != nil {
return nil, logical.CodedError(http.StatusInternalServerError, fmt.Errorf("failed to compute recovery key: %w", err).Error())
2016-04-04 14:44:22 +00:00
}
}
// Verify the recovery key
if err := c.seal.VerifyRecoveryKey(ctx, recoveryKey); err != nil {
c.logger.Error("recovery key verification failed", "error", err)
return nil, logical.CodedError(http.StatusBadRequest, fmt.Errorf("recovery key verification failed: %w", err).Error())
2016-04-04 14:44:22 +00:00
}
// Generate a new root key
newRecoveryKey, err := c.barrier.GenerateKey(c.secureRandomReader)
2016-04-04 14:44:22 +00:00
if err != nil {
c.logger.Error("failed to generate recovery key", "error", err)
return nil, logical.CodedError(http.StatusInternalServerError, fmt.Errorf("recovery key generation failed: %w", err).Error())
2016-04-04 14:44:22 +00:00
}
// Return the root key if only a single key part is used
2016-04-04 14:44:22 +00:00
results := &RekeyResult{
Backup: c.recoveryRekeyConfig.Backup,
}
if c.recoveryRekeyConfig.SecretShares == 1 {
results.SecretShares = append(results.SecretShares, newRecoveryKey)
2016-04-04 14:44:22 +00:00
} else {
// Split the root key using the Shamir algorithm
shares, err := shamir.Split(newRecoveryKey, c.recoveryRekeyConfig.SecretShares, c.recoveryRekeyConfig.SecretThreshold)
2016-04-04 14:44:22 +00:00
if err != nil {
c.logger.Error("failed to generate shares", "error", err)
return nil, logical.CodedError(http.StatusInternalServerError, fmt.Errorf("failed to generate shares: %w", err).Error())
2016-04-04 14:44:22 +00:00
}
results.SecretShares = shares
}
if len(c.recoveryRekeyConfig.PGPKeys) > 0 {
hexEncodedShares := make([][]byte, len(results.SecretShares))
for i := range results.SecretShares {
2016-04-04 14:44:22 +00:00
hexEncodedShares[i] = []byte(hex.EncodeToString(results.SecretShares[i]))
}
results.PGPFingerprints, results.SecretShares, err = pgpkeys.EncryptShares(hexEncodedShares, c.recoveryRekeyConfig.PGPKeys)
if err != nil {
return nil, logical.CodedError(http.StatusInternalServerError, fmt.Errorf("failed to encrypt shares: %w", err).Error())
2016-04-04 14:44:22 +00:00
}
if c.recoveryRekeyConfig.Backup {
backupInfo := map[string][]string{}
for i := 0; i < len(results.PGPFingerprints); i++ {
encShare := bytes.NewBuffer(results.SecretShares[i])
if backupInfo[results.PGPFingerprints[i]] == nil {
backupInfo[results.PGPFingerprints[i]] = []string{hex.EncodeToString(encShare.Bytes())}
} else {
backupInfo[results.PGPFingerprints[i]] = append(backupInfo[results.PGPFingerprints[i]], hex.EncodeToString(encShare.Bytes()))
}
}
backupVals := &RekeyBackup{
Nonce: c.recoveryRekeyConfig.Nonce,
Keys: backupInfo,
}
buf, err := json.Marshal(backupVals)
if err != nil {
c.logger.Error("failed to marshal recovery key backup", "error", err)
return nil, logical.CodedError(http.StatusInternalServerError, fmt.Errorf("failed to marshal recovery key backup: %w", err).Error())
2016-04-04 14:44:22 +00:00
}
pe := &physical.Entry{
Key: coreRecoveryUnsealKeysBackupPath,
Value: buf,
}
if err = c.physical.Put(ctx, pe); err != nil {
c.logger.Error("failed to save unseal key backup", "error", err)
return nil, logical.CodedError(http.StatusInternalServerError, fmt.Errorf("failed to save unseal key backup: %w", err).Error())
2016-04-04 14:44:22 +00:00
}
}
}
2018-05-18 01:17:52 +00:00
// If we are requiring validation, return now; otherwise save the recovery
// key
if c.recoveryRekeyConfig.VerificationRequired {
nonce, err := uuid.GenerateUUID()
if err != nil {
c.recoveryRekeyConfig = nil
return nil, logical.CodedError(http.StatusInternalServerError, fmt.Errorf("failed to generate verification nonce: %w", err).Error())
2018-05-18 01:17:52 +00:00
}
c.recoveryRekeyConfig.VerificationNonce = nonce
c.recoveryRekeyConfig.VerificationKey = newRecoveryKey
2018-05-18 01:17:52 +00:00
results.VerificationRequired = true
results.VerificationNonce = nonce
return results, nil
}
if err := c.performRecoveryRekey(ctx, newRecoveryKey); err != nil {
return nil, logical.CodedError(http.StatusInternalServerError, fmt.Errorf("failed to perform recovery rekey: %w", err).Error())
2018-05-18 01:17:52 +00:00
}
c.recoveryRekeyConfig = nil
return results, nil
}
func (c *Core) performRecoveryRekey(ctx context.Context, newRootKey []byte) logical.HTTPCodedError {
if err := c.seal.SetRecoveryKey(ctx, newRootKey); err != nil {
c.logger.Error("failed to set recovery key", "error", err)
return logical.CodedError(http.StatusInternalServerError, fmt.Errorf("failed to set recovery key: %w", err).Error())
2016-04-04 14:44:22 +00:00
}
2018-05-18 01:17:52 +00:00
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 logical.CodedError(http.StatusInternalServerError, fmt.Errorf("failed to save rekey seal configuration: %w", err).Error())
}
2017-02-24 15:45:29 +00:00
// Write to the canary path, which will force a synchronous truing during
// replication
if err := c.barrier.Put(ctx, &logical.StorageEntry{
2017-02-24 15:45:29 +00:00
Key: coreKeyringCanaryPath,
Value: []byte(c.recoveryRekeyConfig.Nonce),
}); err != nil {
c.logger.Error("error saving keyring canary", "error", err)
return logical.CodedError(http.StatusInternalServerError, fmt.Errorf("failed to save keyring canary: %w", err).Error())
2017-02-24 15:45:29 +00:00
}
2018-05-29 17:13:47 +00:00
c.recoveryRekeyConfig.RekeyProgress = nil
2018-05-18 01:17:52 +00:00
return nil
}
2018-05-21 21:46:32 +00:00
func (c *Core) RekeyVerify(ctx context.Context, key []byte, nonce string, recovery bool) (ret *RekeyVerifyResult, retErr logical.HTTPCodedError) {
2018-05-18 01:17:52 +00:00
// Ensure we are already unsealed
c.stateLock.RLock()
defer c.stateLock.RUnlock()
if c.Sealed() {
return nil, logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error())
2018-05-18 01:17:52 +00:00
}
if c.standby {
return nil, logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error())
2018-05-18 01:17:52 +00:00
}
// Verify the key length
min, max := c.barrier.KeyLength()
max += shamir.ShareOverhead
if len(key) < min {
return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("key is shorter than minimum %d bytes", min))
2018-05-18 01:17:52 +00:00
}
if len(key) > max {
return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("key is longer than maximum %d bytes", max))
2018-05-18 01:17:52 +00:00
}
c.rekeyLock.Lock()
defer c.rekeyLock.Unlock()
2018-05-21 21:46:32 +00:00
config := c.barrierRekeyConfig
if recovery {
config = c.recoveryRekeyConfig
}
2018-05-18 01:17:52 +00:00
// Ensure a rekey is in progress
2018-05-21 21:46:32 +00:00
if config == nil {
return nil, logical.CodedError(http.StatusBadRequest, "no rekey in progress")
2018-05-18 01:17:52 +00:00
}
2018-05-29 17:13:47 +00:00
if len(config.VerificationKey) == 0 {
return nil, logical.CodedError(http.StatusBadRequest, "no rekey verification in progress")
}
2018-05-21 21:46:32 +00:00
if nonce != config.VerificationNonce {
return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("incorrect nonce supplied; nonce for this verify operation is %q", config.VerificationNonce))
2018-05-20 06:42:15 +00:00
}
2018-05-18 01:17:52 +00:00
// Check if we already have this piece
2018-05-21 21:46:32 +00:00
for _, existing := range config.VerificationProgress {
2018-05-18 01:30:39 +00:00
if subtle.ConstantTimeCompare(existing, key) == 1 {
return nil, logical.CodedError(http.StatusBadRequest, "given key has already been provided during this verify operation")
2018-05-18 01:17:52 +00:00
}
}
// Store this key
2018-05-21 21:46:32 +00:00
config.VerificationProgress = append(config.VerificationProgress, key)
2018-05-18 01:17:52 +00:00
// Check if we don't have enough keys to unlock
2018-05-21 21:46:32 +00:00
if len(config.VerificationProgress) < config.SecretThreshold {
2018-05-18 01:17:52 +00:00
if c.logger.IsDebug() {
2018-05-21 21:46:32 +00:00
c.logger.Debug("cannot verify yet, not enough keys", "keys", len(config.VerificationProgress), "threshold", config.SecretThreshold)
2018-05-18 01:17:52 +00:00
}
return nil, nil
}
// Schedule the progress for forgetting and rotate the nonce if possible
defer func() {
2018-05-21 21:46:32 +00:00
config.VerificationProgress = nil
2018-05-22 00:54:20 +00:00
if ret != nil && ret.Complete {
return
}
// Not complete, so rotate nonce
2018-05-21 21:46:32 +00:00
nonce, err := uuid.GenerateUUID()
2018-05-21 22:25:58 +00:00
if err == nil {
config.VerificationNonce = nonce
if ret != nil {
ret.Nonce = nonce
2018-05-18 01:17:52 +00:00
}
}
}()
// Recover the root key or recovery key
2018-05-18 01:17:52 +00:00
var recoveredKey []byte
2018-05-21 21:46:32 +00:00
if config.SecretThreshold == 1 {
recoveredKey = config.VerificationProgress[0]
2018-05-18 01:17:52 +00:00
} else {
2018-05-21 18:47:00 +00:00
var err error
2018-05-21 21:46:32 +00:00
recoveredKey, err = shamir.Combine(config.VerificationProgress)
2018-05-18 01:17:52 +00:00
if err != nil {
return nil, logical.CodedError(http.StatusInternalServerError, fmt.Errorf("failed to compute key for verification: %w", err).Error())
2018-05-18 01:17:52 +00:00
}
}
2018-05-21 21:46:32 +00:00
if subtle.ConstantTimeCompare(recoveredKey, config.VerificationKey) != 1 {
2018-05-18 01:17:52 +00:00
c.logger.Error("rekey verification failed")
return nil, logical.CodedError(http.StatusBadRequest, "rekey verification failed; incorrect key shares supplied")
2018-05-18 01:30:39 +00:00
}
2018-05-21 21:46:32 +00:00
switch recovery {
case false:
if err := c.performBarrierRekey(ctx, recoveredKey); err != nil {
return nil, logical.CodedError(http.StatusInternalServerError, fmt.Errorf("failed to perform rekey: %w", err).Error())
2018-05-18 01:30:39 +00:00
}
2018-05-21 21:46:32 +00:00
c.barrierRekeyConfig = nil
default:
if err := c.performRecoveryRekey(ctx, recoveredKey); err != nil {
return nil, logical.CodedError(http.StatusInternalServerError, fmt.Errorf("failed to perform recovery key rekey: %w", err).Error())
2018-05-18 01:30:39 +00:00
}
2018-05-21 21:46:32 +00:00
c.recoveryRekeyConfig = nil
2018-05-18 01:30:39 +00:00
}
res := &RekeyVerifyResult{
2018-05-22 00:54:20 +00:00
Nonce: config.VerificationNonce,
Complete: true,
2018-05-18 01:30:39 +00:00
}
return res, nil
}
// RekeyCancel is used to cancel an in-progress rekey
func (c *Core) RekeyCancel(recovery bool) logical.HTTPCodedError {
c.stateLock.RLock()
defer c.stateLock.RUnlock()
if c.Sealed() {
return logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error())
}
if c.standby {
return logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error())
}
2016-04-04 14:44:22 +00:00
c.rekeyLock.Lock()
defer c.rekeyLock.Unlock()
// Clear any progress or config
2016-04-04 14:44:22 +00:00
if recovery {
c.recoveryRekeyConfig = nil
} else {
c.barrierRekeyConfig = nil
}
return nil
}
// RekeyVerifyRestart is used to start the verification process over
func (c *Core) RekeyVerifyRestart(recovery bool) logical.HTTPCodedError {
2018-05-18 01:17:52 +00:00
c.stateLock.RLock()
defer c.stateLock.RUnlock()
if c.Sealed() {
return logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error())
2018-05-18 01:17:52 +00:00
}
if c.standby {
return logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error())
2018-05-18 01:17:52 +00:00
}
c.rekeyLock.Lock()
defer c.rekeyLock.Unlock()
// Attempt to generate a new nonce, but don't bail if it doesn't succeed
// (which is extraordinarily unlikely)
nonce, nonceErr := uuid.GenerateUUID()
// Clear any progress or config
if recovery {
if c.recoveryRekeyConfig != nil {
c.recoveryRekeyConfig.VerificationProgress = nil
if nonceErr == nil {
c.recoveryRekeyConfig.VerificationNonce = nonce
}
2018-05-18 01:17:52 +00:00
}
} else {
if c.barrierRekeyConfig != nil {
c.barrierRekeyConfig.VerificationProgress = nil
if nonceErr == nil {
c.barrierRekeyConfig.VerificationNonce = nonce
}
2018-05-18 01:17:52 +00:00
}
}
return nil
}
// RekeyRetrieveBackup is used to retrieve any backed-up PGP-encrypted unseal
// keys
func (c *Core) RekeyRetrieveBackup(ctx context.Context, recovery bool) (*RekeyBackup, logical.HTTPCodedError) {
if c.Sealed() {
return nil, logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error())
}
if c.standby {
return nil, logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error())
}
2016-04-04 14:44:22 +00:00
c.rekeyLock.RLock()
defer c.rekeyLock.RUnlock()
var entry *physical.Entry
var err error
if recovery {
entry, err = c.physical.Get(ctx, coreRecoveryUnsealKeysBackupPath)
2016-04-04 14:44:22 +00:00
} else {
entry, err = c.physical.Get(ctx, coreBarrierUnsealKeysBackupPath)
2016-04-04 14:44:22 +00:00
}
if err != nil {
return nil, logical.CodedError(http.StatusInternalServerError, fmt.Errorf("error getting keys from backup: %w", err).Error())
}
if entry == nil {
return nil, nil
}
ret := &RekeyBackup{}
err = jsonutil.DecodeJSON(entry.Value, ret)
if err != nil {
return nil, logical.CodedError(http.StatusInternalServerError, fmt.Errorf("error decoding backup keys: %w", 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) logical.HTTPCodedError {
if c.Sealed() {
return logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error())
}
if c.standby {
return logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error())
}
2016-04-04 14:44:22 +00:00
c.rekeyLock.Lock()
defer c.rekeyLock.Unlock()
if recovery {
err := c.physical.Delete(ctx, coreRecoveryUnsealKeysBackupPath)
if err != nil {
return logical.CodedError(http.StatusInternalServerError, fmt.Errorf("error deleting backup keys: %w", err).Error())
}
return nil
}
err := c.physical.Delete(ctx, coreBarrierUnsealKeysBackupPath)
if err != nil {
return logical.CodedError(http.StatusInternalServerError, fmt.Errorf("error deleting backup keys: %w", err).Error())
2016-04-04 14:44:22 +00:00
}
return nil
}