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

     1  package macaroons
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"crypto/rand"
     7  	"fmt"
     8  	"io"
     9  	"sync"
    10  
    11  	"github.com/btcsuite/btcwallet/walletdb"
    12  	"github.com/decred/dcrlnd/kvdb"
    13  
    14  	"github.com/decred/dcrlnd/internal/snacl"
    15  )
    16  
    17  const (
    18  	// RootKeyLen is the length of a root key.
    19  	RootKeyLen = 32
    20  )
    21  
    22  var (
    23  	// rootKeyBucketName is the name of the root key store bucket.
    24  	rootKeyBucketName = []byte("macrootkeys")
    25  
    26  	// DefaultRootKeyID is the ID of the default root key. The first is
    27  	// just 0, to emulate the memory storage that comes with bakery.
    28  	DefaultRootKeyID = []byte("0")
    29  
    30  	// encryptionKeyID is the name of the database key that stores the
    31  	// encryption key, encrypted with a salted + hashed password. The
    32  	// format is 32 bytes of salt, and the rest is encrypted key.
    33  	encryptionKeyID = []byte("enckey")
    34  
    35  	// ErrAlreadyUnlocked specifies that the store has already been
    36  	// unlocked.
    37  	ErrAlreadyUnlocked = fmt.Errorf("macaroon store already unlocked")
    38  
    39  	// ErrStoreLocked specifies that the store needs to be unlocked with
    40  	// a password.
    41  	ErrStoreLocked = fmt.Errorf("macaroon store is locked")
    42  
    43  	// ErrPasswordRequired specifies that a nil password has been passed.
    44  	ErrPasswordRequired = fmt.Errorf("a non-nil password is required")
    45  
    46  	// ErrKeyValueForbidden is used when the root key ID uses encryptedKeyID as
    47  	// its value.
    48  	ErrKeyValueForbidden = fmt.Errorf("root key ID value is not allowed")
    49  
    50  	// ErrRootKeyBucketNotFound specifies that there is no macaroon root key
    51  	// bucket yet which can/should only happen if the store has been
    52  	// corrupted or was initialized incorrectly.
    53  	ErrRootKeyBucketNotFound = fmt.Errorf("root key bucket not found")
    54  
    55  	// ErrEncKeyNotFound specifies that there was no encryption key found
    56  	// even if one was expected to be generated.
    57  	ErrEncKeyNotFound = fmt.Errorf("macaroon encryption key not found")
    58  )
    59  
    60  // RootKeyStorage implements the bakery.RootKeyStorage interface.
    61  type RootKeyStorage struct {
    62  	kvdb.Backend
    63  
    64  	encKeyMtx sync.RWMutex
    65  	encKey    *snacl.SecretKey
    66  }
    67  
    68  // NewRootKeyStorage creates a RootKeyStorage instance.
    69  func NewRootKeyStorage(db kvdb.Backend) (*RootKeyStorage, error) {
    70  	// If the store's bucket doesn't exist, create it.
    71  	err := kvdb.Update(db, func(tx kvdb.RwTx) error {
    72  		_, err := tx.CreateTopLevelBucket(rootKeyBucketName)
    73  		return err
    74  	}, func() {})
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  
    79  	// Return the DB wrapped in a RootKeyStorage object.
    80  	return &RootKeyStorage{
    81  		Backend: db,
    82  		encKey:  nil,
    83  	}, nil
    84  }
    85  
    86  // CreateUnlock sets an encryption key if one is not already set, otherwise it
    87  // checks if the password is correct for the stored encryption key.
    88  func (r *RootKeyStorage) CreateUnlock(password *[]byte) error {
    89  	r.encKeyMtx.Lock()
    90  	defer r.encKeyMtx.Unlock()
    91  
    92  	// Check if we've already unlocked the store; return an error if so.
    93  	if r.encKey != nil {
    94  		return ErrAlreadyUnlocked
    95  	}
    96  
    97  	// Check if a nil password has been passed; return an error if so.
    98  	if password == nil {
    99  		return ErrPasswordRequired
   100  	}
   101  
   102  	return kvdb.Update(r.Backend, func(tx kvdb.RwTx) error {
   103  		bucket := tx.ReadWriteBucket(rootKeyBucketName)
   104  		if bucket == nil {
   105  			return ErrRootKeyBucketNotFound
   106  		}
   107  		dbKey := bucket.Get(encryptionKeyID)
   108  		if len(dbKey) > 0 {
   109  			// We've already stored a key, so try to unlock with
   110  			// the password.
   111  			encKey := &snacl.SecretKey{}
   112  			err := encKey.Unmarshal(dbKey)
   113  			if err != nil {
   114  				return err
   115  			}
   116  
   117  			err = encKey.DeriveKey(password)
   118  			if err != nil {
   119  				return err
   120  			}
   121  
   122  			r.encKey = encKey
   123  			return nil
   124  		}
   125  
   126  		// We haven't yet stored a key, so create a new one.
   127  		encKey, err := snacl.NewSecretKey(
   128  			password, scryptN, scryptR, scryptP,
   129  		)
   130  		if err != nil {
   131  			return err
   132  		}
   133  
   134  		err = bucket.Put(encryptionKeyID, encKey.Marshal())
   135  		if err != nil {
   136  			return err
   137  		}
   138  
   139  		r.encKey = encKey
   140  		return nil
   141  	}, func() {})
   142  }
   143  
   144  // ChangePassword decrypts the macaroon root key with the old password and then
   145  // encrypts it again with the new password.
   146  func (r *RootKeyStorage) ChangePassword(oldPw, newPw []byte) error {
   147  	// We need the store to already be unlocked. With this we can make sure
   148  	// that there already is a key in the DB.
   149  	if r.encKey == nil {
   150  		return ErrStoreLocked
   151  	}
   152  
   153  	// Check if a nil password has been passed; return an error if so.
   154  	if oldPw == nil || newPw == nil {
   155  		return ErrPasswordRequired
   156  	}
   157  
   158  	return kvdb.Update(r.Backend, func(tx kvdb.RwTx) error {
   159  		bucket := tx.ReadWriteBucket(rootKeyBucketName)
   160  		if bucket == nil {
   161  			return ErrRootKeyBucketNotFound
   162  		}
   163  		encKeyDb := bucket.Get(encryptionKeyID)
   164  		rootKeyDb := bucket.Get(DefaultRootKeyID)
   165  
   166  		// Both the encryption key and the root key must be present
   167  		// otherwise we are in the wrong state to change the password.
   168  		if len(encKeyDb) == 0 || len(rootKeyDb) == 0 {
   169  			return ErrEncKeyNotFound
   170  		}
   171  
   172  		// Unmarshal parameters for old encryption key and derive the
   173  		// old key with them.
   174  		encKeyOld := &snacl.SecretKey{}
   175  		err := encKeyOld.Unmarshal(encKeyDb)
   176  		if err != nil {
   177  			return err
   178  		}
   179  		err = encKeyOld.DeriveKey(&oldPw)
   180  		if err != nil {
   181  			return err
   182  		}
   183  
   184  		// Create a new encryption key from the new password.
   185  		encKeyNew, err := snacl.NewSecretKey(
   186  			&newPw, scryptN, scryptR, scryptP,
   187  		)
   188  		if err != nil {
   189  			return err
   190  		}
   191  
   192  		// Now try to decrypt the root key with the old encryption key,
   193  		// encrypt it with the new one and then store it in the DB.
   194  		decryptedKey, err := encKeyOld.Decrypt(rootKeyDb)
   195  		if err != nil {
   196  			return err
   197  		}
   198  		rootKey := make([]byte, len(decryptedKey))
   199  		copy(rootKey, decryptedKey)
   200  		encryptedKey, err := encKeyNew.Encrypt(rootKey)
   201  		if err != nil {
   202  			return err
   203  		}
   204  		err = bucket.Put(DefaultRootKeyID, encryptedKey)
   205  		if err != nil {
   206  			return err
   207  		}
   208  
   209  		// Finally, store the new encryption key parameters in the DB
   210  		// as well.
   211  		err = bucket.Put(encryptionKeyID, encKeyNew.Marshal())
   212  		if err != nil {
   213  			return err
   214  		}
   215  
   216  		r.encKey = encKeyNew
   217  		return nil
   218  	}, func() {})
   219  }
   220  
   221  // Get implements the Get method for the bakery.RootKeyStorage interface.
   222  func (r *RootKeyStorage) Get(_ context.Context, id []byte) ([]byte, error) {
   223  	r.encKeyMtx.RLock()
   224  	defer r.encKeyMtx.RUnlock()
   225  
   226  	if r.encKey == nil {
   227  		return nil, ErrStoreLocked
   228  	}
   229  	var rootKey []byte
   230  	err := kvdb.View(r.Backend, func(tx kvdb.RTx) error {
   231  		bucket := tx.ReadBucket(rootKeyBucketName)
   232  		if bucket == nil {
   233  			return ErrRootKeyBucketNotFound
   234  		}
   235  		dbKey := bucket.Get(id)
   236  		if len(dbKey) == 0 {
   237  			return fmt.Errorf("root key with id %s doesn't exist",
   238  				string(id))
   239  		}
   240  
   241  		decKey, err := r.encKey.Decrypt(dbKey)
   242  		if err != nil {
   243  			return err
   244  		}
   245  
   246  		rootKey = make([]byte, len(decKey))
   247  		copy(rootKey, decKey)
   248  		return nil
   249  	}, func() {
   250  		rootKey = nil
   251  	})
   252  	if err != nil {
   253  		return nil, err
   254  	}
   255  
   256  	return rootKey, nil
   257  }
   258  
   259  // RootKey implements the RootKey method for the bakery.RootKeyStorage
   260  // interface.
   261  func (r *RootKeyStorage) RootKey(ctx context.Context) ([]byte, []byte, error) {
   262  	r.encKeyMtx.RLock()
   263  	defer r.encKeyMtx.RUnlock()
   264  
   265  	if r.encKey == nil {
   266  		return nil, nil, ErrStoreLocked
   267  	}
   268  	var rootKey []byte
   269  
   270  	// Read the root key ID from the context. If no key is specified in the
   271  	// context, an error will be returned.
   272  	id, err := RootKeyIDFromContext(ctx)
   273  	if err != nil {
   274  		return nil, nil, err
   275  	}
   276  
   277  	if bytes.Equal(id, encryptionKeyID) {
   278  		return nil, nil, ErrKeyValueForbidden
   279  	}
   280  
   281  	err = kvdb.Update(r.Backend, func(tx kvdb.RwTx) error {
   282  		bucket := tx.ReadWriteBucket(rootKeyBucketName)
   283  		if bucket == nil {
   284  			return ErrRootKeyBucketNotFound
   285  		}
   286  		dbKey := bucket.Get(id)
   287  
   288  		// If there's a root key stored in the bucket, decrypt it and
   289  		// return it.
   290  		if len(dbKey) != 0 {
   291  			decKey, err := r.encKey.Decrypt(dbKey)
   292  			if err != nil {
   293  				return err
   294  			}
   295  
   296  			rootKey = make([]byte, len(decKey))
   297  			copy(rootKey, decKey)
   298  			return nil
   299  		}
   300  
   301  		// Otherwise, create a new root key, encrypt it,
   302  		// and store it in the bucket.
   303  		newKey, err := generateAndStoreNewRootKey(bucket, id, r.encKey)
   304  		rootKey = newKey
   305  		return err
   306  	}, func() {
   307  		rootKey = nil
   308  	})
   309  	if err != nil {
   310  		return nil, nil, err
   311  	}
   312  
   313  	return rootKey, id, nil
   314  }
   315  
   316  // GenerateNewRootKey generates a new macaroon root key, replacing the previous
   317  // root key if it existed.
   318  func (r *RootKeyStorage) GenerateNewRootKey() error {
   319  	// We need the store to already be unlocked. With this we can make sure
   320  	// that there already is a key in the DB that can be replaced.
   321  	if r.encKey == nil {
   322  		return ErrStoreLocked
   323  	}
   324  	return kvdb.Update(r.Backend, func(tx kvdb.RwTx) error {
   325  		bucket := tx.ReadWriteBucket(rootKeyBucketName)
   326  		if bucket == nil {
   327  			return ErrRootKeyBucketNotFound
   328  		}
   329  		_, err := generateAndStoreNewRootKey(
   330  			bucket, DefaultRootKeyID, r.encKey,
   331  		)
   332  		return err
   333  	}, func() {})
   334  }
   335  
   336  // Close closes the underlying database and zeroes the encryption key stored
   337  // in memory.
   338  func (r *RootKeyStorage) Close() error {
   339  	r.encKeyMtx.Lock()
   340  	defer r.encKeyMtx.Unlock()
   341  
   342  	if r.encKey != nil {
   343  		r.encKey.Zero()
   344  		r.encKey = nil
   345  	}
   346  
   347  	// Since we're not responsible for _creating_ the connection to our DB
   348  	// backend, we also shouldn't close it. This should be handled
   349  	// externally as to not interfere with remote DB connections in case we
   350  	// need to open/close the store twice as happens in the password change
   351  	// case.
   352  	return nil
   353  }
   354  
   355  // generateAndStoreNewRootKey creates a new random RootKeyLen-byte root key,
   356  // encrypts it with the given encryption key and stores it in the bucket.
   357  // Any previously set key will be overwritten.
   358  func generateAndStoreNewRootKey(bucket walletdb.ReadWriteBucket, id []byte,
   359  	key *snacl.SecretKey) ([]byte, error) {
   360  
   361  	rootKey := make([]byte, RootKeyLen)
   362  	if _, err := io.ReadFull(rand.Reader, rootKey); err != nil {
   363  		return nil, err
   364  	}
   365  
   366  	encryptedKey, err := key.Encrypt(rootKey)
   367  	if err != nil {
   368  		return nil, err
   369  	}
   370  	return rootKey, bucket.Put(id, encryptedKey)
   371  }
   372  
   373  // ListMacaroonIDs returns all the root key ID values except the value of
   374  // encryptedKeyID.
   375  func (r *RootKeyStorage) ListMacaroonIDs(_ context.Context) ([][]byte, error) {
   376  	r.encKeyMtx.RLock()
   377  	defer r.encKeyMtx.RUnlock()
   378  
   379  	// Check it's unlocked.
   380  	if r.encKey == nil {
   381  		return nil, ErrStoreLocked
   382  	}
   383  
   384  	var rootKeySlice [][]byte
   385  
   386  	// Read all the items in the bucket and append the keys, which are the
   387  	// root key IDs we want.
   388  	err := kvdb.View(r.Backend, func(tx kvdb.RTx) error {
   389  
   390  		// appendRootKey is a function closure that appends root key ID
   391  		// to rootKeySlice.
   392  		appendRootKey := func(k, _ []byte) error {
   393  			// Only append when the key value is not encryptedKeyID.
   394  			if !bytes.Equal(k, encryptionKeyID) {
   395  				rootKeySlice = append(rootKeySlice, k)
   396  			}
   397  			return nil
   398  		}
   399  
   400  		return tx.ReadBucket(rootKeyBucketName).ForEach(appendRootKey)
   401  	}, func() {
   402  		rootKeySlice = nil
   403  	})
   404  	if err != nil {
   405  		return nil, err
   406  	}
   407  
   408  	return rootKeySlice, nil
   409  }
   410  
   411  // DeleteMacaroonID removes one specific root key ID. If the root key ID is
   412  // found and deleted, it will be returned.
   413  func (r *RootKeyStorage) DeleteMacaroonID(
   414  	_ context.Context, rootKeyID []byte) ([]byte, error) {
   415  
   416  	r.encKeyMtx.RLock()
   417  	defer r.encKeyMtx.RUnlock()
   418  
   419  	// Check it's unlocked.
   420  	if r.encKey == nil {
   421  		return nil, ErrStoreLocked
   422  	}
   423  
   424  	// Check the rootKeyID is not empty.
   425  	if len(rootKeyID) == 0 {
   426  		return nil, ErrMissingRootKeyID
   427  	}
   428  
   429  	// Deleting encryptedKeyID or DefaultRootKeyID is not allowed.
   430  	if bytes.Equal(rootKeyID, encryptionKeyID) ||
   431  		bytes.Equal(rootKeyID, DefaultRootKeyID) {
   432  
   433  		return nil, ErrDeletionForbidden
   434  	}
   435  
   436  	var rootKeyIDDeleted []byte
   437  	err := kvdb.Update(r.Backend, func(tx kvdb.RwTx) error {
   438  		bucket := tx.ReadWriteBucket(rootKeyBucketName)
   439  
   440  		// Check the key can be found. If not, return nil.
   441  		if bucket.Get(rootKeyID) == nil {
   442  			return nil
   443  		}
   444  
   445  		// Once the key is found, we do the deletion.
   446  		if err := bucket.Delete(rootKeyID); err != nil {
   447  			return err
   448  		}
   449  		rootKeyIDDeleted = rootKeyID
   450  
   451  		return nil
   452  	}, func() {
   453  		rootKeyIDDeleted = nil
   454  	})
   455  	if err != nil {
   456  		return nil, err
   457  	}
   458  
   459  	return rootKeyIDDeleted, nil
   460  }