2017-08-18 04:50:15 +03:00
|
|
|
package macaroons
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/rand"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
|
2018-01-16 19:18:41 +03:00
|
|
|
"golang.org/x/net/context"
|
|
|
|
|
2017-08-18 04:50:15 +03:00
|
|
|
"github.com/boltdb/bolt"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
// RootKeyLen is the length of a root key.
|
|
|
|
RootKeyLen = 32
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
// rootKeyBucketName is the name of the root key store bucket.
|
|
|
|
rootKeyBucketName = []byte("macrootkeys")
|
|
|
|
|
|
|
|
// defaultRootKeyID is the ID of the default root key. The first is
|
|
|
|
// just 0, to emulate the memory storage that comes with bakery.
|
2017-08-22 09:18:19 +03:00
|
|
|
//
|
2017-08-18 04:50:15 +03:00
|
|
|
// TODO(aakselrod): Add support for key rotation.
|
2018-01-16 19:18:41 +03:00
|
|
|
defaultRootKeyID = []byte("0")
|
2017-08-18 04:50:15 +03:00
|
|
|
|
|
|
|
// macaroonBucketName is the name of the macaroon store bucket.
|
|
|
|
macaroonBucketName = []byte("macaroons")
|
|
|
|
)
|
|
|
|
|
|
|
|
// RootKeyStorage implements the bakery.RootKeyStorage interface.
|
|
|
|
type RootKeyStorage struct {
|
|
|
|
*bolt.DB
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewRootKeyStorage creates a RootKeyStorage instance.
|
|
|
|
// TODO(aakselrod): Add support for encryption of data with passphrase.
|
|
|
|
func NewRootKeyStorage(db *bolt.DB) (*RootKeyStorage, error) {
|
|
|
|
// If the store's bucket doesn't exist, create it.
|
|
|
|
err := db.Update(func(tx *bolt.Tx) error {
|
|
|
|
_, err := tx.CreateBucketIfNotExists(rootKeyBucketName)
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2017-08-22 09:18:19 +03:00
|
|
|
|
2017-08-18 04:50:15 +03:00
|
|
|
// Return the DB wrapped in a RootKeyStorage object.
|
|
|
|
return &RootKeyStorage{db}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get implements the Get method for the bakery.RootKeyStorage interface.
|
2018-01-16 19:18:41 +03:00
|
|
|
func (r *RootKeyStorage) Get(_ context.Context, id []byte) ([]byte, error) {
|
2017-08-18 04:50:15 +03:00
|
|
|
var rootKey []byte
|
|
|
|
err := r.View(func(tx *bolt.Tx) error {
|
2018-01-16 19:18:41 +03:00
|
|
|
dbKey := tx.Bucket(rootKeyBucketName).Get(id)
|
2017-08-23 21:30:09 +03:00
|
|
|
if len(dbKey) == 0 {
|
2017-08-18 04:50:15 +03:00
|
|
|
return fmt.Errorf("root key with id %s doesn't exist",
|
2018-01-16 19:18:41 +03:00
|
|
|
string(id))
|
2017-08-18 04:50:15 +03:00
|
|
|
}
|
2017-08-23 21:30:09 +03:00
|
|
|
|
|
|
|
rootKey = make([]byte, len(dbKey))
|
|
|
|
copy(rootKey[:], dbKey)
|
2017-08-18 04:50:15 +03:00
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2017-08-22 09:18:19 +03:00
|
|
|
|
2017-08-18 04:50:15 +03:00
|
|
|
return rootKey, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// RootKey implements the RootKey method for the bakery.RootKeyStorage
|
|
|
|
// interface.
|
|
|
|
// TODO(aakselrod): Add support for key rotation.
|
2018-01-16 19:18:41 +03:00
|
|
|
func (r *RootKeyStorage) RootKey(_ context.Context) ([]byte, []byte, error) {
|
2017-08-18 04:50:15 +03:00
|
|
|
var rootKey []byte
|
|
|
|
id := defaultRootKeyID
|
|
|
|
err := r.Update(func(tx *bolt.Tx) error {
|
|
|
|
ns := tx.Bucket(rootKeyBucketName)
|
2018-01-16 19:18:41 +03:00
|
|
|
rootKey = ns.Get(id)
|
2017-08-18 04:50:15 +03:00
|
|
|
|
|
|
|
// If there's no root key stored in the bucket yet, create one.
|
|
|
|
if len(rootKey) != 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a RootKeyLen-byte root key.
|
|
|
|
rootKey = make([]byte, RootKeyLen)
|
|
|
|
if _, err := io.ReadFull(rand.Reader, rootKey[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-01-16 19:18:41 +03:00
|
|
|
return ns.Put(id, rootKey)
|
2017-08-18 04:50:15 +03:00
|
|
|
})
|
|
|
|
if err != nil {
|
2018-01-16 19:18:41 +03:00
|
|
|
return nil, nil, err
|
2017-08-18 04:50:15 +03:00
|
|
|
}
|
2017-08-22 09:18:19 +03:00
|
|
|
|
2017-08-18 04:50:15 +03:00
|
|
|
return rootKey, id, nil
|
|
|
|
}
|