mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-11-10 22:27:22 +01:00
macaroons: add BakeFromRootKey function
This commit is contained in:
68
macaroons/bake.go
Normal file
68
macaroons/bake.go
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
package macaroons
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"gopkg.in/macaroon-bakery.v2/bakery"
|
||||||
|
"gopkg.in/macaroon.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// inMemoryRootKeyStore is a simple implementation of bakery.RootKeyStore that
|
||||||
|
// stores a single root key in memory.
|
||||||
|
type inMemoryRootKeyStore struct {
|
||||||
|
rootKey []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// A compile-time check to ensure that inMemoryRootKeyStore implements
|
||||||
|
// bakery.RootKeyStore.
|
||||||
|
var _ bakery.RootKeyStore = (*inMemoryRootKeyStore)(nil)
|
||||||
|
|
||||||
|
// Get returns the root key for the given id. If the item is not there, it
|
||||||
|
// returns ErrNotFound.
|
||||||
|
func (s *inMemoryRootKeyStore) Get(_ context.Context, id []byte) ([]byte,
|
||||||
|
error) {
|
||||||
|
|
||||||
|
if !bytes.Equal(id, DefaultRootKeyID) {
|
||||||
|
return nil, bakery.ErrNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.rootKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RootKey returns the root key to be used for making a new macaroon, and an id
|
||||||
|
// that can be used to look it up later with the Get method.
|
||||||
|
func (s *inMemoryRootKeyStore) RootKey(context.Context) ([]byte, []byte,
|
||||||
|
error) {
|
||||||
|
|
||||||
|
return s.rootKey, DefaultRootKeyID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BakeFromRootKey creates a new macaroon that is derived from the given root
|
||||||
|
// key and permissions.
|
||||||
|
func BakeFromRootKey(rootKey []byte,
|
||||||
|
permissions []bakery.Op) (*macaroon.Macaroon, error) {
|
||||||
|
|
||||||
|
if len(rootKey) != RootKeyLen {
|
||||||
|
return nil, fmt.Errorf("root key must be %d bytes, is %d",
|
||||||
|
RootKeyLen, len(rootKey))
|
||||||
|
}
|
||||||
|
|
||||||
|
rootKeyStore := &inMemoryRootKeyStore{
|
||||||
|
rootKey: rootKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
service, err := NewService(rootKeyStore, "lnd", false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to create service: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
mac, err := service.NewMacaroon(ctx, DefaultRootKeyID, permissions...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to create macaroon: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return mac.M(), nil
|
||||||
|
}
|
||||||
58
macaroons/bake_test.go
Normal file
58
macaroons/bake_test.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
package macaroons_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/hex"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/lightningnetwork/lnd/macaroons"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"google.golang.org/grpc/metadata"
|
||||||
|
"gopkg.in/macaroon-bakery.v2/bakery"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestBakeFromRootKey tests that a macaroon can be baked from a root key
|
||||||
|
// directly without needing to create a store or service first.
|
||||||
|
func TestBakeFromRootKey(t *testing.T) {
|
||||||
|
// Create a test store and unlock it.
|
||||||
|
_, store := newTestStore(t)
|
||||||
|
|
||||||
|
pw := []byte("weks")
|
||||||
|
err := store.CreateUnlock(&pw)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Force the store to create a new random root key.
|
||||||
|
key, id, err := store.RootKey(defaultRootKeyIDContext)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, key, 32)
|
||||||
|
|
||||||
|
tmpKey, err := store.Get(defaultRootKeyIDContext, id)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, key, tmpKey)
|
||||||
|
|
||||||
|
// Create a service that uses the root key store.
|
||||||
|
service, err := macaroons.NewService(store, "lnd", false)
|
||||||
|
require.NoError(t, err, "Error creating new service")
|
||||||
|
defer func() {
|
||||||
|
require.NoError(t, service.Close())
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Call the BakeFromRootKey function that derives a macaroon directly
|
||||||
|
// from the root key.
|
||||||
|
perms := []bakery.Op{{Entity: "foo", Action: "bar"}}
|
||||||
|
mac, err := macaroons.BakeFromRootKey(key, perms)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
macaroonBytes, err := mac.MarshalBinary()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
md := metadata.New(map[string]string{
|
||||||
|
"macaroon": hex.EncodeToString(macaroonBytes),
|
||||||
|
})
|
||||||
|
macCtx := metadata.NewIncomingContext(context.Background(), md)
|
||||||
|
|
||||||
|
// The macaroon should be valid for the service, since the root key was
|
||||||
|
// the same.
|
||||||
|
err = service.ValidateMacaroon(macCtx, nil, "baz")
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user