Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 8 additions & 10 deletions accounts/rpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (

"github.com/btcsuite/btcd/btcutil"
"github.com/lightninglabs/lightning-terminal/litrpc"
"github.com/lightninglabs/lightning-terminal/session"
litmac "github.com/lightninglabs/lightning-terminal/macaroons"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/macaroons"
Expand All @@ -22,12 +22,12 @@ type RPCServer struct {

service *InterceptorService

superMacBaker session.MacaroonBaker
superMacBaker litmac.Baker
}

// NewRPCServer returns a new RPC server for the given service.
func NewRPCServer(service *InterceptorService,
superMacBaker session.MacaroonBaker) *RPCServer {
superMacBaker litmac.Baker) *RPCServer {

return &RPCServer{
service: service,
Expand Down Expand Up @@ -79,19 +79,17 @@ func (s *RPCServer) CreateAccount(ctx context.Context,

var rootKeyIdSuffix [4]byte
copy(rootKeyIdSuffix[:], account.ID[0:4])
macRootKey := session.NewSuperMacaroonRootKeyID(rootKeyIdSuffix)
macRootKey := litmac.NewSuperMacaroonRootKeyID(rootKeyIdSuffix)

accountCaveat := checkers.Condition(
macaroons.CondLndCustom,
fmt.Sprintf("%s %x", CondAccount, account.ID[:]),
)

macHex, err := s.superMacBaker(ctx, macRootKey, &session.MacaroonRecipe{
Permissions: MacaroonPermissions,
Caveats: []macaroon.Caveat{{
Id: []byte(accountCaveat),
}},
})
macHex, err := s.superMacBaker(
ctx, macRootKey, MacaroonPermissions,
[]macaroon.Caveat{{Id: []byte(accountCaveat)}},
)
if err != nil {
return nil, fmt.Errorf("error baking account macaroon: %w", err)
}
Expand Down
6 changes: 3 additions & 3 deletions cmd/litcli/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"encoding/json"
"os"

"github.com/lightninglabs/lightning-terminal/session"
"github.com/lightninglabs/lightning-terminal/macaroons"
"github.com/urfave/cli"
)

Expand Down Expand Up @@ -66,7 +66,7 @@ func superMacRootKey(ctx *cli.Context) error {
}
}

id := session.NewSuperMacaroonRootKeyID(suffix)
id := macaroons.NewSuperMacaroonRootKeyID(suffix)

printJSON(struct {
RootKeyID uint64 `json:"root_key_id"`
Expand Down Expand Up @@ -97,7 +97,7 @@ var isSuperMacaroonCmd = cli.Command{
// isSuperMacaroon checks if the users given macaroon is considered a super
// macaroon.
func isSuperMacaroon(ctx *cli.Context) error {
isSuperMac := session.IsSuperMacaroon(ctx.String("mac"))
isSuperMac := macaroons.IsSuperMacaroon(ctx.String("mac"))

printJSON(struct {
IsSuperMacaroon bool `json:"is_super_macaroon"`
Expand Down
3 changes: 2 additions & 1 deletion itest/litd_firewall_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/lightninglabs/lightning-terminal/firewall"
"github.com/lightninglabs/lightning-terminal/firewalldb"
"github.com/lightninglabs/lightning-terminal/litrpc"
"github.com/lightninglabs/lightning-terminal/macaroons"
"github.com/lightninglabs/lightning-terminal/rules"
"github.com/lightninglabs/lightning-terminal/session"
"github.com/lightningnetwork/lnd"
Expand Down Expand Up @@ -2566,7 +2567,7 @@ func (c *caveatCredentials) GetRequestMetadata(ctx context.Context,
return metadata, nil
}

mac, err := session.ParseMacaroon(macHex)
mac, err := macaroons.ParseMacaroon(macHex)
if err != nil {
return nil, err
}
Expand Down
35 changes: 35 additions & 0 deletions macaroons/macaroons.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package macaroons

import (
"context"
"fmt"
"strconv"

"github.com/lightningnetwork/lnd/lnrpc"
"google.golang.org/protobuf/proto"
"gopkg.in/macaroon-bakery.v2/bakery"
"gopkg.in/macaroon.v2"
)

// Baker is a function type for baking a super macaroon.
type Baker func(ctx context.Context, rootKeyID uint64,
perms []bakery.Op, caveats []macaroon.Caveat) (string, error)

// RootKeyIDFromMacaroon extracts the root key ID of the passed macaroon.
func RootKeyIDFromMacaroon(mac *macaroon.Macaroon) (uint64, error) {
rawID := mac.Id()
if rawID[0] != byte(bakery.LatestVersion) {
return 0, fmt.Errorf("mac id is not on the latest version")
}

decodedID := &lnrpc.MacaroonId{}
idProto := rawID[1:]
err := proto.Unmarshal(idProto, decodedID)
if err != nil {
return 0, err
}

// The storage ID is a string representation of a 64-bit unsigned
// number.
return strconv.ParseUint(string(decodedID.StorageId), 10, 64)
}
120 changes: 120 additions & 0 deletions macaroons/super_mac.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package macaroons

import (
"bytes"
"context"
"encoding/binary"
"encoding/hex"
"errors"

"github.com/lightningnetwork/lnd/lnrpc"
"gopkg.in/macaroon-bakery.v2/bakery"
"gopkg.in/macaroon.v2"
)

// SuperMacaroonRootKeyPrefix is the prefix we set on a super macaroon's root
// key to clearly mark it as such.
var SuperMacaroonRootKeyPrefix = [4]byte{0xFF, 0xEE, 0xDD, 0xCC}

// SuperMacaroonValidator is a function type for validating a super macaroon.
type SuperMacaroonValidator func(ctx context.Context,
superMacaroon []byte, requiredPermissions []bakery.Op,
fullMethod string) error

// NewSuperMacaroonRootKeyID returns a new macaroon root key ID that has the
// prefix to mark it as a super macaroon root key.
func NewSuperMacaroonRootKeyID(id [4]byte) uint64 {
rootKeyBytes := make([]byte, 8)
copy(rootKeyBytes[:], SuperMacaroonRootKeyPrefix[:])
copy(rootKeyBytes[4:], id[:])

return binary.BigEndian.Uint64(rootKeyBytes)
}

// ParseMacaroon parses a hex encoded macaroon into its native struct.
func ParseMacaroon(macHex string) (*macaroon.Macaroon, error) {
macBytes, err := hex.DecodeString(macHex)
if err != nil {
return nil, err
}

mac := &macaroon.Macaroon{}
if err := mac.UnmarshalBinary(macBytes); err != nil {
return nil, err
}

return mac, nil
}

// IsSuperMacaroon returns true if the given hex encoded macaroon is a super
// macaroon baked by LiT which can be identified by its root key ID.
func IsSuperMacaroon(macHex string) bool {
mac, err := ParseMacaroon(macHex)
if err != nil {
return false
}

rootKeyID, err := RootKeyIDFromMacaroon(mac)
if err != nil {
return false
}

return isSuperMacaroonRootKeyID(rootKeyID)
}

// isSuperMacaroonRootKeyID returns true if the given macaroon root key ID (also
// known as storage ID) is a super macaroon, which can be identified by its
// first 4 bytes.
func isSuperMacaroonRootKeyID(rootKeyID uint64) bool {
rootKeyBytes := make([]byte, 8)
binary.BigEndian.PutUint64(rootKeyBytes, rootKeyID)
return bytes.HasPrefix(rootKeyBytes, SuperMacaroonRootKeyPrefix[:])
}

// BakeSuperMacaroon uses the lnd client to bake a macaroon that can include
// permissions for multiple daemons.
func BakeSuperMacaroon(ctx context.Context, lnd lnrpc.LightningClient,
rootKeyID uint64, perms []bakery.Op, caveats []macaroon.Caveat) (string,
error) {

if lnd == nil {
return "", errors.New("lnd not yet connected")
}

req := &lnrpc.BakeMacaroonRequest{
Permissions: make(
[]*lnrpc.MacaroonPermission, len(perms),
),
AllowExternalPermissions: true,
RootKeyId: rootKeyID,
}
for idx, perm := range perms {
req.Permissions[idx] = &lnrpc.MacaroonPermission{
Entity: perm.Entity,
Action: perm.Action,
}
}

res, err := lnd.BakeMacaroon(ctx, req)
if err != nil {
return "", err
}

mac, err := ParseMacaroon(res.Macaroon)
if err != nil {
return "", err
}

for _, caveat := range caveats {
if err := mac.AddFirstPartyCaveat(caveat.Id); err != nil {
return "", err
}
}

macBytes, err := mac.MarshalBinary()
if err != nil {
return "", err
}

return hex.EncodeToString(macBytes), err
}
10 changes: 9 additions & 1 deletion session/macaroon_test.go → macaroons/super_mac_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package session
package macaroons

import (
"testing"
Expand All @@ -25,13 +25,21 @@ var (
"60a6caf"
)

// TestSuperMacaroonRootKeyID tests that adding the super macaroon prefix to
// a root key ID results in a valid super macaroon root key ID.
func TestSuperMacaroonRootKeyID(t *testing.T) {
t.Parallel()

someBytes := [4]byte{02, 03, 44, 88}
rootKeyID := NewSuperMacaroonRootKeyID(someBytes)
require.True(t, isSuperMacaroonRootKeyID(rootKeyID))
require.False(t, isSuperMacaroonRootKeyID(123))
}

// TestIsSuperMacaroon tests that we can correctly identify an example super
// macaroon.
func TestIsSuperMacaroon(t *testing.T) {
t.Parallel()

require.True(t, IsSuperMacaroon(testMacHex))
}
10 changes: 6 additions & 4 deletions rpc_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import (

"github.com/improbable-eng/grpc-web/go/grpcweb"
"github.com/lightninglabs/lightning-terminal/litrpc"
litmac "github.com/lightninglabs/lightning-terminal/macaroons"
"github.com/lightninglabs/lightning-terminal/perms"
"github.com/lightninglabs/lightning-terminal/session"
litstatus "github.com/lightninglabs/lightning-terminal/status"
"github.com/lightninglabs/lightning-terminal/subservers"
"github.com/lightningnetwork/lnd/lncfg"
Expand Down Expand Up @@ -71,7 +71,7 @@ func (e *proxyErr) Unwrap() error {
// or REST request and delegate (and convert if necessary) it to the correct
// component.
func newRpcProxy(cfg *Config, validator macaroons.MacaroonValidator,
superMacValidator session.SuperMacaroonValidator,
superMacValidator litmac.SuperMacaroonValidator,
permsMgr *perms.Manager, subServerMgr *subservers.Manager,
statusMgr *litstatus.Manager, getLNDClient lndBasicClientFn) *rpcProxy {

Expand Down Expand Up @@ -176,7 +176,7 @@ type rpcProxy struct {
bakeSuperMac bakeSuperMac

macValidator macaroons.MacaroonValidator
superMacValidator session.SuperMacaroonValidator
superMacValidator litmac.SuperMacaroonValidator

superMacaroon string

Expand Down Expand Up @@ -331,7 +331,9 @@ func (p *rpcProxy) makeDirector(allowLitRPC bool) func(ctx context.Context,
))
}

case len(macHeader) == 1 && session.IsSuperMacaroon(macHeader[0]):
case len(macHeader) == 1 &&
litmac.IsSuperMacaroon(macHeader[0]):

// If we have a macaroon, and it's a super macaroon,
// then we need to convert it into the actual daemon
// macaroon if they're running in remote mode.
Expand Down
8 changes: 2 additions & 6 deletions session/interface.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package session

import (
"context"
"fmt"
"time"

"github.com/btcsuite/btcd/btcec/v2"
"github.com/lightninglabs/lightning-node-connect/mailbox"
"github.com/lightninglabs/lightning-terminal/macaroons"
"gopkg.in/macaroon-bakery.v2/bakery"
"gopkg.in/macaroon.v2"
)
Expand Down Expand Up @@ -71,10 +71,6 @@ type Session struct {
GroupID ID
}

// MacaroonBaker is a function type for baking a super macaroon.
type MacaroonBaker func(ctx context.Context, rootKeyID uint64,
recipe *MacaroonRecipe) (string, error)

// NewSession creates a new session with the given user-defined parameters.
func NewSession(id ID, localPrivKey *btcec.PrivateKey, label string, typ Type,
expiry time.Time, serverAddr string, devServer bool, perms []bakery.Op,
Expand All @@ -86,7 +82,7 @@ func NewSession(id ID, localPrivKey *btcec.PrivateKey, label string, typ Type,
return nil, fmt.Errorf("error deriving pairing secret: %v", err)
}

macRootKey := NewSuperMacaroonRootKeyID(id)
macRootKey := macaroons.NewSuperMacaroonRootKeyID(id)

// The group ID will by default be the same as the Session ID
// unless this session links to a previous session.
Expand Down
Loading
Loading