Skip to content

Commit 1ff2411

Browse files
guggeroellemouton
authored andcommitted
session: add super macaroon root key ID
Whenever we bake a supermacaroon, we want it to be recognizable as such. For that we use the 64bit unsigned integer that is used as the macaroon's storage ID (or root key ID) and set the first 4 bytes to a specific value that we can detect later on.
1 parent 3b5d990 commit 1ff2411

File tree

3 files changed

+121
-2
lines changed

3 files changed

+121
-2
lines changed

session/interface.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package session
22

33
import (
4-
"encoding/binary"
54
"fmt"
65
"time"
76

@@ -69,7 +68,10 @@ func NewSession(label string, typ Type, expiry time.Time, serverAddr string,
6968
return nil, fmt.Errorf("error deriving private key: %v", err)
7069
}
7170
pubKey := privateKey.PubKey()
72-
macRootKey := binary.BigEndian.Uint64(pubKey.SerializeCompressed()[0:8])
71+
72+
var macRootKeyBase [4]byte
73+
copy(macRootKeyBase[:], pubKey.SerializeCompressed())
74+
macRootKey := NewSuperMacaroonRootKeyID(macRootKeyBase)
7375

7476
sess := &Session{
7577
Label: label,

session/macaroon.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package session
2+
3+
import (
4+
"bytes"
5+
"encoding/binary"
6+
"encoding/hex"
7+
"strconv"
8+
9+
"github.com/lightningnetwork/lnd/lnrpc"
10+
"google.golang.org/protobuf/proto"
11+
"gopkg.in/macaroon-bakery.v2/bakery"
12+
"gopkg.in/macaroon.v2"
13+
)
14+
15+
var (
16+
// SuperMacaroonRootKeyPrefix is the prefix we set on a super macaroon's
17+
// root key to clearly mark it as such.
18+
SuperMacaroonRootKeyPrefix = [4]byte{0xFF, 0xEE, 0xDD, 0xCC}
19+
)
20+
21+
// NewSuperMacaroonRootKeyID returns a new macaroon root key ID that has the
22+
// prefix to mark it as a super macaroon root key.
23+
func NewSuperMacaroonRootKeyID(id [4]byte) uint64 {
24+
rootKeyBytes := make([]byte, 8)
25+
copy(rootKeyBytes[:], SuperMacaroonRootKeyPrefix[:])
26+
copy(rootKeyBytes[4:], id[:])
27+
return binary.BigEndian.Uint64(rootKeyBytes)
28+
}
29+
30+
// ParseMacaroon parses a hex encoded macaroon into its native struct.
31+
func ParseMacaroon(macHex string) (*macaroon.Macaroon, error) {
32+
macBytes, err := hex.DecodeString(macHex)
33+
if err != nil {
34+
return nil, err
35+
}
36+
37+
mac := &macaroon.Macaroon{}
38+
if err := mac.UnmarshalBinary(macBytes); err != nil {
39+
return nil, err
40+
}
41+
42+
return mac, nil
43+
}
44+
45+
// IsSuperMacaroon returns true if the given hex encoded macaroon is a super
46+
// macaroon baked by LiT which can be identified by its root key ID.
47+
func IsSuperMacaroon(macHex string) bool {
48+
mac, err := ParseMacaroon(macHex)
49+
if err != nil {
50+
return false
51+
}
52+
53+
rawID := mac.Id()
54+
if rawID[0] != byte(bakery.LatestVersion) {
55+
return false
56+
}
57+
decodedID := &lnrpc.MacaroonId{}
58+
idProto := rawID[1:]
59+
err = proto.Unmarshal(idProto, decodedID)
60+
if err != nil {
61+
return false
62+
}
63+
64+
// The storage ID is a string representation of a 64bit unsigned number.
65+
rootKeyID, err := strconv.ParseUint(string(decodedID.StorageId), 10, 64)
66+
if err != nil {
67+
return false
68+
}
69+
70+
return isSuperMacaroonRootKeyID(rootKeyID)
71+
}
72+
73+
// isSuperMacaroonRootKeyID returns true if the given macaroon root key ID (also
74+
// known as storage ID) is a super macaroon, which can be identified by its
75+
// first 4 bytes.
76+
func isSuperMacaroonRootKeyID(rootKeyID uint64) bool {
77+
rootKeyBytes := make([]byte, 8)
78+
binary.BigEndian.PutUint64(rootKeyBytes, rootKeyID)
79+
return bytes.HasPrefix(rootKeyBytes, SuperMacaroonRootKeyPrefix[:])
80+
}

session/macaroon_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package session
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
)
8+
9+
var (
10+
// testMacHex is a read-only supermacaroon.
11+
testMacHex = "0201036c6e6402f802030a1011540404373b4d0b3682b15ea7af60c" +
12+
"c121431383434313932313339323432393138343738341a0f0a076163636" +
13+
"f756e741204726561641a0f0a0761756374696f6e1204726561641a0d0a0" +
14+
"561756469741204726561641a0c0a04617574681204726561641a0c0a046" +
15+
"96e666f1204726561641a100a08696e7369676874731204726561641a100" +
16+
"a08696e766f696365731204726561641a0f0a046c6f6f701202696e12036" +
17+
"f75741a100a086d616361726f6f6e1204726561641a0f0a076d657373616" +
18+
"7651204726561641a100a086f6666636861696e1204726561641a0f0a076" +
19+
"f6e636861696e1204726561641a0d0a056f726465721204726561641a0d0" +
20+
"a0570656572731204726561641a0d0a0572617465731204726561641a160" +
21+
"a0e7265636f6d6d656e646174696f6e1204726561641a0e0a067265706f7" +
22+
"2741204726561641a130a0b73756767657374696f6e731204726561641a0" +
23+
"c0a04737761701204726561641a0d0a057465726d7312047265616400000" +
24+
"6202362c91888e95dfbbf1eb995bd0fef2b549e2de7f4e9fa11aff445273" +
25+
"60a6caf"
26+
)
27+
28+
func TestSuperMacaroonRootKeyID(t *testing.T) {
29+
someBytes := [4]byte{02, 03, 44, 88}
30+
rootKeyID := NewSuperMacaroonRootKeyID(someBytes)
31+
require.True(t, isSuperMacaroonRootKeyID(rootKeyID))
32+
require.False(t, isSuperMacaroonRootKeyID(123))
33+
}
34+
35+
func TestIsSuperMacaroon(t *testing.T) {
36+
require.True(t, IsSuperMacaroon(testMacHex))
37+
}

0 commit comments

Comments
 (0)