macaroons: add ability to change the password or create a new root key
This commit is contained in:
parent
cd85e17b19
commit
ae71d60715
@ -257,8 +257,8 @@ func (svc *Service) ValidateMacaroon(ctx context.Context,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the method being called against the permitted operation and
|
// Check the method being called against the permitted operation, the
|
||||||
// the expiration time and IP address and return the result.
|
// expiration time and IP address and return the result.
|
||||||
authChecker := svc.Checker.Auth(macaroon.Slice{mac})
|
authChecker := svc.Checker.Auth(macaroon.Slice{mac})
|
||||||
_, err = authChecker.Allow(ctx, requiredPermissions...)
|
_, err = authChecker.Allow(ctx, requiredPermissions...)
|
||||||
|
|
||||||
@ -325,3 +325,15 @@ func (svc *Service) DeleteMacaroonID(ctxt context.Context,
|
|||||||
rootKeyID []byte) ([]byte, error) {
|
rootKeyID []byte) ([]byte, error) {
|
||||||
return svc.rks.DeleteMacaroonID(ctxt, rootKeyID)
|
return svc.rks.DeleteMacaroonID(ctxt, rootKeyID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GenerateNewRootKey calls the underlying root key store's GenerateNewRootKey
|
||||||
|
// and returns the result.
|
||||||
|
func (svc *Service) GenerateNewRootKey() error {
|
||||||
|
return svc.rks.GenerateNewRootKey()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChangePassword calls the underlying root key store's ChangePassword and
|
||||||
|
// returns the result.
|
||||||
|
func (svc *Service) ChangePassword(oldPw, newPw []byte) error {
|
||||||
|
return svc.rks.ChangePassword(oldPw, newPw)
|
||||||
|
}
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/channeldb/kvdb"
|
"github.com/lightningnetwork/lnd/channeldb/kvdb"
|
||||||
|
|
||||||
"github.com/btcsuite/btcwallet/snacl"
|
"github.com/btcsuite/btcwallet/snacl"
|
||||||
|
"github.com/btcsuite/btcwallet/walletdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -26,10 +27,10 @@ var (
|
|||||||
// just 0, to emulate the memory storage that comes with bakery.
|
// just 0, to emulate the memory storage that comes with bakery.
|
||||||
DefaultRootKeyID = []byte("0")
|
DefaultRootKeyID = []byte("0")
|
||||||
|
|
||||||
// encryptedKeyID is the name of the database key that stores the
|
// encryptionKeyID is the name of the database key that stores the
|
||||||
// encryption key, encrypted with a salted + hashed password. The
|
// encryption key, encrypted with a salted + hashed password. The
|
||||||
// format is 32 bytes of salt, and the rest is encrypted key.
|
// format is 32 bytes of salt, and the rest is encrypted key.
|
||||||
encryptedKeyID = []byte("enckey")
|
encryptionKeyID = []byte("enckey")
|
||||||
|
|
||||||
// ErrAlreadyUnlocked specifies that the store has already been
|
// ErrAlreadyUnlocked specifies that the store has already been
|
||||||
// unlocked.
|
// unlocked.
|
||||||
@ -45,6 +46,15 @@ var (
|
|||||||
// ErrKeyValueForbidden is used when the root key ID uses encryptedKeyID as
|
// ErrKeyValueForbidden is used when the root key ID uses encryptedKeyID as
|
||||||
// its value.
|
// its value.
|
||||||
ErrKeyValueForbidden = fmt.Errorf("root key ID value is not allowed")
|
ErrKeyValueForbidden = fmt.Errorf("root key ID value is not allowed")
|
||||||
|
|
||||||
|
// ErrRootKeyBucketNotFound specifies that there is no macaroon root key
|
||||||
|
// bucket yet which can/should only happen if the store has been
|
||||||
|
// corrupted or was initialized incorrectly.
|
||||||
|
ErrRootKeyBucketNotFound = fmt.Errorf("root key bucket not found")
|
||||||
|
|
||||||
|
// ErrEncKeyNotFound specifies that there was no encryption key found
|
||||||
|
// even if one was expected to be generated.
|
||||||
|
ErrEncKeyNotFound = fmt.Errorf("macaroon encryption key not found")
|
||||||
)
|
)
|
||||||
|
|
||||||
// RootKeyStorage implements the bakery.RootKeyStorage interface.
|
// RootKeyStorage implements the bakery.RootKeyStorage interface.
|
||||||
@ -89,7 +99,10 @@ func (r *RootKeyStorage) CreateUnlock(password *[]byte) error {
|
|||||||
|
|
||||||
return kvdb.Update(r, func(tx kvdb.RwTx) error {
|
return kvdb.Update(r, func(tx kvdb.RwTx) error {
|
||||||
bucket := tx.ReadWriteBucket(rootKeyBucketName)
|
bucket := tx.ReadWriteBucket(rootKeyBucketName)
|
||||||
dbKey := bucket.Get(encryptedKeyID)
|
if bucket == nil {
|
||||||
|
return ErrRootKeyBucketNotFound
|
||||||
|
}
|
||||||
|
dbKey := bucket.Get(encryptionKeyID)
|
||||||
if len(dbKey) > 0 {
|
if len(dbKey) > 0 {
|
||||||
// We've already stored a key, so try to unlock with
|
// We've already stored a key, so try to unlock with
|
||||||
// the password.
|
// the password.
|
||||||
@ -116,7 +129,7 @@ func (r *RootKeyStorage) CreateUnlock(password *[]byte) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = bucket.Put(encryptedKeyID, encKey.Marshal())
|
err = bucket.Put(encryptionKeyID, encKey.Marshal())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -126,6 +139,83 @@ func (r *RootKeyStorage) CreateUnlock(password *[]byte) error {
|
|||||||
}, func() {})
|
}, func() {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ChangePassword decrypts the macaroon root key with the old password and then
|
||||||
|
// encrypts it again with the new password.
|
||||||
|
func (r *RootKeyStorage) ChangePassword(oldPw, newPw []byte) error {
|
||||||
|
// We need the store to already be unlocked. With this we can make sure
|
||||||
|
// that there already is a key in the DB.
|
||||||
|
if r.encKey == nil {
|
||||||
|
return ErrStoreLocked
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a nil password has been passed; return an error if so.
|
||||||
|
if oldPw == nil || newPw == nil {
|
||||||
|
return ErrPasswordRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
return kvdb.Update(r, func(tx kvdb.RwTx) error {
|
||||||
|
bucket := tx.ReadWriteBucket(rootKeyBucketName)
|
||||||
|
if bucket == nil {
|
||||||
|
return ErrRootKeyBucketNotFound
|
||||||
|
}
|
||||||
|
encKeyDb := bucket.Get(encryptionKeyID)
|
||||||
|
rootKeyDb := bucket.Get(DefaultRootKeyID)
|
||||||
|
|
||||||
|
// Both the encryption key and the root key must be present
|
||||||
|
// otherwise we are in the wrong state to change the password.
|
||||||
|
if len(encKeyDb) == 0 || len(rootKeyDb) == 0 {
|
||||||
|
return ErrEncKeyNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal parameters for old encryption key and derive the
|
||||||
|
// old key with them.
|
||||||
|
encKeyOld := &snacl.SecretKey{}
|
||||||
|
err := encKeyOld.Unmarshal(encKeyDb)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = encKeyOld.DeriveKey(&oldPw)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new encryption key from the new password.
|
||||||
|
encKeyNew, err := snacl.NewSecretKey(
|
||||||
|
&newPw, scryptN, scryptR, scryptP,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now try to decrypt the root key with the old encryption key,
|
||||||
|
// encrypt it with the new one and then store it in the DB.
|
||||||
|
decryptedKey, err := encKeyOld.Decrypt(rootKeyDb)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rootKey := make([]byte, len(decryptedKey))
|
||||||
|
copy(rootKey, decryptedKey)
|
||||||
|
encryptedKey, err := encKeyNew.Encrypt(rootKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = bucket.Put(DefaultRootKeyID, encryptedKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, store the new encryption key parameters in the DB
|
||||||
|
// as well.
|
||||||
|
err = bucket.Put(encryptionKeyID, encKeyNew.Marshal())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
r.encKey = encKeyNew
|
||||||
|
return nil
|
||||||
|
}, func() {})
|
||||||
|
}
|
||||||
|
|
||||||
// Get implements the Get method for the bakery.RootKeyStorage interface.
|
// Get implements the Get method for the bakery.RootKeyStorage interface.
|
||||||
func (r *RootKeyStorage) Get(_ context.Context, id []byte) ([]byte, error) {
|
func (r *RootKeyStorage) Get(_ context.Context, id []byte) ([]byte, error) {
|
||||||
r.encKeyMtx.RLock()
|
r.encKeyMtx.RLock()
|
||||||
@ -136,7 +226,11 @@ func (r *RootKeyStorage) Get(_ context.Context, id []byte) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
var rootKey []byte
|
var rootKey []byte
|
||||||
err := kvdb.View(r, func(tx kvdb.RTx) error {
|
err := kvdb.View(r, func(tx kvdb.RTx) error {
|
||||||
dbKey := tx.ReadBucket(rootKeyBucketName).Get(id)
|
bucket := tx.ReadBucket(rootKeyBucketName)
|
||||||
|
if bucket == nil {
|
||||||
|
return ErrRootKeyBucketNotFound
|
||||||
|
}
|
||||||
|
dbKey := bucket.Get(id)
|
||||||
if len(dbKey) == 0 {
|
if len(dbKey) == 0 {
|
||||||
return fmt.Errorf("root key with id %s doesn't exist",
|
return fmt.Errorf("root key with id %s doesn't exist",
|
||||||
string(id))
|
string(id))
|
||||||
@ -178,13 +272,16 @@ func (r *RootKeyStorage) RootKey(ctx context.Context) ([]byte, []byte, error) {
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if bytes.Equal(id, encryptedKeyID) {
|
if bytes.Equal(id, encryptionKeyID) {
|
||||||
return nil, nil, ErrKeyValueForbidden
|
return nil, nil, ErrKeyValueForbidden
|
||||||
}
|
}
|
||||||
|
|
||||||
err = kvdb.Update(r, func(tx kvdb.RwTx) error {
|
err = kvdb.Update(r, func(tx kvdb.RwTx) error {
|
||||||
ns := tx.ReadWriteBucket(rootKeyBucketName)
|
bucket := tx.ReadWriteBucket(rootKeyBucketName)
|
||||||
dbKey := ns.Get(id)
|
if bucket == nil {
|
||||||
|
return ErrRootKeyBucketNotFound
|
||||||
|
}
|
||||||
|
dbKey := bucket.Get(id)
|
||||||
|
|
||||||
// If there's a root key stored in the bucket, decrypt it and
|
// If there's a root key stored in the bucket, decrypt it and
|
||||||
// return it.
|
// return it.
|
||||||
@ -199,18 +296,11 @@ func (r *RootKeyStorage) RootKey(ctx context.Context) ([]byte, []byte, error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, create a RootKeyLen-byte root key, encrypt it,
|
// Otherwise, create a new root key, encrypt it,
|
||||||
// and store it in the bucket.
|
// and store it in the bucket.
|
||||||
rootKey = make([]byte, RootKeyLen)
|
newKey, err := generateAndStoreNewRootKey(bucket, id, r.encKey)
|
||||||
if _, err := io.ReadFull(rand.Reader, rootKey[:]); err != nil {
|
rootKey = newKey
|
||||||
return err
|
return err
|
||||||
}
|
|
||||||
|
|
||||||
encKey, err := r.encKey.Encrypt(rootKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return ns.Put(id, encKey)
|
|
||||||
}, func() {
|
}, func() {
|
||||||
rootKey = nil
|
rootKey = nil
|
||||||
})
|
})
|
||||||
@ -221,6 +311,26 @@ func (r *RootKeyStorage) RootKey(ctx context.Context) ([]byte, []byte, error) {
|
|||||||
return rootKey, id, nil
|
return rootKey, id, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GenerateNewRootKey generates a new macaroon root key, replacing the previous
|
||||||
|
// root key if it existed.
|
||||||
|
func (r *RootKeyStorage) GenerateNewRootKey() error {
|
||||||
|
// We need the store to already be unlocked. With this we can make sure
|
||||||
|
// that there already is a key in the DB that can be replaced.
|
||||||
|
if r.encKey == nil {
|
||||||
|
return ErrStoreLocked
|
||||||
|
}
|
||||||
|
return kvdb.Update(r, func(tx kvdb.RwTx) error {
|
||||||
|
bucket := tx.ReadWriteBucket(rootKeyBucketName)
|
||||||
|
if bucket == nil {
|
||||||
|
return ErrRootKeyBucketNotFound
|
||||||
|
}
|
||||||
|
_, err := generateAndStoreNewRootKey(
|
||||||
|
bucket, DefaultRootKeyID, r.encKey,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}, func() {})
|
||||||
|
}
|
||||||
|
|
||||||
// Close closes the underlying database and zeroes the encryption key stored
|
// Close closes the underlying database and zeroes the encryption key stored
|
||||||
// in memory.
|
// in memory.
|
||||||
func (r *RootKeyStorage) Close() error {
|
func (r *RootKeyStorage) Close() error {
|
||||||
@ -229,10 +339,29 @@ func (r *RootKeyStorage) Close() error {
|
|||||||
|
|
||||||
if r.encKey != nil {
|
if r.encKey != nil {
|
||||||
r.encKey.Zero()
|
r.encKey.Zero()
|
||||||
|
r.encKey = nil
|
||||||
}
|
}
|
||||||
return r.Backend.Close()
|
return r.Backend.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generateAndStoreNewRootKey creates a new random RootKeyLen-byte root key,
|
||||||
|
// encrypts it with the given encryption key and stores it in the bucket.
|
||||||
|
// Any previously set key will be overwritten.
|
||||||
|
func generateAndStoreNewRootKey(bucket walletdb.ReadWriteBucket, id []byte,
|
||||||
|
key *snacl.SecretKey) ([]byte, error) {
|
||||||
|
|
||||||
|
rootKey := make([]byte, RootKeyLen)
|
||||||
|
if _, err := io.ReadFull(rand.Reader, rootKey); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptedKey, err := key.Encrypt(rootKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return rootKey, bucket.Put(id, encryptedKey)
|
||||||
|
}
|
||||||
|
|
||||||
// ListMacaroonIDs returns all the root key ID values except the value of
|
// ListMacaroonIDs returns all the root key ID values except the value of
|
||||||
// encryptedKeyID.
|
// encryptedKeyID.
|
||||||
func (r *RootKeyStorage) ListMacaroonIDs(_ context.Context) ([][]byte, error) {
|
func (r *RootKeyStorage) ListMacaroonIDs(_ context.Context) ([][]byte, error) {
|
||||||
@ -254,7 +383,7 @@ func (r *RootKeyStorage) ListMacaroonIDs(_ context.Context) ([][]byte, error) {
|
|||||||
// to rootKeySlice.
|
// to rootKeySlice.
|
||||||
appendRootKey := func(k, _ []byte) error {
|
appendRootKey := func(k, _ []byte) error {
|
||||||
// Only append when the key value is not encryptedKeyID.
|
// Only append when the key value is not encryptedKeyID.
|
||||||
if !bytes.Equal(k, encryptedKeyID) {
|
if !bytes.Equal(k, encryptionKeyID) {
|
||||||
rootKeySlice = append(rootKeySlice, k)
|
rootKeySlice = append(rootKeySlice, k)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -290,7 +419,7 @@ func (r *RootKeyStorage) DeleteMacaroonID(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Deleting encryptedKeyID or DefaultRootKeyID is not allowed.
|
// Deleting encryptedKeyID or DefaultRootKeyID is not allowed.
|
||||||
if bytes.Equal(rootKeyID, encryptedKeyID) ||
|
if bytes.Equal(rootKeyID, encryptionKeyID) ||
|
||||||
bytes.Equal(rootKeyID, DefaultRootKeyID) {
|
bytes.Equal(rootKeyID, DefaultRootKeyID) {
|
||||||
|
|
||||||
return nil, ErrDeletionForbidden
|
return nil, ErrDeletionForbidden
|
||||||
|
@ -14,12 +14,31 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestStore(t *testing.T) {
|
var (
|
||||||
|
defaultRootKeyIDContext = macaroons.ContextWithRootKeyID(
|
||||||
|
context.Background(), macaroons.DefaultRootKeyID,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// newTestStore creates a new bolt DB in a temporary directory and then
|
||||||
|
// initializes a root key storage for that DB.
|
||||||
|
func newTestStore(t *testing.T) (string, func(), *macaroons.RootKeyStorage) {
|
||||||
tempDir, err := ioutil.TempDir("", "macaroonstore-")
|
tempDir, err := ioutil.TempDir("", "macaroonstore-")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer func() {
|
|
||||||
|
cleanup, store := openTestStore(t, tempDir)
|
||||||
|
cleanup2 := func() {
|
||||||
|
cleanup()
|
||||||
_ = os.RemoveAll(tempDir)
|
_ = os.RemoveAll(tempDir)
|
||||||
}()
|
}
|
||||||
|
|
||||||
|
return tempDir, cleanup2, store
|
||||||
|
}
|
||||||
|
|
||||||
|
// openTestStore opens an existing bolt DB and then initializes a root key
|
||||||
|
// storage for that DB.
|
||||||
|
func openTestStore(t *testing.T, tempDir string) (func(),
|
||||||
|
*macaroons.RootKeyStorage) {
|
||||||
|
|
||||||
db, err := kvdb.Create(
|
db, err := kvdb.Create(
|
||||||
kvdb.BoltBackendName, path.Join(tempDir, "weks.db"), true,
|
kvdb.BoltBackendName, path.Join(tempDir, "weks.db"), true,
|
||||||
@ -31,11 +50,21 @@ func TestStore(t *testing.T) {
|
|||||||
_ = db.Close()
|
_ = db.Close()
|
||||||
t.Fatalf("Error creating root key store: %v", err)
|
t.Fatalf("Error creating root key store: %v", err)
|
||||||
}
|
}
|
||||||
defer func() {
|
|
||||||
_ = store.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
_, _, err = store.RootKey(context.TODO())
|
cleanup := func() {
|
||||||
|
_ = store.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
return cleanup, store
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestStore tests the normal use cases of the store like creating, unlocking,
|
||||||
|
// reading keys and closing it.
|
||||||
|
func TestStore(t *testing.T) {
|
||||||
|
tempDir, cleanup, store := newTestStore(t)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
_, _, err := store.RootKey(context.TODO())
|
||||||
require.Equal(t, macaroons.ErrStoreLocked, err)
|
require.Equal(t, macaroons.ErrStoreLocked, err)
|
||||||
|
|
||||||
_, err = store.Get(context.TODO(), nil)
|
_, err = store.Get(context.TODO(), nil)
|
||||||
@ -63,16 +92,13 @@ func TestStore(t *testing.T) {
|
|||||||
require.Equal(t, macaroons.ErrKeyValueForbidden, err)
|
require.Equal(t, macaroons.ErrKeyValueForbidden, err)
|
||||||
|
|
||||||
// Create a context with root key ID value.
|
// Create a context with root key ID value.
|
||||||
ctx := macaroons.ContextWithRootKeyID(
|
key, id, err := store.RootKey(defaultRootKeyIDContext)
|
||||||
context.TODO(), macaroons.DefaultRootKeyID,
|
|
||||||
)
|
|
||||||
key, id, err := store.RootKey(ctx)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
rootID := id
|
rootID := id
|
||||||
require.Equal(t, macaroons.DefaultRootKeyID, rootID)
|
require.Equal(t, macaroons.DefaultRootKeyID, rootID)
|
||||||
|
|
||||||
key2, err := store.Get(ctx, id)
|
key2, err := store.Get(defaultRootKeyIDContext, id)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, key, key2)
|
require.Equal(t, key, key2)
|
||||||
|
|
||||||
@ -85,16 +111,7 @@ func TestStore(t *testing.T) {
|
|||||||
// Between here and the re-opening of the store, it's possible to get
|
// Between here and the re-opening of the store, it's possible to get
|
||||||
// a double-close, but that's not such a big deal since the tests will
|
// a double-close, but that's not such a big deal since the tests will
|
||||||
// fail anyway in that case.
|
// fail anyway in that case.
|
||||||
db, err = kvdb.Create(
|
_, store = openTestStore(t, tempDir)
|
||||||
kvdb.BoltBackendName, path.Join(tempDir, "weks.db"), true,
|
|
||||||
)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
store, err = macaroons.NewRootKeyStorage(db)
|
|
||||||
if err != nil {
|
|
||||||
_ = db.Close()
|
|
||||||
t.Fatalf("Error creating root key store: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = store.CreateUnlock(&badpw)
|
err = store.CreateUnlock(&badpw)
|
||||||
require.Equal(t, snacl.ErrInvalidPassword, err)
|
require.Equal(t, snacl.ErrInvalidPassword, err)
|
||||||
@ -102,21 +119,104 @@ func TestStore(t *testing.T) {
|
|||||||
err = store.CreateUnlock(nil)
|
err = store.CreateUnlock(nil)
|
||||||
require.Equal(t, macaroons.ErrPasswordRequired, err)
|
require.Equal(t, macaroons.ErrPasswordRequired, err)
|
||||||
|
|
||||||
_, _, err = store.RootKey(ctx)
|
_, _, err = store.RootKey(defaultRootKeyIDContext)
|
||||||
require.Equal(t, macaroons.ErrStoreLocked, err)
|
require.Equal(t, macaroons.ErrStoreLocked, err)
|
||||||
|
|
||||||
_, err = store.Get(ctx, nil)
|
_, err = store.Get(defaultRootKeyIDContext, nil)
|
||||||
require.Equal(t, macaroons.ErrStoreLocked, err)
|
require.Equal(t, macaroons.ErrStoreLocked, err)
|
||||||
|
|
||||||
err = store.CreateUnlock(&pw)
|
err = store.CreateUnlock(&pw)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
key, err = store.Get(ctx, rootID)
|
key, err = store.Get(defaultRootKeyIDContext, rootID)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, key, key2)
|
require.Equal(t, key, key2)
|
||||||
|
|
||||||
key, id, err = store.RootKey(ctx)
|
key, id, err = store.RootKey(defaultRootKeyIDContext)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, key, key2)
|
require.Equal(t, key, key2)
|
||||||
require.Equal(t, rootID, id)
|
require.Equal(t, rootID, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestStoreGenerateNewRootKey tests that a root key can be replaced with a new
|
||||||
|
// one in the store without changing the password.
|
||||||
|
func TestStoreGenerateNewRootKey(t *testing.T) {
|
||||||
|
_, cleanup, store := newTestStore(t)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
// The store must be unlocked to replace the root key.
|
||||||
|
err := store.GenerateNewRootKey()
|
||||||
|
require.Equal(t, macaroons.ErrStoreLocked, err)
|
||||||
|
|
||||||
|
// Unlock the store and read the current key.
|
||||||
|
pw := []byte("weks")
|
||||||
|
err = store.CreateUnlock(&pw)
|
||||||
|
require.NoError(t, err)
|
||||||
|
oldRootKey, _, err := store.RootKey(defaultRootKeyIDContext)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Replace the root key with a new random key.
|
||||||
|
err = store.GenerateNewRootKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Finally, read the root key from the DB and compare it to the one
|
||||||
|
// we got returned earlier. This makes sure that the encryption/
|
||||||
|
// decryption of the key in the DB worked as expected too.
|
||||||
|
newRootKey, _, err := store.RootKey(defaultRootKeyIDContext)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEqual(t, oldRootKey, newRootKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestStoreChangePassword tests that the password for the store can be changed
|
||||||
|
// without changing the root key.
|
||||||
|
func TestStoreChangePassword(t *testing.T) {
|
||||||
|
tempDir, cleanup, store := newTestStore(t)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
// The store must be unlocked to replace the root key.
|
||||||
|
err := store.ChangePassword(nil, nil)
|
||||||
|
require.Equal(t, macaroons.ErrStoreLocked, err)
|
||||||
|
|
||||||
|
// Unlock the DB and read the current root key. This will need to stay
|
||||||
|
// the same after changing the password for the test to succeed.
|
||||||
|
pw := []byte("weks")
|
||||||
|
err = store.CreateUnlock(&pw)
|
||||||
|
require.NoError(t, err)
|
||||||
|
rootKey, _, err := store.RootKey(defaultRootKeyIDContext)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Both passwords must be set.
|
||||||
|
err = store.ChangePassword(nil, nil)
|
||||||
|
require.Equal(t, macaroons.ErrPasswordRequired, err)
|
||||||
|
|
||||||
|
// Make sure that an error is returned if we try to change the password
|
||||||
|
// without the correct old password.
|
||||||
|
wrongPw := []byte("wrong")
|
||||||
|
newPw := []byte("newpassword")
|
||||||
|
err = store.ChangePassword(wrongPw, newPw)
|
||||||
|
require.Equal(t, snacl.ErrInvalidPassword, err)
|
||||||
|
|
||||||
|
// Now really do change the password.
|
||||||
|
err = store.ChangePassword(pw, newPw)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Close the store. This will close the underlying DB and we need to
|
||||||
|
// create a new store instance. Let's make sure we can't use it again
|
||||||
|
// after closing.
|
||||||
|
err = store.Close()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = store.CreateUnlock(&newPw)
|
||||||
|
require.Error(t, err)
|
||||||
|
|
||||||
|
// Let's open it again and try unlocking with the new password.
|
||||||
|
_, store = openTestStore(t, tempDir)
|
||||||
|
err = store.CreateUnlock(&newPw)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Finally read the root key from the DB using the new password and
|
||||||
|
// make sure the root key stayed the same.
|
||||||
|
rootKeyDb, _, err := store.RootKey(defaultRootKeyIDContext)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, rootKey, rootKeyDb)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user