|
| 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 | +} |
0 commit comments