github.com/decred/dcrlnd@v0.7.6/macaroons/store_test.go (about)

     1  package macaroons_test
     2  
     3  import (
     4  	"context"
     5  	"io/ioutil"
     6  	"os"
     7  	"path"
     8  	"testing"
     9  
    10  	"github.com/decred/dcrlnd/internal/snacl"
    11  	"github.com/decred/dcrlnd/kvdb"
    12  	"github.com/decred/dcrlnd/macaroons"
    13  
    14  	"github.com/stretchr/testify/require"
    15  )
    16  
    17  var (
    18  	defaultRootKeyIDContext = macaroons.ContextWithRootKeyID(
    19  		context.Background(), macaroons.DefaultRootKeyID,
    20  	)
    21  )
    22  
    23  // newTestStore creates a new bolt DB in a temporary directory and then
    24  // initializes a root key storage for that DB.
    25  func newTestStore(t *testing.T) (string, func(), *macaroons.RootKeyStorage) {
    26  	tempDir, err := ioutil.TempDir("", "macaroonstore-")
    27  	require.NoError(t, err)
    28  
    29  	cleanup, store := openTestStore(t, tempDir)
    30  	cleanup2 := func() {
    31  		cleanup()
    32  		_ = os.RemoveAll(tempDir)
    33  	}
    34  
    35  	return tempDir, cleanup2, store
    36  }
    37  
    38  // openTestStore opens an existing bolt DB and then initializes a root key
    39  // storage for that DB.
    40  func openTestStore(t *testing.T, tempDir string) (func(),
    41  	*macaroons.RootKeyStorage) {
    42  
    43  	db, err := kvdb.Create(
    44  		kvdb.BoltBackendName, path.Join(tempDir, "weks.db"), true,
    45  		kvdb.DefaultDBTimeout,
    46  	)
    47  	require.NoError(t, err)
    48  
    49  	store, err := macaroons.NewRootKeyStorage(db)
    50  	if err != nil {
    51  		_ = db.Close()
    52  		t.Fatalf("Error creating root key store: %v", err)
    53  	}
    54  
    55  	cleanup := func() {
    56  		_ = store.Close()
    57  		_ = db.Close()
    58  	}
    59  
    60  	return cleanup, store
    61  }
    62  
    63  // TestStore tests the normal use cases of the store like creating, unlocking,
    64  // reading keys and closing it.
    65  func TestStore(t *testing.T) {
    66  	tempDir, cleanup, store := newTestStore(t)
    67  	defer cleanup()
    68  
    69  	_, _, err := store.RootKey(context.TODO())
    70  	require.Equal(t, macaroons.ErrStoreLocked, err)
    71  
    72  	_, err = store.Get(context.TODO(), nil)
    73  	require.Equal(t, macaroons.ErrStoreLocked, err)
    74  
    75  	pw := []byte("weks")
    76  	err = store.CreateUnlock(&pw)
    77  	require.NoError(t, err)
    78  
    79  	// Check ErrContextRootKeyID is returned when no root key ID found in
    80  	// context.
    81  	_, _, err = store.RootKey(context.TODO())
    82  	require.Equal(t, macaroons.ErrContextRootKeyID, err)
    83  
    84  	// Check ErrMissingRootKeyID is returned when empty root key ID is used.
    85  	emptyKeyID := make([]byte, 0)
    86  	badCtx := macaroons.ContextWithRootKeyID(context.TODO(), emptyKeyID)
    87  	_, _, err = store.RootKey(badCtx)
    88  	require.Equal(t, macaroons.ErrMissingRootKeyID, err)
    89  
    90  	// Create a context with illegal root key ID value.
    91  	encryptedKeyID := []byte("enckey")
    92  	badCtx = macaroons.ContextWithRootKeyID(context.TODO(), encryptedKeyID)
    93  	_, _, err = store.RootKey(badCtx)
    94  	require.Equal(t, macaroons.ErrKeyValueForbidden, err)
    95  
    96  	// Create a context with root key ID value.
    97  	key, id, err := store.RootKey(defaultRootKeyIDContext)
    98  	require.NoError(t, err)
    99  
   100  	rootID := id
   101  	require.Equal(t, macaroons.DefaultRootKeyID, rootID)
   102  
   103  	key2, err := store.Get(defaultRootKeyIDContext, id)
   104  	require.NoError(t, err)
   105  	require.Equal(t, key, key2)
   106  
   107  	badpw := []byte("badweks")
   108  	err = store.CreateUnlock(&badpw)
   109  	require.Equal(t, macaroons.ErrAlreadyUnlocked, err)
   110  
   111  	_ = store.Close()
   112  	_ = store.Backend.Close()
   113  
   114  	// Between here and the re-opening of the store, it's possible to get
   115  	// a double-close, but that's not such a big deal since the tests will
   116  	// fail anyway in that case.
   117  	_, store = openTestStore(t, tempDir)
   118  
   119  	err = store.CreateUnlock(&badpw)
   120  	require.Equal(t, snacl.ErrInvalidPassword, err)
   121  
   122  	err = store.CreateUnlock(nil)
   123  	require.Equal(t, macaroons.ErrPasswordRequired, err)
   124  
   125  	_, _, err = store.RootKey(defaultRootKeyIDContext)
   126  	require.Equal(t, macaroons.ErrStoreLocked, err)
   127  
   128  	_, err = store.Get(defaultRootKeyIDContext, nil)
   129  	require.Equal(t, macaroons.ErrStoreLocked, err)
   130  
   131  	err = store.CreateUnlock(&pw)
   132  	require.NoError(t, err)
   133  
   134  	key, err = store.Get(defaultRootKeyIDContext, rootID)
   135  	require.NoError(t, err)
   136  	require.Equal(t, key, key2)
   137  
   138  	key, id, err = store.RootKey(defaultRootKeyIDContext)
   139  	require.NoError(t, err)
   140  	require.Equal(t, key, key2)
   141  	require.Equal(t, rootID, id)
   142  }
   143  
   144  // TestStoreGenerateNewRootKey tests that a root key can be replaced with a new
   145  // one in the store without changing the password.
   146  func TestStoreGenerateNewRootKey(t *testing.T) {
   147  	_, cleanup, store := newTestStore(t)
   148  	defer cleanup()
   149  
   150  	// The store must be unlocked to replace the root key.
   151  	err := store.GenerateNewRootKey()
   152  	require.Equal(t, macaroons.ErrStoreLocked, err)
   153  
   154  	// Unlock the store and read the current key.
   155  	pw := []byte("weks")
   156  	err = store.CreateUnlock(&pw)
   157  	require.NoError(t, err)
   158  	oldRootKey, _, err := store.RootKey(defaultRootKeyIDContext)
   159  	require.NoError(t, err)
   160  
   161  	// Replace the root key with a new random key.
   162  	err = store.GenerateNewRootKey()
   163  	require.NoError(t, err)
   164  
   165  	// Finally, read the root key from the DB and compare it to the one
   166  	// we got returned earlier. This makes sure that the encryption/
   167  	// decryption of the key in the DB worked as expected too.
   168  	newRootKey, _, err := store.RootKey(defaultRootKeyIDContext)
   169  	require.NoError(t, err)
   170  	require.NotEqual(t, oldRootKey, newRootKey)
   171  }
   172  
   173  // TestStoreChangePassword tests that the password for the store can be changed
   174  // without changing the root key.
   175  func TestStoreChangePassword(t *testing.T) {
   176  	tempDir, cleanup, store := newTestStore(t)
   177  	defer cleanup()
   178  
   179  	// The store must be unlocked to replace the root key.
   180  	err := store.ChangePassword(nil, nil)
   181  	require.Equal(t, macaroons.ErrStoreLocked, err)
   182  
   183  	// Unlock the DB and read the current root key. This will need to stay
   184  	// the same after changing the password for the test to succeed.
   185  	pw := []byte("weks")
   186  	err = store.CreateUnlock(&pw)
   187  	require.NoError(t, err)
   188  	rootKey, _, err := store.RootKey(defaultRootKeyIDContext)
   189  	require.NoError(t, err)
   190  
   191  	// Both passwords must be set.
   192  	err = store.ChangePassword(nil, nil)
   193  	require.Equal(t, macaroons.ErrPasswordRequired, err)
   194  
   195  	// Make sure that an error is returned if we try to change the password
   196  	// without the correct old password.
   197  	wrongPw := []byte("wrong")
   198  	newPw := []byte("newpassword")
   199  	err = store.ChangePassword(wrongPw, newPw)
   200  	require.Equal(t, snacl.ErrInvalidPassword, err)
   201  
   202  	// Now really do change the password.
   203  	err = store.ChangePassword(pw, newPw)
   204  	require.NoError(t, err)
   205  
   206  	// Close the store. This will close the underlying DB and we need to
   207  	// create a new store instance. Let's make sure we can't use it again
   208  	// after closing.
   209  	err = store.Close()
   210  	require.NoError(t, err)
   211  	err = store.Backend.Close()
   212  	require.NoError(t, err)
   213  
   214  	err = store.CreateUnlock(&newPw)
   215  	require.Error(t, err)
   216  
   217  	// Let's open it again and try unlocking with the new password.
   218  	_, store = openTestStore(t, tempDir)
   219  	err = store.CreateUnlock(&newPw)
   220  	require.NoError(t, err)
   221  
   222  	// Finally read the root key from the DB using the new password and
   223  	// make sure the root key stayed the same.
   224  	rootKeyDb, _, err := store.RootKey(defaultRootKeyIDContext)
   225  	require.NoError(t, err)
   226  	require.Equal(t, rootKey, rootKeyDb)
   227  }