2016-01-14 22:01:04 +00:00
package vault
import (
"bytes"
2018-01-19 06:44:44 +00:00
"context"
2018-05-18 01:17:52 +00:00
"crypto/subtle"
2016-01-14 22:01:04 +00:00
"encoding/hex"
"encoding/json"
"fmt"
2018-05-20 03:43:48 +00:00
"net/http"
2016-01-14 22:01:04 +00:00
2022-08-23 19:37:16 +00:00
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
aeadwrapper "github.com/hashicorp/go-kms-wrapping/wrappers/aead/v2"
2019-10-18 18:46:00 +00:00
"github.com/hashicorp/go-uuid"
2019-04-13 07:44:06 +00:00
"github.com/hashicorp/vault/helper/pgpkeys"
2019-04-12 21:54:35 +00:00
"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"
2016-01-14 22:01:04 +00:00
"github.com/hashicorp/vault/shamir"
2019-06-20 19:14:58 +00:00
"github.com/hashicorp/vault/vault/seal"
2016-01-14 22:01:04 +00:00
)
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"
)
2016-01-14 22:01:04 +00:00
// 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
2016-01-14 22:01:04 +00:00
}
// 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.
2018-05-20 03:43:48 +00:00
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 ( )
2018-07-24 20:57:25 +00:00
if c . Sealed ( ) {
2018-05-20 03:43:48 +00:00
return 0 , logical . CodedError ( http . StatusServiceUnavailable , consts . ErrSealed . Error ( ) )
2016-04-04 14:44:22 +00:00
}
if c . standby {
2018-05-20 03:43:48 +00:00
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.
2018-01-19 08:44:06 +00:00
if recovery || c . seal . RecoveryKeySupported ( ) {
2018-01-19 06:44:44 +00:00
config , err = c . seal . RecoveryConfig ( ctx )
2016-04-04 14:44:22 +00:00
} else {
2018-01-19 06:44:44 +00:00
config , err = c . seal . BarrierConfig ( ctx )
2016-04-04 14:44:22 +00:00
}
if err != nil {
2021-05-11 17:12:54 +00:00
return 0 , logical . CodedError ( http . StatusInternalServerError , fmt . Errorf ( "unable to look up config: %w" , err ) . Error ( ) )
2018-05-20 03:43:48 +00:00
}
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).
2018-05-28 04:39:53 +00:00
func ( c * Core ) RekeyProgress ( recovery , verification bool ) ( bool , int , logical . HTTPCodedError ) {
2016-01-14 22:01:04 +00:00
c . stateLock . RLock ( )
defer c . stateLock . RUnlock ( )
2018-07-24 20:57:25 +00:00
if c . Sealed ( ) {
2018-05-28 04:39:53 +00:00
return false , 0 , logical . CodedError ( http . StatusServiceUnavailable , consts . ErrSealed . Error ( ) )
2016-01-14 22:01:04 +00:00
}
if c . standby {
2018-05-28 04:39:53 +00:00
return false , 0 , logical . CodedError ( http . StatusBadRequest , consts . ErrStandby . Error ( ) )
2016-01-14 22:01:04 +00:00
}
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
}
2016-01-14 22:01:04 +00:00
2018-05-28 04:39:53 +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 {
2018-05-28 04:39:53 +00:00
return len ( conf . VerificationKey ) > 0 , len ( conf . VerificationProgress ) , nil
2018-05-18 01:17:52 +00:00
}
2018-05-28 04:39:53 +00:00
return true , len ( conf . RekeyProgress ) , nil
2018-05-18 01:17:52 +00:00
}
2016-01-14 22:01:04 +00:00
// RekeyConfig is used to read the rekey configuration
2018-05-20 03:43:48 +00:00
func ( c * Core ) RekeyConfig ( recovery bool ) ( * SealConfig , logical . HTTPCodedError ) {
2016-01-14 22:01:04 +00:00
c . stateLock . RLock ( )
defer c . stateLock . RUnlock ( )
2018-07-24 20:57:25 +00:00
if c . Sealed ( ) {
2018-05-20 03:43:48 +00:00
return nil , logical . CodedError ( http . StatusServiceUnavailable , consts . ErrSealed . Error ( ) )
2016-01-14 22:01:04 +00:00
}
if c . standby {
2018-05-20 03:43:48 +00:00
return nil , logical . CodedError ( http . StatusBadRequest , consts . ErrStandby . Error ( ) )
2016-01-14 22:01:04 +00:00
}
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-01-14 22:01:04 +00:00
}
2016-04-04 14:44:22 +00:00
2016-01-14 22:01:04 +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.
2018-05-20 03:43:48 +00:00
func ( c * Core ) RekeyInit ( config * SealConfig , recovery bool ) logical . HTTPCodedError {
2018-05-29 22:38:14 +00:00
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 {
2018-01-19 08:44:06 +00:00
return c . RecoveryRekeyInit ( config )
2016-04-04 14:44:22 +00:00
}
2018-01-19 08:44:06 +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
2018-05-20 03:43:48 +00:00
func ( c * Core ) BarrierRekeyInit ( config * SealConfig ) logical . HTTPCodedError {
2019-10-18 18:46:00 +00:00
switch c . seal . BarrierType ( ) {
2022-08-23 19:37:16 +00:00
case wrapping . WrapperTypeShamir :
2019-10-18 18:46:00 +00:00
// 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 {
2018-05-20 03:43:48 +00:00
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 {
2018-05-20 03:43:48 +00:00
return logical . CodedError ( http . StatusBadRequest , "key backup not supported when using stored keys" )
2016-04-04 14:44:22 +00:00
}
2019-10-18 18:46:00 +00:00
}
2016-04-04 14:44:22 +00:00
2019-10-18 18:46:00 +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
}
2019-10-18 18:46:00 +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
2016-01-14 22:01:04 +00:00
if err := config . Validate ( ) ; err != nil {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "invalid rekey seal configuration" , "error" , err )
2021-05-11 17:12:54 +00:00
return logical . CodedError ( http . StatusInternalServerError , fmt . Errorf ( "invalid rekey seal configuration: %w" , err ) . Error ( ) )
2016-01-14 22:01:04 +00:00
}
c . stateLock . RLock ( )
defer c . stateLock . RUnlock ( )
2018-07-24 20:57:25 +00:00
if c . Sealed ( ) {
2018-05-20 03:43:48 +00:00
return logical . CodedError ( http . StatusServiceUnavailable , consts . ErrSealed . Error ( ) )
2016-01-14 22:01:04 +00:00
}
if c . standby {
2018-05-20 03:43:48 +00:00
return logical . CodedError ( http . StatusBadRequest , consts . ErrStandby . Error ( ) )
2016-01-14 22:01:04 +00:00
}
2016-04-04 14:44:22 +00:00
c . rekeyLock . Lock ( )
defer c . rekeyLock . Unlock ( )
2016-01-14 22:01:04 +00:00
// Prevent multiple concurrent re-keys
2016-04-04 14:44:22 +00:00
if c . barrierRekeyConfig != nil {
2018-05-20 03:43:48 +00:00
return logical . CodedError ( http . StatusBadRequest , "rekey already in progress" )
2016-01-14 22:01:04 +00:00
}
// Copy the configuration
2016-04-04 14:44:22 +00:00
c . barrierRekeyConfig = config . Clone ( )
2016-01-14 22:01:04 +00:00
// Initialize the nonce
nonce , err := uuid . GenerateUUID ( )
if err != nil {
2016-04-04 14:44:22 +00:00
c . barrierRekeyConfig = nil
2021-05-11 17:12:54 +00:00
return logical . CodedError ( http . StatusInternalServerError , fmt . Errorf ( "error generating nonce for procedure: %w" , err ) . Error ( ) )
2016-01-14 22:01:04 +00:00
}
2016-04-04 14:44:22 +00:00
c . barrierRekeyConfig . Nonce = nonce
2016-01-14 22:01:04 +00:00
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
}
2016-01-14 22:01:04 +00:00
return nil
}
2016-04-04 14:44:22 +00:00
// RecoveryRekeyInit is used to initialize the rekey settings for the recovery key
2018-05-20 03:43:48 +00:00
func ( c * Core ) RecoveryRekeyInit ( config * SealConfig ) logical . HTTPCodedError {
2016-04-04 14:44:22 +00:00
if config . StoredShares > 0 {
2018-05-20 03:43:48 +00:00
return logical . CodedError ( http . StatusBadRequest , "stored shares not supported by recovery key" )
2016-01-14 22:01:04 +00:00
}
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 {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "invalid recovery configuration" , "error" , err )
2021-05-11 17:12:54 +00:00
return logical . CodedError ( http . StatusInternalServerError , fmt . Errorf ( "invalid recovery configuration: %w" , err ) . Error ( ) )
2016-01-14 22:01:04 +00:00
}
2018-01-19 08:44:06 +00:00
if ! c . seal . RecoveryKeySupported ( ) {
2018-05-20 03:43:48 +00:00
return logical . CodedError ( http . StatusBadRequest , "recovery keys not supported" )
2016-04-04 14:44:22 +00:00
}
c . stateLock . RLock ( )
defer c . stateLock . RUnlock ( )
2018-07-24 20:57:25 +00:00
if c . Sealed ( ) {
2018-05-20 03:43:48 +00:00
return logical . CodedError ( http . StatusServiceUnavailable , consts . ErrSealed . Error ( ) )
2016-04-04 14:44:22 +00:00
}
if c . standby {
2018-05-20 03:43:48 +00:00
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 {
2018-05-20 03:43:48 +00:00
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 ( )
2016-01-14 22:01:04 +00:00
if err != nil {
2016-04-04 14:44:22 +00:00
c . recoveryRekeyConfig = nil
2021-05-11 17:12:54 +00:00
return logical . CodedError ( http . StatusInternalServerError , fmt . Errorf ( "error generating nonce for procedure: %w" , err ) . Error ( ) )
2016-01-14 22:01:04 +00:00
}
2016-04-04 14:44:22 +00:00
c . recoveryRekeyConfig . Nonce = nonce
2016-01-14 22:01:04 +00:00
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.
2018-05-20 03:43:48 +00:00
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 {
2018-01-19 06:44:44 +00:00
return c . RecoveryRekeyUpdate ( ctx , key , nonce )
2016-01-14 22:01:04 +00:00
}
2018-01-19 06:44:44 +00:00
return c . BarrierRekeyUpdate ( ctx , key , nonce )
2016-04-04 14:44:22 +00:00
}
2016-01-14 22:01:04 +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.
2018-05-20 03:43:48 +00:00
func ( c * Core ) BarrierRekeyUpdate ( ctx context . Context , key [ ] byte , nonce string ) ( * RekeyResult , logical . HTTPCodedError ) {
2016-01-14 22:01:04 +00:00
// Ensure we are already unsealed
c . stateLock . RLock ( )
defer c . stateLock . RUnlock ( )
2018-07-24 20:57:25 +00:00
if c . Sealed ( ) {
2018-05-20 03:43:48 +00:00
return nil , logical . CodedError ( http . StatusServiceUnavailable , consts . ErrSealed . Error ( ) )
2016-01-14 22:01:04 +00:00
}
if c . standby {
2018-05-20 03:43:48 +00:00
return nil , logical . CodedError ( http . StatusBadRequest , consts . ErrStandby . Error ( ) )
2016-01-14 22:01:04 +00:00
}
2016-04-04 14:44:22 +00:00
// Verify the key length
min , max := c . barrier . KeyLength ( )
max += shamir . ShareOverhead
if len ( key ) < min {
2018-05-20 03:43:48 +00:00
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 {
2018-05-20 03:43:48 +00:00
return nil , logical . CodedError ( http . StatusBadRequest , fmt . Sprintf ( "key is longer than maximum %d bytes" , max ) )
2016-04-04 14:44:22 +00:00
}
2016-01-14 22:01:04 +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
2021-12-07 01:12:20 +00:00
var useRecovery bool // Determines whether recovery key is being used to rekey the root key
2020-01-11 01:39:52 +00:00
if c . seal . StoredKeysSupported ( ) == seal . StoredKeysSupportedGeneric && c . seal . RecoveryKeySupported ( ) {
2018-01-19 06:44:44 +00:00
existingConfig , err = c . seal . RecoveryConfig ( ctx )
2017-10-23 18:59:37 +00:00
useRecovery = true
} else {
2018-01-19 06:44:44 +00:00
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 {
2021-05-11 17:12:54 +00:00
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 {
2018-05-20 03:43:48 +00:00
return nil , logical . CodedError ( http . StatusBadRequest , ErrNotInit . Error ( ) )
2016-04-04 14:44:22 +00:00
}
2016-01-14 22:01:04 +00:00
// Ensure a rekey is in progress
2016-04-04 14:44:22 +00:00
if c . barrierRekeyConfig == nil {
2018-05-20 03:43:48 +00:00
return nil , logical . CodedError ( http . StatusBadRequest , "no barrier rekey in progress" )
2016-01-14 22:01:04 +00:00
}
2018-05-22 01:02:45 +00:00
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 {
2018-05-20 03:43:48 +00:00
return nil , logical . CodedError ( http . StatusBadRequest , fmt . Sprintf ( "incorrect nonce supplied; nonce for this rekey operation is %q" , c . barrierRekeyConfig . Nonce ) )
2016-01-14 22:01:04 +00:00
}
// 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 {
2018-05-20 03:43:48 +00:00
return nil , logical . CodedError ( http . StatusBadRequest , "given key has already been provided during this generation operation" )
2016-01-14 22:01:04 +00:00
}
}
// Store this key
2018-05-21 21:46:32 +00:00
c . barrierRekeyConfig . RekeyProgress = append ( c . barrierRekeyConfig . RekeyProgress , key )
2016-01-14 22:01:04 +00:00
// 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
}
2016-01-14 22:01:04 +00:00
return nil , nil
}
2021-12-07 01:12:20 +00:00
// 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 ]
2018-11-20 01:03:07 +00:00
c . barrierRekeyConfig . RekeyProgress = nil
2016-01-14 22:01:04 +00:00
} else {
2018-05-21 21:46:32 +00:00
recoveredKey , err = shamir . Combine ( c . barrierRekeyConfig . RekeyProgress )
2018-11-20 01:03:07 +00:00
c . barrierRekeyConfig . RekeyProgress = nil
2016-01-14 22:01:04 +00:00
if err != nil {
2021-12-07 01:12:20 +00:00
return nil , logical . CodedError ( http . StatusInternalServerError , fmt . Errorf ( "failed to compute root key: %w" , err ) . Error ( ) )
2016-01-14 22:01:04 +00:00
}
}
2019-10-18 18:46:00 +00:00
switch {
case useRecovery :
2018-01-19 06:44:44 +00:00
if err := c . seal . VerifyRecoveryKey ( ctx , recoveredKey ) ; err != nil {
2018-05-20 03:43:48 +00:00
c . logger . Error ( "rekey recovery key verification failed" , "error" , err )
2021-05-11 17:12:54 +00:00
return nil , logical . CodedError ( http . StatusBadRequest , fmt . Errorf ( "recovery key verification failed: %w" , err ) . Error ( ) )
2017-10-23 18:59:37 +00:00
}
2022-08-23 19:37:16 +00:00
case c . seal . BarrierType ( ) == wrapping . WrapperTypeShamir :
2021-12-07 01:12:20 +00:00
if c . seal . StoredKeysSupported ( ) == seal . StoredKeysSupportedShamirRoot {
2020-01-11 01:39:52 +00:00
testseal := NewDefaultSeal ( & seal . Access {
2022-08-23 19:37:16 +00:00
Wrapper : aeadwrapper . NewShamirWrapper ( ) ,
2020-01-11 01:39:52 +00:00
} )
2019-10-18 18:46:00 +00:00
testseal . SetCore ( c )
2022-08-23 19:37:16 +00:00
err = testseal . GetAccess ( ) . Wrapper . ( * aeadwrapper . ShamirWrapper ) . SetAesGcmKeyBytes ( recoveredKey )
2019-10-18 18:46:00 +00:00
if err != nil {
2021-05-11 17:12:54 +00:00
return nil , logical . CodedError ( http . StatusInternalServerError , fmt . Errorf ( "failed to setup unseal key: %w" , err ) . Error ( ) )
2019-10-18 18:46:00 +00:00
}
cfg , err := c . seal . BarrierConfig ( ctx )
if err != nil {
2021-05-11 17:12:54 +00:00
return nil , logical . CodedError ( http . StatusInternalServerError , fmt . Errorf ( "failed to setup test barrier config: %w" , err ) . Error ( ) )
2019-10-18 18:46:00 +00:00
}
testseal . SetCachedBarrierConfig ( cfg )
stored , err := testseal . GetStoredKeys ( ctx )
if err != nil {
2021-12-07 01:12:20 +00:00
return nil , logical . CodedError ( http . StatusInternalServerError , fmt . Errorf ( "failed to read root key: %w" , err ) . Error ( ) )
2019-10-18 18:46:00 +00:00
}
recoveredKey = stored [ 0 ]
}
2021-12-07 01:12:20 +00:00
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
}
2016-01-14 22:01:04 +00:00
}
2021-12-07 01:12:20 +00:00
// Generate a new key: for AutoUnseal, this is a new root key; for Shamir,
2019-10-18 18:46:00 +00:00
// this is a new unseal key, and performBarrierRekey will also generate a
2021-12-07 01:12:20 +00:00
// new root key.
2019-10-18 18:46:00 +00:00
newKey , err := c . barrier . GenerateKey ( c . secureRandomReader )
2016-01-14 22:01:04 +00:00
if err != nil {
2021-12-07 01:12:20 +00:00
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 ( ) )
2016-01-14 22:01:04 +00:00
}
results := & RekeyResult {
2016-04-04 14:44:22 +00:00
Backup : c . barrierRekeyConfig . Backup ,
2016-01-14 22:01:04 +00:00
}
2020-01-11 01:39:52 +00:00
if c . seal . StoredKeysSupported ( ) != seal . StoredKeysSupportedGeneric {
2019-10-18 18:46:00 +00:00
// 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 )
2021-05-11 17:12:54 +00:00
return nil , logical . CodedError ( http . StatusInternalServerError , fmt . Errorf ( "failed to generate shares: %w" , err ) . Error ( ) )
2019-10-18 18:46:00 +00:00
}
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 ) )
2021-04-08 16:43:39 +00:00
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 )
2016-01-14 22:01:04 +00:00
if err != nil {
2021-05-11 17:12:54 +00:00
return nil , logical . CodedError ( http . StatusInternalServerError , fmt . Errorf ( "failed to encrypt shares: %w" , err ) . Error ( ) )
2016-01-14 22:01:04 +00:00
}
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 {
2016-01-14 22:01:04 +00:00
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 ,
2016-01-14 22:01:04 +00:00
Keys : backupInfo ,
}
buf , err := json . Marshal ( backupVals )
if err != nil {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "failed to marshal unseal key backup" , "error" , err )
2021-05-11 17:12:54 +00:00
return nil , logical . CodedError ( http . StatusInternalServerError , fmt . Errorf ( "failed to marshal unseal key backup: %w" , err ) . Error ( ) )
2016-01-14 22:01:04 +00:00
}
pe := & physical . Entry {
2016-04-04 14:44:22 +00:00
Key : coreBarrierUnsealKeysBackupPath ,
2016-01-14 22:01:04 +00:00
Value : buf ,
}
2018-01-19 06:44:44 +00:00
if err = c . physical . Put ( ctx , pe ) ; err != nil {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "failed to save unseal key backup" , "error" , err )
2021-05-11 17:12:54 +00:00
return nil , logical . CodedError ( http . StatusInternalServerError , fmt . Errorf ( "failed to save unseal key backup: %w" , err ) . Error ( ) )
2016-01-14 22:01:04 +00:00
}
}
}
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
2021-05-11 17:12:54 +00:00
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
2019-10-18 18:46:00 +00:00
c . barrierRekeyConfig . VerificationKey = newKey
2018-05-18 01:17:52 +00:00
results . VerificationRequired = true
results . VerificationNonce = nonce
return results , nil
}
2019-10-18 18:46:00 +00:00
if err := c . performBarrierRekey ( ctx , newKey ) ; err != nil {
2021-05-11 17:12:54 +00:00
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
}
2019-10-18 18:46:00 +00:00
func ( c * Core ) performBarrierRekey ( ctx context . Context , newSealKey [ ] byte ) logical . HTTPCodedError {
2020-01-11 01:39:52 +00:00
legacyUpgrade := c . seal . StoredKeysSupported ( ) == seal . StoredKeysNotSupported
2019-10-18 18:46:00 +00:00
if legacyUpgrade {
// We won't be able to call SetStoredKeys without setting StoredShares=1.
existingConfig , err := c . seal . BarrierConfig ( ctx )
if err != nil {
2021-05-11 17:12:54 +00:00
return logical . CodedError ( http . StatusInternalServerError , fmt . Errorf ( "failed to fetch existing config: %w" , err ) . Error ( ) )
2019-10-18 18:46:00 +00:00
}
existingConfig . StoredShares = 1
c . seal . SetCachedBarrierConfig ( existingConfig )
}
2020-01-11 01:39:52 +00:00
if c . seal . StoredKeysSupported ( ) != seal . StoredKeysSupportedGeneric {
2022-08-23 19:37:16 +00:00
err := c . seal . GetAccess ( ) . Wrapper . ( * aeadwrapper . ShamirWrapper ) . SetAesGcmKeyBytes ( newSealKey )
2019-10-18 18:46:00 +00:00
if err != nil {
2021-05-11 17:12:54 +00:00
return logical . CodedError ( http . StatusInternalServerError , fmt . Errorf ( "failed to update barrier seal key: %w" , err ) . Error ( ) )
2019-10-18 18:46:00 +00:00
}
}
2021-12-07 01:12:20 +00:00
newRootKey , err := c . barrier . GenerateKey ( c . secureRandomReader )
2019-10-18 18:46:00 +00:00
if err != nil {
2021-05-11 17:12:54 +00:00
return logical . CodedError ( http . StatusInternalServerError , fmt . Errorf ( "failed to perform rekey: %w" , err ) . Error ( ) )
2019-10-18 18:46:00 +00:00
}
2021-12-07 01:12:20 +00:00
if err := c . seal . SetStoredKeys ( ctx , [ ] [ ] byte { newRootKey } ) ; err != nil {
2019-10-18 18:46:00 +00:00
c . logger . Error ( "failed to store keys" , "error" , err )
2021-05-11 17:12:54 +00:00
return logical . CodedError ( http . StatusInternalServerError , fmt . Errorf ( "failed to store keys: %w" , err ) . Error ( ) )
2019-10-18 18:46:00 +00:00
}
2016-01-14 22:01:04 +00:00
// Rekey the barrier
2021-12-07 01:12:20 +00:00
if err := c . barrier . Rekey ( ctx , newRootKey ) ; err != nil {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "failed to rekey barrier" , "error" , err )
2021-05-11 17:12:54 +00:00
return logical . CodedError ( http . StatusInternalServerError , fmt . Errorf ( "failed to rekey barrier: %w" , err ) . Error ( ) )
2016-01-14 22:01:04 +00:00
}
2016-08-19 20:45:17 +00:00
if c . logger . IsInfo ( ) {
2019-10-18 18:46:00 +00:00
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 )
2021-05-11 17:12:54 +00:00
return logical . CodedError ( http . StatusInternalServerError , fmt . Errorf ( "failed to store new seal key: %w" , err ) . Error ( ) )
2019-10-18 18:46:00 +00:00
}
2016-08-19 20:45:17 +00:00
}
2018-05-18 01:17:52 +00:00
c . barrierRekeyConfig . VerificationKey = nil
2018-01-19 06:44:44 +00:00
if err := c . seal . SetBarrierConfig ( ctx , c . barrierRekeyConfig ) ; err != nil {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "error saving rekey seal configuration" , "error" , err )
2021-05-11 17:12:54 +00:00
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
2019-01-31 14:25:18 +00:00
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 {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "error saving keyring canary" , "error" , err )
2021-05-11 17:12:54 +00:00
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-01-14 22:01:04 +00:00
2016-04-04 14:44:22 +00:00
// RecoveryRekeyUpdate is used to provide a new key part
2018-05-20 03:43:48 +00:00
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 ( )
2018-07-24 20:57:25 +00:00
if c . Sealed ( ) {
2018-05-20 03:43:48 +00:00
return nil , logical . CodedError ( http . StatusServiceUnavailable , consts . ErrSealed . Error ( ) )
2016-01-14 22:01:04 +00:00
}
2016-04-04 14:44:22 +00:00
if c . standby {
2018-05-20 03:43:48 +00:00
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 {
2018-05-20 03:43:48 +00:00
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 {
2018-05-20 03:43:48 +00:00
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
2018-01-19 06:44:44 +00:00
existingConfig , err := c . seal . RecoveryConfig ( ctx )
2016-04-04 14:44:22 +00:00
if err != nil {
2021-05-11 17:12:54 +00:00
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 {
2018-05-20 03:43:48 +00:00
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 {
2018-05-20 03:43:48 +00:00
return nil , logical . CodedError ( http . StatusBadRequest , "no recovery rekey in progress" )
2016-04-04 14:44:22 +00:00
}
2018-05-22 01:02:45 +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 {
2018-05-20 03:43:48 +00:00
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 {
2018-05-20 03:43:48 +00:00
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
}
2021-12-07 01:12:20 +00:00
// 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 ]
2018-11-20 01:03:07 +00:00
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 )
2018-11-20 01:03:07 +00:00
c . recoveryRekeyConfig . RekeyProgress = nil
2016-04-04 14:44:22 +00:00
if err != nil {
2021-05-11 17:12:54 +00:00
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
2018-01-19 06:44:44 +00:00
if err := c . seal . VerifyRecoveryKey ( ctx , recoveryKey ) ; err != nil {
2018-05-20 03:43:48 +00:00
c . logger . Error ( "recovery key verification failed" , "error" , err )
2021-05-11 17:12:54 +00:00
return nil , logical . CodedError ( http . StatusBadRequest , fmt . Errorf ( "recovery key verification failed: %w" , err ) . Error ( ) )
2016-04-04 14:44:22 +00:00
}
2021-12-07 01:12:20 +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 {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "failed to generate recovery key" , "error" , err )
2021-05-11 17:12:54 +00:00
return nil , logical . CodedError ( http . StatusInternalServerError , fmt . Errorf ( "recovery key generation failed: %w" , err ) . Error ( ) )
2016-04-04 14:44:22 +00:00
}
2021-12-07 01:12:20 +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 {
2021-12-07 01:12:20 +00:00
results . SecretShares = append ( results . SecretShares , newRecoveryKey )
2016-04-04 14:44:22 +00:00
} else {
2021-12-07 01:12:20 +00:00
// 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 {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "failed to generate shares" , "error" , err )
2021-05-11 17:12:54 +00:00
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 ) )
2021-04-08 16:43:39 +00:00
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 {
2021-05-11 17:12:54 +00:00
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 {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "failed to marshal recovery key backup" , "error" , err )
2021-05-11 17:12:54 +00:00
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 ,
}
2018-01-19 06:44:44 +00:00
if err = c . physical . Put ( ctx , pe ) ; err != nil {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "failed to save unseal key backup" , "error" , err )
2021-05-11 17:12:54 +00:00
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
2021-05-11 17:12:54 +00:00
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
2021-12-07 01:12:20 +00:00
c . recoveryRekeyConfig . VerificationKey = newRecoveryKey
2018-05-18 01:17:52 +00:00
results . VerificationRequired = true
results . VerificationNonce = nonce
return results , nil
}
2021-12-07 01:12:20 +00:00
if err := c . performRecoveryRekey ( ctx , newRecoveryKey ) ; err != nil {
2021-05-11 17:12:54 +00:00
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
}
2021-12-07 01:12:20 +00:00
func ( c * Core ) performRecoveryRekey ( ctx context . Context , newRootKey [ ] byte ) logical . HTTPCodedError {
if err := c . seal . SetRecoveryKey ( ctx , newRootKey ) ; err != nil {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "failed to set recovery key" , "error" , err )
2021-05-11 17:12:54 +00:00
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
2018-01-19 06:44:44 +00:00
if err := c . seal . SetRecoveryConfig ( ctx , c . recoveryRekeyConfig ) ; err != nil {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "error saving rekey seal configuration" , "error" , err )
2021-05-11 17:12:54 +00:00
return logical . CodedError ( http . StatusInternalServerError , fmt . Errorf ( "failed to save rekey seal configuration: %w" , err ) . Error ( ) )
2016-01-14 22:01:04 +00:00
}
2017-02-24 15:45:29 +00:00
// Write to the canary path, which will force a synchronous truing during
// replication
2019-01-31 14:25:18 +00:00
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 {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "error saving keyring canary" , "error" , err )
2021-05-11 17:12:54 +00:00
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 ( )
2018-07-24 20:57:25 +00:00
if c . Sealed ( ) {
2018-05-20 03:43:48 +00:00
return nil , logical . CodedError ( http . StatusServiceUnavailable , consts . ErrSealed . Error ( ) )
2018-05-18 01:17:52 +00:00
}
if c . standby {
2018-05-20 03:43:48 +00:00
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 {
2018-05-20 03:43:48 +00:00
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 {
2018-05-20 03:43:48 +00:00
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 {
2018-05-28 04:39:53 +00:00
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 {
2018-05-20 03:43:48 +00:00
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
}
}
} ( )
2021-12-07 01:12:20 +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 {
2021-05-11 17:12:54 +00:00
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" )
2018-05-22 01:06:38 +00:00
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 {
2021-05-11 17:12:54 +00:00
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 {
2021-05-11 17:12:54 +00:00
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
2018-05-20 03:43:48 +00:00
func ( c * Core ) RekeyCancel ( recovery bool ) logical . HTTPCodedError {
2016-01-14 22:01:04 +00:00
c . stateLock . RLock ( )
defer c . stateLock . RUnlock ( )
2018-07-24 20:57:25 +00:00
if c . Sealed ( ) {
2018-05-20 03:43:48 +00:00
return logical . CodedError ( http . StatusServiceUnavailable , consts . ErrSealed . Error ( ) )
2016-01-14 22:01:04 +00:00
}
if c . standby {
2018-05-20 03:43:48 +00:00
return logical . CodedError ( http . StatusBadRequest , consts . ErrStandby . Error ( ) )
2016-01-14 22:01:04 +00:00
}
2016-04-04 14:44:22 +00:00
c . rekeyLock . Lock ( )
defer c . rekeyLock . Unlock ( )
2016-01-14 22:01:04 +00:00
// Clear any progress or config
2016-04-04 14:44:22 +00:00
if recovery {
c . recoveryRekeyConfig = nil
} else {
c . barrierRekeyConfig = nil
}
2016-01-14 22:01:04 +00:00
return nil
}
2018-05-28 04:39:53 +00:00
// RekeyVerifyRestart is used to start the verification process over
2018-05-20 03:43:48 +00:00
func ( c * Core ) RekeyVerifyRestart ( recovery bool ) logical . HTTPCodedError {
2018-05-18 01:17:52 +00:00
c . stateLock . RLock ( )
defer c . stateLock . RUnlock ( )
2018-07-24 20:57:25 +00:00
if c . Sealed ( ) {
2018-05-20 03:43:48 +00:00
return logical . CodedError ( http . StatusServiceUnavailable , consts . ErrSealed . Error ( ) )
2018-05-18 01:17:52 +00:00
}
if c . standby {
2018-05-20 03:43:48 +00:00
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 {
2020-10-07 18:06:17 +00:00
if c . recoveryRekeyConfig != nil {
c . recoveryRekeyConfig . VerificationProgress = nil
if nonceErr == nil {
c . recoveryRekeyConfig . VerificationNonce = nonce
}
2018-05-18 01:17:52 +00:00
}
} else {
2020-10-07 18:06:17 +00:00
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
}
2016-01-14 22:01:04 +00:00
// RekeyRetrieveBackup is used to retrieve any backed-up PGP-encrypted unseal
// keys
2018-05-20 03:43:48 +00:00
func ( c * Core ) RekeyRetrieveBackup ( ctx context . Context , recovery bool ) ( * RekeyBackup , logical . HTTPCodedError ) {
2018-07-24 20:57:25 +00:00
if c . Sealed ( ) {
2018-05-20 03:43:48 +00:00
return nil , logical . CodedError ( http . StatusServiceUnavailable , consts . ErrSealed . Error ( ) )
2016-01-14 22:01:04 +00:00
}
if c . standby {
2018-05-20 03:43:48 +00:00
return nil , logical . CodedError ( http . StatusBadRequest , consts . ErrStandby . Error ( ) )
2016-01-14 22:01:04 +00:00
}
2016-04-04 14:44:22 +00:00
c . rekeyLock . RLock ( )
defer c . rekeyLock . RUnlock ( )
var entry * physical . Entry
var err error
if recovery {
2018-01-19 06:44:44 +00:00
entry , err = c . physical . Get ( ctx , coreRecoveryUnsealKeysBackupPath )
2016-04-04 14:44:22 +00:00
} else {
2018-01-19 06:44:44 +00:00
entry , err = c . physical . Get ( ctx , coreBarrierUnsealKeysBackupPath )
2016-04-04 14:44:22 +00:00
}
2016-01-14 22:01:04 +00:00
if err != nil {
2021-05-11 17:12:54 +00:00
return nil , logical . CodedError ( http . StatusInternalServerError , fmt . Errorf ( "error getting keys from backup: %w" , err ) . Error ( ) )
2016-01-14 22:01:04 +00:00
}
if entry == nil {
return nil , nil
}
ret := & RekeyBackup { }
2016-07-06 16:25:40 +00:00
err = jsonutil . DecodeJSON ( entry . Value , ret )
2016-01-14 22:01:04 +00:00
if err != nil {
2021-05-11 17:12:54 +00:00
return nil , logical . CodedError ( http . StatusInternalServerError , fmt . Errorf ( "error decoding backup keys: %w" , err ) . Error ( ) )
2016-01-14 22:01:04 +00:00
}
return ret , nil
}
// RekeyDeleteBackup is used to delete any backed-up PGP-encrypted unseal keys
2018-05-20 03:43:48 +00:00
func ( c * Core ) RekeyDeleteBackup ( ctx context . Context , recovery bool ) logical . HTTPCodedError {
2018-07-24 20:57:25 +00:00
if c . Sealed ( ) {
2018-05-20 03:43:48 +00:00
return logical . CodedError ( http . StatusServiceUnavailable , consts . ErrSealed . Error ( ) )
2016-01-14 22:01:04 +00:00
}
if c . standby {
2018-05-20 03:43:48 +00:00
return logical . CodedError ( http . StatusBadRequest , consts . ErrStandby . Error ( ) )
2016-01-14 22:01:04 +00:00
}
2016-04-04 14:44:22 +00:00
c . rekeyLock . Lock ( )
defer c . rekeyLock . Unlock ( )
if recovery {
2018-05-20 03:43:48 +00:00
err := c . physical . Delete ( ctx , coreRecoveryUnsealKeysBackupPath )
if err != nil {
2021-05-11 17:12:54 +00:00
return logical . CodedError ( http . StatusInternalServerError , fmt . Errorf ( "error deleting backup keys: %w" , err ) . Error ( ) )
2018-05-20 03:43:48 +00:00
}
return nil
}
err := c . physical . Delete ( ctx , coreBarrierUnsealKeysBackupPath )
if err != nil {
2021-05-11 17:12:54 +00:00
return logical . CodedError ( http . StatusInternalServerError , fmt . Errorf ( "error deleting backup keys: %w" , err ) . Error ( ) )
2016-04-04 14:44:22 +00:00
}
2018-05-20 03:43:48 +00:00
return nil
2016-01-14 22:01:04 +00:00
}