diff --git a/shamir/shamir.go b/shamir/shamir.go index bc493b4027..5180202ddc 100644 --- a/shamir/shamir.go +++ b/shamir/shamir.go @@ -5,6 +5,13 @@ import ( "fmt" ) +const ( + // ShareOverhead is the byte size overhead of each share + // when using Split on a secret. This is caused by appending + // a one byte tag to the share. + ShareOverhead = 1 +) + // polynomial represents a polynomial of arbitrary degree type polynomial struct { coefficients []uint8 diff --git a/vault/barrier.go b/vault/barrier.go index 04e3accea9..9dd8f2fa59 100644 --- a/vault/barrier.go +++ b/vault/barrier.go @@ -39,6 +39,9 @@ type SecurityBarrier interface { // GenerateKey is used to generate a new key GenerateKey() ([]byte, error) + // KeyLength is used to sanity check a key + KeyLength() (int, int) + // Sealed checks if the barrier has been unlocked yet. The Barrier // is not expected to be able to perform any CRUD until it is unsealed. Sealed() (bool, error) diff --git a/vault/barrier_aes_gcm.go b/vault/barrier_aes_gcm.go index 2bcc94ae06..3c5a62cc66 100644 --- a/vault/barrier_aes_gcm.go +++ b/vault/barrier_aes_gcm.go @@ -121,6 +121,11 @@ func (b *AESGCMBarrier) GenerateKey() ([]byte, error) { return buf, err } +// KeyLength is used to sanity check a key +func (b *AESGCMBarrier) KeyLength() (int, int) { + return aes.BlockSize, aes.BlockSize +} + // Sealed checks if the barrier has been unlocked yet. The Barrier // is not expected to be able to perform any CRUD until it is unsealed. func (b *AESGCMBarrier) Sealed() (bool, error) { diff --git a/vault/barrier_test.go b/vault/barrier_test.go index 1895a7e9c7..74758bf927 100644 --- a/vault/barrier_test.go +++ b/vault/barrier_test.go @@ -50,6 +50,15 @@ func testBarrier(t *testing.T, b SecurityBarrier) { t.Fatalf("err: %v", err) } + // Validate minimum key length + min, max := b.KeyLength() + if min < 16 { + t.Fatalf("minimum key size too small: %d", min) + } + if max < min { + t.Fatalf("maximum key size smaller than min") + } + // Unseal should not work if err := b.Unseal(key); err != ErrBarrierNotInit { t.Fatalf("err: %v", err) diff --git a/vault/core.go b/vault/core.go index 8f8f9aadc6..24d125a67d 100644 --- a/vault/core.go +++ b/vault/core.go @@ -54,6 +54,12 @@ func (s *SealConfig) Validate() error { if s.SecretThreshold < 1 { return fmt.Errorf("secret threshold must be at least one") } + if s.SecretShares > 255 { + return fmt.Errorf("secret shares must be less than 256") + } + if s.SecretThreshold > 255 { + return fmt.Errorf("secret threshold must be less than 256") + } if s.SecretThreshold > s.SecretShares { return fmt.Errorf("secret threshold cannot be larger than secret shares") } @@ -66,6 +72,16 @@ type InitResult struct { SecretShares [][]byte } +// ErrInvalidKey is returned if there is an error with a +// provided unseal key. +type ErrInvalidKey struct { + Reason string +} + +func (e *ErrInvalidKey) Error() string { + return fmt.Sprintf("invalid key: %v", e.Reason) +} + // Core is used as the central manager of Vault activity. It is the primary point of // interface for API handlers and is responsible for managing the logical and physical // backends, router, security barrier, and audit trails. @@ -285,6 +301,16 @@ func (c *Core) SecretProgress() int { // Unseal is used to provide one of the key parts to // unseal the Vault. func (c *Core) Unseal(key []byte) (bool, error) { + // Verify the key length + min, max := c.barrier.KeyLength() + max += shamir.ShareOverhead + if len(key) < min { + return false, &ErrInvalidKey{fmt.Sprintf("key is shorter than minimum %d bytes", min)} + } + if len(key) > max { + return false, &ErrInvalidKey{fmt.Sprintf("key is longer than maximum %d bytes", max)} + } + // Get the seal configuration config, err := c.SealConfig() if err != nil { diff --git a/vault/core_test.go b/vault/core_test.go index 2d18d2d26c..7458c892a0 100644 --- a/vault/core_test.go +++ b/vault/core_test.go @@ -7,6 +7,11 @@ import ( "github.com/hashicorp/vault/physical" ) +var ( + // invalidKey is used to test Unseal + invalidKey = []byte("abcdefghijklmnopqrstuvwxyz")[:17] +) + func testCore(t *testing.T) *Core { inm := physical.NewInmem() conf := &CoreConfig{Physical: inm} @@ -159,7 +164,7 @@ func TestCore_Init_MultiShare(t *testing.T) { func TestCore_Unseal_MultiShare(t *testing.T) { c := testCore(t) - _, err := c.Unseal([]byte("testing")) + _, err := c.Unseal(invalidKey) if err != ErrNotInit { t.Fatalf("err: %v", err) } @@ -244,7 +249,7 @@ func TestCore_Unseal_MultiShare(t *testing.T) { func TestCore_Unseal_Single(t *testing.T) { c := testCore(t) - _, err := c.Unseal([]byte("testing")) + _, err := c.Unseal(invalidKey) if err != ErrNotInit { t.Fatalf("err: %v", err) }