Update rekey methods to indicate proper error codes in responses

This commit is contained in:
Jeff Mitchell 2018-05-19 23:43:48 -04:00
parent 9b796f1aca
commit cfd087b155
5 changed files with 234 additions and 165 deletions

View File

@ -169,31 +169,35 @@ func (c *Sys) RekeyDeleteRecoveryBackup() error {
}
type RekeyInitRequest struct {
SecretShares int `json:"secret_shares"`
SecretThreshold int `json:"secret_threshold"`
StoredShares int `json:"stored_shares"`
PGPKeys []string `json:"pgp_keys"`
Backup bool
SecretShares int `json:"secret_shares"`
SecretThreshold int `json:"secret_threshold"`
StoredShares int `json:"stored_shares"`
PGPKeys []string `json:"pgp_keys"`
Backup bool
RequireVerification bool `json:"require_verification"`
}
type RekeyStatusResponse struct {
Nonce string `json:"nonce"`
Started bool `json:"started"`
T int `json:"t"`
N int `json:"n"`
Progress int `json:"progress"`
Required int `json:"required"`
PGPFingerprints []string `json:"pgp_fingerprints"`
Backup bool `json:"backup"`
Nonce string `json:"nonce"`
Started bool `json:"started"`
T int `json:"t"`
N int `json:"n"`
Progress int `json:"progress"`
Required int `json:"required"`
PGPFingerprints []string `json:"pgp_fingerprints"`
Backup bool `json:"backup"`
VerificationRequired bool `json:"verification_required"`
}
type RekeyUpdateResponse struct {
Nonce string `json:"nonce"`
Complete bool `json:"complete"`
Keys []string `json:"keys"`
KeysB64 []string `json:"keys_base64"`
PGPFingerprints []string `json:"pgp_fingerprints"`
Backup bool `json:"backup"`
Nonce string `json:"nonce"`
Complete bool `json:"complete"`
Keys []string `json:"keys"`
KeysB64 []string `json:"keys_base64"`
PGPFingerprints []string `json:"pgp_fingerprints"`
Backup bool `json:"backup"`
VerificationRequired bool `json:"verification_required"`
VerificationNonce string `json:"verification_nonce,omitempty"`
}
type RekeyRetrieveResponse struct {
@ -201,3 +205,15 @@ type RekeyRetrieveResponse struct {
Keys map[string][]string `json:"keys"`
KeysB64 map[string][]string `json:"keys_base64"`
}
type RekeyVerificationStatusResponse struct {
Nonce string `json:"nonce"`
T int `json:"t"`
N int `json:"n"`
Progress int `json:"progress"`
}
type RekeyVerificationUpdateResponse struct {
Nonce string `json:"nonce"`
Complete bool `json:"complete"`
}

38
api/sys_rekey_ext_test.go Normal file
View File

@ -0,0 +1,38 @@
package api_test
import (
"testing"
"github.com/hashicorp/vault/api"
vaulthttp "github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/vault"
)
func TestSysRekey_Verification(t *testing.T) {
cluster := vault.NewTestCluster(t, nil, &vault.TestClusterOptions{
HandlerFunc: vaulthttp.Handler,
})
cluster.Start()
defer cluster.Cleanup()
vault.TestWaitActive(t, cluster.Cores[0].Core)
client := cluster.Cores[0].Client
vault.DefaultSealPretendsToAllowRecoveryKeys = true
vault.DefaultSealPretendsToAllowStoredShares = true
status, err := client.Sys().RekeyInit(&api.RekeyInitRequest{
StoredShares: 1,
RequireVerification: true,
})
if err != nil {
t.Fatal(err)
}
if status == nil {
t.Fatal("empty status")
}
/*
cluster.EnsureCoresSealed(t)
cluster.UnsealCores(t)
*/
}

View File

@ -47,9 +47,9 @@ func handleSysRekeyInit(core *vault.Core, recovery bool) http.Handler {
}
func handleSysRekeyInitGet(ctx context.Context, core *vault.Core, recovery bool, w http.ResponseWriter, r *http.Request) {
barrierConfig, err := core.SealAccess().BarrierConfig(ctx)
if err != nil {
respondError(w, http.StatusInternalServerError, err)
barrierConfig, barrierConfErr := core.SealAccess().BarrierConfig(ctx)
if barrierConfErr != nil {
respondError(w, http.StatusInternalServerError, barrierConfErr)
return
}
if barrierConfig == nil {
@ -60,20 +60,20 @@ func handleSysRekeyInitGet(ctx context.Context, core *vault.Core, recovery bool,
// Get the rekey configuration
rekeyConf, err := core.RekeyConfig(recovery)
if err != nil {
respondError(w, http.StatusInternalServerError, err)
respondError(w, err.Code(), err)
return
}
// Get the progress
progress, err := core.RekeyProgress(recovery)
if err != nil {
respondError(w, http.StatusInternalServerError, err)
respondError(w, err.Code(), err)
return
}
sealThreshold, err := core.RekeyThreshold(ctx, recovery)
if err != nil {
respondError(w, http.StatusInternalServerError, err)
respondError(w, err.Code(), err)
return
}
@ -146,7 +146,7 @@ func handleSysRekeyInitPut(ctx context.Context, core *vault.Core, recovery bool,
VerificationRequired: req.RequireVerification,
}, recovery)
if err != nil {
respondError(w, http.StatusBadRequest, err)
respondError(w, err.Code(), err)
return
}
@ -156,7 +156,7 @@ func handleSysRekeyInitPut(ctx context.Context, core *vault.Core, recovery bool,
func handleSysRekeyInitDelete(core *vault.Core, recovery bool, w http.ResponseWriter, r *http.Request) {
err := core.RekeyCancel(recovery)
if err != nil {
respondError(w, http.StatusInternalServerError, err)
respondError(w, err.Code(), err)
return
}
respondOk(w, nil)
@ -203,9 +203,9 @@ func handleSysRekeyUpdate(core *vault.Core, recovery bool) http.Handler {
defer cancel()
// Use the key to make progress on rekey
result, err := core.RekeyUpdate(ctx, key, req.Nonce, recovery)
if err != nil {
respondError(w, http.StatusBadRequest, err)
result, rekeyErr := core.RekeyUpdate(ctx, key, req.Nonce, recovery)
if rekeyErr != nil {
respondError(w, rekeyErr.Code(), err)
return
}
@ -269,9 +269,9 @@ func handleSysRekeyVerify(core *vault.Core, recovery bool) http.Handler {
}
func handleSysRekeyVerifyGet(ctx context.Context, core *vault.Core, recovery bool, w http.ResponseWriter, r *http.Request) {
barrierConfig, err := core.SealAccess().BarrierConfig(ctx)
if err != nil {
respondError(w, http.StatusInternalServerError, err)
barrierConfig, barrierConfErr := core.SealAccess().BarrierConfig(ctx)
if barrierConfErr != nil {
respondError(w, http.StatusInternalServerError, barrierConfErr)
return
}
if barrierConfig == nil {
@ -282,7 +282,7 @@ func handleSysRekeyVerifyGet(ctx context.Context, core *vault.Core, recovery boo
// Get the rekey configuration
rekeyConf, err := core.RekeyConfig(recovery)
if err != nil {
respondError(w, http.StatusInternalServerError, err)
respondError(w, err.Code(), err)
return
}
if rekeyConf == nil {
@ -293,7 +293,7 @@ func handleSysRekeyVerifyGet(ctx context.Context, core *vault.Core, recovery boo
// Get the progress
progress, err := core.RekeyVerifyProgress(recovery)
if err != nil {
respondError(w, http.StatusInternalServerError, err)
respondError(w, err.Code(), err)
return
}
@ -310,7 +310,7 @@ func handleSysRekeyVerifyGet(ctx context.Context, core *vault.Core, recovery boo
func handleSysRekeyVerifyDelete(core *vault.Core, recovery bool, w http.ResponseWriter, r *http.Request) {
err := core.RekeyVerifyRestart(recovery)
if err != nil {
respondError(w, http.StatusInternalServerError, err)
respondError(w, err.Code(), err)
return
}
@ -354,9 +354,9 @@ func handleSysRekeyVerifyPut(ctx context.Context, core *vault.Core, recovery boo
defer cancel()
// Use the key to make progress on rekey
result, err := core.RekeyVerify(ctx, key, recovery)
if err != nil {
respondError(w, http.StatusBadRequest, err)
result, rekeyErr := core.RekeyVerify(ctx, key, recovery)
if rekeyErr != nil {
respondError(w, rekeyErr.Code(), err)
return
}

View File

@ -6,14 +6,15 @@ import (
"crypto/subtle"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"net/http"
"github.com/hashicorp/errwrap"
"github.com/hashicorp/go-uuid"
"github.com/hashicorp/vault/helper/consts"
"github.com/hashicorp/vault/helper/jsonutil"
"github.com/hashicorp/vault/helper/pgpkeys"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/physical"
"github.com/hashicorp/vault/shamir"
)
@ -56,14 +57,14 @@ type RekeyBackup struct {
// the recovery key threshold, depending on whether rekey is being
// performed on the recovery key, or whether the seal supports
// recovery keys.
func (c *Core) RekeyThreshold(ctx context.Context, recovery bool) (int, error) {
func (c *Core) RekeyThreshold(ctx context.Context, recovery bool) (int, logical.HTTPCodedError) {
c.stateLock.RLock()
defer c.stateLock.RUnlock()
if c.sealed {
return 0, consts.ErrSealed
return 0, logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error())
}
if c.standby {
return 0, consts.ErrStandby
return 0, logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error())
}
c.rekeyLock.RLock()
@ -80,21 +81,24 @@ func (c *Core) RekeyThreshold(ctx context.Context, recovery bool) (int, error) {
config, err = c.seal.BarrierConfig(ctx)
}
if err != nil {
return 0, err
return 0, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("unable to look up config: {{err}}", err).Error())
}
if config == nil {
return 0, logical.CodedError(http.StatusBadRequest, ErrNotInit.Error())
}
return config.SecretThreshold, nil
}
// RekeyProgress is used to return the rekey progress (num shares).
func (c *Core) RekeyProgress(recovery bool) (int, error) {
func (c *Core) RekeyProgress(recovery bool) (int, logical.HTTPCodedError) {
c.stateLock.RLock()
defer c.stateLock.RUnlock()
if c.sealed {
return 0, consts.ErrSealed
return 0, logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error())
}
if c.standby {
return 0, consts.ErrStandby
return 0, logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error())
}
c.rekeyLock.RLock()
@ -108,14 +112,14 @@ func (c *Core) RekeyProgress(recovery bool) (int, error) {
// RekeyVerifyProgress is used to return the rekey progress (num shares) during
// verification.
func (c *Core) RekeyVerifyProgress(recovery bool) (int, error) {
func (c *Core) RekeyVerifyProgress(recovery bool) (int, logical.HTTPCodedError) {
c.stateLock.RLock()
defer c.stateLock.RUnlock()
if c.sealed {
return 0, consts.ErrSealed
return 0, logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error())
}
if c.standby {
return 0, consts.ErrStandby
return 0, logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error())
}
c.rekeyLock.RLock()
@ -128,14 +132,14 @@ func (c *Core) RekeyVerifyProgress(recovery bool) (int, error) {
}
// RekeyConfig is used to read the rekey configuration
func (c *Core) RekeyConfig(recovery bool) (*SealConfig, error) {
func (c *Core) RekeyConfig(recovery bool) (*SealConfig, logical.HTTPCodedError) {
c.stateLock.RLock()
defer c.stateLock.RUnlock()
if c.sealed {
return nil, consts.ErrSealed
return nil, logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error())
}
if c.standby {
return nil, consts.ErrStandby
return nil, logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error())
}
c.rekeyLock.Lock()
@ -158,7 +162,7 @@ func (c *Core) RekeyConfig(recovery bool) (*SealConfig, error) {
// RekeyInit will either initialize the rekey of barrier or recovery key.
// recovery determines whether this is a rekey on the barrier or recovery key.
func (c *Core) RekeyInit(config *SealConfig, recovery bool) error {
func (c *Core) RekeyInit(config *SealConfig, recovery bool) logical.HTTPCodedError {
if recovery {
return c.RecoveryRekeyInit(config)
}
@ -166,39 +170,39 @@ func (c *Core) RekeyInit(config *SealConfig, recovery bool) error {
}
// BarrierRekeyInit is used to initialize the rekey settings for the barrier key
func (c *Core) BarrierRekeyInit(config *SealConfig) error {
func (c *Core) BarrierRekeyInit(config *SealConfig) logical.HTTPCodedError {
if config.StoredShares > 0 {
if !c.seal.StoredKeysSupported() {
return fmt.Errorf("storing keys not supported by barrier seal")
return logical.CodedError(http.StatusBadRequest, "storing keys not supported by barrier seal")
}
if len(config.PGPKeys) > 0 {
return fmt.Errorf("PGP key encryption not supported when using stored keys")
return logical.CodedError(http.StatusBadRequest, "PGP key encryption not supported when using stored keys")
}
if config.Backup {
return fmt.Errorf("key backup not supported when using stored keys")
return logical.CodedError(http.StatusBadRequest, "key backup not supported when using stored keys")
}
}
if c.seal.RecoveryKeySupported() && c.seal.RecoveryType() == config.Type {
c.logger.Debug("using recovery seal configuration to rekey barrier key")
if config.VerificationRequired {
return fmt.Errorf("requiring verification not supported when rekeying the barrier key with recovery keys")
return logical.CodedError(http.StatusBadRequest, "requiring verification not supported when rekeying the barrier key with recovery keys")
}
}
// Check if the seal configuration is valid
if err := config.Validate(); err != nil {
c.logger.Error("invalid rekey seal configuration", "error", err)
return errwrap.Wrapf("invalid rekey seal configuration: {{err}}", err)
return logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("invalid rekey seal configuration: {{err}}", err).Error())
}
c.stateLock.RLock()
defer c.stateLock.RUnlock()
if c.sealed {
return consts.ErrSealed
return logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error())
}
if c.standby {
return consts.ErrStandby
return logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error())
}
c.rekeyLock.Lock()
@ -206,7 +210,7 @@ func (c *Core) BarrierRekeyInit(config *SealConfig) error {
// Prevent multiple concurrent re-keys
if c.barrierRekeyConfig != nil {
return fmt.Errorf("rekey already in progress")
return logical.CodedError(http.StatusBadRequest, "rekey already in progress")
}
// Copy the configuration
@ -216,7 +220,7 @@ func (c *Core) BarrierRekeyInit(config *SealConfig) error {
nonce, err := uuid.GenerateUUID()
if err != nil {
c.barrierRekeyConfig = nil
return err
return logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("error generating nonce for procedure: {{err}}", err).Error())
}
c.barrierRekeyConfig.Nonce = nonce
@ -227,28 +231,28 @@ func (c *Core) BarrierRekeyInit(config *SealConfig) error {
}
// RecoveryRekeyInit is used to initialize the rekey settings for the recovery key
func (c *Core) RecoveryRekeyInit(config *SealConfig) error {
func (c *Core) RecoveryRekeyInit(config *SealConfig) logical.HTTPCodedError {
if config.StoredShares > 0 {
return fmt.Errorf("stored shares not supported by recovery key")
return logical.CodedError(http.StatusBadRequest, "stored shares not supported by recovery key")
}
// Check if the seal configuration is valid
if err := config.Validate(); err != nil {
c.logger.Error("invalid recovery configuration", "error", err)
return errwrap.Wrapf("invalid recovery configuration: {{err}}", err)
return logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("invalid recovery configuration: {{err}}", err).Error())
}
if !c.seal.RecoveryKeySupported() {
return fmt.Errorf("recovery keys not supported")
return logical.CodedError(http.StatusBadRequest, "recovery keys not supported")
}
c.stateLock.RLock()
defer c.stateLock.RUnlock()
if c.sealed {
return consts.ErrSealed
return logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error())
}
if c.standby {
return consts.ErrStandby
return logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error())
}
c.rekeyLock.Lock()
@ -256,7 +260,7 @@ func (c *Core) RecoveryRekeyInit(config *SealConfig) error {
// Prevent multiple concurrent re-keys
if c.recoveryRekeyConfig != nil {
return fmt.Errorf("rekey already in progress")
return logical.CodedError(http.StatusBadRequest, "rekey already in progress")
}
// Copy the configuration
@ -266,7 +270,7 @@ func (c *Core) RecoveryRekeyInit(config *SealConfig) error {
nonce, err := uuid.GenerateUUID()
if err != nil {
c.recoveryRekeyConfig = nil
return err
return logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("error generating nonce for procedure: {{err}}", err).Error())
}
c.recoveryRekeyConfig.Nonce = nonce
@ -277,7 +281,7 @@ func (c *Core) RecoveryRekeyInit(config *SealConfig) error {
}
// RekeyUpdate is used to provide a new key part for the barrier or recovery key.
func (c *Core) RekeyUpdate(ctx context.Context, key []byte, nonce string, recovery bool) (*RekeyResult, error) {
func (c *Core) RekeyUpdate(ctx context.Context, key []byte, nonce string, recovery bool) (*RekeyResult, logical.HTTPCodedError) {
if recovery {
return c.RecoveryRekeyUpdate(ctx, key, nonce)
}
@ -289,25 +293,25 @@ func (c *Core) RekeyUpdate(ctx context.Context, key []byte, nonce string, recove
// key.
//
// N.B.: If recovery keys are used to rekey, the new barrier key shares are not returned.
func (c *Core) BarrierRekeyUpdate(ctx context.Context, key []byte, nonce string) (*RekeyResult, error) {
func (c *Core) BarrierRekeyUpdate(ctx context.Context, key []byte, nonce string) (*RekeyResult, logical.HTTPCodedError) {
// Ensure we are already unsealed
c.stateLock.RLock()
defer c.stateLock.RUnlock()
if c.sealed {
return nil, consts.ErrSealed
return nil, logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error())
}
if c.standby {
return nil, consts.ErrStandby
return nil, logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error())
}
// Verify the key length
min, max := c.barrier.KeyLength()
max += shamir.ShareOverhead
if len(key) < min {
return nil, &ErrInvalidKey{fmt.Sprintf("key is shorter than minimum %d bytes", min)}
return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("key is shorter than minimum %d bytes", min))
}
if len(key) > max {
return nil, &ErrInvalidKey{fmt.Sprintf("key is longer than maximum %d bytes", max)}
return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("key is longer than maximum %d bytes", max))
}
c.rekeyLock.Lock()
@ -324,27 +328,26 @@ func (c *Core) BarrierRekeyUpdate(ctx context.Context, key []byte, nonce string)
existingConfig, err = c.seal.BarrierConfig(ctx)
}
if err != nil {
return nil, err
return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to fetch existing config: {{err}}", err).Error())
}
// Ensure the barrier is initialized
if existingConfig == nil {
return nil, ErrNotInit
return nil, logical.CodedError(http.StatusBadRequest, ErrNotInit.Error())
}
// Ensure a rekey is in progress
if c.barrierRekeyConfig == nil {
return nil, fmt.Errorf("no barrier rekey in progress")
return nil, logical.CodedError(http.StatusBadRequest, "no barrier rekey in progress")
}
if nonce != c.barrierRekeyConfig.Nonce {
return nil, fmt.Errorf("incorrect nonce supplied; nonce for this rekey operation is %q", c.barrierRekeyConfig.Nonce)
return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("incorrect nonce supplied; nonce for this rekey operation is %q", c.barrierRekeyConfig.Nonce))
}
// Check if we already have this piece
for _, existing := range c.barrierRekeyProgress {
if subtle.ConstantTimeCompare(existing, key) == 1 {
return nil, fmt.Errorf("given key has already been provided during this generation operation")
return nil, logical.CodedError(http.StatusBadRequest, "given key has already been provided during this generation operation")
}
}
@ -371,19 +374,19 @@ func (c *Core) BarrierRekeyUpdate(ctx context.Context, key []byte, nonce string)
} else {
recoveredKey, err = shamir.Combine(c.barrierRekeyProgress)
if err != nil {
return nil, errwrap.Wrapf("failed to compute master key: {{err}}", err)
return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to compute master key: {{err}}", err).Error())
}
}
if useRecovery {
if err := c.seal.VerifyRecoveryKey(ctx, recoveredKey); err != nil {
c.logger.Error("rekey aborted, recovery key verification failed", "error", err)
return nil, err
c.logger.Error("rekey recovery key verification failed", "error", err)
return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("recovery key verification failed: {{err}}", err).Error())
}
} else {
if err := c.barrier.VerifyMaster(recoveredKey); err != nil {
c.logger.Error("rekey aborted, master key verification failed", "error", err)
return nil, err
c.logger.Error("master key verification failed", "error", err)
return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("master key verification failed: {{err}}", err).Error())
}
}
@ -391,7 +394,7 @@ func (c *Core) BarrierRekeyUpdate(ctx context.Context, key []byte, nonce string)
newMasterKey, err := c.barrier.GenerateKey()
if err != nil {
c.logger.Error("failed to generate master key", "error", err)
return nil, errwrap.Wrapf("master key generation failed: {{err}}", err)
return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("master key generation failed: {{err}}", err).Error())
}
results := &RekeyResult{
@ -406,7 +409,7 @@ func (c *Core) BarrierRekeyUpdate(ctx context.Context, key []byte, nonce string)
shares, err := shamir.Split(newMasterKey, c.barrierRekeyConfig.SecretShares, c.barrierRekeyConfig.SecretThreshold)
if err != nil {
c.logger.Error("failed to generate shares", "error", err)
return nil, errwrap.Wrapf("failed to generate shares: {{err}}", err)
return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to generate shares: {{err}}", err).Error())
}
results.SecretShares = shares
}
@ -429,7 +432,7 @@ func (c *Core) BarrierRekeyUpdate(ctx context.Context, key []byte, nonce string)
}
results.PGPFingerprints, results.SecretShares, err = pgpkeys.EncryptShares(hexEncodedShares, c.barrierRekeyConfig.PGPKeys)
if err != nil {
return nil, err
return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to encrypt shares: {{err}}", err).Error())
}
// If backup is enabled, store backup info in vault.coreBarrierUnsealKeysBackupPath
@ -451,7 +454,7 @@ func (c *Core) BarrierRekeyUpdate(ctx context.Context, key []byte, nonce string)
buf, err := json.Marshal(backupVals)
if err != nil {
c.logger.Error("failed to marshal unseal key backup", "error", err)
return nil, errwrap.Wrapf("failed to marshal unseal key backup: {{err}}", err)
return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to marshal unseal key backup: {{err}}", err).Error())
}
pe := &physical.Entry{
Key: coreBarrierUnsealKeysBackupPath,
@ -459,7 +462,7 @@ func (c *Core) BarrierRekeyUpdate(ctx context.Context, key []byte, nonce string)
}
if err = c.physical.Put(ctx, pe); err != nil {
c.logger.Error("failed to save unseal key backup", "error", err)
return nil, errwrap.Wrapf("failed to save unseal key backup: {{err}}", err)
return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to save unseal key backup: {{err}}", err).Error())
}
}
}
@ -467,7 +470,7 @@ func (c *Core) BarrierRekeyUpdate(ctx context.Context, key []byte, nonce string)
if keysToStore != nil {
if err := c.seal.SetStoredKeys(ctx, keysToStore); err != nil {
c.logger.Error("failed to store keys", "error", err)
return nil, errwrap.Wrapf("failed to store keys: {{err}}", err)
return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to store keys: {{err}}", err).Error())
}
}
@ -476,7 +479,7 @@ func (c *Core) BarrierRekeyUpdate(ctx context.Context, key []byte, nonce string)
nonce, err := uuid.GenerateUUID()
if err != nil {
c.barrierRekeyConfig = nil
return nil, err
return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to generate verification nonce: {{err}}", err).Error())
}
c.barrierRekeyConfig.VerificationNonce = nonce
c.barrierRekeyConfig.VerificationKey = newMasterKey
@ -487,18 +490,18 @@ func (c *Core) BarrierRekeyUpdate(ctx context.Context, key []byte, nonce string)
}
if err := c.performBarrierRekey(ctx, newMasterKey); err != nil {
return nil, err
return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to perform barrier rekey: {{err}}", err).Error())
}
c.barrierRekeyConfig = nil
return results, nil
}
func (c *Core) performBarrierRekey(ctx context.Context, newMasterKey []byte) error {
func (c *Core) performBarrierRekey(ctx context.Context, newMasterKey []byte) logical.HTTPCodedError {
// Rekey the barrier
if err := c.barrier.Rekey(ctx, newMasterKey); err != nil {
c.logger.Error("failed to rekey barrier", "error", err)
return errwrap.Wrapf("failed to rekey barrier: {{err}}", err)
return logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to rekey barrier: {{err}}", err).Error())
}
if c.logger.IsInfo() {
c.logger.Info("security barrier rekeyed", "shares", c.barrierRekeyConfig.SecretShares, "threshold", c.barrierRekeyConfig.SecretThreshold)
@ -508,7 +511,7 @@ func (c *Core) performBarrierRekey(ctx context.Context, newMasterKey []byte) err
if err := c.seal.SetBarrierConfig(ctx, c.barrierRekeyConfig); err != nil {
c.logger.Error("error saving rekey seal configuration", "error", err)
return errwrap.Wrapf("failed to save rekey seal configuration: {{err}}", err)
return logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to save rekey seal configuration: {{err}}", err).Error())
}
// Write to the canary path, which will force a synchronous truing during
@ -518,32 +521,32 @@ func (c *Core) performBarrierRekey(ctx context.Context, newMasterKey []byte) err
Value: []byte(c.barrierRekeyConfig.Nonce),
}); err != nil {
c.logger.Error("error saving keyring canary", "error", err)
return errwrap.Wrapf("failed to save keyring canary: {{err}}", err)
return logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to save keyring canary: {{err}}", err).Error())
}
return nil
}
// RecoveryRekeyUpdate is used to provide a new key part
func (c *Core) RecoveryRekeyUpdate(ctx context.Context, key []byte, nonce string) (*RekeyResult, error) {
func (c *Core) RecoveryRekeyUpdate(ctx context.Context, key []byte, nonce string) (*RekeyResult, logical.HTTPCodedError) {
// Ensure we are already unsealed
c.stateLock.RLock()
defer c.stateLock.RUnlock()
if c.sealed {
return nil, consts.ErrSealed
return nil, logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error())
}
if c.standby {
return nil, consts.ErrStandby
return nil, logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error())
}
// Verify the key length
min, max := c.barrier.KeyLength()
max += shamir.ShareOverhead
if len(key) < min {
return nil, &ErrInvalidKey{fmt.Sprintf("key is shorter than minimum %d bytes", min)}
return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("key is shorter than minimum %d bytes", min))
}
if len(key) > max {
return nil, &ErrInvalidKey{fmt.Sprintf("key is longer than maximum %d bytes", max)}
return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("key is longer than maximum %d bytes", max))
}
c.rekeyLock.Lock()
@ -552,27 +555,26 @@ func (c *Core) RecoveryRekeyUpdate(ctx context.Context, key []byte, nonce string
// Get the seal configuration
existingConfig, err := c.seal.RecoveryConfig(ctx)
if err != nil {
return nil, err
return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to fetch existing recovery config: {{err}}", err).Error())
}
// Ensure the seal is initialized
if existingConfig == nil {
return nil, ErrNotInit
return nil, logical.CodedError(http.StatusBadRequest, ErrNotInit.Error())
}
// Ensure a rekey is in progress
if c.recoveryRekeyConfig == nil {
return nil, fmt.Errorf("no recovery rekey in progress")
return nil, logical.CodedError(http.StatusBadRequest, "no recovery rekey in progress")
}
if nonce != c.recoveryRekeyConfig.Nonce {
return nil, fmt.Errorf("incorrect nonce supplied; nonce for this rekey operation is %q", c.recoveryRekeyConfig.Nonce)
return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("incorrect nonce supplied; nonce for this rekey operation is %q", c.recoveryRekeyConfig.Nonce))
}
// Check if we already have this piece
for _, existing := range c.recoveryRekeyProgress {
if subtle.ConstantTimeCompare(existing, key) == 1 {
return nil, fmt.Errorf("given key has already been provided during this rekey operation")
return nil, logical.CodedError(http.StatusBadRequest, "given key has already been provided during this rekey operation")
}
}
@ -599,21 +601,21 @@ func (c *Core) RecoveryRekeyUpdate(ctx context.Context, key []byte, nonce string
} else {
recoveryKey, err = shamir.Combine(c.recoveryRekeyProgress)
if err != nil {
return nil, errwrap.Wrapf("failed to compute recovery key: {{err}}", err)
return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to compute recovery key: {{err}}", err).Error())
}
}
// Verify the recovery key
if err := c.seal.VerifyRecoveryKey(ctx, recoveryKey); err != nil {
c.logger.Error("rekey aborted, recovery key verification failed", "error", err)
return nil, err
c.logger.Error("recovery key verification failed", "error", err)
return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("recovery key verification failed: {{err}}", err).Error())
}
// Generate a new master key
newMasterKey, err := c.barrier.GenerateKey()
if err != nil {
c.logger.Error("failed to generate recovery key", "error", err)
return nil, errwrap.Wrapf("recovery key generation failed: {{err}}", err)
return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("recovery key generation failed: {{err}}", err).Error())
}
// Return the master key if only a single key part is used
@ -628,7 +630,7 @@ func (c *Core) RecoveryRekeyUpdate(ctx context.Context, key []byte, nonce string
shares, err := shamir.Split(newMasterKey, c.recoveryRekeyConfig.SecretShares, c.recoveryRekeyConfig.SecretThreshold)
if err != nil {
c.logger.Error("failed to generate shares", "error", err)
return nil, errwrap.Wrapf("failed to generate shares: {{err}}", err)
return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to generate shares: {{err}}", err).Error())
}
results.SecretShares = shares
}
@ -640,7 +642,7 @@ func (c *Core) RecoveryRekeyUpdate(ctx context.Context, key []byte, nonce string
}
results.PGPFingerprints, results.SecretShares, err = pgpkeys.EncryptShares(hexEncodedShares, c.recoveryRekeyConfig.PGPKeys)
if err != nil {
return nil, err
return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to encrypt shares: {{err}}", err).Error())
}
if c.recoveryRekeyConfig.Backup {
@ -661,7 +663,7 @@ func (c *Core) RecoveryRekeyUpdate(ctx context.Context, key []byte, nonce string
buf, err := json.Marshal(backupVals)
if err != nil {
c.logger.Error("failed to marshal recovery key backup", "error", err)
return nil, errwrap.Wrapf("failed to marshal recovery key backup: {{err}}", err)
return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to marshal recovery key backup: {{err}}", err).Error())
}
pe := &physical.Entry{
Key: coreRecoveryUnsealKeysBackupPath,
@ -669,7 +671,7 @@ func (c *Core) RecoveryRekeyUpdate(ctx context.Context, key []byte, nonce string
}
if err = c.physical.Put(ctx, pe); err != nil {
c.logger.Error("failed to save unseal key backup", "error", err)
return nil, errwrap.Wrapf("failed to save unseal key backup: {{err}}", err)
return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to save unseal key backup: {{err}}", err).Error())
}
}
}
@ -680,7 +682,7 @@ func (c *Core) RecoveryRekeyUpdate(ctx context.Context, key []byte, nonce string
nonce, err := uuid.GenerateUUID()
if err != nil {
c.recoveryRekeyConfig = nil
return nil, err
return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to generate verification nonce: {{err}}", err).Error())
}
c.recoveryRekeyConfig.VerificationNonce = nonce
c.recoveryRekeyConfig.VerificationKey = newMasterKey
@ -691,24 +693,24 @@ func (c *Core) RecoveryRekeyUpdate(ctx context.Context, key []byte, nonce string
}
if err := c.performRecoveryRekey(ctx, newMasterKey); err != nil {
return nil, err
return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to perform recovery rekey: {{err}}", err).Error())
}
c.recoveryRekeyConfig = nil
return results, nil
}
func (c *Core) performRecoveryRekey(ctx context.Context, newMasterKey []byte) error {
func (c *Core) performRecoveryRekey(ctx context.Context, newMasterKey []byte) logical.HTTPCodedError {
if err := c.seal.SetRecoveryKey(ctx, newMasterKey); err != nil {
c.logger.Error("failed to set recovery key", "error", err)
return errwrap.Wrapf("failed to set recovery key: {{err}}", err)
return logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to set recovery key: {{err}}", err).Error())
}
c.recoveryRekeyConfig.VerificationKey = nil
if err := c.seal.SetRecoveryConfig(ctx, c.recoveryRekeyConfig); err != nil {
c.logger.Error("error saving rekey seal configuration", "error", err)
return errwrap.Wrapf("failed to save rekey seal configuration: {{err}}", err)
return logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to save rekey seal configuration: {{err}}", err).Error())
}
// Write to the canary path, which will force a synchronous truing during
@ -718,38 +720,38 @@ func (c *Core) performRecoveryRekey(ctx context.Context, newMasterKey []byte) er
Value: []byte(c.recoveryRekeyConfig.Nonce),
}); err != nil {
c.logger.Error("error saving keyring canary", "error", err)
return errwrap.Wrapf("failed to save keyring canary: {{err}}", err)
return logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to save keyring canary: {{err}}", err).Error())
}
return nil
}
func (c *Core) RekeyVerify(ctx context.Context, key []byte, recovery bool) (*RekeyVerifyResult, error) {
func (c *Core) RekeyVerify(ctx context.Context, key []byte, recovery bool) (*RekeyVerifyResult, logical.HTTPCodedError) {
if recovery {
return c.RecoveryRekeyVerify(ctx, key)
}
return c.BarrierRekeyVerify(ctx, key)
}
func (c *Core) BarrierRekeyVerify(ctx context.Context, key []byte) (*RekeyVerifyResult, error) {
func (c *Core) BarrierRekeyVerify(ctx context.Context, key []byte) (*RekeyVerifyResult, logical.HTTPCodedError) {
// Ensure we are already unsealed
c.stateLock.RLock()
defer c.stateLock.RUnlock()
if c.sealed {
return nil, consts.ErrSealed
return nil, logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error())
}
if c.standby {
return nil, consts.ErrStandby
return nil, logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error())
}
// Verify the key length
min, max := c.barrier.KeyLength()
max += shamir.ShareOverhead
if len(key) < min {
return nil, &ErrInvalidKey{fmt.Sprintf("key is shorter than minimum %d bytes", min)}
return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("key is shorter than minimum %d bytes", min))
}
if len(key) > max {
return nil, &ErrInvalidKey{fmt.Sprintf("key is longer than maximum %d bytes", max)}
return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("key is longer than maximum %d bytes", max))
}
c.rekeyLock.Lock()
@ -757,13 +759,13 @@ func (c *Core) BarrierRekeyVerify(ctx context.Context, key []byte) (*RekeyVerify
// Ensure a rekey is in progress
if c.barrierRekeyConfig == nil {
return nil, fmt.Errorf("no barrier rekey in progress")
return nil, logical.CodedError(http.StatusBadRequest, "no barrier rekey in progress")
}
// Check if we already have this piece
for _, existing := range c.barrierRekeyVerifyProgress {
if subtle.ConstantTimeCompare(existing, key) == 1 {
return nil, fmt.Errorf("given key has already been provided during this verify operation")
return nil, logical.CodedError(http.StatusBadRequest, "given key has already been provided during this verify operation")
}
}
@ -797,17 +799,17 @@ func (c *Core) BarrierRekeyVerify(ctx context.Context, key []byte) (*RekeyVerify
} else {
recoveredKey, err = shamir.Combine(c.barrierRekeyVerifyProgress)
if err != nil {
return nil, errwrap.Wrapf("failed to compute master key for verification: {{err}}", err)
return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to compute master key for verification: {{err}}", err).Error())
}
}
if subtle.ConstantTimeCompare(recoveredKey, c.barrierRekeyConfig.VerificationKey) != 1 {
c.logger.Error("rekey verification failed")
return nil, errors.New("rekey verification failed")
return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("verification for barrier rekey failed: {{err}}", err).Error())
}
if err := c.performBarrierRekey(ctx, recoveredKey); err != nil {
return nil, err
return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to perform barrier rekey: {{err}}", err).Error())
}
res := &RekeyVerifyResult{
@ -818,25 +820,25 @@ func (c *Core) BarrierRekeyVerify(ctx context.Context, key []byte) (*RekeyVerify
return res, nil
}
func (c *Core) RecoveryRekeyVerify(ctx context.Context, key []byte) (*RekeyVerifyResult, error) {
func (c *Core) RecoveryRekeyVerify(ctx context.Context, key []byte) (*RekeyVerifyResult, logical.HTTPCodedError) {
// Ensure we are already unsealed
c.stateLock.RLock()
defer c.stateLock.RUnlock()
if c.sealed {
return nil, consts.ErrSealed
return nil, logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error())
}
if c.standby {
return nil, consts.ErrStandby
return nil, logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error())
}
// Verify the key length
min, max := c.barrier.KeyLength()
max += shamir.ShareOverhead
if len(key) < min {
return nil, &ErrInvalidKey{fmt.Sprintf("key is shorter than minimum %d bytes", min)}
return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("key is shorter than minimum %d bytes", min))
}
if len(key) > max {
return nil, &ErrInvalidKey{fmt.Sprintf("key is longer than maximum %d bytes", max)}
return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("key is longer than maximum %d bytes", max))
}
c.rekeyLock.Lock()
@ -844,13 +846,13 @@ func (c *Core) RecoveryRekeyVerify(ctx context.Context, key []byte) (*RekeyVerif
// Ensure a rekey is in progress
if c.recoveryRekeyConfig == nil {
return nil, fmt.Errorf("no recovery rekey in progress")
return nil, logical.CodedError(http.StatusBadRequest, "no recovery rekey in progress")
}
// Check if we already have this piece
for _, existing := range c.recoveryRekeyVerifyProgress {
if subtle.ConstantTimeCompare(existing, key) == 1 {
return nil, fmt.Errorf("given key has already been provided during this verify operation")
return nil, logical.CodedError(http.StatusBadRequest, "given key has already been provided during this verify operation")
}
}
@ -884,18 +886,18 @@ func (c *Core) RecoveryRekeyVerify(ctx context.Context, key []byte) (*RekeyVerif
} else {
recoveryKey, err = shamir.Combine(c.recoveryRekeyVerifyProgress)
if err != nil {
return nil, errwrap.Wrapf("failed to compute recovery key: {{err}}", err)
return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to compute recovery key: {{err}}", err).Error())
}
}
// Verify the recovery key
if subtle.ConstantTimeCompare(recoveryKey, c.recoveryRekeyConfig.VerificationKey) != 1 {
c.logger.Error("rekey verification failed", "error", err)
return nil, errors.New("rekey verification failed")
return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("verification for recovery rekey failed: {{err}}", err).Error())
}
if err := c.performRecoveryRekey(ctx, recoveryKey); err != nil {
return nil, err
return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("failed to perform recovery key rekey: {{err}}", err).Error())
}
res := &RekeyVerifyResult{
@ -907,14 +909,14 @@ func (c *Core) RecoveryRekeyVerify(ctx context.Context, key []byte) (*RekeyVerif
}
// RekeyCancel is used to cancel an in-progress rekey
func (c *Core) RekeyCancel(recovery bool) error {
func (c *Core) RekeyCancel(recovery bool) logical.HTTPCodedError {
c.stateLock.RLock()
defer c.stateLock.RUnlock()
if c.sealed {
return consts.ErrSealed
return logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error())
}
if c.standby {
return consts.ErrStandby
return logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error())
}
c.rekeyLock.Lock()
@ -932,14 +934,14 @@ func (c *Core) RekeyCancel(recovery bool) error {
}
// RekeyVerifyCancel is used to start the verification process over
func (c *Core) RekeyVerifyRestart(recovery bool) error {
func (c *Core) RekeyVerifyRestart(recovery bool) logical.HTTPCodedError {
c.stateLock.RLock()
defer c.stateLock.RUnlock()
if c.sealed {
return consts.ErrSealed
return logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error())
}
if c.standby {
return consts.ErrStandby
return logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error())
}
c.rekeyLock.Lock()
@ -967,14 +969,14 @@ func (c *Core) RekeyVerifyRestart(recovery bool) error {
// RekeyRetrieveBackup is used to retrieve any backed-up PGP-encrypted unseal
// keys
func (c *Core) RekeyRetrieveBackup(ctx context.Context, recovery bool) (*RekeyBackup, error) {
func (c *Core) RekeyRetrieveBackup(ctx context.Context, recovery bool) (*RekeyBackup, logical.HTTPCodedError) {
c.stateLock.RLock()
defer c.stateLock.RUnlock()
if c.sealed {
return nil, consts.ErrSealed
return nil, logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error())
}
if c.standby {
return nil, consts.ErrStandby
return nil, logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error())
}
c.rekeyLock.RLock()
@ -988,7 +990,7 @@ func (c *Core) RekeyRetrieveBackup(ctx context.Context, recovery bool) (*RekeyBa
entry, err = c.physical.Get(ctx, coreBarrierUnsealKeysBackupPath)
}
if err != nil {
return nil, err
return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("error getting keys from backup: {{err}}", err).Error())
}
if entry == nil {
return nil, nil
@ -997,28 +999,36 @@ func (c *Core) RekeyRetrieveBackup(ctx context.Context, recovery bool) (*RekeyBa
ret := &RekeyBackup{}
err = jsonutil.DecodeJSON(entry.Value, ret)
if err != nil {
return nil, err
return nil, logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("error decoding backup keys: {{err}}", err).Error())
}
return ret, nil
}
// RekeyDeleteBackup is used to delete any backed-up PGP-encrypted unseal keys
func (c *Core) RekeyDeleteBackup(ctx context.Context, recovery bool) error {
func (c *Core) RekeyDeleteBackup(ctx context.Context, recovery bool) logical.HTTPCodedError {
c.stateLock.RLock()
defer c.stateLock.RUnlock()
if c.sealed {
return consts.ErrSealed
return logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error())
}
if c.standby {
return consts.ErrStandby
return logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error())
}
c.rekeyLock.Lock()
defer c.rekeyLock.Unlock()
if recovery {
return c.physical.Delete(ctx, coreRecoveryUnsealKeysBackupPath)
err := c.physical.Delete(ctx, coreRecoveryUnsealKeysBackupPath)
if err != nil {
return logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("error deleting backup keys: {{err}}", err).Error())
}
return nil
}
return c.physical.Delete(ctx, coreBarrierUnsealKeysBackupPath)
err := c.physical.Delete(ctx, coreBarrierUnsealKeysBackupPath)
if err != nil {
return logical.CodedError(http.StatusInternalServerError, errwrap.Wrapf("error deleting backup keys: {{err}}", err).Error())
}
return nil
}

View File

@ -86,6 +86,11 @@ type Seal interface {
VerifyRecoveryKey(context.Context, []byte) error
}
var (
DefaultSealPretendsToAllowRecoveryKeys bool
DefaultSealPretendsToAllowStoredShares bool
)
type defaultSeal struct {
config atomic.Value
core *Core
@ -121,11 +126,11 @@ func (d *defaultSeal) BarrierType() string {
}
func (d *defaultSeal) StoredKeysSupported() bool {
return false
return DefaultSealPretendsToAllowStoredShares
}
func (d *defaultSeal) RecoveryKeySupported() bool {
return false
return DefaultSealPretendsToAllowRecoveryKeys
}
func (d *defaultSeal) SetStoredKeys(ctx context.Context, keys [][]byte) error {