Add seal tests and update generate-root and others to handle dualseal.

This commit is contained in:
Jeff Mitchell 2016-04-25 19:39:04 +00:00
parent 30ba5b7887
commit 98d09b0dc6
11 changed files with 481 additions and 153 deletions

View File

@ -24,8 +24,8 @@ func (c *InitCommand) Run(args []string) int {
flags.IntVar(&threshold, "key-threshold", 3, "")
flags.IntVar(&storedShares, "stored-shares", 0, "")
flags.Var(&pgpKeys, "pgp-keys", "")
flags.IntVar(&recoveryShares, "recovery-shares", 0, "")
flags.IntVar(&recoveryThreshold, "recovery-threshold", 0, "")
flags.IntVar(&recoveryShares, "recovery-shares", 5, "")
flags.IntVar(&recoveryThreshold, "recovery-threshold", 3, "")
flags.Var(&recoveryPgpKeys, "recovery-pgp-keys", "")
flags.BoolVar(&check, "check", false, "")
if err := flags.Parse(args); err != nil {
@ -161,10 +161,10 @@ Init Options:
'vault unseal' command, you will need to hex decode
and decrypt; this will be the plaintext unseal key.
-recovery-shares=0 The number of key shares to split the recovery key
-recovery-shares=5 The number of key shares to split the recovery key
into. This is not normally available.
-recovery-threshold=0 The number of key shares required to reconstruct
-recovery-threshold=3 The number of key shares required to reconstruct
the recovery key. This is not normally available.
-recovery-pgp-keys If provided, behaves like "pgp-keys" but for the

View File

@ -2,6 +2,7 @@ package http
import (
"encoding/hex"
"fmt"
"net/http"
"github.com/hashicorp/errwrap"
@ -55,6 +56,30 @@ func handleSysInitPut(core *vault.Core, w http.ResponseWriter, r *http.Request)
PGPKeys: req.RecoveryPGPKeys,
}
if core.SealAccess().StoredKeysSupported() {
if barrierConfig.SecretShares != 1 {
respondError(w, http.StatusBadRequest, fmt.Errorf("secret shares must be 1"))
return
}
if barrierConfig.SecretThreshold != barrierConfig.SecretShares {
respondError(w, http.StatusBadRequest, fmt.Errorf("secret threshold must be same as secret shares"))
return
}
if barrierConfig.StoredShares != barrierConfig.SecretShares {
respondError(w, http.StatusBadRequest, fmt.Errorf("stored shares must be same as secret shares"))
return
}
if barrierConfig.PGPKeys != nil && len(barrierConfig.PGPKeys) > 0 {
respondError(w, http.StatusBadRequest, fmt.Errorf("PGP keys not supported when storing shares"))
return
}
} else {
if barrierConfig.StoredShares > 0 {
respondError(w, http.StatusBadRequest, fmt.Errorf("stored keys are not supported"))
return
}
}
result, initErr := core.Initialize(barrierConfig, recoveryConfig)
if initErr != nil {
if !errwrap.ContainsType(initErr, new(vault.NonFatalError)) {

View File

@ -56,6 +56,7 @@ func handleSysRekeyInitGet(core *vault.Core, recovery bool, w http.ResponseWrite
sealThreshold, err := core.RekeyThreshold(recovery)
if err != nil {
respondError(w, http.StatusInternalServerError, err)
return
}
// Format the status
@ -75,6 +76,7 @@ func handleSysRekeyInitGet(core *vault.Core, recovery bool, w http.ResponseWrite
pgpFingerprints, err := pgpkeys.GetFingerprints(rekeyConf.PGPKeys, nil)
if err != nil {
respondError(w, http.StatusInternalServerError, err)
return
}
status.PGPFingerprints = pgpFingerprints
status.Backup = rekeyConf.Backup
@ -93,6 +95,15 @@ func handleSysRekeyInitPut(core *vault.Core, recovery bool, w http.ResponseWrite
if req.Backup && len(req.PGPKeys) == 0 {
respondError(w, http.StatusBadRequest, fmt.Errorf("cannot request a backup of the new keys without providing PGP keys for encryption"))
return
}
// Right now we don't support this, but the rest of the code is ready for
// when we do, hence the check below for this to be false if
// StoredShares is greater than zero
if core.SealAccess().StoredKeysSupported() {
respondError(w, http.StatusBadRequest, fmt.Errorf("rekeying of barrier not supported when stored key support is available"))
return
}
// Initialize the rekey

View File

@ -11,9 +11,16 @@ import (
func TestCore_GenerateRoot_Lifecycle(t *testing.T) {
c, master, _ := TestCoreUnsealed(t)
testCore_GenerateRoot_Lifecycle_Common(t, c, [][]byte{master})
bc, rc := TestSealDefConfigs()
c, _, recoveryKeys, _ := TestCoreUnsealedWithConfigs(t, bc, rc)
testCore_GenerateRoot_Lifecycle_Common(t, c, recoveryKeys)
}
func testCore_GenerateRoot_Lifecycle_Common(t *testing.T, c *Core, keys [][]byte) {
// Verify update not allowed
if _, err := c.GenerateRootUpdate(master, ""); err == nil {
if _, err := c.GenerateRootUpdate(keys[0], ""); err == nil {
t.Fatalf("no root generation in progress")
}
@ -76,7 +83,14 @@ func TestCore_GenerateRoot_Lifecycle(t *testing.T) {
func TestCore_GenerateRoot_Init(t *testing.T) {
c, _, _ := TestCoreUnsealed(t)
testCore_GenerateRoot_Init_Common(t, c)
bc, rc := TestSealDefConfigs()
c, _, _, _ = TestCoreUnsealedWithConfigs(t, bc, rc)
testCore_GenerateRoot_Init_Common(t, c)
}
func testCore_GenerateRoot_Init_Common(t *testing.T, c *Core) {
otpBytes, err := GenerateRandBytes(16)
if err != nil {
t.Fatal(err)
@ -94,9 +108,26 @@ func TestCore_GenerateRoot_Init(t *testing.T) {
}
}
func TestCore_GenerateRoot_InvalidMaster(t *testing.T) {
func TestCore_GenerateRoot_InvalidMasterNonce(t *testing.T) {
c, master, _ := TestCoreUnsealed(t)
// Make the master invalid
master[0]++
testCore_GenerateRoot_InvalidMasterNonce_Common(t, c, [][]byte{master})
bc, rc := TestSealDefConfigs()
// For ease of use let's make the threshold the same as the shares and also
// no stored shares so we get an error after the full set
bc.StoredShares = 0
bc.SecretShares = 5
bc.SecretThreshold = 5
rc.SecretShares = 5
rc.SecretThreshold = 5
// In this case, pass in master keys instead as they'll be invalid
c, masterKeys, _, _ := TestCoreUnsealedWithConfigs(t, bc, rc)
testCore_GenerateRoot_InvalidMasterNonce_Common(t, c, masterKeys)
}
func testCore_GenerateRoot_InvalidMasterNonce_Common(t *testing.T, c *Core, keys [][]byte) {
otpBytes, err := GenerateRandBytes(16)
if err != nil {
t.Fatal(err)
@ -116,29 +147,16 @@ func TestCore_GenerateRoot_InvalidMaster(t *testing.T) {
t.Fatalf("bad: no rekey config received")
}
// Provide the master (invalid)
master[0]++
_, err = c.GenerateRootUpdate(master, rgconf.Nonce)
// Provide the nonce (invalid)
_, err = c.GenerateRootUpdate(keys[0], "abcd")
if err == nil {
t.Fatalf("expected error")
}
}
func TestCore_GenerateRoot_InvalidNonce(t *testing.T) {
c, master, _ := TestCoreUnsealed(t)
otpBytes, err := GenerateRandBytes(16)
if err != nil {
t.Fatal(err)
// Provide the master (invalid)
for _, key := range keys {
_, err = c.GenerateRootUpdate(key, rgconf.Nonce)
}
err = c.GenerateRootInit(base64.StdEncoding.EncodeToString(otpBytes), "")
if err != nil {
t.Fatalf("err: %v", err)
}
// Provide the nonce (invalid)
_, err = c.GenerateRootUpdate(master, "abcd")
if err == nil {
t.Fatalf("expected error")
}
@ -146,7 +164,14 @@ func TestCore_GenerateRoot_InvalidNonce(t *testing.T) {
func TestCore_GenerateRoot_Update_OTP(t *testing.T) {
c, master, _ := TestCoreUnsealed(t)
testCore_GenerateRoot_Update_OTP_Common(t, c, [][]byte{master})
bc, rc := TestSealDefConfigs()
c, _, recoveryKeys, _ := TestCoreUnsealedWithConfigs(t, bc, rc)
testCore_GenerateRoot_Update_OTP_Common(t, c, recoveryKeys[0:rc.SecretThreshold])
}
func testCore_GenerateRoot_Update_OTP_Common(t *testing.T, c *Core, keys [][]byte) {
otpBytes, err := GenerateRandBytes(16)
if err != nil {
t.Fatal(err)
@ -168,10 +193,13 @@ func TestCore_GenerateRoot_Update_OTP(t *testing.T) {
t.Fatalf("bad: no root generation config received")
}
// Provide the master
result, err := c.GenerateRootUpdate(master, rkconf.Nonce)
if err != nil {
t.Fatalf("err: %v", err)
// Provide the keys
var result *GenerateRootResult
for _, key := range keys {
result, err = c.GenerateRootUpdate(key, rkconf.Nonce)
if err != nil {
t.Fatalf("err: %v", err)
}
}
if result == nil {
t.Fatalf("Bad, result is nil")
@ -222,7 +250,14 @@ func TestCore_GenerateRoot_Update_OTP(t *testing.T) {
func TestCore_GenerateRoot_Update_PGP(t *testing.T) {
c, master, _ := TestCoreUnsealed(t)
testCore_GenerateRoot_Update_PGP_Common(t, c, [][]byte{master})
bc, rc := TestSealDefConfigs()
c, _, recoveryKeys, _ := TestCoreUnsealedWithConfigs(t, bc, rc)
testCore_GenerateRoot_Update_PGP_Common(t, c, recoveryKeys[0:rc.SecretThreshold])
}
func testCore_GenerateRoot_Update_PGP_Common(t *testing.T, c *Core, keys [][]byte) {
// Start a root generation
err := c.GenerateRootInit("", pgpkeys.TestPubKey1)
if err != nil {
@ -238,10 +273,13 @@ func TestCore_GenerateRoot_Update_PGP(t *testing.T) {
t.Fatalf("bad: no root generation config received")
}
// Provide the master
result, err := c.GenerateRootUpdate(master, rkconf.Nonce)
if err != nil {
t.Fatalf("err: %v", err)
// Provide the keys
var result *GenerateRootResult
for _, key := range keys {
result, err = c.GenerateRootUpdate(key, rkconf.Nonce)
if err != nil {
t.Fatalf("err: %v", err)
}
}
if result == nil {
t.Fatalf("Bad, result is nil")

View File

@ -85,34 +85,14 @@ func (c *Core) Initialize(barrierConfig, recoveryConfig *SealConfig) (*InitResul
return nil, fmt.Errorf("recovery configuration must be supplied")
}
if recoveryConfig.SecretShares == 0 {
return nil, fmt.Errorf("recovery configuration must specify a positive number of shares, or a negative number to disable")
if recoveryConfig.SecretShares < 1 {
return nil, fmt.Errorf("recovery configuration must specify a positive number of shares")
}
if recoveryConfig.SecretShares > 0 {
// Check if the seal configuraiton is valid
if err := recoveryConfig.Validate(); err != nil {
c.logger.Printf("[ERR] core: invalid recovery configuration: %v", err)
return nil, fmt.Errorf("invalid recovery configuration: %v", err)
}
}
}
if c.seal.StoredKeysSupported() {
if barrierConfig.SecretShares != 1 {
return nil, fmt.Errorf("secret shares must be 1")
}
if barrierConfig.SecretThreshold != barrierConfig.SecretShares {
return nil, fmt.Errorf("secret threshold must be same as secret shares")
}
if barrierConfig.StoredShares != barrierConfig.SecretShares {
return nil, fmt.Errorf("stored shares must be same as secret shares")
}
if barrierConfig.PGPKeys != nil && len(barrierConfig.PGPKeys) > 0 {
return nil, fmt.Errorf("PGP keys not supported when storing shares")
}
} else {
if barrierConfig.StoredShares > 0 {
return nil, fmt.Errorf("stored keys are not supported")
// Check if the seal configuraiton is valid
if err := recoveryConfig.Validate(); err != nil {
c.logger.Printf("[ERR] core: invalid recovery configuration: %v", err)
return nil, fmt.Errorf("invalid recovery configuration: %v", err)
}
}

View File

@ -9,6 +9,17 @@ import (
)
func TestCore_Init(t *testing.T) {
c, conf := testCore_NewTestCore(t, nil)
testCore_Init_Common(t, c, conf, &SealConfig{SecretShares: 5, SecretThreshold: 3}, nil)
c, conf = testCore_NewTestCore(t, &TestSeal{})
bc, rc := TestSealDefConfigs()
rc.SecretShares = 4
rc.SecretThreshold = 2
testCore_Init_Common(t, c, conf, bc, rc)
}
func testCore_NewTestCore(t *testing.T, seal Seal) (*Core, *CoreConfig) {
inm := physical.NewInmem()
conf := &CoreConfig{
Physical: inm,
@ -16,12 +27,16 @@ func TestCore_Init(t *testing.T) {
LogicalBackends: map[string]logical.Factory{
"generic": LeasedPassthroughBackendFactory,
},
Seal: seal,
}
c, err := NewCore(conf)
if err != nil {
t.Fatalf("err: %v", err)
}
return c, conf
}
func testCore_Init_Common(t *testing.T, c *Core, conf *CoreConfig, barrierConf, recoveryConf *SealConfig) {
init, err := c.Initialized()
if err != nil {
t.Fatalf("err: %v", err)
@ -39,24 +54,35 @@ func TestCore_Init(t *testing.T) {
if outConf != nil {
t.Fatalf("bad: %v", outConf)
}
sealConf := &SealConfig{
SecretShares: 1,
SecretThreshold: 1,
if recoveryConf != nil {
outConf, err := c.seal.RecoveryConfig()
if err != nil {
t.Fatalf("err: %v", err)
}
if outConf != nil {
t.Fatalf("bad: %v", outConf)
}
}
res, err := c.Initialize(sealConf, nil)
res, err := c.Initialize(barrierConf, recoveryConf)
if err != nil {
t.Fatalf("err: %v", err)
}
if len(res.SecretShares) != 1 {
t.Fatalf("Bad: %v", res)
if len(res.SecretShares) != (barrierConf.SecretShares - barrierConf.StoredShares) {
t.Fatalf("Bad: got\n%#v\nexpected conf matching\n%#v\n", *res, *barrierConf)
}
if res.RootToken == "" {
t.Fatalf("Bad: %v", res)
if recoveryConf != nil {
if len(res.RecoveryShares) != recoveryConf.SecretShares {
t.Fatalf("Bad: got\n%#v\nexpected conf matching\n%#v\n", *res, *recoveryConf)
}
}
_, err = c.Initialize(sealConf, nil)
if res.RootToken == "" {
t.Fatalf("Bad: %#v", res)
}
_, err = c.Initialize(barrierConf, recoveryConf)
if err != ErrAlreadyInit {
t.Fatalf("err: %v", err)
}
@ -75,8 +101,17 @@ func TestCore_Init(t *testing.T) {
if err != nil {
t.Fatalf("err: %v", err)
}
if !reflect.DeepEqual(outConf, sealConf) {
t.Fatalf("bad: %v expect: %v", outConf, sealConf)
if !reflect.DeepEqual(outConf, barrierConf) {
t.Fatalf("bad: %v expect: %v", outConf, barrierConf)
}
if recoveryConf != nil {
outConf, err = c.seal.RecoveryConfig()
if err != nil {
t.Fatalf("err: %v", err)
}
if !reflect.DeepEqual(outConf, recoveryConf) {
t.Fatalf("bad: %v expect: %v", outConf, recoveryConf)
}
}
// New Core, same backend
@ -85,7 +120,7 @@ func TestCore_Init(t *testing.T) {
t.Fatalf("err: %v", err)
}
_, err = c2.Initialize(sealConf, nil)
_, err = c2.Initialize(barrierConf, recoveryConf)
if err != ErrAlreadyInit {
t.Fatalf("err: %v", err)
}
@ -104,7 +139,16 @@ func TestCore_Init(t *testing.T) {
if err != nil {
t.Fatalf("err: %v", err)
}
if !reflect.DeepEqual(outConf, sealConf) {
t.Fatalf("bad: %v expect: %v", outConf, sealConf)
if !reflect.DeepEqual(outConf, barrierConf) {
t.Fatalf("bad: %v expect: %v", outConf, barrierConf)
}
if recoveryConf != nil {
outConf, err = c2.seal.RecoveryConfig()
if err != nil {
t.Fatalf("err: %v", err)
}
if !reflect.DeepEqual(outConf, recoveryConf) {
t.Fatalf("bad: %v expect: %v", outConf, recoveryConf)
}
}
}

View File

@ -124,13 +124,6 @@ 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 {
// Right now we don't support this, but the rest of the code is ready for
// when we do, hence the check below for this to be false if
// config.StoredShares is greater than zero
if c.seal.StoredKeysSupported() {
return fmt.Errorf("rekeying of barrier not supported when stored key support is available")
}
if config.StoredShares > 0 {
if !c.seal.StoredKeysSupported() {
return fmt.Errorf("storing keys not supported by barrier seal")

View File

@ -1,6 +1,7 @@
package vault
import (
"fmt"
"reflect"
"testing"
@ -9,14 +10,25 @@ import (
func TestCore_Rekey_Lifecycle(t *testing.T) {
c, master, _ := TestCoreUnsealed(t)
testCore_Rekey_Lifecycle_Common(t, c, [][]byte{master}, false)
bc, rc := TestSealDefConfigs()
c, masterKeys, recoveryKeys, _ := TestCoreUnsealedWithConfigs(t, bc, rc)
if len(masterKeys) != 3 {
t.Fatalf("expected %d keys, got %d", bc.SecretShares-bc.StoredShares, len(masterKeys))
}
testCore_Rekey_Lifecycle_Common(t, c, masterKeys, false)
testCore_Rekey_Lifecycle_Common(t, c, recoveryKeys, true)
}
func testCore_Rekey_Lifecycle_Common(t *testing.T, c *Core, masterKeys [][]byte, recovery bool) {
// Verify update not allowed
if _, err := c.RekeyUpdate(master, "", false); err == nil {
t.Fatalf("no rekey in progress")
if _, err := c.RekeyUpdate(masterKeys[0], "", recovery); err == nil {
t.Fatalf("no rekey should be in progress")
}
// Should be no progress
num, err := c.RekeyProgress(false)
num, err := c.RekeyProgress(recovery)
if err != nil {
t.Fatalf("err: %v", err)
}
@ -25,7 +37,7 @@ func TestCore_Rekey_Lifecycle(t *testing.T) {
}
// Should be no config
conf, err := c.RekeyConfig(false)
conf, err := c.RekeyConfig(recovery)
if err != nil {
t.Fatalf("err: %v", err)
}
@ -44,13 +56,13 @@ func TestCore_Rekey_Lifecycle(t *testing.T) {
SecretThreshold: 3,
SecretShares: 5,
}
err = c.RekeyInit(newConf, false)
err = c.RekeyInit(newConf, recovery)
if err != nil {
t.Fatalf("err: %v", err)
}
// Should get config
conf, err = c.RekeyConfig(false)
conf, err = c.RekeyConfig(recovery)
if err != nil {
t.Fatalf("err: %v", err)
}
@ -60,13 +72,13 @@ func TestCore_Rekey_Lifecycle(t *testing.T) {
}
// Cancel should be clear
err = c.RekeyCancel(false)
err = c.RekeyCancel(recovery)
if err != nil {
t.Fatalf("err: %v", err)
}
// Should be no config
conf, err = c.RekeyConfig(false)
conf, err = c.RekeyConfig(recovery)
if err != nil {
t.Fatalf("err: %v", err)
}
@ -77,13 +89,21 @@ func TestCore_Rekey_Lifecycle(t *testing.T) {
func TestCore_Rekey_Init(t *testing.T) {
c, _, _ := TestCoreUnsealed(t)
testCore_Rekey_Init_Common(t, c, false)
bc, rc := TestSealDefConfigs()
c, _, _, _ = TestCoreUnsealedWithConfigs(t, bc, rc)
testCore_Rekey_Init_Common(t, c, false)
testCore_Rekey_Init_Common(t, c, true)
}
func testCore_Rekey_Init_Common(t *testing.T, c *Core, recovery bool) {
// Try an invalid config
badConf := &SealConfig{
SecretThreshold: 5,
SecretShares: 1,
}
err := c.RekeyInit(badConf, false)
err := c.RekeyInit(badConf, recovery)
if err == nil {
t.Fatalf("should fail")
}
@ -93,13 +113,13 @@ func TestCore_Rekey_Init(t *testing.T) {
SecretThreshold: 3,
SecretShares: 5,
}
err = c.RekeyInit(newConf, false)
err = c.RekeyInit(newConf, recovery)
if err != nil {
t.Fatalf("err: %v", err)
}
// Second should fail
err = c.RekeyInit(newConf, false)
err = c.RekeyInit(newConf, recovery)
if err == nil {
t.Fatalf("should fail")
}
@ -107,20 +127,29 @@ func TestCore_Rekey_Init(t *testing.T) {
func TestCore_Rekey_Update(t *testing.T) {
c, master, root := TestCoreUnsealed(t)
testCore_Rekey_Update_Common(t, c, [][]byte{master}, root, false)
bc, rc := TestSealDefConfigs()
bc.StoredShares = 0
c, masterKeys, recoveryKeys, root := TestCoreUnsealedWithConfigs(t, bc, rc)
testCore_Rekey_Update_Common(t, c, masterKeys, root, false)
testCore_Rekey_Update_Common(t, c, recoveryKeys, root, true)
}
func testCore_Rekey_Update_Common(t *testing.T, c *Core, keys [][]byte, root string, recovery bool) {
// Start a rekey
newConf := &SealConfig{
Type: "shamir",
SecretThreshold: 3,
SecretShares: 5,
}
err := c.RekeyInit(newConf, false)
err := c.RekeyInit(newConf, recovery)
if err != nil {
t.Fatalf("err: %v", err)
}
// Fetch new config with generated nonce
rkconf, err := c.RekeyConfig(false)
rkconf, err := c.RekeyConfig(recovery)
if err != nil {
t.Fatalf("err: %v", err)
}
@ -129,16 +158,22 @@ func TestCore_Rekey_Update(t *testing.T) {
}
// Provide the master
result, err := c.RekeyUpdate(master, rkconf.Nonce, false)
if err != nil {
t.Fatalf("err: %v", err)
var result *RekeyResult
for _, key := range keys {
result, err = c.RekeyUpdate(key, rkconf.Nonce, recovery)
if err != nil {
t.Fatalf("err: %v", err)
}
if result != nil {
break
}
}
if result == nil || len(result.SecretShares) != 5 {
t.Fatalf("Bad: %#v", result)
}
// Should be no progress
num, err := c.RekeyProgress(false)
num, err := c.RekeyProgress(recovery)
if err != nil {
t.Fatalf("err: %v", err)
}
@ -147,7 +182,7 @@ func TestCore_Rekey_Update(t *testing.T) {
}
// Should be no config
conf, err := c.RekeyConfig(false)
conf, err := c.RekeyConfig(recovery)
if err != nil {
t.Fatalf("err: %v", err)
}
@ -156,7 +191,12 @@ func TestCore_Rekey_Update(t *testing.T) {
}
// SealConfig should update
sealConf, err := c.seal.BarrierConfig()
var sealConf *SealConfig
if recovery {
sealConf, err = c.seal.RecoveryConfig()
} else {
sealConf, err = c.seal.BarrierConfig()
}
if err != nil {
t.Fatalf("err: %v", err)
}
@ -169,19 +209,21 @@ func TestCore_Rekey_Update(t *testing.T) {
t.Fatalf("\nexpected: %#v\nactual: %#v\n", newConf, sealConf)
}
// Attempt unseal
err = c.Seal(root)
if err != nil {
t.Fatalf("err: %v", err)
}
for i := 0; i < 3; i++ {
_, err = c.Unseal(result.SecretShares[i])
// Attempt unseal if this was not recovery mode
if !recovery {
err = c.Seal(root)
if err != nil {
t.Fatalf("err: %v", err)
}
}
if sealed, _ := c.Sealed(); sealed {
t.Fatalf("should be unsealed")
for i := 0; i < 3; i++ {
_, err = c.Unseal(result.SecretShares[i])
if err != nil {
t.Fatalf("err: %v", err)
}
}
if sealed, _ := c.Sealed(); sealed {
t.Fatalf("should be unsealed")
}
}
// Start another rekey, this time we require a quorum!
@ -190,13 +232,13 @@ func TestCore_Rekey_Update(t *testing.T) {
SecretThreshold: 1,
SecretShares: 1,
}
err = c.RekeyInit(newConf, false)
err = c.RekeyInit(newConf, recovery)
if err != nil {
t.Fatalf("err: %v", err)
}
// Fetch new config with generated nonce
rkconf, err = c.RekeyConfig(false)
rkconf, err = c.RekeyConfig(recovery)
if err != nil {
t.Fatalf("err: %v", err)
}
@ -207,13 +249,13 @@ func TestCore_Rekey_Update(t *testing.T) {
// Provide the parts master
oldResult := result
for i := 0; i < 3; i++ {
result, err = c.RekeyUpdate(oldResult.SecretShares[i], rkconf.Nonce, false)
result, err = c.RekeyUpdate(oldResult.SecretShares[i], rkconf.Nonce, recovery)
if err != nil {
t.Fatalf("err: %v", err)
}
// Should be progress
num, err := c.RekeyProgress(false)
num, err := c.RekeyProgress(recovery)
if err != nil {
t.Fatalf("err: %v", err)
}
@ -225,21 +267,27 @@ func TestCore_Rekey_Update(t *testing.T) {
t.Fatalf("Bad: %#v", result)
}
// Attempt unseal
err = c.Seal(root)
if err != nil {
t.Fatalf("err: %v", err)
}
unseal, err := c.Unseal(result.SecretShares[0])
if err != nil {
t.Fatalf("err: %v", err)
}
if !unseal {
t.Fatalf("should be unsealed")
// Attempt unseal if this was not recovery mode
if !recovery {
err = c.Seal(root)
if err != nil {
t.Fatalf("err: %v", err)
}
unseal, err := c.Unseal(result.SecretShares[0])
if err != nil {
t.Fatalf("err: %v", err)
}
if !unseal {
t.Fatalf("should be unsealed")
}
}
// SealConfig should update
sealConf, err = c.seal.BarrierConfig()
if recovery {
sealConf, err = c.seal.RecoveryConfig()
} else {
sealConf, err = c.seal.BarrierConfig()
}
if err != nil {
t.Fatalf("err: %v", err)
}
@ -250,21 +298,34 @@ func TestCore_Rekey_Update(t *testing.T) {
}
}
func TestCore_Rekey_InvalidMaster(t *testing.T) {
func TestCore_Rekey_Invalid(t *testing.T) {
c, master, _ := TestCoreUnsealed(t)
testCore_Rekey_Invalid_Common(t, c, [][]byte{master}, false)
bc, rc := TestSealDefConfigs()
bc.StoredShares = 0
bc.SecretShares = 1
bc.SecretThreshold = 1
rc.SecretShares = 1
rc.SecretThreshold = 1
c, masterKeys, recoveryKeys, _ := TestCoreUnsealedWithConfigs(t, bc, rc)
testCore_Rekey_Invalid_Common(t, c, masterKeys, false)
testCore_Rekey_Invalid_Common(t, c, recoveryKeys, true)
}
func testCore_Rekey_Invalid_Common(t *testing.T, c *Core, keys [][]byte, recovery bool) {
// Start a rekey
newConf := &SealConfig{
SecretThreshold: 3,
SecretShares: 5,
}
err := c.RekeyInit(newConf, false)
err := c.RekeyInit(newConf, recovery)
if err != nil {
t.Fatalf("err: %v", err)
}
// Fetch new config with generated nonce
rkconf, err := c.RekeyConfig(false)
rkconf, err := c.RekeyConfig(recovery)
if err != nil {
t.Fatalf("err: %v", err)
}
@ -272,32 +333,21 @@ func TestCore_Rekey_InvalidMaster(t *testing.T) {
t.Fatalf("bad: no rekey config received")
}
// Provide the master (invalid)
master[0]++
_, err = c.RekeyUpdate(master, rkconf.Nonce, false)
if err == nil {
t.Fatalf("expected error")
}
}
func TestCore_Rekey_InvalidNonce(t *testing.T) {
c, master, _ := TestCoreUnsealed(t)
// Start a rekey
newConf := &SealConfig{
SecretThreshold: 3,
SecretShares: 5,
}
err := c.RekeyInit(newConf, false)
if err != nil {
t.Fatalf("err: %v", err)
}
// Provide the nonce (invalid)
_, err = c.RekeyUpdate(master, "abcd", false)
_, err = c.RekeyUpdate(keys[0], "abcd", recovery)
if err == nil {
t.Fatalf("expected error")
}
// Provide the key (invalid)
key := keys[0]
oldkeystr := fmt.Sprintf("%#v", key)
key[0]++
newkeystr := fmt.Sprintf("%#v", key)
ret, err := c.RekeyUpdate(key, rkconf.Nonce, recovery)
if err == nil {
t.Fatalf("expected error, ret is %#v\noldkeystr: %s\nnewkeystr: %s", *ret, oldkeystr, newkeystr)
}
}
func TestCore_Standby_Rekey(t *testing.T) {

128
vault/seal_test.go Normal file
View File

@ -0,0 +1,128 @@
package vault
import (
"bytes"
"fmt"
"reflect"
"testing"
)
type TestSeal struct {
defseal *DefaultSeal
barrierKeys [][]byte
recoveryKey []byte
recoveryConfig *SealConfig
}
func (d *TestSeal) checkCore() error {
if d.defseal.core == nil {
return fmt.Errorf("seal does not have a core set")
}
return nil
}
func (d *TestSeal) SetCore(core *Core) {
d.defseal = &DefaultSeal{}
d.defseal.core = core
}
func (d *TestSeal) Init() error {
d.barrierKeys = [][]byte{}
return d.defseal.Init()
}
func (d *TestSeal) Finalize() error {
return d.defseal.Finalize()
}
func (d *TestSeal) BarrierType() string {
return "shamir"
}
func (d *TestSeal) StoredKeysSupported() bool {
return true
}
func (d *TestSeal) RecoveryKeySupported() bool {
return true
}
func (d *TestSeal) SetStoredKeys(keys [][]byte) error {
d.barrierKeys = keys
return nil
}
func (d *TestSeal) GetStoredKeys() ([][]byte, error) {
return d.barrierKeys, nil
}
func (d *TestSeal) BarrierConfig() (*SealConfig, error) {
return d.defseal.BarrierConfig()
}
func (d *TestSeal) SetBarrierConfig(config *SealConfig) error {
return d.defseal.SetBarrierConfig(config)
}
func (d *TestSeal) RecoveryType() string {
return "shamir"
}
func (d *TestSeal) RecoveryConfig() (*SealConfig, error) {
return d.recoveryConfig, nil
}
func (d *TestSeal) SetRecoveryConfig(config *SealConfig) error {
d.recoveryConfig = config
return nil
}
func (d *TestSeal) VerifyRecoveryKey(key []byte) error {
if bytes.Equal(d.recoveryKey, key) {
return nil
}
return fmt.Errorf("not equivalent")
}
func (d *TestSeal) SetRecoveryKey(key []byte) error {
newbuf := bytes.NewBuffer(nil)
newbuf.Write(key)
d.recoveryKey = newbuf.Bytes()
return nil
}
func TestDefaultSeal_Config(t *testing.T) {
bc, _ := TestSealDefConfigs()
// Change these to non-default values to ensure we are seeing the real
// config we set
bc.SecretShares = 4
bc.SecretThreshold = 2
core, _, _ := TestCoreUnsealed(t)
defSeal := &DefaultSeal{}
defSeal.SetCore(core)
err := defSeal.SetBarrierConfig(bc)
if err != nil {
t.Fatal(err)
}
newBc, err := defSeal.BarrierConfig()
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(*bc, *newBc) {
t.Fatal("config mismatch")
}
// Now, test without the benefit of the cached value in the seal
defSeal = &DefaultSeal{}
defSeal.SetCore(core)
newBc, err = defSeal.BarrierConfig()
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(*bc, *newBc) {
t.Fatal("config mismatch")
}
}

View File

@ -62,6 +62,12 @@ oOyBJU/HMVvBfv4g+OVFLVgSwwm6owwsouZ0+D/LasbuHqYyqYqdyPJQYzWA2Y+F
// TestCore returns a pure in-memory, uninitialized core for testing.
func TestCore(t *testing.T) *Core {
return TestCoreWithSeal(t, nil)
}
// TestCoreWithSeal returns a pure in-memory, uninitialized core with the
// specified seal for testing.
func TestCoreWithSeal(t *testing.T, testSeal Seal) *Core {
noopAudits := map[string]audit.Factory{
"noop": func(config *audit.BackendConfig) (audit.Backend, error) {
view := &logical.InmemStorage{}
@ -101,13 +107,18 @@ func TestCore(t *testing.T) *Core {
}
physicalBackend := physical.NewInmem()
c, err := NewCore(&CoreConfig{
conf := &CoreConfig{
Physical: physicalBackend,
AuditBackends: noopAudits,
LogicalBackends: logicalBackends,
CredentialBackends: noopBackends,
DisableMlock: true,
})
}
if testSeal != nil {
conf.Seal = testSeal
}
c, err := NewCore(conf)
if err != nil {
t.Fatalf("err: %s", err)
}
@ -363,3 +374,49 @@ func GenerateRandBytes(length int) ([]byte, error) {
return buf, nil
}
/*
* TestSeal items
*/
func TestCoreUnsealedWithConfigs(t *testing.T, barrierConf, recoveryConf *SealConfig) (*Core, [][]byte, [][]byte, string) {
seal := &TestSeal{}
core := TestCoreWithSeal(t, seal)
result, err := core.Initialize(barrierConf, recoveryConf)
if err != nil {
t.Fatalf("err: %s", err)
}
err = core.UnsealWithStoredKeys()
if err != nil {
t.Fatalf("err: %s", err)
}
if sealed, _ := core.Sealed(); sealed {
for _, key := range result.SecretShares {
if _, err := core.Unseal(key); err != nil {
t.Fatalf("unseal err: %s", err)
}
}
sealed, err = core.Sealed()
if err != nil {
t.Fatalf("err checking seal status: %s", err)
}
if sealed {
t.Fatal("should not be sealed")
}
}
return core, result.SecretShares, result.RecoveryShares, result.RootToken
}
func TestSealDefConfigs() (*SealConfig, *SealConfig) {
return &SealConfig{
SecretShares: 5,
SecretThreshold: 3,
StoredShares: 2,
}, &SealConfig{
SecretShares: 5,
SecretThreshold: 3,
}
}

View File

@ -524,7 +524,9 @@ func (ts *TokenStore) storeCommon(entry *TokenEntry, writeSecondary bool) error
}
// UseToken is used to manage restricted use tokens and decrement
// their available uses.
// their available uses. Note: this is potentially racy, but the simple
// solution of a global lock would be severely detrimental to performance. Also
// note the specific revoke case below.
func (ts *TokenStore) UseToken(te *TokenEntry) error {
// If the token is not restricted, there is nothing to do
if te.NumUses == 0 {