lnd.xprv/macaroons/store_test.go

223 lines
6.5 KiB
Go
Raw Normal View History

2018-02-01 03:04:56 +03:00
package macaroons_test
import (
"context"
2018-02-01 03:04:56 +03:00
"io/ioutil"
"os"
"path"
"testing"
"github.com/lightningnetwork/lnd/channeldb/kvdb"
2018-02-01 03:04:56 +03:00
"github.com/lightningnetwork/lnd/macaroons"
"github.com/btcsuite/btcwallet/snacl"
2020-10-06 18:23:29 +03:00
"github.com/stretchr/testify/require"
2018-02-01 03:04:56 +03:00
)
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) {
2018-02-01 03:04:56 +03:00
tempDir, err := ioutil.TempDir("", "macaroonstore-")
2020-10-06 18:23:29 +03:00
require.NoError(t, err)
cleanup, store := openTestStore(t, tempDir)
cleanup2 := func() {
cleanup()
2020-10-06 18:23:29 +03:00
_ = 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) {
2018-02-01 03:04:56 +03:00
db, err := kvdb.Create(
kvdb.BoltBackendName, path.Join(tempDir, "weks.db"), true,
)
2020-10-06 18:23:29 +03:00
require.NoError(t, err)
2018-02-01 03:04:56 +03:00
store, err := macaroons.NewRootKeyStorage(db)
if err != nil {
2020-10-06 18:23:29 +03:00
_ = db.Close()
2018-02-01 03:04:56 +03:00
t.Fatalf("Error creating root key store: %v", err)
}
cleanup := func() {
2020-10-06 18:23:29 +03:00
_ = store.Close()
}
2018-02-01 03:04:56 +03:00
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())
2020-10-06 18:23:29 +03:00
require.Equal(t, macaroons.ErrStoreLocked, err)
2018-02-01 03:04:56 +03:00
_, err = store.Get(context.TODO(), nil)
2020-10-06 18:23:29 +03:00
require.Equal(t, macaroons.ErrStoreLocked, err)
2018-02-01 03:04:56 +03:00
pw := []byte("weks")
err = store.CreateUnlock(&pw)
2020-10-06 18:23:29 +03:00
require.NoError(t, err)
2018-02-01 03:04:56 +03:00
// Check ErrContextRootKeyID is returned when no root key ID found in
// context.
_, _, err = store.RootKey(context.TODO())
2020-10-06 18:23:29 +03:00
require.Equal(t, macaroons.ErrContextRootKeyID, err)
// Check ErrMissingRootKeyID is returned when empty root key ID is used.
2020-10-06 18:23:29 +03:00
emptyKeyID := make([]byte, 0)
badCtx := macaroons.ContextWithRootKeyID(context.TODO(), emptyKeyID)
_, _, err = store.RootKey(badCtx)
2020-10-06 18:23:29 +03:00
require.Equal(t, macaroons.ErrMissingRootKeyID, err)
// Create a context with illegal root key ID value.
encryptedKeyID := []byte("enckey")
badCtx = macaroons.ContextWithRootKeyID(context.TODO(), encryptedKeyID)
_, _, err = store.RootKey(badCtx)
2020-10-06 18:23:29 +03:00
require.Equal(t, macaroons.ErrKeyValueForbidden, err)
// Create a context with root key ID value.
key, id, err := store.RootKey(defaultRootKeyIDContext)
2020-10-06 18:23:29 +03:00
require.NoError(t, err)
2018-02-01 03:04:56 +03:00
rootID := id
2020-10-06 18:23:29 +03:00
require.Equal(t, macaroons.DefaultRootKeyID, rootID)
2018-02-01 03:04:56 +03:00
key2, err := store.Get(defaultRootKeyIDContext, id)
2020-10-06 18:23:29 +03:00
require.NoError(t, err)
require.Equal(t, key, key2)
2018-02-01 03:04:56 +03:00
badpw := []byte("badweks")
err = store.CreateUnlock(&badpw)
2020-10-06 18:23:29 +03:00
require.Equal(t, macaroons.ErrAlreadyUnlocked, err)
2018-02-01 03:04:56 +03:00
2020-10-06 18:23:29 +03:00
_ = store.Close()
2018-02-01 03:04:56 +03:00
// 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
// fail anyway in that case.
_, store = openTestStore(t, tempDir)
2018-02-01 03:04:56 +03:00
err = store.CreateUnlock(&badpw)
2020-10-06 18:23:29 +03:00
require.Equal(t, snacl.ErrInvalidPassword, err)
2018-02-01 03:04:56 +03:00
err = store.CreateUnlock(nil)
2020-10-06 18:23:29 +03:00
require.Equal(t, macaroons.ErrPasswordRequired, err)
2018-02-01 03:04:56 +03:00
_, _, err = store.RootKey(defaultRootKeyIDContext)
2020-10-06 18:23:29 +03:00
require.Equal(t, macaroons.ErrStoreLocked, err)
2018-02-01 03:04:56 +03:00
_, err = store.Get(defaultRootKeyIDContext, nil)
2020-10-06 18:23:29 +03:00
require.Equal(t, macaroons.ErrStoreLocked, err)
2018-02-01 03:04:56 +03:00
err = store.CreateUnlock(&pw)
2020-10-06 18:23:29 +03:00
require.NoError(t, err)
2018-02-01 03:04:56 +03:00
key, err = store.Get(defaultRootKeyIDContext, rootID)
2020-10-06 18:23:29 +03:00
require.NoError(t, err)
require.Equal(t, key, key2)
2018-02-01 03:04:56 +03:00
key, id, err = store.RootKey(defaultRootKeyIDContext)
2020-10-06 18:23:29 +03:00
require.NoError(t, err)
require.Equal(t, key, key2)
require.Equal(t, rootID, id)
2018-02-01 03:04:56 +03:00
}
// 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)
}