WIP
This commit is contained in:
parent
cec2123a98
commit
a9d8be3c4d
|
@ -80,8 +80,10 @@ func Handler(core *vault.Core) http.Handler {
|
|||
mux.Handle("/v1/sys/generate-root/update", handleRequestForwarding(core, handleSysGenerateRootUpdate(core, vault.GenerateStandardRootTokenStrategy)))
|
||||
mux.Handle("/v1/sys/rekey/init", handleRequestForwarding(core, handleSysRekeyInit(core, false)))
|
||||
mux.Handle("/v1/sys/rekey/update", handleRequestForwarding(core, handleSysRekeyUpdate(core, false)))
|
||||
mux.Handle("/v1/sys/rekey/verify", handleRequestForwarding(core, handleSysRekeyVerify(core, false)))
|
||||
mux.Handle("/v1/sys/rekey-recovery-key/init", handleRequestForwarding(core, handleSysRekeyInit(core, true)))
|
||||
mux.Handle("/v1/sys/rekey-recovery-key/update", handleRequestForwarding(core, handleSysRekeyUpdate(core, true)))
|
||||
mux.Handle("/v1/sys/rekey-recovery-key/verify", handleRequestForwarding(core, handleSysRekeyVerify(core, true)))
|
||||
mux.Handle("/v1/sys/wrapping/lookup", handleRequestForwarding(core, handleLogical(core, false, wrappingVerificationFunc)))
|
||||
mux.Handle("/v1/sys/wrapping/rewrap", handleRequestForwarding(core, handleLogical(core, false, wrappingVerificationFunc)))
|
||||
mux.Handle("/v1/sys/wrapping/unwrap", handleRequestForwarding(core, handleLogical(core, false, wrappingVerificationFunc)))
|
||||
|
|
|
@ -90,6 +90,7 @@ func handleSysRekeyInitGet(ctx context.Context, core *vault.Core, recovery bool,
|
|||
status.Started = true
|
||||
status.T = rekeyConf.SecretThreshold
|
||||
status.N = rekeyConf.SecretShares
|
||||
status.VerificationRequired = rekeyConf.VerificationRequired
|
||||
if rekeyConf.PGPKeys != nil && len(rekeyConf.PGPKeys) != 0 {
|
||||
pgpFingerprints, err := pgpkeys.GetFingerprints(rekeyConf.PGPKeys, nil)
|
||||
if err != nil {
|
||||
|
@ -137,11 +138,12 @@ func handleSysRekeyInitPut(ctx context.Context, core *vault.Core, recovery bool,
|
|||
|
||||
// Initialize the rekey
|
||||
err := core.RekeyInit(&vault.SealConfig{
|
||||
SecretShares: req.SecretShares,
|
||||
SecretThreshold: req.SecretThreshold,
|
||||
StoredShares: req.StoredShares,
|
||||
PGPKeys: req.PGPKeys,
|
||||
Backup: req.Backup,
|
||||
SecretShares: req.SecretShares,
|
||||
SecretThreshold: req.SecretThreshold,
|
||||
StoredShares: req.StoredShares,
|
||||
PGPKeys: req.PGPKeys,
|
||||
Backup: req.Backup,
|
||||
VerificationRequired: req.RequireVerification,
|
||||
}, recovery)
|
||||
if err != nil {
|
||||
respondError(w, http.StatusBadRequest, err)
|
||||
|
@ -214,6 +216,8 @@ func handleSysRekeyUpdate(core *vault.Core, recovery bool) http.Handler {
|
|||
resp.Nonce = req.Nonce
|
||||
resp.Backup = result.Backup
|
||||
resp.PGPFingerprints = result.PGPFingerprints
|
||||
resp.VerificationRequired = result.VerificationRequired
|
||||
resp.VerificationNonce = result.VerificationNonce
|
||||
|
||||
// Encode the keys
|
||||
keys := make([]string, 0, len(result.SecretShares))
|
||||
|
@ -231,23 +235,161 @@ func handleSysRekeyUpdate(core *vault.Core, recovery bool) http.Handler {
|
|||
})
|
||||
}
|
||||
|
||||
func handleSysRekeyVerify(core *vault.Core, recovery bool) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
standby, _ := core.Standby()
|
||||
if standby {
|
||||
respondStandby(core, w, r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
repState := core.ReplicationState()
|
||||
if repState.HasState(consts.ReplicationPerformanceSecondary) {
|
||||
respondError(w, http.StatusBadRequest,
|
||||
fmt.Errorf("rekeying can only be performed on the primary cluster when replication is activated"))
|
||||
return
|
||||
}
|
||||
|
||||
ctx, cancel := core.GetContext()
|
||||
defer cancel()
|
||||
|
||||
switch {
|
||||
case recovery && !core.SealAccess().RecoveryKeySupported():
|
||||
respondError(w, http.StatusBadRequest, fmt.Errorf("recovery rekeying not supported"))
|
||||
case r.Method == "GET":
|
||||
handleSysRekeyVerifyGet(ctx, core, recovery, w, r)
|
||||
case r.Method == "POST" || r.Method == "PUT":
|
||||
handleSysRekeyVerifyPut(ctx, core, recovery, w, r)
|
||||
case r.Method == "DELETE":
|
||||
handleSysRekeyVerifyDelete(core, recovery, w, r)
|
||||
default:
|
||||
respondError(w, http.StatusMethodNotAllowed, nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func handleSysRekeyVerifyGet(ctx context.Context, core *vault.Core, recovery bool, w http.ResponseWriter, r *http.Request) {
|
||||
barrierConfig, err := core.SealAccess().BarrierConfig(ctx)
|
||||
if err != nil {
|
||||
respondError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
if barrierConfig == nil {
|
||||
respondError(w, http.StatusBadRequest, fmt.Errorf("server is not yet initialized"))
|
||||
return
|
||||
}
|
||||
|
||||
// Get the rekey configuration
|
||||
rekeyConf, err := core.RekeyConfig(recovery)
|
||||
if err != nil {
|
||||
respondError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
if rekeyConf == nil {
|
||||
respondError(w, http.StatusBadRequest, errors.New("no rekey configuration found"))
|
||||
return
|
||||
}
|
||||
|
||||
// Get the progress
|
||||
progress, err := core.RekeyVerifyProgress(recovery)
|
||||
if err != nil {
|
||||
respondError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Format the status
|
||||
status := &RekeyVerificationStatusResponse{
|
||||
Nonce: rekeyConf.Nonce,
|
||||
T: rekeyConf.SecretThreshold,
|
||||
N: rekeyConf.SecretShares,
|
||||
Progress: progress,
|
||||
}
|
||||
respondOk(w, status)
|
||||
}
|
||||
|
||||
func handleSysRekeyVerifyDelete(core *vault.Core, recovery bool, w http.ResponseWriter, r *http.Request) {
|
||||
err := core.RekeyVerifyRestart(recovery)
|
||||
if err != nil {
|
||||
respondError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx, cancel := core.GetContext()
|
||||
defer cancel()
|
||||
|
||||
handleSysRekeyVerifyGet(ctx, core, recovery, w, r)
|
||||
}
|
||||
|
||||
func handleSysRekeyVerifyPut(ctx context.Context, core *vault.Core, recovery bool, w http.ResponseWriter, r *http.Request) {
|
||||
// Parse the request
|
||||
var req RekeyVerificationUpdateRequest
|
||||
if err := parseRequest(r, w, &req); err != nil {
|
||||
respondError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
if req.Key == "" {
|
||||
respondError(
|
||||
w, http.StatusBadRequest,
|
||||
errors.New("'key' must be specified in request body as JSON"))
|
||||
return
|
||||
}
|
||||
|
||||
// Decode the key, which is base64 or hex encoded
|
||||
min, max := core.BarrierKeyLength()
|
||||
key, err := hex.DecodeString(req.Key)
|
||||
// We check min and max here to ensure that a string that is base64
|
||||
// encoded but also valid hex will not be valid and we instead base64
|
||||
// decode it
|
||||
if err != nil || len(key) < min || len(key) > max {
|
||||
key, err = base64.StdEncoding.DecodeString(req.Key)
|
||||
if err != nil {
|
||||
respondError(
|
||||
w, http.StatusBadRequest,
|
||||
errors.New("'key' must be a valid hex or base64 string"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ctx, cancel := core.GetContext()
|
||||
defer cancel()
|
||||
|
||||
// Use the key to make progress on rekey
|
||||
result, err := core.RekeyVerify(ctx, key, recovery)
|
||||
if err != nil {
|
||||
respondError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Format the response
|
||||
resp := &RekeyVerificationUpdateResponse{}
|
||||
if result != nil {
|
||||
resp.Complete = true
|
||||
resp.Nonce = result.Nonce
|
||||
respondOk(w, resp)
|
||||
} else {
|
||||
handleSysRekeyVerifyGet(ctx, core, recovery, w, r)
|
||||
}
|
||||
}
|
||||
|
||||
type RekeyRequest struct {
|
||||
SecretShares int `json:"secret_shares"`
|
||||
SecretThreshold int `json:"secret_threshold"`
|
||||
StoredShares int `json:"stored_shares"`
|
||||
PGPKeys []string `json:"pgp_keys"`
|
||||
Backup bool `json:"backup"`
|
||||
SecretShares int `json:"secret_shares"`
|
||||
SecretThreshold int `json:"secret_threshold"`
|
||||
StoredShares int `json:"stored_shares"`
|
||||
PGPKeys []string `json:"pgp_keys"`
|
||||
Backup bool `json:"backup"`
|
||||
RequireVerification bool `json:"require_verification"`
|
||||
}
|
||||
|
||||
type RekeyStatusResponse struct {
|
||||
Nonce string `json:"nonce"`
|
||||
Started bool `json:"started"`
|
||||
T int `json:"t"`
|
||||
N int `json:"n"`
|
||||
Progress int `json:"progress"`
|
||||
Required int `json:"required"`
|
||||
PGPFingerprints []string `json:"pgp_fingerprints"`
|
||||
Backup bool `json:"backup"`
|
||||
Nonce string `json:"nonce"`
|
||||
Started bool `json:"started"`
|
||||
T int `json:"t"`
|
||||
N int `json:"n"`
|
||||
Progress int `json:"progress"`
|
||||
Required int `json:"required"`
|
||||
PGPFingerprints []string `json:"pgp_fingerprints"`
|
||||
Backup bool `json:"backup"`
|
||||
VerificationRequired bool `json:"verification_required"`
|
||||
}
|
||||
|
||||
type RekeyUpdateRequest struct {
|
||||
|
@ -256,10 +398,28 @@ type RekeyUpdateRequest struct {
|
|||
}
|
||||
|
||||
type RekeyUpdateResponse struct {
|
||||
Nonce string `json:"nonce"`
|
||||
Complete bool `json:"complete"`
|
||||
Keys []string `json:"keys"`
|
||||
KeysB64 []string `json:"keys_base64"`
|
||||
PGPFingerprints []string `json:"pgp_fingerprints"`
|
||||
Backup bool `json:"backup"`
|
||||
Nonce string `json:"nonce"`
|
||||
Complete bool `json:"complete"`
|
||||
Keys []string `json:"keys"`
|
||||
KeysB64 []string `json:"keys_base64"`
|
||||
PGPFingerprints []string `json:"pgp_fingerprints"`
|
||||
Backup bool `json:"backup"`
|
||||
VerificationRequired bool `json:"verification_required"`
|
||||
VerificationNonce string `json:"verification_nonce"`
|
||||
}
|
||||
|
||||
type RekeyVerificationUpdateRequest struct {
|
||||
Key string
|
||||
}
|
||||
|
||||
type RekeyVerificationStatusResponse struct {
|
||||
Nonce string `json:"nonce"`
|
||||
T int `json:"t"`
|
||||
N int `json:"n"`
|
||||
Progress int `json:"progress"`
|
||||
}
|
||||
|
||||
type RekeyVerificationUpdateResponse struct {
|
||||
Nonce string `json:"nonce"`
|
||||
Complete bool `json:"complete"`
|
||||
}
|
||||
|
|
|
@ -210,11 +210,13 @@ type Core struct {
|
|||
// These variables holds the config and shares we have until we reach
|
||||
// enough to verify the appropriate master key. Note that the same lock is
|
||||
// used; this isn't time-critical so this shouldn't be a problem.
|
||||
barrierRekeyConfig *SealConfig
|
||||
barrierRekeyProgress [][]byte
|
||||
recoveryRekeyConfig *SealConfig
|
||||
recoveryRekeyProgress [][]byte
|
||||
rekeyLock sync.RWMutex
|
||||
barrierRekeyConfig *SealConfig
|
||||
barrierRekeyProgress [][]byte
|
||||
barrierRekeyVerifyProgress [][]byte
|
||||
recoveryRekeyConfig *SealConfig
|
||||
recoveryRekeyProgress [][]byte
|
||||
recoveryRekeyVerifyProgress [][]byte
|
||||
rekeyLock sync.RWMutex
|
||||
|
||||
// mounts is loaded after unseal since it is a protected
|
||||
// configuration
|
||||
|
@ -1762,8 +1764,10 @@ func (c *Core) preSeal() error {
|
|||
// Clear any rekey progress
|
||||
c.barrierRekeyConfig = nil
|
||||
c.barrierRekeyProgress = nil
|
||||
c.barrierRekeyVerifyProgress = nil
|
||||
c.recoveryRekeyConfig = nil
|
||||
c.recoveryRekeyProgress = nil
|
||||
c.recoveryRekeyVerifyProgress = nil
|
||||
|
||||
if c.metricsCh != nil {
|
||||
close(c.metricsCh)
|
||||
|
|
262
vault/rekey.go
262
vault/rekey.go
|
@ -3,8 +3,10 @@ package vault
|
|||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/subtle"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
|
@ -31,10 +33,16 @@ const (
|
|||
// RekeyResult is used to provide the key parts back after
|
||||
// they are generated as part of the rekey.
|
||||
type RekeyResult struct {
|
||||
SecretShares [][]byte
|
||||
PGPFingerprints []string
|
||||
Backup bool
|
||||
RecoveryKey bool
|
||||
SecretShares [][]byte
|
||||
PGPFingerprints []string
|
||||
Backup bool
|
||||
RecoveryKey bool
|
||||
VerificationRequired bool
|
||||
VerificationNonce string
|
||||
}
|
||||
|
||||
type RekeyVerifyResult struct {
|
||||
Nonce string
|
||||
}
|
||||
|
||||
// RekeyBackup stores the backup copy of PGP-encrypted keys
|
||||
|
@ -98,6 +106,27 @@ func (c *Core) RekeyProgress(recovery bool) (int, error) {
|
|||
return len(c.barrierRekeyProgress), nil
|
||||
}
|
||||
|
||||
// RekeyVerifyProgress is used to return the rekey progress (num shares) during
|
||||
// verification.
|
||||
func (c *Core) RekeyVerifyProgress(recovery bool) (int, error) {
|
||||
c.stateLock.RLock()
|
||||
defer c.stateLock.RUnlock()
|
||||
if c.sealed {
|
||||
return 0, consts.ErrSealed
|
||||
}
|
||||
if c.standby {
|
||||
return 0, consts.ErrStandby
|
||||
}
|
||||
|
||||
c.rekeyLock.RLock()
|
||||
defer c.rekeyLock.RUnlock()
|
||||
|
||||
if recovery {
|
||||
return len(c.recoveryRekeyVerifyProgress), nil
|
||||
}
|
||||
return len(c.barrierRekeyVerifyProgress), nil
|
||||
}
|
||||
|
||||
// RekeyConfig is used to read the rekey configuration
|
||||
func (c *Core) RekeyConfig(recovery bool) (*SealConfig, error) {
|
||||
c.stateLock.RLock()
|
||||
|
@ -152,6 +181,9 @@ func (c *Core) BarrierRekeyInit(config *SealConfig) error {
|
|||
|
||||
if c.seal.RecoveryKeySupported() && c.seal.RecoveryType() == config.Type {
|
||||
c.logger.Debug("using recovery seal configuration to rekey barrier key")
|
||||
if config.VerificationRequired {
|
||||
return fmt.Errorf("requiring verification not supported when rekeying the barrier key with recovery keys")
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the seal configuration is valid
|
||||
|
@ -189,7 +221,7 @@ func (c *Core) BarrierRekeyInit(config *SealConfig) error {
|
|||
c.barrierRekeyConfig.Nonce = nonce
|
||||
|
||||
if c.logger.IsInfo() {
|
||||
c.logger.Info("rekey initialized", "nonce", c.barrierRekeyConfig.Nonce, "shares", c.barrierRekeyConfig.SecretShares, "threshold", c.barrierRekeyConfig.SecretThreshold)
|
||||
c.logger.Info("rekey initialized", "nonce", c.barrierRekeyConfig.Nonce, "shares", c.barrierRekeyConfig.SecretShares, "threshold", c.barrierRekeyConfig.SecretThreshold, "validation_required", c.barrierRekeyConfig.VerificationRequired)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -239,7 +271,7 @@ func (c *Core) RecoveryRekeyInit(config *SealConfig) error {
|
|||
c.recoveryRekeyConfig.Nonce = nonce
|
||||
|
||||
if c.logger.IsInfo() {
|
||||
c.logger.Info("rekey initialized", "nonce", c.recoveryRekeyConfig.Nonce, "shares", c.recoveryRekeyConfig.SecretShares, "threshold", c.recoveryRekeyConfig.SecretThreshold)
|
||||
c.logger.Info("rekey initialized", "nonce", c.recoveryRekeyConfig.Nonce, "shares", c.recoveryRekeyConfig.SecretShares, "threshold", c.recoveryRekeyConfig.SecretThreshold, "validation_required", c.recoveryRekeyConfig.VerificationRequired)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -327,14 +359,17 @@ func (c *Core) BarrierRekeyUpdate(ctx context.Context, key []byte, nonce string)
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
// Schedule the rekey progress for forgetting
|
||||
defer func() {
|
||||
c.barrierRekeyProgress = nil
|
||||
}()
|
||||
|
||||
// Recover the master key or recovery key
|
||||
var recoveredKey []byte
|
||||
if existingConfig.SecretThreshold == 1 {
|
||||
recoveredKey = c.barrierRekeyProgress[0]
|
||||
c.barrierRekeyProgress = nil
|
||||
} else {
|
||||
recoveredKey, err = shamir.Combine(c.barrierRekeyProgress)
|
||||
c.barrierRekeyProgress = nil
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("failed to compute master key: {{err}}", err)
|
||||
}
|
||||
|
@ -436,17 +471,44 @@ func (c *Core) BarrierRekeyUpdate(ctx context.Context, key []byte, nonce string)
|
|||
}
|
||||
}
|
||||
|
||||
// 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, err
|
||||
}
|
||||
c.barrierRekeyConfig.VerificationNonce = nonce
|
||||
c.barrierRekeyConfig.VerificationKey = newMasterKey
|
||||
|
||||
results.VerificationRequired = true
|
||||
results.VerificationNonce = nonce
|
||||
return results, nil
|
||||
}
|
||||
|
||||
if err := c.performBarrierRekey(ctx, newMasterKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.barrierRekeyConfig = nil
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (c *Core) performBarrierRekey(ctx context.Context, newMasterKey []byte) error {
|
||||
// Rekey the barrier
|
||||
if err := c.barrier.Rekey(ctx, newMasterKey); err != nil {
|
||||
c.logger.Error("failed to rekey barrier", "error", err)
|
||||
return nil, errwrap.Wrapf("failed to rekey barrier: {{err}}", err)
|
||||
return errwrap.Wrapf("failed to rekey barrier: {{err}}", err)
|
||||
}
|
||||
if c.logger.IsInfo() {
|
||||
c.logger.Info("security barrier rekeyed", "shares", c.barrierRekeyConfig.SecretShares, "threshold", c.barrierRekeyConfig.SecretThreshold)
|
||||
}
|
||||
|
||||
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 nil, errwrap.Wrapf("failed to save rekey seal configuration: {{err}}", err)
|
||||
return errwrap.Wrapf("failed to save rekey seal configuration: {{err}}", err)
|
||||
}
|
||||
|
||||
// Write to the canary path, which will force a synchronous truing during
|
||||
|
@ -456,13 +518,10 @@ func (c *Core) BarrierRekeyUpdate(ctx context.Context, key []byte, nonce string)
|
|||
Value: []byte(c.barrierRekeyConfig.Nonce),
|
||||
}); err != nil {
|
||||
c.logger.Error("error saving keyring canary", "error", err)
|
||||
return nil, errwrap.Wrapf("failed to save keyring canary: {{err}}", err)
|
||||
return errwrap.Wrapf("failed to save keyring canary: {{err}}", err)
|
||||
}
|
||||
|
||||
// Done!
|
||||
c.barrierRekeyProgress = nil
|
||||
c.barrierRekeyConfig = nil
|
||||
return results, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// RecoveryRekeyUpdate is used to provide a new key part
|
||||
|
@ -528,14 +587,17 @@ func (c *Core) RecoveryRekeyUpdate(ctx context.Context, key []byte, nonce string
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
// Schedule the rekey progress for forgetting
|
||||
defer func() {
|
||||
c.recoveryRekeyProgress = nil
|
||||
}()
|
||||
|
||||
// Recover the master key
|
||||
var recoveryKey []byte
|
||||
if existingConfig.SecretThreshold == 1 {
|
||||
recoveryKey = c.recoveryRekeyProgress[0]
|
||||
c.recoveryRekeyProgress = nil
|
||||
} else {
|
||||
recoveryKey, err = shamir.Combine(c.recoveryRekeyProgress)
|
||||
c.recoveryRekeyProgress = nil
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("failed to compute recovery key: {{err}}", err)
|
||||
}
|
||||
|
@ -612,14 +674,41 @@ func (c *Core) RecoveryRekeyUpdate(ctx context.Context, key []byte, nonce string
|
|||
}
|
||||
}
|
||||
|
||||
// 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, err
|
||||
}
|
||||
c.recoveryRekeyConfig.VerificationNonce = nonce
|
||||
c.recoveryRekeyConfig.VerificationKey = newMasterKey
|
||||
|
||||
results.VerificationRequired = true
|
||||
results.VerificationNonce = nonce
|
||||
return results, nil
|
||||
}
|
||||
|
||||
if err := c.performRecoveryRekey(ctx, newMasterKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.recoveryRekeyConfig = nil
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (c *Core) performRecoveryRekey(ctx context.Context, newMasterKey []byte) error {
|
||||
if err := c.seal.SetRecoveryKey(ctx, newMasterKey); err != nil {
|
||||
c.logger.Error("failed to set recovery key", "error", err)
|
||||
return nil, errwrap.Wrapf("failed to set recovery key: {{err}}", err)
|
||||
return errwrap.Wrapf("failed to set recovery key: {{err}}", err)
|
||||
}
|
||||
|
||||
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 nil, errwrap.Wrapf("failed to save rekey seal configuration: {{err}}", err)
|
||||
return errwrap.Wrapf("failed to save rekey seal configuration: {{err}}", err)
|
||||
}
|
||||
|
||||
// Write to the canary path, which will force a synchronous truing during
|
||||
|
@ -629,13 +718,104 @@ func (c *Core) RecoveryRekeyUpdate(ctx context.Context, key []byte, nonce string
|
|||
Value: []byte(c.recoveryRekeyConfig.Nonce),
|
||||
}); err != nil {
|
||||
c.logger.Error("error saving keyring canary", "error", err)
|
||||
return nil, errwrap.Wrapf("failed to save keyring canary: {{err}}", err)
|
||||
return errwrap.Wrapf("failed to save keyring canary: {{err}}", err)
|
||||
}
|
||||
|
||||
// Done!
|
||||
c.recoveryRekeyProgress = nil
|
||||
c.recoveryRekeyConfig = nil
|
||||
return results, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Core) RekeyVerify(ctx context.Context, key []byte, recovery bool) (*RekeyVerifyResult, error) {
|
||||
if recovery {
|
||||
//return c.RecoveryRekeyVerify(ctx, key)
|
||||
}
|
||||
return c.BarrierRekeyVerify(ctx, key)
|
||||
}
|
||||
|
||||
func (c *Core) BarrierRekeyVerify(ctx context.Context, key []byte) (*RekeyVerifyResult, error) {
|
||||
// Ensure we are already unsealed
|
||||
c.stateLock.RLock()
|
||||
defer c.stateLock.RUnlock()
|
||||
if c.sealed {
|
||||
return nil, consts.ErrSealed
|
||||
}
|
||||
if c.standby {
|
||||
return nil, consts.ErrStandby
|
||||
}
|
||||
|
||||
// Verify the key length
|
||||
min, max := c.barrier.KeyLength()
|
||||
max += shamir.ShareOverhead
|
||||
if len(key) < min {
|
||||
return nil, &ErrInvalidKey{fmt.Sprintf("key is shorter than minimum %d bytes", min)}
|
||||
}
|
||||
if len(key) > max {
|
||||
return nil, &ErrInvalidKey{fmt.Sprintf("key is longer than maximum %d bytes", max)}
|
||||
}
|
||||
|
||||
c.rekeyLock.Lock()
|
||||
defer c.rekeyLock.Unlock()
|
||||
|
||||
// Ensure a rekey is in progress
|
||||
if c.barrierRekeyConfig == nil {
|
||||
return nil, fmt.Errorf("no rekey in progress")
|
||||
}
|
||||
|
||||
// Check if we already have this piece
|
||||
for _, existing := range c.barrierRekeyVerifyProgress {
|
||||
if bytes.Equal(existing, key) {
|
||||
return nil, fmt.Errorf("given key has already been provided during this verify operation")
|
||||
}
|
||||
}
|
||||
|
||||
// Store this key
|
||||
c.barrierRekeyVerifyProgress = append(c.barrierRekeyVerifyProgress, key)
|
||||
|
||||
// Check if we don't have enough keys to unlock
|
||||
if len(c.barrierRekeyVerifyProgress) < c.barrierRekeyConfig.SecretThreshold {
|
||||
if c.logger.IsDebug() {
|
||||
c.logger.Debug("cannot verify yet, not enough keys", "keys", len(c.barrierRekeyVerifyProgress), "threshold", c.barrierRekeyConfig.SecretThreshold)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Schedule the progress for forgetting and rotate the nonce if possible
|
||||
defer func() {
|
||||
c.barrierRekeyVerifyProgress = nil
|
||||
if c.barrierRekeyConfig != nil {
|
||||
nonce, err := uuid.GenerateUUID()
|
||||
if err == nil {
|
||||
c.barrierRekeyConfig.VerificationNonce = nonce
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Recover the master key or recovery key
|
||||
var recoveredKey []byte
|
||||
var err error
|
||||
if c.barrierRekeyConfig.SecretThreshold == 1 {
|
||||
recoveredKey = c.barrierRekeyVerifyProgress[0]
|
||||
} else {
|
||||
recoveredKey, err = shamir.Combine(c.barrierRekeyVerifyProgress)
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("failed to compute master key for verification: {{err}}", err)
|
||||
}
|
||||
}
|
||||
|
||||
if subtle.ConstantTimeCompare(recoveredKey, c.barrierRekeyConfig.VerificationKey) != 1 {
|
||||
c.logger.Error("rekey verification failed")
|
||||
return nil, errors.New("rekey verification failed")
|
||||
}
|
||||
|
||||
if err := c.performBarrierRekey(ctx, recoveredKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := &RekeyVerifyResult{
|
||||
Nonce: c.barrierRekeyConfig.VerificationNonce,
|
||||
}
|
||||
|
||||
c.barrierRekeyConfig = nil
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// RekeyCancel is used to cancel an inprogress rekey
|
||||
|
@ -663,6 +843,40 @@ func (c *Core) RekeyCancel(recovery bool) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// RekeyVerifyCancel is used to start the verification process over
|
||||
func (c *Core) RekeyVerifyRestart(recovery bool) error {
|
||||
c.stateLock.RLock()
|
||||
defer c.stateLock.RUnlock()
|
||||
if c.sealed {
|
||||
return consts.ErrSealed
|
||||
}
|
||||
if c.standby {
|
||||
return consts.ErrStandby
|
||||
}
|
||||
|
||||
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 {
|
||||
c.recoveryRekeyVerifyProgress = nil
|
||||
if nonceErr == nil {
|
||||
c.recoveryRekeyConfig.VerificationNonce = nonce
|
||||
}
|
||||
} else {
|
||||
c.barrierRekeyVerifyProgress = nil
|
||||
if nonceErr == nil {
|
||||
c.barrierRekeyConfig.VerificationNonce = nonce
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RekeyRetrieveBackup is used to retrieve any backed-up PGP-encrypted unseal
|
||||
// keys
|
||||
func (c *Core) RekeyRetrieveBackup(ctx context.Context, recovery bool) (*RekeyBackup, error) {
|
||||
|
|
|
@ -272,6 +272,19 @@ type SealConfig struct {
|
|||
|
||||
// How many keys to store, for seals that support storage.
|
||||
StoredShares int `json:"stored_shares"`
|
||||
|
||||
// VerificationRequired indicates that after a rekey validation must be
|
||||
// performed (via providing shares from the new key) before the new key is
|
||||
// actually installed. This is ommitted from JSON as we don't persist the
|
||||
// new key, it lives only in memory.
|
||||
VerificationRequired bool `json:"-"`
|
||||
|
||||
// VerificationKey is the new key that we will roll to after successful
|
||||
// validation
|
||||
VerificationKey []byte `json:"-"`
|
||||
|
||||
// VerificationNonce stores the current operation nonce for verification
|
||||
VerificationNonce string `json:"-"`
|
||||
}
|
||||
|
||||
// Validate is used to sanity check the seal configuration
|
||||
|
@ -317,16 +330,21 @@ func (s *SealConfig) Validate() error {
|
|||
|
||||
func (s *SealConfig) Clone() *SealConfig {
|
||||
ret := &SealConfig{
|
||||
Type: s.Type,
|
||||
SecretShares: s.SecretShares,
|
||||
SecretThreshold: s.SecretThreshold,
|
||||
Nonce: s.Nonce,
|
||||
Backup: s.Backup,
|
||||
StoredShares: s.StoredShares,
|
||||
Type: s.Type,
|
||||
SecretShares: s.SecretShares,
|
||||
SecretThreshold: s.SecretThreshold,
|
||||
Nonce: s.Nonce,
|
||||
Backup: s.Backup,
|
||||
StoredShares: s.StoredShares,
|
||||
VerificationRequired: s.VerificationRequired,
|
||||
}
|
||||
if len(s.PGPKeys) > 0 {
|
||||
ret.PGPKeys = make([]string, len(s.PGPKeys))
|
||||
copy(ret.PGPKeys, s.PGPKeys)
|
||||
}
|
||||
if len(s.VerificationKey) > 0 {
|
||||
ret.VerificationKey = make([]byte, len(s.VerificationKey))
|
||||
copy(ret.VerificationKey, s.VerificationKey)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue