watchtower: use ECDH interface for watchtower session

This commit is contained in:
Oliver Gugger 2020-04-28 10:06:31 +02:00
parent 535a22c590
commit f97e7b9951
No known key found for this signature in database
GPG Key ID: 8E4256593F177720
12 changed files with 53 additions and 93 deletions

21
lnd.go

@ -557,17 +557,14 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error {
}
defer towerDB.Close()
towerPrivKey, err := activeChainControl.wallet.DerivePrivKey(
keychain.KeyDescriptor{
KeyLocator: keychain.KeyLocator{
Family: keychain.KeyFamilyTowerID,
Index: 0,
},
towerKeyDesc, err := activeChainControl.keyRing.DeriveKey(
keychain.KeyLocator{
Family: keychain.KeyFamilyTowerID,
Index: 0,
},
)
if err != nil {
err := fmt.Errorf("unable to derive watchtower "+
"private key: %v", err)
err := fmt.Errorf("error deriving tower key: %v", err)
ltndLog.Error(err)
return err
}
@ -582,9 +579,11 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error {
lnwallet.WitnessPubKey, false,
)
},
NodeKeyECDH: towerPrivKey,
PublishTx: activeChainControl.wallet.PublishTransaction,
ChainHash: *activeNetParams.GenesisHash,
NodeKeyECDH: keychain.NewPubKeyECDH(
towerKeyDesc, activeChainControl.keyRing,
),
PublishTx: activeChainControl.wallet.PublishTransaction,
ChainHash: *activeNetParams.GenesisHash,
}
// If there is a tor controller (user wants auto hidden services), then

@ -5,10 +5,10 @@ import (
"net"
"time"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/tor"
"github.com/lightningnetwork/lnd/watchtower/lookout"
)
@ -65,7 +65,7 @@ type Config struct {
// NodeKeyECDH is the ECDH capable wrapper of the key to be used in
// accepting new brontide connections.
NodeKeyECDH *btcec.PrivateKey
NodeKeyECDH keychain.SingleKeyECDH
// PublishTx provides the ability to send a signed transaction to the
// network.

@ -72,7 +72,7 @@ func New(cfg *Config) (*Standalone, error) {
listeners := make([]net.Listener, 0, len(cfg.ListenAddrs))
for _, listenAddr := range cfg.ListenAddrs {
listener, err := brontide.NewListener(
nil/*TODO fix in next commit*/, listenAddr.String(),
cfg.NodeKeyECDH, listenAddr.String(),
)
if err != nil {
return nil, err

@ -11,6 +11,7 @@ import (
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/watchtower/wtdb"
@ -337,7 +338,7 @@ func New(config *Config) (*TowerClient, error) {
// NOTE: This method should only be used when deserialization of a
// ClientSession's Tower and SessionPrivKey fields is desired, otherwise, the
// existing ListClientSessions method should be used.
func getClientSessions(db DB, keyRing SecretKeyRing, forTower *wtdb.TowerID,
func getClientSessions(db DB, keyRing ECDHKeyRing, forTower *wtdb.TowerID,
passesFilter func(*wtdb.ClientSession) bool) (
map[wtdb.SessionID]*wtdb.ClientSession, error) {
@ -358,11 +359,14 @@ func getClientSessions(db DB, keyRing SecretKeyRing, forTower *wtdb.TowerID,
}
s.Tower = tower
sessionKey, err := DeriveSessionKey(keyRing, s.KeyIndex)
towerKeyDesc, err := keyRing.DeriveKey(keychain.KeyLocator{
Family: keychain.KeyFamilyTowerSession,
Index: s.KeyIndex,
})
if err != nil {
return nil, err
}
s.SessionKeyECDH = sessionKey
s.SessionKeyECDH = keychain.NewPubKeyECDH(towerKeyDesc, keyRing)
// If an optional filter was provided, use it to filter out any
// undesired sessions.
@ -897,10 +901,10 @@ func (c *TowerClient) taskRejected(task *backupTask, curStatus reserveStatus) {
// dial connects the peer at addr using privKey as our secret key for the
// connection. The connection will use the configured Net's resolver to resolve
// the address for either Tor or clear net connections.
func (c *TowerClient) dial(privKey *btcec.PrivateKey,
func (c *TowerClient) dial(localKey keychain.SingleKeyECDH,
addr *lnwire.NetAddress) (wtserver.Peer, error) {
return c.cfg.AuthDial(privKey, addr, c.cfg.Dial)
return c.cfg.AuthDial(localKey, addr, c.cfg.Dial)
}
// readMessage receives and parses the next message from the given Peer. An

@ -101,10 +101,10 @@ func (m *mockNet) ResolveTCPAddr(network string, address string) (*net.TCPAddr,
panic("not implemented")
}
func (m *mockNet) AuthDial(localPriv *btcec.PrivateKey, netAddr *lnwire.NetAddress,
func (m *mockNet) AuthDial(local keychain.SingleKeyECDH, netAddr *lnwire.NetAddress,
dialer func(string, string) (net.Conn, error)) (wtserver.Peer, error) {
localPk := localPriv.PubKey()
localPk := local.PubKey()
localAddr := &net.TCPAddr{
IP: net.IP{0x32, 0x31, 0x30, 0x29},
Port: 36723,
@ -401,6 +401,7 @@ func newHarness(t *testing.T, cfg harnessCfg) *testHarness {
if err != nil {
t.Fatalf("Unable to generate tower private key: %v", err)
}
privKeyECDH := &keychain.PrivKeyECDH{PrivKey: privKey}
towerPubKey := privKey.PubKey()
@ -416,7 +417,7 @@ func newHarness(t *testing.T, cfg harnessCfg) *testHarness {
DB: serverDB,
ReadTimeout: timeout,
WriteTimeout: timeout,
NodeKeyECDH: privKey,
NodeKeyECDH: privKeyECDH,
NewAddress: func() (btcutil.Address, error) {
return addr, nil
},

@ -1,24 +0,0 @@
package wtclient
import (
"github.com/btcsuite/btcd/btcec"
"github.com/lightningnetwork/lnd/keychain"
)
// DeriveSessionKey accepts an session key index for an existing session and
// derives the HD private key to be used to authenticate the brontide transport
// and authenticate requests sent to the tower. The key will use the
// keychain.KeyFamilyTowerSession and the provided index, giving a BIP43
// derivation path of:
//
// * m/1017'/coinType'/8/0/index
func DeriveSessionKey(keyRing ECDHKeyRing,
index uint32) (*btcec.PrivateKey, error) {
return keyRing.DerivePrivKey(keychain.KeyDescriptor{
KeyLocator: keychain.KeyLocator{
Family: keychain.KeyFamilyTowerSession,
Index: index,
},
})
}

@ -102,14 +102,14 @@ type Dial func(net, addr string) (net.Conn, error)
// AuthDialer connects to a remote node using an authenticated transport, such as
// brontide. The dialer argument is used to specify a resolver, which allows
// this method to be used over Tor or clear net connections.
type AuthDialer func(localPriv *btcec.PrivateKey, netAddr *lnwire.NetAddress,
type AuthDialer func(localKey keychain.SingleKeyECDH, netAddr *lnwire.NetAddress,
dialer func(string, string) (net.Conn, error)) (wtserver.Peer, error)
// AuthDial is the watchtower client's default method of dialing.
func AuthDial(localPriv *btcec.PrivateKey, netAddr *lnwire.NetAddress,
func AuthDial(localKey keychain.SingleKeyECDH, netAddr *lnwire.NetAddress,
dialer func(string, string) (net.Conn, error)) (wtserver.Peer, error) {
return brontide.Dial(nil/*TODO fix in next commit*/, netAddr, dialer)
return brontide.Dial(localKey, netAddr, dialer)
}
// ECDHKeyRing abstracts the ability to derive shared ECDH keys given a
@ -117,10 +117,6 @@ func AuthDial(localPriv *btcec.PrivateKey, netAddr *lnwire.NetAddress,
type ECDHKeyRing interface {
keychain.ECDHRing
// DerivePrivKey derives the private key from the root seed using a
// key descriptor specifying the key's derivation path.
DerivePrivKey(loc keychain.KeyDescriptor) (*btcec.PrivateKey, error)
// DeriveKey attempts to derive an arbitrary key specified by the
// passed KeyLocator. This may be used in several recovery scenarios,
// or when manually rotating something like our current default node

@ -5,8 +5,8 @@ import (
"sync"
"time"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/watchtower/blob"
"github.com/lightningnetwork/lnd/watchtower/wtdb"
@ -58,7 +58,8 @@ type NegotiatorConfig struct {
// Dial initiates an outbound brontide connection to the given address
// using a specified private key. The peer is returned in the event of a
// successful connection.
Dial func(*btcec.PrivateKey, *lnwire.NetAddress) (wtserver.Peer, error)
Dial func(keychain.SingleKeyECDH, *lnwire.NetAddress) (wtserver.Peer,
error)
// SendMessage writes a wtwire message to remote peer.
SendMessage func(wtserver.Peer, wtwire.Message) error
@ -315,13 +316,21 @@ func (n *sessionNegotiator) createSession(tower *wtdb.Tower,
return ErrNoTowerAddrs
}
sessionPriv, err := DeriveSessionKey(n.cfg.SecretKeyRing, keyIndex)
sessionKeyDesc, err := n.cfg.SecretKeyRing.DeriveKey(
keychain.KeyLocator{
Family: keychain.KeyFamilyTowerSession,
Index: keyIndex,
},
)
if err != nil {
return err
}
sessionKey := keychain.NewPubKeyECDH(
sessionKeyDesc, n.cfg.SecretKeyRing,
)
for _, lnAddr := range tower.LNAddrs() {
err = n.tryAddress(sessionPriv, keyIndex, tower, lnAddr)
err := n.tryAddress(sessionKey, keyIndex, tower, lnAddr)
switch {
case err == ErrPermanentTowerFailure:
// TODO(conner): report to iterator? can then be reset
@ -346,11 +355,11 @@ func (n *sessionNegotiator) createSession(tower *wtdb.Tower,
// The address should belong to the tower's set of addresses. This method only
// returns true if all steps succeed and the new session has been persisted, and
// fails otherwise.
func (n *sessionNegotiator) tryAddress(privKey *btcec.PrivateKey,
func (n *sessionNegotiator) tryAddress(sessionKey keychain.SingleKeyECDH,
keyIndex uint32, tower *wtdb.Tower, lnAddr *lnwire.NetAddress) error {
// Connect to the tower address using our generated session key.
conn, err := n.cfg.Dial(privKey, lnAddr)
conn, err := n.cfg.Dial(sessionKey, lnAddr)
if err != nil {
return err
}
@ -417,9 +426,7 @@ func (n *sessionNegotiator) tryAddress(privKey *btcec.PrivateKey,
// TODO(conner): validate reward address
rewardPkScript := createSessionReply.Data
sessionID := wtdb.NewSessionIDFromPubKey(
privKey.PubKey(),
)
sessionID := wtdb.NewSessionIDFromPubKey(sessionKey.PubKey())
clientSession := &wtdb.ClientSession{
ClientSessionBody: wtdb.ClientSessionBody{
TowerID: tower.ID,
@ -428,7 +435,7 @@ func (n *sessionNegotiator) tryAddress(privKey *btcec.PrivateKey,
RewardPkScript: rewardPkScript,
},
Tower: tower,
SessionKeyECDH: privKey,
SessionKeyECDH: sessionKey,
ID: sessionID,
}

@ -6,9 +6,9 @@ import (
"sync"
"time"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/watchtower/wtdb"
"github.com/lightningnetwork/lnd/watchtower/wtserver"
@ -41,8 +41,8 @@ type sessionQueueConfig struct {
// Dial allows the client to dial the tower using it's public key and
// net address.
Dial func(*btcec.PrivateKey,
*lnwire.NetAddress) (wtserver.Peer, error)
Dial func(keychain.SingleKeyECDH, *lnwire.NetAddress) (wtserver.Peer,
error)
// SendMessage encodes, encrypts, and writes a message to the given peer.
SendMessage func(wtserver.Peer, wtwire.Message) error

@ -4,7 +4,7 @@ import (
"fmt"
"io"
"github.com/btcsuite/btcd/btcec"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/watchtower/blob"
"github.com/lightningnetwork/lnd/watchtower/wtpolicy"
@ -65,7 +65,7 @@ type ClientSession struct {
//
// NOTE: This value is not serialized. It is derived using the KeyIndex
// on startup to avoid storing private keys on disk.
SessionKeyECDH *btcec.PrivateKey
SessionKeyECDH keychain.SingleKeyECDH
}
// ClientSessionBody represents the primary components of a ClientSession that

@ -20,29 +20,6 @@ func NewSecretKeyRing() *SecretKeyRing {
}
}
// DerivePrivKey derives the private key for a given key descriptor. If
// this method is called twice with the same argument, it will return the same
// private key.
func (m *SecretKeyRing) DerivePrivKey(
desc keychain.KeyDescriptor) (*btcec.PrivateKey, error) {
m.mu.Lock()
defer m.mu.Unlock()
if key, ok := m.keys[desc.KeyLocator]; ok {
return key, nil
}
privKey, err := btcec.NewPrivateKey(btcec.S256())
if err != nil {
return nil, err
}
m.keys[desc.KeyLocator] = privKey
return privKey, nil
}
// DeriveKey attempts to derive an arbitrary key specified by the
// passed KeyLocator. This may be used in several recovery scenarios,
// or when manually rotating something like our current default node

@ -8,10 +8,10 @@ import (
"sync"
"time"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/connmgr"
"github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/watchtower/wtdb"
"github.com/lightningnetwork/lnd/watchtower/wtwire"
@ -35,7 +35,7 @@ type Config struct {
// NodeKeyECDH is the the ECDH capable wrapper of the key to be used in
// accepting new brontide connections.
NodeKeyECDH *btcec.PrivateKey
NodeKeyECDH keychain.SingleKeyECDH
// Listeners specifies which address to which clients may connect.
Listeners []net.Listener