mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-03-12 18:52:14 +01:00
Merge branch 'v0-16-3-branch-7705' into v0-16-3-branch
This commit is contained in:
commit
fcbe6844c0
3 changed files with 116 additions and 38 deletions
|
@ -6,5 +6,12 @@ Optimized [mempool
|
||||||
management](https://github.com/lightningnetwork/lnd/pull/7681) to lower the CPU
|
management](https://github.com/lightningnetwork/lnd/pull/7681) to lower the CPU
|
||||||
usage.
|
usage.
|
||||||
|
|
||||||
|
## Bug Fixes
|
||||||
|
|
||||||
|
* [Re-encrypt/regenerate](https://github.com/lightningnetwork/lnd/pull/7705)
|
||||||
|
all macaroon DB root keys on `ChangePassword`/`GenerateNewRootKey`
|
||||||
|
respectively.
|
||||||
|
|
||||||
# Contributors (Alphabetical Order)
|
# Contributors (Alphabetical Order)
|
||||||
|
* Elle Mouton
|
||||||
* Yong Yu
|
* Yong Yu
|
||||||
|
|
|
@ -54,6 +54,10 @@ var (
|
||||||
// ErrEncKeyNotFound specifies that there was no encryption key found
|
// ErrEncKeyNotFound specifies that there was no encryption key found
|
||||||
// even if one was expected to be generated.
|
// even if one was expected to be generated.
|
||||||
ErrEncKeyNotFound = fmt.Errorf("macaroon encryption key not found")
|
ErrEncKeyNotFound = fmt.Errorf("macaroon encryption key not found")
|
||||||
|
|
||||||
|
// ErrDefaultRootKeyNotFound is returned when the default root key is
|
||||||
|
// not found in the DB when it is expected to be.
|
||||||
|
ErrDefaultRootKeyNotFound = fmt.Errorf("default root key not found")
|
||||||
)
|
)
|
||||||
|
|
||||||
// RootKeyStorage implements the bakery.RootKeyStorage interface.
|
// RootKeyStorage implements the bakery.RootKeyStorage interface.
|
||||||
|
@ -140,8 +144,8 @@ func (r *RootKeyStorage) CreateUnlock(password *[]byte) error {
|
||||||
}, func() {})
|
}, func() {})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChangePassword decrypts the macaroon root key with the old password and then
|
// ChangePassword decrypts all the macaroon root keys with the old password and
|
||||||
// encrypts it again with the new password.
|
// then encrypts them again with the new password.
|
||||||
func (r *RootKeyStorage) ChangePassword(oldPw, newPw []byte) error {
|
func (r *RootKeyStorage) ChangePassword(oldPw, newPw []byte) error {
|
||||||
// We need the store to already be unlocked. With this we can make sure
|
// We need the store to already be unlocked. With this we can make sure
|
||||||
// that there already is a key in the DB.
|
// that there already is a key in the DB.
|
||||||
|
@ -159,19 +163,18 @@ func (r *RootKeyStorage) ChangePassword(oldPw, newPw []byte) error {
|
||||||
if bucket == nil {
|
if bucket == nil {
|
||||||
return ErrRootKeyBucketNotFound
|
return ErrRootKeyBucketNotFound
|
||||||
}
|
}
|
||||||
encKeyDb := bucket.Get(encryptionKeyID)
|
|
||||||
rootKeyDb := bucket.Get(DefaultRootKeyID)
|
|
||||||
|
|
||||||
// Both the encryption key and the root key must be present
|
// The encryption key must be present, otherwise we are in the
|
||||||
// otherwise we are in the wrong state to change the password.
|
// wrong state to change the password.
|
||||||
if len(encKeyDb) == 0 || len(rootKeyDb) == 0 {
|
encKeyDB := bucket.Get(encryptionKeyID)
|
||||||
|
if len(encKeyDB) == 0 {
|
||||||
return ErrEncKeyNotFound
|
return ErrEncKeyNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unmarshal parameters for old encryption key and derive the
|
// Unmarshal parameters for old encryption key and derive the
|
||||||
// old key with them.
|
// old key with them.
|
||||||
encKeyOld := &snacl.SecretKey{}
|
encKeyOld := &snacl.SecretKey{}
|
||||||
err := encKeyOld.Unmarshal(encKeyDb)
|
err := encKeyOld.Unmarshal(encKeyDB)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -188,21 +191,42 @@ func (r *RootKeyStorage) ChangePassword(oldPw, newPw []byte) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now try to decrypt the root key with the old encryption key,
|
// foundDefaultRootKey is used to keep track of if we have
|
||||||
// encrypt it with the new one and then store it in the DB.
|
// found and re-encrypted the default root key so that we can
|
||||||
decryptedKey, err := encKeyOld.Decrypt(rootKeyDb)
|
// return an error if it is not found.
|
||||||
|
var foundDefaultRootKey bool
|
||||||
|
err = bucket.ForEach(func(k, v []byte) error {
|
||||||
|
// Skip the key if it is the encryption key ID since
|
||||||
|
// we do not want to re-encrypt this.
|
||||||
|
if bytes.Equal(k, encryptionKeyID) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytes.Equal(k, DefaultRootKeyID) {
|
||||||
|
foundDefaultRootKey = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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(v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptedKey, err := encKeyNew.Encrypt(decryptedKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return bucket.Put(k, encryptedKey)
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
rootKey := make([]byte, len(decryptedKey))
|
|
||||||
copy(rootKey, decryptedKey)
|
if !foundDefaultRootKey {
|
||||||
encryptedKey, err := encKeyNew.Encrypt(rootKey)
|
return ErrDefaultRootKeyNotFound
|
||||||
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
|
// Finally, store the new encryption key parameters in the DB
|
||||||
|
@ -325,10 +349,34 @@ func (r *RootKeyStorage) GenerateNewRootKey() error {
|
||||||
if bucket == nil {
|
if bucket == nil {
|
||||||
return ErrRootKeyBucketNotFound
|
return ErrRootKeyBucketNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The default root key should be created even if it does not
|
||||||
|
// yet exist, so we do this separately from the rest of the
|
||||||
|
// root keys.
|
||||||
_, err := generateAndStoreNewRootKey(
|
_, err := generateAndStoreNewRootKey(
|
||||||
bucket, DefaultRootKeyID, r.encKey,
|
bucket, DefaultRootKeyID, r.encKey,
|
||||||
)
|
)
|
||||||
return err
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now iterate over all the other root keys that may exist
|
||||||
|
// and re-generate each of them.
|
||||||
|
return bucket.ForEach(func(k, v []byte) error {
|
||||||
|
if bytes.Equal(k, encryptionKeyID) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytes.Equal(k, DefaultRootKeyID) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := generateAndStoreNewRootKey(
|
||||||
|
bucket, k, r.encKey,
|
||||||
|
)
|
||||||
|
|
||||||
|
return err
|
||||||
|
})
|
||||||
}, func() {})
|
}, func() {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,10 @@ var (
|
||||||
defaultRootKeyIDContext = macaroons.ContextWithRootKeyID(
|
defaultRootKeyIDContext = macaroons.ContextWithRootKeyID(
|
||||||
context.Background(), macaroons.DefaultRootKeyID,
|
context.Background(), macaroons.DefaultRootKeyID,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
nonDefaultRootKeyIDContext = macaroons.ContextWithRootKeyID(
|
||||||
|
context.Background(), []byte{1},
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
// newTestStore creates a new bolt DB in a temporary directory and then
|
// newTestStore creates a new bolt DB in a temporary directory and then
|
||||||
|
@ -131,8 +135,8 @@ func TestStore(t *testing.T) {
|
||||||
require.Equal(t, rootID, id)
|
require.Equal(t, rootID, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestStoreGenerateNewRootKey tests that a root key can be replaced with a new
|
// TestStoreGenerateNewRootKey tests that root keys can be replaced with new
|
||||||
// one in the store without changing the password.
|
// ones in the store without changing the password.
|
||||||
func TestStoreGenerateNewRootKey(t *testing.T) {
|
func TestStoreGenerateNewRootKey(t *testing.T) {
|
||||||
_, store := newTestStore(t)
|
_, store := newTestStore(t)
|
||||||
|
|
||||||
|
@ -140,23 +144,33 @@ func TestStoreGenerateNewRootKey(t *testing.T) {
|
||||||
err := store.GenerateNewRootKey()
|
err := store.GenerateNewRootKey()
|
||||||
require.Equal(t, macaroons.ErrStoreLocked, err)
|
require.Equal(t, macaroons.ErrStoreLocked, err)
|
||||||
|
|
||||||
// Unlock the store and read the current key.
|
// Unlock the store.
|
||||||
pw := []byte("weks")
|
pw := []byte("weks")
|
||||||
err = store.CreateUnlock(&pw)
|
err = store.CreateUnlock(&pw)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
oldRootKey, _, err := store.RootKey(defaultRootKeyIDContext)
|
|
||||||
|
// Read the default root key.
|
||||||
|
oldRootKey1, _, err := store.RootKey(defaultRootKeyIDContext)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Replace the root key with a new random key.
|
// Read the non-default root-key.
|
||||||
|
oldRootKey2, _, err := store.RootKey(nonDefaultRootKeyIDContext)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Replace the root keys with new random keys.
|
||||||
err = store.GenerateNewRootKey()
|
err = store.GenerateNewRootKey()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Finally, read the root key from the DB and compare it to the one
|
// Finally, read both root keys from the DB and compare them to the ones
|
||||||
// we got returned earlier. This makes sure that the encryption/
|
// we got returned earlier. This makes sure that the encryption/
|
||||||
// decryption of the key in the DB worked as expected too.
|
// decryption of the key in the DB worked as expected too.
|
||||||
newRootKey, _, err := store.RootKey(defaultRootKeyIDContext)
|
newRootKey1, _, err := store.RootKey(defaultRootKeyIDContext)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotEqual(t, oldRootKey, newRootKey)
|
require.NotEqual(t, oldRootKey1, newRootKey1)
|
||||||
|
|
||||||
|
newRootKey2, _, err := store.RootKey(nonDefaultRootKeyIDContext)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEqual(t, oldRootKey2, newRootKey2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestStoreSetRootKey tests that a root key can be set to a specified value.
|
// TestStoreSetRootKey tests that a root key can be set to a specified value.
|
||||||
|
@ -195,20 +209,25 @@ func TestStoreSetRootKey(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestStoreChangePassword tests that the password for the store can be changed
|
// TestStoreChangePassword tests that the password for the store can be changed
|
||||||
// without changing the root key.
|
// without changing the root keys.
|
||||||
func TestStoreChangePassword(t *testing.T) {
|
func TestStoreChangePassword(t *testing.T) {
|
||||||
tempDir, store := newTestStore(t)
|
tempDir, store := newTestStore(t)
|
||||||
|
|
||||||
// The store must be unlocked to replace the root key.
|
// The store must be unlocked to replace the root keys.
|
||||||
err := store.ChangePassword(nil, nil)
|
err := store.ChangePassword(nil, nil)
|
||||||
require.Equal(t, macaroons.ErrStoreLocked, err)
|
require.Equal(t, macaroons.ErrStoreLocked, err)
|
||||||
|
|
||||||
// Unlock the DB and read the current root key. This will need to stay
|
// Unlock the DB and read the current default root key and one other
|
||||||
// the same after changing the password for the test to succeed.
|
// non-default root key. Both of these should stay the same after
|
||||||
|
// changing the password for the test to succeed.
|
||||||
pw := []byte("weks")
|
pw := []byte("weks")
|
||||||
err = store.CreateUnlock(&pw)
|
err = store.CreateUnlock(&pw)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
rootKey, _, err := store.RootKey(defaultRootKeyIDContext)
|
|
||||||
|
rootKey1, _, err := store.RootKey(defaultRootKeyIDContext)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
rootKey2, _, err := store.RootKey(nonDefaultRootKeyIDContext)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Both passwords must be set.
|
// Both passwords must be set.
|
||||||
|
@ -242,9 +261,13 @@ func TestStoreChangePassword(t *testing.T) {
|
||||||
err = store.CreateUnlock(&newPw)
|
err = store.CreateUnlock(&newPw)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Finally read the root key from the DB using the new password and
|
// Finally, read the root keys from the DB using the new password and
|
||||||
// make sure the root key stayed the same.
|
// make sure that both root keys stayed the same.
|
||||||
rootKeyDb, _, err := store.RootKey(defaultRootKeyIDContext)
|
rootKeyDB1, _, err := store.RootKey(defaultRootKeyIDContext)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, rootKey, rootKeyDb)
|
require.Equal(t, rootKey1, rootKeyDB1)
|
||||||
|
|
||||||
|
rootKeyDB2, _, err := store.RootKey(nonDefaultRootKeyIDContext)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, rootKey2, rootKeyDB2)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue