From d6ba0a5b85d0280e4c3d1886c5bacbf2d77b158b Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 28 Apr 2020 10:06:17 +0200 Subject: [PATCH 01/15] keychain: add new abstraction interfaces --- keychain/derivation.go | 62 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/keychain/derivation.go b/keychain/derivation.go index 2fbc5387..18a1b345 100644 --- a/keychain/derivation.go +++ b/keychain/derivation.go @@ -167,8 +167,8 @@ type KeyRing interface { DeriveKey(keyLoc KeyLocator) (KeyDescriptor, error) } -// SecretKeyRing is a similar to the regular KeyRing interface, but it is also -// able to derive *private keys*. As this is a super-set of the regular +// SecretKeyRing is a ring similar to the regular KeyRing interface, but it is +// also able to derive *private keys*. As this is a super-set of the regular // KeyRing, we also expect the SecretKeyRing to implement the fully KeyRing // interface. The methods in this struct may be used to extract the node key in // order to accept inbound network connections, or to do manual signing for @@ -194,5 +194,63 @@ type SecretKeyRing interface { ScalarMult(keyDesc KeyDescriptor, pubKey *btcec.PublicKey) ([]byte, error) } +// DigestSignerRing is an interface that abstracts away basic low-level ECDSA +// signing on keys within a key ring. +type DigestSignerRing interface { + // SignDigest signs the given SHA256 message digest with the private key + // described in the key descriptor. + SignDigest(keyDesc KeyDescriptor, digest [32]byte) (*btcec.Signature, + error) + + // SignDigestCompact signs the given SHA256 message digest with the + // private key described in the key descriptor and returns the signature + // in the compact, public key recoverable format. + SignDigestCompact(keyDesc KeyDescriptor, digest [32]byte) ([]byte, error) +} + +// SingleKeyDigestSigner is an abstraction interface that hides the +// implementation of the low-level ECDSA signing operations by wrapping a +// single, specific private key. +type SingleKeyDigestSigner interface { + // PubKey returns the public key of the wrapped private key. + PubKey() *btcec.PublicKey + + // SignDigest signs the given SHA256 message digest with the wrapped + // private key. + SignDigest(digest [32]byte) (*btcec.Signature, error) + + // SignDigestCompact signs the given SHA256 message digest with the + // wrapped private key and returns the signature in the compact, public + // key recoverable format. + SignDigestCompact(digest [32]byte) ([]byte, error) +} + +// ECDHRing is an interface that abstracts away basic low-level ECDH shared key +// generation on keys within a key ring. +type ECDHRing interface { + // ECDH performs a scalar multiplication (ECDH-like operation) between + // the target key descriptor and remote public key. The output + // returned will be the sha256 of the resulting shared point serialized + // in compressed format. If k is our private key, and P is the public + // key, we perform the following operation: + // + // sx := k*P + // s := sha256(sx.SerializeCompressed()) + ECDH(keyDesc KeyDescriptor, pubKey *btcec.PublicKey) ([32]byte, error) +} + +// SingleKeyECDH is an abstraction interface that hides the implementation of an +// ECDH operation by wrapping a single, specific private key. +type SingleKeyECDH interface { + // PubKey returns the public key of the wrapped private key. + PubKey() *btcec.PublicKey + + // ECDH performs a scalar multiplication (ECDH-like operation) between + // the wrapped private key and remote public key. The output returned + // will be the sha256 of the resulting shared point serialized in + // compressed format. + ECDH(pubKey *btcec.PublicKey) ([32]byte, error) +} + // TODO(roasbeef): extend to actually support scalar mult of key? // * would allow to push in initial handshake auth into interface as well From 6cf1844b0eaeda4919e39ad04973ba24b4a50a4f Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 28 Apr 2020 10:06:19 +0200 Subject: [PATCH 02/15] keychain: add new interface methods to wallet --- keychain/btcwallet.go | 60 +++++++++++++++++++++++++++++++++++++++++- keychain/derivation.go | 4 +++ mock.go | 20 ++++++++++++++ 3 files changed, 83 insertions(+), 1 deletion(-) diff --git a/keychain/btcwallet.go b/keychain/btcwallet.go index b8a47b12..42737daf 100644 --- a/keychain/btcwallet.go +++ b/keychain/btcwallet.go @@ -247,7 +247,9 @@ func (b *BtcWalletKeyRing) DeriveKey(keyLoc KeyLocator) (KeyDescriptor, error) { // passed key descriptor. // // NOTE: This is part of the keychain.SecretKeyRing interface. -func (b *BtcWalletKeyRing) DerivePrivKey(keyDesc KeyDescriptor) (*btcec.PrivateKey, error) { +func (b *BtcWalletKeyRing) DerivePrivKey(keyDesc KeyDescriptor) ( + *btcec.PrivateKey, error) { + var key *btcec.PrivateKey db := b.wallet.Database() @@ -371,3 +373,59 @@ func (b *BtcWalletKeyRing) ScalarMult(keyDesc KeyDescriptor, return h[:], nil } + +// ECDH performs a scalar multiplication (ECDH-like operation) between the +// target key descriptor and remote public key. The output returned will be +// the sha256 of the resulting shared point serialized in compressed format. If +// k is our private key, and P is the public key, we perform the following +// operation: +// +// sx := k*P s := sha256(sx.SerializeCompressed()) +// +// NOTE: This is part of the keychain.ECDHRing interface. +func (b *BtcWalletKeyRing) ECDH(keyDesc KeyDescriptor, + pub *btcec.PublicKey) ([32]byte, error) { + + privKey, err := b.DerivePrivKey(keyDesc) + if err != nil { + return [32]byte{}, err + } + + s := &btcec.PublicKey{} + x, y := btcec.S256().ScalarMult(pub.X, pub.Y, privKey.D.Bytes()) + s.X = x + s.Y = y + + h := sha256.Sum256(s.SerializeCompressed()) + + return h, nil +} + +// SignDigest signs the given SHA256 message digest with the private key +// described in the key descriptor. +// +// NOTE: This is part of the keychain.DigestSignerRing interface. +func (b *BtcWalletKeyRing) SignDigest(keyDesc KeyDescriptor, + digest [32]byte) (*btcec.Signature, error) { + + privKey, err := b.DerivePrivKey(keyDesc) + if err != nil { + return nil, err + } + return privKey.Sign(digest[:]) +} + +// SignDigestCompact signs the given SHA256 message digest with the private key +// described in the key descriptor and returns the signature in the compact, +// public key recoverable format. +// +// NOTE: This is part of the keychain.DigestSignerRing interface. +func (b *BtcWalletKeyRing) SignDigestCompact(keyDesc KeyDescriptor, + digest [32]byte) ([]byte, error) { + + privKey, err := b.DerivePrivKey(keyDesc) + if err != nil { + return nil, err + } + return btcec.SignCompact(btcec.S256(), privKey, digest[:], true) +} diff --git a/keychain/derivation.go b/keychain/derivation.go index 18a1b345..67c901d0 100644 --- a/keychain/derivation.go +++ b/keychain/derivation.go @@ -176,6 +176,10 @@ type KeyRing interface { type SecretKeyRing interface { KeyRing + ECDHRing + + DigestSignerRing + // DerivePrivKey attempts to derive the private key that corresponds to // the passed key descriptor. If the public key is set, then this // method will perform an in-order scan over the key set, with a max of diff --git a/mock.go b/mock.go index 2b13e75f..7729bb76 100644 --- a/mock.go +++ b/mock.go @@ -363,3 +363,23 @@ func (m *mockSecretKeyRing) ScalarMult(keyDesc keychain.KeyDescriptor, pubKey *btcec.PublicKey) ([]byte, error) { return nil, nil } + +func (m *mockSecretKeyRing) ECDH(_ keychain.KeyDescriptor, + pubKey *btcec.PublicKey) ([32]byte, error) { + + return [32]byte{}, nil +} + +func (m *mockSecretKeyRing) SignDigest(_ keychain.KeyDescriptor, + digest [32]byte) (*btcec.Signature, error) { + + return m.rootKey.Sign(digest[:]) +} + +func (m *mockSecretKeyRing) SignDigestCompact(_ keychain.KeyDescriptor, + digest [32]byte) ([]byte, error) { + + return btcec.SignCompact(btcec.S256(), m.rootKey, digest[:], true) +} + +var _ keychain.SecretKeyRing = (*mockSecretKeyRing)(nil) From cf0380ac81eddafe70092e7c5afcccdc1121bc97 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 28 Apr 2020 10:06:20 +0200 Subject: [PATCH 03/15] keychain: add single key wrapper implementations --- keychain/ecdh.go | 82 ++++++++++++++++++++++++++++++++++++++++++++++ keychain/signer.go | 56 +++++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 keychain/ecdh.go create mode 100644 keychain/signer.go diff --git a/keychain/ecdh.go b/keychain/ecdh.go new file mode 100644 index 00000000..3d20c2d2 --- /dev/null +++ b/keychain/ecdh.go @@ -0,0 +1,82 @@ +package keychain + +import ( + "crypto/sha256" + + "github.com/btcsuite/btcd/btcec" +) + +// NewPubKeyECDH wraps the given key of the key ring so it adheres to the +// SingleKeyECDH interface. +func NewPubKeyECDH(keyDesc KeyDescriptor, ecdh ECDHRing) *PubKeyECDH { + return &PubKeyECDH{ + keyDesc: keyDesc, + ecdh: ecdh, + } +} + +// PubKeyECDH is an implementation of the SingleKeyECDH interface. It wraps an +// ECDH key ring so it can perform ECDH shared key generation against a single +// abstracted away private key. +type PubKeyECDH struct { + keyDesc KeyDescriptor + ecdh ECDHRing +} + +// PubKey returns the public key of the private key that is abstracted away by +// the interface. +// +// NOTE: This is part of the SingleKeyECDH interface. +func (p *PubKeyECDH) PubKey() *btcec.PublicKey { + return p.keyDesc.PubKey +} + +// ECDH performs a scalar multiplication (ECDH-like operation) between the +// abstracted private key and a remote public key. The output returned will be +// the sha256 of the resulting shared point serialized in compressed format. If +// k is our private key, and P is the public key, we perform the following +// operation: +// +// sx := k*P +// s := sha256(sx.SerializeCompressed()) +// +// NOTE: This is part of the SingleKeyECDH interface. +func (p *PubKeyECDH) ECDH(pubKey *btcec.PublicKey) ([32]byte, error) { + return p.ecdh.ECDH(p.keyDesc, pubKey) +} + +// PrivKeyECDH is an implementation of the SingleKeyECDH in which we do have the +// full private key. This can be used to wrap a temporary key to conform to the +// SingleKeyECDH interface. +type PrivKeyECDH struct { + // PrivKey is the private key that is used for the ECDH operation. + PrivKey *btcec.PrivateKey +} + +// PubKey returns the public key of the private key that is abstracted away by +// the interface. +// +// NOTE: This is part of the SingleKeyECDH interface. +func (p *PrivKeyECDH) PubKey() *btcec.PublicKey { + return p.PrivKey.PubKey() +} + +// ECDH performs a scalar multiplication (ECDH-like operation) between the +// abstracted private key and a remote public key. The output returned will be +// the sha256 of the resulting shared point serialized in compressed format. If +// k is our private key, and P is the public key, we perform the following +// operation: +// +// sx := k*P +// s := sha256(sx.SerializeCompressed()) +// +// NOTE: This is part of the SingleKeyECDH interface. +func (p *PrivKeyECDH) ECDH(pub *btcec.PublicKey) ([32]byte, error) { + s := &btcec.PublicKey{} + s.X, s.Y = btcec.S256().ScalarMult(pub.X, pub.Y, p.PrivKey.D.Bytes()) + + return sha256.Sum256(s.SerializeCompressed()), nil +} + +var _ SingleKeyECDH = (*PubKeyECDH)(nil) +var _ SingleKeyECDH = (*PrivKeyECDH)(nil) diff --git a/keychain/signer.go b/keychain/signer.go new file mode 100644 index 00000000..cdd512eb --- /dev/null +++ b/keychain/signer.go @@ -0,0 +1,56 @@ +package keychain + +import "github.com/btcsuite/btcd/btcec" + +func NewPubKeyDigestSigner(keyDesc KeyDescriptor, + signer DigestSignerRing) *PubKeyDigestSigner { + + return &PubKeyDigestSigner{ + keyDesc: keyDesc, + digestSigner: signer, + } +} + +type PubKeyDigestSigner struct { + keyDesc KeyDescriptor + digestSigner DigestSignerRing +} + +func (p *PubKeyDigestSigner) PubKey() *btcec.PublicKey { + return p.keyDesc.PubKey +} + +func (p *PubKeyDigestSigner) SignDigest(digest [32]byte) (*btcec.Signature, + error) { + + return p.digestSigner.SignDigest(p.keyDesc, digest) +} + +func (p *PubKeyDigestSigner) SignDigestCompact(digest [32]byte) ([]byte, + error) { + + return p.digestSigner.SignDigestCompact(p.keyDesc, digest) +} + +type PrivKeyDigestSigner struct { + PrivKey *btcec.PrivateKey +} + +func (p *PrivKeyDigestSigner) PubKey() *btcec.PublicKey { + return p.PrivKey.PubKey() +} + +func (p *PrivKeyDigestSigner) SignDigest(digest [32]byte) (*btcec.Signature, + error) { + + return p.PrivKey.Sign(digest[:]) +} + +func (p *PrivKeyDigestSigner) SignDigestCompact(digest [32]byte) ([]byte, + error) { + + return btcec.SignCompact(btcec.S256(), p.PrivKey, digest[:], true) +} + +var _ SingleKeyDigestSigner = (*PubKeyDigestSigner)(nil) +var _ SingleKeyDigestSigner = (*PrivKeyDigestSigner)(nil) From 7f1b865b533256b2be14dc1ea34f2527d0a135df Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 28 Apr 2020 10:06:21 +0200 Subject: [PATCH 04/15] multi: rename server's identityPriv to identityECDH --- peer.go | 2 +- pilot.go | 2 +- rpcserver.go | 6 +++--- server.go | 30 +++++++++++++++--------------- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/peer.go b/peer.go index aa4cbce9..4b5d6643 100644 --- a/peer.go +++ b/peer.go @@ -555,7 +555,7 @@ func (p *peer) loadActiveChannels(chans []*channeldb.OpenChannel) ( // particular channel. var selfPolicy *channeldb.ChannelEdgePolicy if info != nil && bytes.Equal(info.NodeKey1Bytes[:], - p.server.identityPriv.PubKey().SerializeCompressed()) { + p.server.identityECDH.PubKey().SerializeCompressed()) { selfPolicy = p1 } else { diff --git a/pilot.go b/pilot.go index 8d9bc61e..64e5eff2 100644 --- a/pilot.go +++ b/pilot.go @@ -170,7 +170,7 @@ func initAutoPilot(svr *server, cfg *lncfg.AutoPilot, // With the heuristic itself created, we can now populate the remainder // of the items that the autopilot agent needs to perform its duties. - self := svr.identityPriv.PubKey() + self := svr.identityECDH.PubKey() pilotCfg := autopilot.Config{ Self: self, Heuristic: weightedAttachment, diff --git a/rpcserver.go b/rpcserver.go index e5ade52c..6e0af9a9 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -1433,7 +1433,7 @@ func (r *rpcServer) ConnectPeer(ctx context.Context, } // Connections to ourselves are disallowed for obvious reasons. - if pubKey.IsEqual(r.server.identityPriv.PubKey()) { + if pubKey.IsEqual(r.server.identityECDH.PubKey()) { return nil, fmt.Errorf("cannot make connection to self") } @@ -1781,7 +1781,7 @@ func (r *rpcServer) parseOpenChannelReq(in *lnrpc.OpenChannelRequest, // Making a channel to ourselves wouldn't be of any use, so we // explicitly disallow them. - if nodePubKey.IsEqual(r.server.identityPriv.PubKey()) { + if nodePubKey.IsEqual(r.server.identityECDH.PubKey()) { return nil, fmt.Errorf("cannot open channel to self") } @@ -2406,7 +2406,7 @@ func (r *rpcServer) GetInfo(ctx context.Context, } nPendingChannels := uint32(len(pendingChannels)) - idPub := r.server.identityPriv.PubKey().SerializeCompressed() + idPub := r.server.identityECDH.PubKey().SerializeCompressed() encodedIDPub := hex.EncodeToString(idPub) bestHash, bestHeight, err := r.server.cc.chainIO.GetBestBlock() diff --git a/server.go b/server.go index 8e84b38d..d7f40901 100644 --- a/server.go +++ b/server.go @@ -135,9 +135,9 @@ type server struct { cfg *Config - // identityPriv is the private key used to authenticate any incoming - // connections. - identityPriv *btcec.PrivateKey + // identityECDH is an ECDH capable wrapper for the private key used + // to authenticate any incoming connections. + identityECDH *btcec.PrivateKey // nodeSigner is an implementation of the MessageSigner implementation // that's backed by the identity private key of the running lnd node. @@ -425,7 +425,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, chanDB *channeldb.DB, channelNotifier: channelnotifier.New(chanDB), - identityPriv: privKey, + identityECDH: privKey, nodeSigner: netann.NewNodeSigner(privKey), listenAddrs: listenAddrs, @@ -651,7 +651,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, chanDB *channeldb.DB, // With the announcement generated, we'll sign it to properly // authenticate the message on the network. authSig, err := netann.SignAnnouncement( - s.nodeSigner, s.identityPriv.PubKey(), nodeAnn, + s.nodeSigner, s.identityECDH.PubKey(), nodeAnn, ) if err != nil { return nil, fmt.Errorf("unable to generate signature for "+ @@ -799,7 +799,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, chanDB *channeldb.DB, SubBatchDelay: time.Second * 5, IgnoreHistoricalFilters: cfg.IgnoreHistoricalGossipFilters, }, - s.identityPriv.PubKey(), + s.identityECDH.PubKey(), ) s.localChanMgr = &localchans.Manager{ @@ -1227,7 +1227,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, chanDB *channeldb.DB, OnAccept: s.InboundPeerConnected, RetryDuration: time.Second * 5, TargetOutbound: 100, - Dial: noiseDial(s.identityPriv, s.cfg.net), + Dial: noiseDial(s.identityECDH, s.cfg.net), OnConnection: s.OutboundPeerConnected, }) if err != nil { @@ -2020,7 +2020,7 @@ func (s *server) createNewHiddenService() error { Color: newNodeAnn.RGBColor, AuthSigBytes: newNodeAnn.Signature.ToSignatureBytes(), } - copy(selfNode.PubKeyBytes[:], s.identityPriv.PubKey().SerializeCompressed()) + copy(selfNode.PubKeyBytes[:], s.identityECDH.PubKey().SerializeCompressed()) if err := s.chanDB.ChannelGraph().SetSourceNode(selfNode); err != nil { return fmt.Errorf("can't set self node: %v", err) } @@ -2050,7 +2050,7 @@ func (s *server) genNodeAnnouncement(refresh bool, // Otherwise, we'll sign a new update after applying all of the passed // modifiers. err := netann.SignNodeAnnouncement( - s.nodeSigner, s.identityPriv.PubKey(), s.currentNodeAnn, + s.nodeSigner, s.identityECDH.PubKey(), s.currentNodeAnn, modifiers..., ) if err != nil { @@ -2102,7 +2102,7 @@ func (s *server) establishPersistentConnections() error { // TODO(roasbeef): instead iterate over link nodes and query graph for // each of the nodes. - selfPub := s.identityPriv.PubKey().SerializeCompressed() + selfPub := s.identityECDH.PubKey().SerializeCompressed() err = sourceNode.ForEachChannel(nil, func( tx kvdb.ReadTx, chanInfo *channeldb.ChannelEdgeInfo, @@ -2552,7 +2552,7 @@ func (s *server) InboundPeerConnected(conn net.Conn) { // not of the same type of the new connection (inbound), then // we'll close out the new connection s.t there's only a single // connection between us. - localPub := s.identityPriv.PubKey() + localPub := s.identityECDH.PubKey() if !connectedPeer.inbound && !shouldDropLocalConnection(localPub, nodePub) { @@ -2663,7 +2663,7 @@ func (s *server) OutboundPeerConnected(connReq *connmgr.ConnReq, conn net.Conn) // not of the same type of the new connection (outbound), then // we'll close out the new connection s.t there's only a single // connection between us. - localPub := s.identityPriv.PubKey() + localPub := s.identityECDH.PubKey() if connectedPeer.inbound && shouldDropLocalConnection(localPub, nodePub) { @@ -3264,7 +3264,7 @@ func (s *server) ConnectToPeer(addr *lnwire.NetAddress, perm bool) error { // notify the caller if the connection attempt has failed. Otherwise, it will be // closed. func (s *server) connectToPeer(addr *lnwire.NetAddress, errChan chan<- error) { - conn, err := brontide.Dial(s.identityPriv, addr, s.cfg.net.Dial) + conn, err := brontide.Dial(s.identityECDH, addr, s.cfg.net.Dial) if err != nil { srvrLog.Errorf("Unable to connect to %v: %v", addr, err) select { @@ -3467,7 +3467,7 @@ func (s *server) fetchNodeAdvertisedAddr(pub *btcec.PublicKey) (net.Addr, error) func (s *server) fetchLastChanUpdate() func(lnwire.ShortChannelID) ( *lnwire.ChannelUpdate, error) { - ourPubKey := s.identityPriv.PubKey().SerializeCompressed() + ourPubKey := s.identityECDH.PubKey().SerializeCompressed() return func(cid lnwire.ShortChannelID) (*lnwire.ChannelUpdate, error) { info, edge1, edge2, err := s.chanRouter.GetChannelByID(cid) if err != nil { @@ -3483,7 +3483,7 @@ func (s *server) fetchLastChanUpdate() func(lnwire.ShortChannelID) ( // applyChannelUpdate applies the channel update to the different sub-systems of // the server. func (s *server) applyChannelUpdate(update *lnwire.ChannelUpdate) error { - pubKey := s.identityPriv.PubKey() + pubKey := s.identityECDH.PubKey() errChan := s.authGossiper.ProcessLocalAnnouncement(update, pubKey) select { case err := <-errChan: From 140731af00753a8823037ab1669d6835bb19977b Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 28 Apr 2020 10:06:23 +0200 Subject: [PATCH 05/15] lnd+watchtower: rename NodePrivKey to NodeKeyECDH --- lnd.go | 2 +- watchtower/config.go | 6 +++--- watchtower/standalone.go | 6 +++--- watchtower/wtclient/client_test.go | 4 ++-- watchtower/wtserver/server.go | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lnd.go b/lnd.go index 840e684f..f519e3f2 100644 --- a/lnd.go +++ b/lnd.go @@ -570,7 +570,7 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error { lnwallet.WitnessPubKey, false, ) }, - NodePrivKey: towerPrivKey, + NodeKeyECDH: towerPrivKey, PublishTx: activeChainControl.wallet.PublishTransaction, ChainHash: *activeNetParams.GenesisHash, } diff --git a/watchtower/config.go b/watchtower/config.go index 7ef8a617..2b649dbc 100644 --- a/watchtower/config.go +++ b/watchtower/config.go @@ -63,9 +63,9 @@ type Config struct { // successfully sent funds can be received. NewAddress func() (btcutil.Address, error) - // NodePrivKey is private key to be used in accepting new brontide - // connections. - NodePrivKey *btcec.PrivateKey + // NodeKeyECDH is the ECDH capable wrapper of the key to be used in + // accepting new brontide connections. + NodeKeyECDH *btcec.PrivateKey // PublishTx provides the ability to send a signed transaction to the // network. diff --git a/watchtower/standalone.go b/watchtower/standalone.go index 6dd3fe6d..8e56d962 100644 --- a/watchtower/standalone.go +++ b/watchtower/standalone.go @@ -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( - cfg.NodePrivKey, listenAddr.String(), + cfg.NodeKeyECDH, listenAddr.String(), ) if err != nil { return nil, err @@ -85,7 +85,7 @@ func New(cfg *Config) (*Standalone, error) { server, err := wtserver.New(&wtserver.Config{ ChainHash: cfg.ChainHash, DB: cfg.DB, - NodePrivKey: cfg.NodePrivKey, + NodeKeyECDH: cfg.NodeKeyECDH, Listeners: listeners, ReadTimeout: cfg.ReadTimeout, WriteTimeout: cfg.WriteTimeout, @@ -190,7 +190,7 @@ func (w *Standalone) createNewHiddenService() error { // // NOTE: Part of the watchtowerrpc.WatchtowerBackend interface. func (w *Standalone) PubKey() *btcec.PublicKey { - return w.cfg.NodePrivKey.PubKey() + return w.cfg.NodeKeyECDH.PubKey() } // ListeningAddrs returns the listening addresses where the watchtower server diff --git a/watchtower/wtclient/client_test.go b/watchtower/wtclient/client_test.go index 577d33f9..fda9c0e2 100644 --- a/watchtower/wtclient/client_test.go +++ b/watchtower/wtclient/client_test.go @@ -416,7 +416,7 @@ func newHarness(t *testing.T, cfg harnessCfg) *testHarness { DB: serverDB, ReadTimeout: timeout, WriteTimeout: timeout, - NodePrivKey: privKey, + NodeKeyECDH: privKey, NewAddress: func() (btcutil.Address, error) { return addr, nil }, @@ -519,7 +519,7 @@ func (h *testHarness) startClient() { h.t.Fatalf("Unable to resolve tower TCP addr: %v", err) } towerAddr := &lnwire.NetAddress{ - IdentityKey: h.serverCfg.NodePrivKey.PubKey(), + IdentityKey: h.serverCfg.NodeKeyECDH.PubKey(), Address: towerTCPAddr, } diff --git a/watchtower/wtserver/server.go b/watchtower/wtserver/server.go index 1c46201b..0152839a 100644 --- a/watchtower/wtserver/server.go +++ b/watchtower/wtserver/server.go @@ -33,9 +33,9 @@ type Config struct { // storing state updates. DB DB - // NodePrivKey is private key to be used in accepting new brontide - // connections. - NodePrivKey *btcec.PrivateKey + // NodeKeyECDH is the the ECDH capable wrapper of the key to be used in + // accepting new brontide connections. + NodeKeyECDH *btcec.PrivateKey // Listeners specifies which address to which clients may connect. Listeners []net.Listener From b0cb110e864d9047409b947ad1a2e2090e33b885 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 28 Apr 2020 10:06:24 +0200 Subject: [PATCH 06/15] watchtower: rename SessionPrivKey to SessionKeyECDH --- watchtower/wtclient/client.go | 2 +- watchtower/wtclient/session_negotiator.go | 2 +- watchtower/wtclient/session_queue.go | 2 +- watchtower/wtdb/client_session.go | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/watchtower/wtclient/client.go b/watchtower/wtclient/client.go index 1f3c1c3c..c3bf70b5 100644 --- a/watchtower/wtclient/client.go +++ b/watchtower/wtclient/client.go @@ -362,7 +362,7 @@ func getClientSessions(db DB, keyRing SecretKeyRing, forTower *wtdb.TowerID, if err != nil { return nil, err } - s.SessionPrivKey = sessionKey + s.SessionKeyECDH = sessionKey // If an optional filter was provided, use it to filter out any // undesired sessions. diff --git a/watchtower/wtclient/session_negotiator.go b/watchtower/wtclient/session_negotiator.go index e6ccbfb9..ae894762 100644 --- a/watchtower/wtclient/session_negotiator.go +++ b/watchtower/wtclient/session_negotiator.go @@ -428,7 +428,7 @@ func (n *sessionNegotiator) tryAddress(privKey *btcec.PrivateKey, RewardPkScript: rewardPkScript, }, Tower: tower, - SessionPrivKey: privKey, + SessionKeyECDH: privKey, ID: sessionID, } diff --git a/watchtower/wtclient/session_queue.go b/watchtower/wtclient/session_queue.go index 87834815..b383644c 100644 --- a/watchtower/wtclient/session_queue.go +++ b/watchtower/wtclient/session_queue.go @@ -285,7 +285,7 @@ func (q *sessionQueue) sessionManager() { // drainBackups attempts to send all pending updates in the queue to the tower. func (q *sessionQueue) drainBackups() { // First, check that we are able to dial this session's tower. - conn, err := q.cfg.Dial(q.cfg.ClientSession.SessionPrivKey, q.towerAddr) + conn, err := q.cfg.Dial(q.cfg.ClientSession.SessionKeyECDH, q.towerAddr) if err != nil { log.Errorf("SessionQueue(%s) unable to dial tower at %v: %v", q.ID(), q.towerAddr, err) diff --git a/watchtower/wtdb/client_session.go b/watchtower/wtdb/client_session.go index cbf58c51..eb76528c 100644 --- a/watchtower/wtdb/client_session.go +++ b/watchtower/wtdb/client_session.go @@ -60,12 +60,12 @@ type ClientSession struct { // tower with TowerID. Tower *Tower - // SessionPrivKey is the ephemeral secret key used to connect to the - // watchtower. + // SessionKeyECDH is the ECDH capable wrapper of the ephemeral secret + // key used to connect to the watchtower. // // NOTE: This value is not serialized. It is derived using the KeyIndex // on startup to avoid storing private keys on disk. - SessionPrivKey *btcec.PrivateKey + SessionKeyECDH *btcec.PrivateKey } // ClientSessionBody represents the primary components of a ClientSession that From 6f702a43aa61f1667ef4ac96b895a5542896e12f Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 28 Apr 2020 10:06:25 +0200 Subject: [PATCH 07/15] watchtower: extend and rename SecretKeyRing --- watchtower/wtclient/client.go | 2 +- watchtower/wtclient/derivation.go | 2 +- watchtower/wtclient/interface.go | 14 ++++-- watchtower/wtclient/session_negotiator.go | 2 +- watchtower/wtmock/keyring.go | 54 +++++++++++++++++++++++ 5 files changed, 68 insertions(+), 6 deletions(-) diff --git a/watchtower/wtclient/client.go b/watchtower/wtclient/client.go index c3bf70b5..de58ecc2 100644 --- a/watchtower/wtclient/client.go +++ b/watchtower/wtclient/client.go @@ -132,7 +132,7 @@ type Config struct { // SecretKeyRing is used to derive the session keys used to communicate // with the tower. The client only stores the KeyLocators internally so // that we never store private keys on disk. - SecretKeyRing SecretKeyRing + SecretKeyRing ECDHKeyRing // Dial connects to an addr using the specified net and returns the // connection object. diff --git a/watchtower/wtclient/derivation.go b/watchtower/wtclient/derivation.go index cb0caec0..0dcb59a1 100644 --- a/watchtower/wtclient/derivation.go +++ b/watchtower/wtclient/derivation.go @@ -12,7 +12,7 @@ import ( // derivation path of: // // * m/1017'/coinType'/8/0/index -func DeriveSessionKey(keyRing SecretKeyRing, +func DeriveSessionKey(keyRing ECDHKeyRing, index uint32) (*btcec.PrivateKey, error) { return keyRing.DerivePrivKey(keychain.KeyDescriptor{ diff --git a/watchtower/wtclient/interface.go b/watchtower/wtclient/interface.go index f0416af3..f7cc40f8 100644 --- a/watchtower/wtclient/interface.go +++ b/watchtower/wtclient/interface.go @@ -112,10 +112,18 @@ func AuthDial(localPriv *btcec.PrivateKey, netAddr *lnwire.NetAddress, return brontide.Dial(localPriv, netAddr, dialer) } -// SecretKeyRing abstracts the ability to derive HD private keys given a -// description of the derivation path. -type SecretKeyRing interface { +// ECDHKeyRing abstracts the ability to derive shared ECDH keys given a +// description of the derivation path of a private key. +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 + // key. + DeriveKey(keyLoc keychain.KeyLocator) (keychain.KeyDescriptor, error) } diff --git a/watchtower/wtclient/session_negotiator.go b/watchtower/wtclient/session_negotiator.go index ae894762..f2ed5232 100644 --- a/watchtower/wtclient/session_negotiator.go +++ b/watchtower/wtclient/session_negotiator.go @@ -44,7 +44,7 @@ type NegotiatorConfig struct { // SecretKeyRing allows the client to derive new session private keys // when attempting to negotiate session with a tower. - SecretKeyRing SecretKeyRing + SecretKeyRing ECDHKeyRing // Candidates is an abstract set of tower candidates that the negotiator // will traverse serially when attempting to negotiate a new session. diff --git a/watchtower/wtmock/keyring.go b/watchtower/wtmock/keyring.go index f18a0fa8..3d23ea04 100644 --- a/watchtower/wtmock/keyring.go +++ b/watchtower/wtmock/keyring.go @@ -42,3 +42,57 @@ func (m *SecretKeyRing) DerivePrivKey( 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 +// key. +// +// NOTE: This is part of the wtclient.ECDHKeyRing interface. +func (m *SecretKeyRing) DeriveKey( + keyLoc keychain.KeyLocator) (keychain.KeyDescriptor, error) { + + m.mu.Lock() + defer m.mu.Unlock() + + if key, ok := m.keys[keyLoc]; ok { + return keychain.KeyDescriptor{ + KeyLocator: keyLoc, + PubKey: key.PubKey(), + }, nil + } + + privKey, err := btcec.NewPrivateKey(btcec.S256()) + if err != nil { + return keychain.KeyDescriptor{}, err + } + + m.keys[keyLoc] = privKey + + return keychain.KeyDescriptor{ + KeyLocator: keyLoc, + PubKey: privKey.PubKey(), + }, nil +} + +// ECDH performs a scalar multiplication (ECDH-like operation) between the +// target key descriptor and remote public key. The output returned will be the +// sha256 of the resulting shared point serialized in compressed format. If k is +// our private key, and P is the public key, we perform the following operation: +// +// sx := k*P +// s := sha256(sx.SerializeCompressed()) +// +// NOTE: This is part of the wtclient.ECDHKeyRing interface. +func (m *SecretKeyRing) ECDH(keyDesc keychain.KeyDescriptor, + pub *btcec.PublicKey) ([32]byte, error) { + + _, err := m.DeriveKey(keyDesc.KeyLocator) + if err != nil { + return [32]byte{}, err + } + + privKey := m.keys[keyDesc.KeyLocator] + ecdh := &keychain.PrivKeyECDH{PrivKey: privKey} + return ecdh.ECDH(pub) +} From be890ef9be421e142e0ce3a34beecdda77638711 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 28 Apr 2020 10:06:27 +0200 Subject: [PATCH 08/15] lnd+server+netann: use signing interface in node signer --- lnd.go | 14 +++++++++++++- netann/chan_status_manager_test.go | 4 +++- netann/channel_update_test.go | 14 ++++++++------ netann/node_signer.go | 30 ++++++++++++------------------ server.go | 12 +++++++++--- test_utils.go | 13 ++++++++----- 6 files changed, 53 insertions(+), 34 deletions(-) diff --git a/lnd.go b/lnd.go index f519e3f2..ed347aac 100644 --- a/lnd.go +++ b/lnd.go @@ -484,6 +484,18 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error { } idPrivKey.Curve = btcec.S256() + idKeyDesc, err := activeChainControl.keyRing.DeriveKey( + keychain.KeyLocator{ + Family: keychain.KeyFamilyNodeKey, + Index: 0, + }, + ) + if err != nil { + err := fmt.Errorf("error deriving node key: %v", err) + ltndLog.Error(err) + return err + } + if cfg.Tor.Active { srvrLog.Infof("Proxying all network traffic via Tor "+ "(stream_isolation=%v)! NOTE: Ensure the backend node "+ @@ -612,7 +624,7 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error { // connections. server, err := newServer( cfg, cfg.Listeners, chanDB, towerClientDB, activeChainControl, - idPrivKey, walletInitParams.ChansToRestore, chainedAcceptor, + idPrivKey, &idKeyDesc, walletInitParams.ChansToRestore, chainedAcceptor, torController, ) if err != nil { diff --git a/netann/chan_status_manager_test.go b/netann/chan_status_manager_test.go index f18774c3..2d155443 100644 --- a/netann/chan_status_manager_test.go +++ b/netann/chan_status_manager_test.go @@ -14,6 +14,7 @@ import ( "github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/netann" ) @@ -309,6 +310,7 @@ func newManagerCfg(t *testing.T, numChannels int, if err != nil { t.Fatalf("unable to generate key pair: %v", err) } + privKeySigner := &keychain.PrivKeyDigestSigner{PrivKey: privKey} graph := newMockGraph( t, numChannels, startEnabled, startEnabled, privKey.PubKey(), @@ -320,7 +322,7 @@ func newManagerCfg(t *testing.T, numChannels int, ChanEnableTimeout: 500 * time.Millisecond, ChanDisableTimeout: time.Second, OurPubKey: privKey.PubKey(), - MessageSigner: netann.NewNodeSigner(privKey), + MessageSigner: netann.NewNodeSigner(privKeySigner), IsChannelActive: htlcSwitch.HasActiveLink, ApplyChannelUpdate: graph.ApplyChannelUpdate, DB: graph, diff --git a/netann/channel_update_test.go b/netann/channel_update_test.go index 1fff1d51..5fc4beeb 100644 --- a/netann/channel_update_test.go +++ b/netann/channel_update_test.go @@ -7,6 +7,7 @@ import ( "github.com/btcsuite/btcd/btcec" "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/netann" @@ -30,7 +31,8 @@ func (m *mockSigner) SignMessage(pk *btcec.PublicKey, var _ lnwallet.MessageSigner = (*mockSigner)(nil) var ( - privKey, _ = btcec.NewPrivateKey(btcec.S256()) + privKey, _ = btcec.NewPrivateKey(btcec.S256()) + privKeySigner = &keychain.PrivKeyDigestSigner{PrivKey: privKey} pubKey = privKey.PubKey() @@ -52,35 +54,35 @@ var updateDisableTests = []updateDisableTest{ startEnabled: true, disable: true, startTime: time.Now(), - signer: netann.NewNodeSigner(privKey), + signer: netann.NewNodeSigner(privKeySigner), }, { name: "working signer enabled to enabled", startEnabled: true, disable: false, startTime: time.Now(), - signer: netann.NewNodeSigner(privKey), + signer: netann.NewNodeSigner(privKeySigner), }, { name: "working signer disabled to enabled", startEnabled: false, disable: false, startTime: time.Now(), - signer: netann.NewNodeSigner(privKey), + signer: netann.NewNodeSigner(privKeySigner), }, { name: "working signer disabled to disabled", startEnabled: false, disable: true, startTime: time.Now(), - signer: netann.NewNodeSigner(privKey), + signer: netann.NewNodeSigner(privKeySigner), }, { name: "working signer future monotonicity", startEnabled: true, disable: true, startTime: time.Now().Add(time.Hour), // must increment - signer: netann.NewNodeSigner(privKey), + signer: netann.NewNodeSigner(privKeySigner), }, { name: "failing signer", diff --git a/netann/node_signer.go b/netann/node_signer.go index 2b97c937..6cb87a29 100644 --- a/netann/node_signer.go +++ b/netann/node_signer.go @@ -6,25 +6,21 @@ 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" ) // NodeSigner is an implementation of the MessageSigner interface backed by the // identity private key of running lnd node. type NodeSigner struct { - privKey *btcec.PrivateKey + keySigner keychain.SingleKeyDigestSigner } // NewNodeSigner creates a new instance of the NodeSigner backed by the target // private key. -func NewNodeSigner(key *btcec.PrivateKey) *NodeSigner { - priv := &btcec.PrivateKey{} - priv.Curve = btcec.S256() - priv.PublicKey.X = key.X - priv.PublicKey.Y = key.Y - priv.D = key.D +func NewNodeSigner(keySigner keychain.SingleKeyDigestSigner) *NodeSigner { return &NodeSigner{ - privKey: priv, + keySigner: keySigner, } } @@ -36,13 +32,14 @@ func (n *NodeSigner) SignMessage(pubKey *btcec.PublicKey, // If this isn't our identity public key, then we'll exit early with an // error as we can't sign with this key. - if !pubKey.IsEqual(n.privKey.PubKey()) { + if !pubKey.IsEqual(n.keySigner.PubKey()) { return nil, fmt.Errorf("unknown public key") } // Otherwise, we'll sign the dsha256 of the target message. - digest := chainhash.DoubleHashB(msg) - sig, err := n.privKey.Sign(digest) + var digest [32]byte + copy(digest[:], chainhash.DoubleHashB(msg)) + sig, err := n.keySigner.SignDigest(digest) if err != nil { return nil, fmt.Errorf("can't sign the message: %v", err) } @@ -63,14 +60,11 @@ func (n *NodeSigner) SignCompact(msg []byte) ([]byte, error) { // SignDigestCompact signs the provided message digest under the resident // node's private key. The returned signature is a pubkey-recoverable signature. func (n *NodeSigner) SignDigestCompact(hash []byte) ([]byte, error) { + var digest [32]byte + copy(digest[:], hash) - // Should the signature reference a compressed public key or not. - isCompressedKey := true - - // btcec.SignCompact returns a pubkey-recoverable signature - sig, err := btcec.SignCompact( - btcec.S256(), n.privKey, hash, isCompressedKey, - ) + // keychain.SignDigestCompact returns a pubkey-recoverable signature. + sig, err := n.keySigner.SignDigestCompact(digest) if err != nil { return nil, fmt.Errorf("can't sign the hash: %v", err) } diff --git a/server.go b/server.go index d7f40901..a3a68ebc 100644 --- a/server.go +++ b/server.go @@ -41,6 +41,7 @@ import ( "github.com/lightningnetwork/lnd/htlcswitch/hop" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/invoices" + "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/lnpeer" "github.com/lightningnetwork/lnd/lnrpc" @@ -324,12 +325,17 @@ func noiseDial(idPriv *btcec.PrivateKey, // passed listener address. func newServer(cfg *Config, listenAddrs []net.Addr, chanDB *channeldb.DB, towerClientDB *wtdb.ClientDB, cc *chainControl, - privKey *btcec.PrivateKey, + privKey *btcec.PrivateKey, nodeKeyDesc *keychain.KeyDescriptor, chansToRestore walletunlocker.ChannelsToRecover, chanPredicate chanacceptor.ChannelAcceptor, torController *tor.Controller) (*server, error) { - var err error + var ( + err error + nodeKeySigner = keychain.NewPubKeyDigestSigner( + *nodeKeyDesc, cc.keyRing, + ) + ) listeners := make([]net.Listener, len(listenAddrs)) for i, listenAddr := range listenAddrs { @@ -426,7 +432,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, chanDB *channeldb.DB, channelNotifier: channelnotifier.New(chanDB), identityECDH: privKey, - nodeSigner: netann.NewNodeSigner(privKey), + nodeSigner: netann.NewNodeSigner(nodeKeySigner), listenAddrs: listenAddrs, diff --git a/test_utils.go b/test_utils.go index 77b5700b..8155f2f7 100644 --- a/test_utils.go +++ b/test_utils.go @@ -103,10 +103,13 @@ func createTestPeer(notifier chainntnfs.ChainNotifier, publTx chan *wire.MsgTx, updateChan func(a, b *channeldb.OpenChannel)) (*peer, *lnwallet.LightningChannel, *lnwallet.LightningChannel, func(), error) { - aliceKeyPriv, aliceKeyPub := btcec.PrivKeyFromBytes(btcec.S256(), - alicesPrivKey) - bobKeyPriv, bobKeyPub := btcec.PrivKeyFromBytes(btcec.S256(), - bobsPrivKey) + aliceKeyPriv, aliceKeyPub := btcec.PrivKeyFromBytes( + btcec.S256(), alicesPrivKey, + ) + aliceKeySigner := &keychain.PrivKeyDigestSigner{PrivKey: aliceKeyPriv} + bobKeyPriv, bobKeyPub := btcec.PrivKeyFromBytes( + btcec.S256(), bobsPrivKey, + ) channelCapacity := btcutil.Amount(10 * 1e8) channelBal := channelCapacity / 2 @@ -402,7 +405,7 @@ func createTestPeer(notifier chainntnfs.ChainNotifier, publTx chan *wire.MsgTx, } s.htlcSwitch = htlcSwitch - nodeSignerAlice := netann.NewNodeSigner(aliceKeyPriv) + nodeSignerAlice := netann.NewNodeSigner(aliceKeySigner) const chanActiveTimeout = time.Minute From ee74e4ba8628abfd989a85e8ecbb653c2b27b940 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 28 Apr 2020 10:06:28 +0200 Subject: [PATCH 09/15] server+mod: use ECDH interface for sphinx router --- go.mod | 2 +- go.sum | 4 ++-- htlcswitch/circuit_test.go | 4 +++- server.go | 5 ++++- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index b3807147..c57f99a9 100644 --- a/go.mod +++ b/go.mod @@ -37,7 +37,7 @@ require ( github.com/kkdai/bstream v0.0.0-20181106074824-b3251f7901ec github.com/lightninglabs/neutrino v0.11.1-0.20200316235139-bffc52e8f200 github.com/lightninglabs/protobuf-hex-display v1.3.3-0.20191212020323-b444784ce75d - github.com/lightningnetwork/lightning-onion v1.0.1 + github.com/lightningnetwork/lightning-onion v1.0.2-0.20200501022730-3c8c8d0b89ea github.com/lightningnetwork/lnd/cert v1.0.2 github.com/lightningnetwork/lnd/queue v1.0.4 github.com/lightningnetwork/lnd/ticker v1.0.0 diff --git a/go.sum b/go.sum index ec887b75..8f07ff2d 100644 --- a/go.sum +++ b/go.sum @@ -159,8 +159,8 @@ github.com/lightninglabs/neutrino v0.11.1-0.20200316235139-bffc52e8f200 h1:j4iZ1 github.com/lightninglabs/neutrino v0.11.1-0.20200316235139-bffc52e8f200/go.mod h1:MlZmoKa7CJP3eR1s5yB7Rm5aSyadpKkxqAwLQmog7N0= github.com/lightninglabs/protobuf-hex-display v1.3.3-0.20191212020323-b444784ce75d h1:QWD/5MPnaZfUVP7P8wLa4M8Td2DI7XXHXt2vhVtUgGI= github.com/lightninglabs/protobuf-hex-display v1.3.3-0.20191212020323-b444784ce75d/go.mod h1:KDb67YMzoh4eudnzClmvs2FbiLG9vxISmLApUkCa4uI= -github.com/lightningnetwork/lightning-onion v1.0.1 h1:qChGgS5+aPxFeR6JiUsGvanei1bn6WJpYbvosw/1604= -github.com/lightningnetwork/lightning-onion v1.0.1/go.mod h1:rigfi6Af/KqsF7Za0hOgcyq2PNH4AN70AaMRxcJkff4= +github.com/lightningnetwork/lightning-onion v1.0.2-0.20200501022730-3c8c8d0b89ea h1:oCj48NQ8u7Vz+MmzHqt0db6mxcFZo3Ho7M5gCJauY/k= +github.com/lightningnetwork/lightning-onion v1.0.2-0.20200501022730-3c8c8d0b89ea/go.mod h1:rigfi6Af/KqsF7Za0hOgcyq2PNH4AN70AaMRxcJkff4= github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 h1:sjOGyegMIhvgfq5oaue6Td+hxZuf3tDC8lAPrFldqFw= github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796/go.mod h1:3p7ZTf9V1sNPI5H8P3NkTFF4LuwMdPl2DodF60qAKqY= github.com/ltcsuite/ltcutil v0.0.0-20181217130922-17f3b04680b6/go.mod h1:8Vg/LTOO0KYa/vlHWJ6XZAevPQThGH5sufO0Hrou/lA= diff --git a/htlcswitch/circuit_test.go b/htlcswitch/circuit_test.go index 1b5d59e8..00a4b6e7 100644 --- a/htlcswitch/circuit_test.go +++ b/htlcswitch/circuit_test.go @@ -13,6 +13,7 @@ import ( "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/htlcswitch/hop" + "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwire" ) @@ -84,7 +85,8 @@ func initTestExtracter() { // db and no garbage collection. func newOnionProcessor(t *testing.T) *hop.OnionProcessor { sphinxRouter := sphinx.NewRouter( - sphinxPrivKey, &bitcoinCfg.SimNetParams, sphinx.NewMemoryReplayLog(), + &keychain.PrivKeyECDH{PrivKey: sphinxPrivKey}, + &bitcoinCfg.SimNetParams, sphinx.NewMemoryReplayLog(), ) if err := sphinxRouter.Start(); err != nil { diff --git a/server.go b/server.go index a3a68ebc..b8b51fc8 100644 --- a/server.go +++ b/server.go @@ -332,6 +332,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, chanDB *channeldb.DB, var ( err error + nodeKeyECDH = keychain.NewPubKeyECDH(*nodeKeyDesc, cc.keyRing) nodeKeySigner = keychain.NewPubKeyDigestSigner( *nodeKeyDesc, cc.keyRing, ) @@ -379,7 +380,9 @@ func newServer(cfg *Config, listenAddrs []net.Addr, chanDB *channeldb.DB, graphDir := chanDB.Path() sharedSecretPath := filepath.Join(graphDir, "sphinxreplay.db") replayLog := htlcswitch.NewDecayedLog(sharedSecretPath, cc.chainNotifier) - sphinxRouter := sphinx.NewRouter(privKey, activeNetParams.Params, replayLog) + sphinxRouter := sphinx.NewRouter( + nodeKeyECDH, activeNetParams.Params, replayLog, + ) writeBufferPool := pool.NewWriteBuffer( pool.DefaultWriteBufferGCInterval, From 535a22c590d9d32a5652a0179685b63e364ee243 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 28 Apr 2020 10:06:29 +0200 Subject: [PATCH 10/15] server+brontide: use ECDH interface for brontide handshake --- brontide/conn.go | 6 ++- brontide/listener.go | 9 ++-- brontide/noise.go | 87 +++++++++++++++++++------------- brontide/noise_test.go | 32 ++++++++---- server.go | 22 ++++---- watchtower/standalone.go | 2 +- watchtower/wtclient/interface.go | 2 +- 7 files changed, 94 insertions(+), 66 deletions(-) diff --git a/brontide/conn.go b/brontide/conn.go index 0ebed66f..33b550b8 100644 --- a/brontide/conn.go +++ b/brontide/conn.go @@ -8,6 +8,7 @@ import ( "time" "github.com/btcsuite/btcd/btcec" + "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwire" ) @@ -32,8 +33,9 @@ var _ net.Conn = (*Conn)(nil) // remote peer located at address which has remotePub as its long-term static // public key. In the case of a handshake failure, the connection is closed and // a non-nil error is returned. -func Dial(localPriv *btcec.PrivateKey, netAddr *lnwire.NetAddress, +func Dial(local keychain.SingleKeyECDH, netAddr *lnwire.NetAddress, dialer func(string, string) (net.Conn, error)) (*Conn, error) { + ipAddr := netAddr.Address.String() var conn net.Conn var err error @@ -44,7 +46,7 @@ func Dial(localPriv *btcec.PrivateKey, netAddr *lnwire.NetAddress, b := &Conn{ conn: conn, - noise: NewBrontideMachine(true, localPriv, netAddr.IdentityKey), + noise: NewBrontideMachine(true, local, netAddr.IdentityKey), } // Initiate the handshake by sending the first act to the receiver. diff --git a/brontide/listener.go b/brontide/listener.go index 95505ecf..f82b8457 100644 --- a/brontide/listener.go +++ b/brontide/listener.go @@ -7,7 +7,7 @@ import ( "net" "time" - "github.com/btcsuite/btcd/btcec" + "github.com/lightningnetwork/lnd/keychain" ) // defaultHandshakes is the maximum number of handshakes that can be done in @@ -20,7 +20,7 @@ const defaultHandshakes = 1000 // details w.r.t the handshake and encryption scheme used within the // connection. type Listener struct { - localStatic *btcec.PrivateKey + localStatic keychain.SingleKeyECDH tcp *net.TCPListener @@ -34,8 +34,9 @@ var _ net.Listener = (*Listener)(nil) // NewListener returns a new net.Listener which enforces the Brontide scheme // during both initial connection establishment and data transfer. -func NewListener(localStatic *btcec.PrivateKey, listenAddr string) (*Listener, - error) { +func NewListener(localStatic keychain.SingleKeyECDH, + listenAddr string) (*Listener, error) { + addr, err := net.ResolveTCPAddr("tcp", listenAddr) if err != nil { return nil, err diff --git a/brontide/noise.go b/brontide/noise.go index 17e0322f..18bdd789 100644 --- a/brontide/noise.go +++ b/brontide/noise.go @@ -14,6 +14,7 @@ import ( "golang.org/x/crypto/hkdf" "github.com/btcsuite/btcd/btcec" + "github.com/lightningnetwork/lnd/keychain" ) const ( @@ -71,14 +72,9 @@ var ( // ecdh performs an ECDH operation between pub and priv. The returned value is // the sha256 of the compressed shared point. -func ecdh(pub *btcec.PublicKey, priv *btcec.PrivateKey) []byte { - s := &btcec.PublicKey{} - x, y := btcec.S256().ScalarMult(pub.X, pub.Y, priv.D.Bytes()) - s.X = x - s.Y = y - - h := sha256.Sum256(s.SerializeCompressed()) - return h[:] +func ecdh(pub *btcec.PublicKey, priv keychain.SingleKeyECDH) ([]byte, error) { + hash, err := priv.ECDH(pub) + return hash[:], err } // cipherState encapsulates the state for the AEAD which will be used to @@ -289,8 +285,8 @@ type handshakeState struct { initiator bool - localStatic *btcec.PrivateKey - localEphemeral *btcec.PrivateKey + localStatic keychain.SingleKeyECDH + localEphemeral keychain.SingleKeyECDH // nolint (false positive) remoteStatic *btcec.PublicKey remoteEphemeral *btcec.PublicKey @@ -300,11 +296,12 @@ type handshakeState struct { // with the prologue and protocol name. If this is the responder's handshake // state, then the remotePub can be nil. func newHandshakeState(initiator bool, prologue []byte, - localPub *btcec.PrivateKey, remotePub *btcec.PublicKey) handshakeState { + localKey keychain.SingleKeyECDH, + remotePub *btcec.PublicKey) handshakeState { h := handshakeState{ initiator: initiator, - localStatic: localPub, + localStatic: localKey, remoteStatic: remotePub, } @@ -322,7 +319,7 @@ func newHandshakeState(initiator bool, prologue []byte, if initiator { h.mixHash(remotePub.SerializeCompressed()) } else { - h.mixHash(localPub.PubKey().SerializeCompressed()) + h.mixHash(localKey.PubKey().SerializeCompressed()) } return h @@ -393,11 +390,11 @@ type Machine struct { // string "lightning" as the prologue. The last parameter is a set of variadic // arguments for adding additional options to the brontide Machine // initialization. -func NewBrontideMachine(initiator bool, localPub *btcec.PrivateKey, +func NewBrontideMachine(initiator bool, localKey keychain.SingleKeyECDH, remotePub *btcec.PublicKey, options ...func(*Machine)) *Machine { handshake := newHandshakeState( - initiator, lightningPrologue, localPub, remotePub, + initiator, lightningPrologue, localKey, remotePub, ) m := &Machine{ @@ -451,22 +448,25 @@ const ( // // -> e, es func (b *Machine) GenActOne() ([ActOneSize]byte, error) { - var ( - err error - actOne [ActOneSize]byte - ) + var actOne [ActOneSize]byte // e - b.localEphemeral, err = b.ephemeralGen() + localEphemeral, err := b.ephemeralGen() if err != nil { return actOne, err } + b.localEphemeral = &keychain.PrivKeyECDH{ + PrivKey: localEphemeral, + } - ephemeral := b.localEphemeral.PubKey().SerializeCompressed() + ephemeral := localEphemeral.PubKey().SerializeCompressed() b.mixHash(ephemeral) // es - s := ecdh(b.remoteStatic, b.localEphemeral) + s, err := ecdh(b.remoteStatic, b.localEphemeral) + if err != nil { + return actOne, err + } b.mixKey(s[:]) authPayload := b.EncryptAndHash([]byte{}) @@ -508,7 +508,10 @@ func (b *Machine) RecvActOne(actOne [ActOneSize]byte) error { b.mixHash(b.remoteEphemeral.SerializeCompressed()) // es - s := ecdh(b.remoteEphemeral, b.localStatic) + s, err := ecdh(b.remoteEphemeral, b.localStatic) + if err != nil { + return err + } b.mixKey(s) // If the initiator doesn't know our static key, then this operation @@ -524,22 +527,25 @@ func (b *Machine) RecvActOne(actOne [ActOneSize]byte) error { // // <- e, ee func (b *Machine) GenActTwo() ([ActTwoSize]byte, error) { - var ( - err error - actTwo [ActTwoSize]byte - ) + var actTwo [ActTwoSize]byte // e - b.localEphemeral, err = b.ephemeralGen() + localEphemeral, err := b.ephemeralGen() if err != nil { return actTwo, err } + b.localEphemeral = &keychain.PrivKeyECDH{ + PrivKey: localEphemeral, + } - ephemeral := b.localEphemeral.PubKey().SerializeCompressed() - b.mixHash(b.localEphemeral.PubKey().SerializeCompressed()) + ephemeral := localEphemeral.PubKey().SerializeCompressed() + b.mixHash(localEphemeral.PubKey().SerializeCompressed()) // ee - s := ecdh(b.remoteEphemeral, b.localEphemeral) + s, err := ecdh(b.remoteEphemeral, b.localEphemeral) + if err != nil { + return actTwo, err + } b.mixKey(s) authPayload := b.EncryptAndHash([]byte{}) @@ -580,7 +586,10 @@ func (b *Machine) RecvActTwo(actTwo [ActTwoSize]byte) error { b.mixHash(b.remoteEphemeral.SerializeCompressed()) // ee - s := ecdh(b.remoteEphemeral, b.localEphemeral) + s, err := ecdh(b.remoteEphemeral, b.localEphemeral) + if err != nil { + return err + } b.mixKey(s) _, err = b.DecryptAndHash(p[:]) @@ -600,7 +609,10 @@ func (b *Machine) GenActThree() ([ActThreeSize]byte, error) { ourPubkey := b.localStatic.PubKey().SerializeCompressed() ciphertext := b.EncryptAndHash(ourPubkey) - s := ecdh(b.remoteEphemeral, b.localStatic) + s, err := ecdh(b.remoteEphemeral, b.localStatic) + if err != nil { + return actThree, err + } b.mixKey(s) authPayload := b.EncryptAndHash([]byte{}) @@ -649,7 +661,10 @@ func (b *Machine) RecvActThree(actThree [ActThreeSize]byte) error { } // se - se := ecdh(b.remoteStatic, b.localEphemeral) + se, err := ecdh(b.remoteStatic, b.localEphemeral) + if err != nil { + return err + } b.mixKey(se) if _, err := b.DecryptAndHash(p[:]); err != nil { @@ -875,11 +890,11 @@ func (b *Machine) ReadBody(r io.Reader, buf []byte) ([]byte, error) { // This allows us to log the Machine object without spammy log messages. func (b *Machine) SetCurveToNil() { if b.localStatic != nil { - b.localStatic.Curve = nil + b.localStatic.PubKey().Curve = nil } if b.localEphemeral != nil { - b.localEphemeral.Curve = nil + b.localEphemeral.PubKey().Curve = nil } if b.remoteStatic != nil { diff --git a/brontide/noise_test.go b/brontide/noise_test.go index ce833170..ed2229c1 100644 --- a/brontide/noise_test.go +++ b/brontide/noise_test.go @@ -11,6 +11,7 @@ import ( "testing/iotest" "github.com/btcsuite/btcd/btcec" + "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwire" ) @@ -25,13 +26,14 @@ func makeListener() (*Listener, *lnwire.NetAddress, error) { if err != nil { return nil, nil, err } + localKeyECDH := &keychain.PrivKeyECDH{PrivKey: localPriv} // Having a port of ":0" means a random port, and interface will be // chosen for our listener. addr := "localhost:0" // Our listener will be local, and the connection remote. - listener, err := NewListener(localPriv, addr) + listener, err := NewListener(localKeyECDH, addr) if err != nil { return nil, nil, err } @@ -57,13 +59,14 @@ func establishTestConnection() (net.Conn, net.Conn, func(), error) { if err != nil { return nil, nil, nil, err } + remoteKeyECDH := &keychain.PrivKeyECDH{PrivKey: remotePriv} // Initiate a connection with a separate goroutine, and listen with our // main one. If both errors are nil, then encryption+auth was // successful. remoteConnChan := make(chan maybeNetConn, 1) go func() { - remoteConn, err := Dial(remotePriv, netAddr, net.Dial) + remoteConn, err := Dial(remoteKeyECDH, netAddr, net.Dial) remoteConnChan <- maybeNetConn{remoteConn, err} }() @@ -190,9 +193,10 @@ func TestConcurrentHandshakes(t *testing.T) { if err != nil { t.Fatalf("unable to generate private key: %v", err) } + remoteKeyECDH := &keychain.PrivKeyECDH{PrivKey: remotePriv} go func() { - remoteConn, err := Dial(remotePriv, netAddr, net.Dial) + remoteConn, err := Dial(remoteKeyECDH, netAddr, net.Dial) connChan <- maybeNetConn{remoteConn, err} }() @@ -314,8 +318,10 @@ func TestBolt0008TestVectors(t *testing.T) { if err != nil { t.Fatalf("unable to decode hex: %v", err) } - initiatorPriv, _ := btcec.PrivKeyFromBytes(btcec.S256(), - initiatorKeyBytes) + initiatorPriv, _ := btcec.PrivKeyFromBytes( + btcec.S256(), initiatorKeyBytes, + ) + initiatorKeyECDH := &keychain.PrivKeyECDH{PrivKey: initiatorPriv} // We'll then do the same for the responder. responderKeyBytes, err := hex.DecodeString("212121212121212121212121" + @@ -323,8 +329,10 @@ func TestBolt0008TestVectors(t *testing.T) { if err != nil { t.Fatalf("unable to decode hex: %v", err) } - responderPriv, responderPub := btcec.PrivKeyFromBytes(btcec.S256(), - responderKeyBytes) + responderPriv, responderPub := btcec.PrivKeyFromBytes( + btcec.S256(), responderKeyBytes, + ) + responderKeyECDH := &keychain.PrivKeyECDH{PrivKey: responderPriv} // With the initiator's key data parsed, we'll now define a custom // EphemeralGenerator function for the state machine to ensure that the @@ -355,10 +363,12 @@ func TestBolt0008TestVectors(t *testing.T) { // Finally, we'll create both brontide state machines, so we can begin // our test. - initiator := NewBrontideMachine(true, initiatorPriv, responderPub, - initiatorEphemeral) - responder := NewBrontideMachine(false, responderPriv, nil, - responderEphemeral) + initiator := NewBrontideMachine( + true, initiatorKeyECDH, responderPub, initiatorEphemeral, + ) + responder := NewBrontideMachine( + false, responderKeyECDH, nil, responderEphemeral, + ) // We'll start with the initiator generating the initial payload for // act one. This should consist of exactly 50 bytes. We'll assert that diff --git a/server.go b/server.go index b8b51fc8..b285cfcb 100644 --- a/server.go +++ b/server.go @@ -138,7 +138,7 @@ type server struct { // identityECDH is an ECDH capable wrapper for the private key used // to authenticate any incoming connections. - identityECDH *btcec.PrivateKey + identityECDH keychain.SingleKeyECDH // nodeSigner is an implementation of the MessageSigner implementation // that's backed by the identity private key of the running lnd node. @@ -312,12 +312,12 @@ func parseAddr(address string, netCfg tor.Net) (net.Addr, error) { // noiseDial is a factory function which creates a connmgr compliant dialing // function by returning a closure which includes the server's identity key. -func noiseDial(idPriv *btcec.PrivateKey, +func noiseDial(idKey keychain.SingleKeyECDH, netCfg tor.Net) func(net.Addr) (net.Conn, error) { return func(a net.Addr) (net.Conn, error) { lnAddr := a.(*lnwire.NetAddress) - return brontide.Dial(idPriv, lnAddr, netCfg.Dial) + return brontide.Dial(idKey, lnAddr, netCfg.Dial) } } @@ -344,7 +344,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, chanDB *channeldb.DB, // doesn't need to call the general lndResolveTCP function // since we are resolving a local address. listeners[i], err = brontide.NewListener( - privKey, listenAddr.String(), + nodeKeyECDH, listenAddr.String(), ) if err != nil { return nil, err @@ -373,7 +373,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, chanDB *channeldb.DB, } var serializedPubKey [33]byte - copy(serializedPubKey[:], privKey.PubKey().SerializeCompressed()) + copy(serializedPubKey[:], nodeKeyECDH.PubKey().SerializeCompressed()) // Initialize the sphinx router, placing it's persistent replay log in // the same directory as the channel graph database. @@ -434,7 +434,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, chanDB *channeldb.DB, channelNotifier: channelnotifier.New(chanDB), - identityECDH: privKey, + identityECDH: nodeKeyECDH, nodeSigner: netann.NewNodeSigner(nodeKeySigner), listenAddrs: listenAddrs, @@ -521,7 +521,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, chanDB *channeldb.DB, ChanStatusSampleInterval: cfg.ChanStatusSampleInterval, ChanEnableTimeout: cfg.ChanEnableTimeout, ChanDisableTimeout: cfg.ChanDisableTimeout, - OurPubKey: privKey.PubKey(), + OurPubKey: nodeKeyECDH.PubKey(), MessageSigner: s.nodeSigner, IsChannelActive: s.htlcSwitch.HasActiveLink, ApplyChannelUpdate: s.applyChannelUpdate, @@ -647,7 +647,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, chanDB *channeldb.DB, Features: s.featureMgr.Get(feature.SetNodeAnn), Color: color, } - copy(selfNode.PubKeyBytes[:], privKey.PubKey().SerializeCompressed()) + copy(selfNode.PubKeyBytes[:], nodeKeyECDH.PubKey().SerializeCompressed()) // Based on the disk representation of the node announcement generated // above, we'll generate a node announcement that can go out on the @@ -989,7 +989,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, chanDB *channeldb.DB, } s.fundingMgr, err = newFundingManager(fundingConfig{ - IDKey: privKey.PubKey(), + IDKey: nodeKeyECDH.PubKey(), Wallet: cc.wallet, PublishTransaction: cc.wallet.PublishTransaction, Notifier: cc.chainNotifier, @@ -997,7 +997,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, chanDB *channeldb.DB, SignMessage: func(pubKey *btcec.PublicKey, msg []byte) (input.Signature, error) { - if pubKey.IsEqual(privKey.PubKey()) { + if pubKey.IsEqual(nodeKeyECDH.PubKey()) { return s.nodeSigner.SignMessage(pubKey, msg) } @@ -1010,7 +1010,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, chanDB *channeldb.DB, optionalFields ...discovery.OptionalMsgField) chan error { return s.authGossiper.ProcessLocalAnnouncement( - msg, privKey.PubKey(), optionalFields..., + msg, nodeKeyECDH.PubKey(), optionalFields..., ) }, NotifyWhenOnline: s.NotifyWhenOnline, diff --git a/watchtower/standalone.go b/watchtower/standalone.go index 8e56d962..26ebed8c 100644 --- a/watchtower/standalone.go +++ b/watchtower/standalone.go @@ -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( - cfg.NodeKeyECDH, listenAddr.String(), + nil/*TODO fix in next commit*/, listenAddr.String(), ) if err != nil { return nil, err diff --git a/watchtower/wtclient/interface.go b/watchtower/wtclient/interface.go index f7cc40f8..c9da8314 100644 --- a/watchtower/wtclient/interface.go +++ b/watchtower/wtclient/interface.go @@ -109,7 +109,7 @@ type AuthDialer func(localPriv *btcec.PrivateKey, netAddr *lnwire.NetAddress, func AuthDial(localPriv *btcec.PrivateKey, netAddr *lnwire.NetAddress, dialer func(string, string) (net.Conn, error)) (wtserver.Peer, error) { - return brontide.Dial(localPriv, netAddr, dialer) + return brontide.Dial(nil/*TODO fix in next commit*/, netAddr, dialer) } // ECDHKeyRing abstracts the ability to derive shared ECDH keys given a From f97e7b99516b13c11c3c8e1b3cbcefb06da813e3 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 28 Apr 2020 10:06:31 +0200 Subject: [PATCH 11/15] watchtower: use ECDH interface for watchtower session --- lnd.go | 21 +++++++++--------- watchtower/config.go | 4 ++-- watchtower/standalone.go | 2 +- watchtower/wtclient/client.go | 14 +++++++----- watchtower/wtclient/client_test.go | 7 +++--- watchtower/wtclient/derivation.go | 24 -------------------- watchtower/wtclient/interface.go | 10 +++------ watchtower/wtclient/session_negotiator.go | 27 ++++++++++++++--------- watchtower/wtclient/session_queue.go | 6 ++--- watchtower/wtdb/client_session.go | 4 ++-- watchtower/wtmock/keyring.go | 23 ------------------- watchtower/wtserver/server.go | 4 ++-- 12 files changed, 53 insertions(+), 93 deletions(-) delete mode 100644 watchtower/wtclient/derivation.go diff --git a/lnd.go b/lnd.go index ed347aac..6a900773 100644 --- a/lnd.go +++ b/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 diff --git a/watchtower/config.go b/watchtower/config.go index 2b649dbc..4da7ba30 100644 --- a/watchtower/config.go +++ b/watchtower/config.go @@ -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. diff --git a/watchtower/standalone.go b/watchtower/standalone.go index 26ebed8c..8e56d962 100644 --- a/watchtower/standalone.go +++ b/watchtower/standalone.go @@ -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 diff --git a/watchtower/wtclient/client.go b/watchtower/wtclient/client.go index de58ecc2..e85ea9cd 100644 --- a/watchtower/wtclient/client.go +++ b/watchtower/wtclient/client.go @@ -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 diff --git a/watchtower/wtclient/client_test.go b/watchtower/wtclient/client_test.go index fda9c0e2..7210a0a7 100644 --- a/watchtower/wtclient/client_test.go +++ b/watchtower/wtclient/client_test.go @@ -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 }, diff --git a/watchtower/wtclient/derivation.go b/watchtower/wtclient/derivation.go deleted file mode 100644 index 0dcb59a1..00000000 --- a/watchtower/wtclient/derivation.go +++ /dev/null @@ -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, - }, - }) -} diff --git a/watchtower/wtclient/interface.go b/watchtower/wtclient/interface.go index c9da8314..d532ee24 100644 --- a/watchtower/wtclient/interface.go +++ b/watchtower/wtclient/interface.go @@ -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 diff --git a/watchtower/wtclient/session_negotiator.go b/watchtower/wtclient/session_negotiator.go index f2ed5232..67be526d 100644 --- a/watchtower/wtclient/session_negotiator.go +++ b/watchtower/wtclient/session_negotiator.go @@ -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, } diff --git a/watchtower/wtclient/session_queue.go b/watchtower/wtclient/session_queue.go index b383644c..ffc61892 100644 --- a/watchtower/wtclient/session_queue.go +++ b/watchtower/wtclient/session_queue.go @@ -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 diff --git a/watchtower/wtdb/client_session.go b/watchtower/wtdb/client_session.go index eb76528c..1d3e01f5 100644 --- a/watchtower/wtdb/client_session.go +++ b/watchtower/wtdb/client_session.go @@ -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 diff --git a/watchtower/wtmock/keyring.go b/watchtower/wtmock/keyring.go index 3d23ea04..dc9eef12 100644 --- a/watchtower/wtmock/keyring.go +++ b/watchtower/wtmock/keyring.go @@ -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 diff --git a/watchtower/wtserver/server.go b/watchtower/wtserver/server.go index 0152839a..faf39819 100644 --- a/watchtower/wtserver/server.go +++ b/watchtower/wtserver/server.go @@ -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 From 4003f252819840483ac81756d103fca26001954b Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 28 Apr 2020 10:06:32 +0200 Subject: [PATCH 12/15] signrpc: use ECDH interface for shared key generation --- lnrpc/signrpc/signer_server.go | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/lnrpc/signrpc/signer_server.go b/lnrpc/signrpc/signer_server.go index 127d113e..1d53be2b 100644 --- a/lnrpc/signrpc/signer_server.go +++ b/lnrpc/signrpc/signer_server.go @@ -5,7 +5,6 @@ package signrpc import ( "bytes" "context" - "crypto/sha256" "fmt" "io/ioutil" "os" @@ -515,31 +514,15 @@ func (s *Server) DeriveSharedKey(_ context.Context, in *SharedKeyRequest) ( locator.Index = uint32(in.KeyLoc.KeyIndex) } - // Derive our node's private key from the key ring. - idPrivKey, err := s.cfg.KeyRing.DerivePrivKey(keychain.KeyDescriptor{ - KeyLocator: locator, - }) + // Derive the shared key using ECDH and hashing the serialized + // compressed shared point. + keyDescriptor := keychain.KeyDescriptor{KeyLocator: locator} + sharedKeyHash, err := s.cfg.KeyRing.ECDH(keyDescriptor, ephemeralPubkey) if err != nil { - err := fmt.Errorf("unable to derive node private key: %v", err) + err := fmt.Errorf("unable to derive shared key: %v", err) log.Error(err) return nil, err } - idPrivKey.Curve = btcec.S256() - // Derive the shared key using ECDH and hashing the serialized - // compressed shared point. - sharedKeyHash := ecdh(ephemeralPubkey, idPrivKey) - return &SharedKeyResponse{SharedKey: sharedKeyHash}, nil -} - -// ecdh performs an ECDH operation between pub and priv. The returned value is -// the sha256 of the compressed shared point. -func ecdh(pub *btcec.PublicKey, priv *btcec.PrivateKey) []byte { - s := &btcec.PublicKey{} - x, y := btcec.S256().ScalarMult(pub.X, pub.Y, priv.D.Bytes()) - s.X = x - s.Y = y - - h := sha256.Sum256(s.SerializeCompressed()) - return h[:] + return &SharedKeyResponse{SharedKey: sharedKeyHash[:]}, nil } From 08d6a61e2aa77f1083a47b905024ea263bce9808 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 28 Apr 2020 10:06:34 +0200 Subject: [PATCH 13/15] signrpc: use DigestSigner interface for signing --- lnrpc/signrpc/signer_server.go | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/lnrpc/signrpc/signer_server.go b/lnrpc/signrpc/signer_server.go index 1d53be2b..db56ac13 100644 --- a/lnrpc/signrpc/signer_server.go +++ b/lnrpc/signrpc/signer_server.go @@ -414,24 +414,21 @@ func (s *Server) SignMessage(ctx context.Context, return nil, fmt.Errorf("a key locator MUST be passed in") } - // Derive the private key we'll be using for signing. - keyLocator := keychain.KeyLocator{ - Family: keychain.KeyFamily(in.KeyLoc.KeyFamily), - Index: uint32(in.KeyLoc.KeyIndex), - } - privKey, err := s.cfg.KeyRing.DerivePrivKey(keychain.KeyDescriptor{ - KeyLocator: keyLocator, - }) - if err != nil { - return nil, fmt.Errorf("can't derive private key: %v", err) + // Describe the private key we'll be using for signing. + keyDescriptor := keychain.KeyDescriptor{ + KeyLocator: keychain.KeyLocator{ + Family: keychain.KeyFamily(in.KeyLoc.KeyFamily), + Index: uint32(in.KeyLoc.KeyIndex), + }, } // The signature is over the sha256 hash of the message. - digest := chainhash.HashB(in.Msg) + var digest [32]byte + copy(digest[:], chainhash.HashB(in.Msg)) // Create the raw ECDSA signature first and convert it to the final wire // format after. - sig, err := privKey.Sign(digest) + sig, err := s.cfg.KeyRing.SignDigest(keyDescriptor, digest) if err != nil { return nil, fmt.Errorf("can't sign the hash: %v", err) } From 36b90382d305a162b64d48dad075782611362809 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 28 Apr 2020 10:06:35 +0200 Subject: [PATCH 14/15] lnd+server+keychain: remove unused code --- keychain/btcwallet.go | 27 --------------------------- keychain/derivation.go | 10 ---------- lnd.go | 16 +--------------- mock.go | 5 ----- server.go | 2 +- 5 files changed, 2 insertions(+), 58 deletions(-) diff --git a/keychain/btcwallet.go b/keychain/btcwallet.go index 42737daf..db548890 100644 --- a/keychain/btcwallet.go +++ b/keychain/btcwallet.go @@ -347,33 +347,6 @@ func (b *BtcWalletKeyRing) DerivePrivKey(keyDesc KeyDescriptor) ( return key, nil } -// ScalarMult performs a scalar multiplication (ECDH-like operation) between -// the target key descriptor and remote public key. The output returned will be -// the sha256 of the resulting shared point serialized in compressed format. If -// k is our private key, and P is the public key, we perform the following -// operation: -// -// sx := k*P s := sha256(sx.SerializeCompressed()) -// -// NOTE: This is part of the keychain.SecretKeyRing interface. -func (b *BtcWalletKeyRing) ScalarMult(keyDesc KeyDescriptor, - pub *btcec.PublicKey) ([]byte, error) { - - privKey, err := b.DerivePrivKey(keyDesc) - if err != nil { - return nil, err - } - - s := &btcec.PublicKey{} - x, y := btcec.S256().ScalarMult(pub.X, pub.Y, privKey.D.Bytes()) - s.X = x - s.Y = y - - h := sha256.Sum256(s.SerializeCompressed()) - - return h[:], nil -} - // ECDH performs a scalar multiplication (ECDH-like operation) between the // target key descriptor and remote public key. The output returned will be // the sha256 of the resulting shared point serialized in compressed format. If diff --git a/keychain/derivation.go b/keychain/derivation.go index 67c901d0..4e416c2d 100644 --- a/keychain/derivation.go +++ b/keychain/derivation.go @@ -186,16 +186,6 @@ type SecretKeyRing interface { // MaxKeyRangeScan keys. In order for this to work, the caller MUST set // the KeyFamily within the partially populated KeyLocator. DerivePrivKey(keyDesc KeyDescriptor) (*btcec.PrivateKey, error) - - // ScalarMult performs a scalar multiplication (ECDH-like operation) - // between the target key descriptor and remote public key. The output - // returned will be the sha256 of the resulting shared point serialized - // in compressed format. If k is our private key, and P is the public - // key, we perform the following operation: - // - // sx := k*P - // s := sha256(sx.SerializeCompressed()) - ScalarMult(keyDesc KeyDescriptor, pubKey *btcec.PublicKey) ([]byte, error) } // DigestSignerRing is an interface that abstracts away basic low-level ECDSA diff --git a/lnd.go b/lnd.go index 6a900773..578f6845 100644 --- a/lnd.go +++ b/lnd.go @@ -27,7 +27,6 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials" - "github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcutil" "github.com/btcsuite/btcwallet/wallet" proxy "github.com/grpc-ecosystem/grpc-gateway/runtime" @@ -471,19 +470,6 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error { cfg.registeredChains.RegisterChain(primaryChain, activeChainControl) // TODO(roasbeef): add rotation - idPrivKey, err := activeChainControl.wallet.DerivePrivKey(keychain.KeyDescriptor{ - KeyLocator: keychain.KeyLocator{ - Family: keychain.KeyFamilyNodeKey, - Index: 0, - }, - }) - if err != nil { - err := fmt.Errorf("unable to derive node private key: %v", err) - ltndLog.Error(err) - return err - } - idPrivKey.Curve = btcec.S256() - idKeyDesc, err := activeChainControl.keyRing.DeriveKey( keychain.KeyLocator{ Family: keychain.KeyFamilyNodeKey, @@ -623,7 +609,7 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error { // connections. server, err := newServer( cfg, cfg.Listeners, chanDB, towerClientDB, activeChainControl, - idPrivKey, &idKeyDesc, walletInitParams.ChansToRestore, chainedAcceptor, + &idKeyDesc, walletInitParams.ChansToRestore, chainedAcceptor, torController, ) if err != nil { diff --git a/mock.go b/mock.go index 7729bb76..d6027614 100644 --- a/mock.go +++ b/mock.go @@ -359,11 +359,6 @@ func (m *mockSecretKeyRing) DerivePrivKey(keyDesc keychain.KeyDescriptor) (*btce return m.rootKey, nil } -func (m *mockSecretKeyRing) ScalarMult(keyDesc keychain.KeyDescriptor, - pubKey *btcec.PublicKey) ([]byte, error) { - return nil, nil -} - func (m *mockSecretKeyRing) ECDH(_ keychain.KeyDescriptor, pubKey *btcec.PublicKey) ([32]byte, error) { diff --git a/server.go b/server.go index b285cfcb..b0f00c5a 100644 --- a/server.go +++ b/server.go @@ -325,7 +325,7 @@ func noiseDial(idKey keychain.SingleKeyECDH, // passed listener address. func newServer(cfg *Config, listenAddrs []net.Addr, chanDB *channeldb.DB, towerClientDB *wtdb.ClientDB, cc *chainControl, - privKey *btcec.PrivateKey, nodeKeyDesc *keychain.KeyDescriptor, + nodeKeyDesc *keychain.KeyDescriptor, chansToRestore walletunlocker.ChannelsToRecover, chanPredicate chanacceptor.ChannelAcceptor, torController *tor.Controller) (*server, error) { From 3afaed8b5c70a0726017a20d2c865cbcc5e3b3da Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Wed, 20 May 2020 09:12:53 +0200 Subject: [PATCH 15/15] rpcserver: fix linter error --- rpcserver.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpcserver.go b/rpcserver.go index 6e0af9a9..9399d10f 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -4637,7 +4637,7 @@ func (r *rpcServer) GetTransactions(ctx context.Context, // To remain backwards compatible with the old api, default to the // special case end height which will return transactions from the start // height until the chain tip, including unconfirmed transactions. - var endHeight int32 = btcwallet.UnconfirmedHeight + var endHeight = btcwallet.UnconfirmedHeight // If the user has provided an end height, we overwrite our default. if req.EndHeight != 0 {