From 6f702a43aa61f1667ef4ac96b895a5542896e12f Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 28 Apr 2020 10:06:25 +0200 Subject: [PATCH] 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) +}