Skip to content

Commit 0e91d98

Browse files
committed
cln: add CLN funding key derivation
1 parent 74b748e commit 0e91d98

File tree

2 files changed

+96
-0
lines changed

2 files changed

+96
-0
lines changed

cln/derivation.go

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package cln
2+
3+
import (
4+
"crypto/sha256"
5+
"encoding/binary"
6+
7+
"github.com/btcsuite/btcd/btcec/v2"
8+
"golang.org/x/crypto/hkdf"
9+
)
10+
11+
var (
12+
InfoPeerSeed = []byte("peer seed")
13+
InfoPerPeer = []byte("per-peer seed")
14+
InfoCLightning = []byte("c-lightning")
15+
)
16+
17+
// FundingKey derives a CLN channel funding key for the given peer and channel
18+
// number (incrementing database index).
19+
func FundingKey(hsmSecret [32]byte, peerPubKey *btcec.PublicKey,
20+
channelNum uint64) (*btcec.PublicKey, error) {
21+
22+
channelBase, err := HkdfSha256(hsmSecret[:], nil, InfoPeerSeed)
23+
if err != nil {
24+
return nil, err
25+
}
26+
27+
peerAndChannel := make([]byte, 33+8)
28+
copy(peerAndChannel[:33], peerPubKey.SerializeCompressed())
29+
binary.LittleEndian.PutUint64(peerAndChannel[33:], channelNum)
30+
31+
channelSeed, err := HkdfSha256(
32+
channelBase[:], peerAndChannel[:], InfoPerPeer,
33+
)
34+
if err != nil {
35+
return nil, err
36+
}
37+
38+
fundingKey, err := HkdfSha256(channelSeed[:], nil, InfoCLightning)
39+
if err != nil {
40+
return nil, err
41+
}
42+
43+
_, pubKey := btcec.PrivKeyFromBytes(fundingKey[:])
44+
return pubKey, nil
45+
}
46+
47+
// HkdfSha256 derives a 32-byte key from the given input key material, salt, and
48+
// info using the HKDF-SHA256 key derivation function.
49+
func HkdfSha256(key, salt, info []byte) ([32]byte, error) {
50+
expander := hkdf.New(sha256.New, key, salt, info)
51+
var outputKey [32]byte
52+
53+
_, err := expander.Read(outputKey[:])
54+
if err != nil {
55+
return [32]byte{}, err
56+
}
57+
58+
return outputKey, nil
59+
}

cln/derivation_test.go

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package cln
2+
3+
import (
4+
"encoding/hex"
5+
"testing"
6+
7+
"github.com/btcsuite/btcd/btcec/v2"
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
var (
12+
hsmSecret = [32]byte{
13+
0x3f, 0x0a, 0x06, 0xc6, 0x38, 0x5b, 0x74, 0x93,
14+
0xf7, 0x5a, 0xa0, 0x08, 0x9f, 0x31, 0x6a, 0x13,
15+
0xbf, 0x72, 0xbe, 0xb4, 0x30, 0xe5, 0x9e, 0x71,
16+
0xb5, 0xac, 0x5a, 0x73, 0x58, 0x1a, 0x62, 0x70,
17+
}
18+
peerPubKeyBytes, _ = hex.DecodeString(
19+
"02678187ca43e6a6f62f9185be98a933bf485313061e6a05578bbd83c54e" +
20+
"88d460",
21+
)
22+
peerPubKey, _ = btcec.ParsePubKey(peerPubKeyBytes)
23+
24+
expectedFundingKeyBytes, _ = hex.DecodeString(
25+
"0326a2171c97673cc8cd7a04a043f0224c59591fc8c9de320a48f7c9b68a" +
26+
"b0ae2b",
27+
)
28+
)
29+
30+
func TestFundingKey(t *testing.T) {
31+
fundingKey, err := FundingKey(hsmSecret, peerPubKey, 1)
32+
require.NoError(t, err)
33+
34+
require.Equal(
35+
t, expectedFundingKeyBytes, fundingKey.SerializeCompressed(),
36+
)
37+
}

0 commit comments

Comments
 (0)