From ca63ecd6bc59c27067341c659267cbb2542a8526 Mon Sep 17 00:00:00 2001 From: Yusef Napora Date: Wed, 11 Dec 2019 17:25:40 -0500 Subject: [PATCH 1/2] use Options to configure transport also, adds NewTransportGenerator for cases when you don't know the libp2p host key when configuring the Noise transport --- p2p/security/noise/integration_test.go | 24 ++++++--- p2p/security/noise/options.go | 44 +++++++++++++++++ p2p/security/noise/transport.go | 67 +++++++++++++++++++++++--- 3 files changed, 122 insertions(+), 13 deletions(-) create mode 100644 p2p/security/noise/options.go diff --git a/p2p/security/noise/integration_test.go b/p2p/security/noise/integration_test.go index f43a28355d..c67b6d01a4 100644 --- a/p2p/security/noise/integration_test.go +++ b/p2p/security/noise/integration_test.go @@ -5,7 +5,7 @@ import ( "context" "crypto/rand" "fmt" - libp2p "github.com/libp2p/go-libp2p" + "github.com/libp2p/go-libp2p" "github.com/libp2p/go-libp2p-core/crypto" "github.com/libp2p/go-libp2p-core/host" net "github.com/libp2p/go-libp2p-core/network" @@ -39,13 +39,10 @@ func makeNode(t *testing.T, seed int64, port int, kp *Keypair) (host.Host, error t.Fatal(err) } - pid, err := peer.IDFromPrivateKey(priv) + tpt, err := NewTransport(priv, NoiseKeyPair(kp)) if err != nil { t.Fatal(err) } - - tpt := NewTransport(pid, priv, false, kp) - ip := "0.0.0.0" addr, err := ma.NewMultiaddr(fmt.Sprintf("/ip4/%s/tcp/%d", ip, port)) if err != nil { @@ -69,12 +66,11 @@ func makeNodePipes(t *testing.T, seed int64, port int, rpid peer.ID, rpubkey [32 t.Fatal(err) } - pid, err := peer.IDFromPrivateKey(priv) + tpt, err := NewTransport(priv, UseNoisePipes, NoiseKeyPair(kp)) if err != nil { t.Fatal(err) } - tpt := NewTransport(pid, priv, true, kp) tpt.NoiseStaticKeyCache = NewKeyCache() tpt.NoiseStaticKeyCache.Store(rpid, rpubkey) @@ -256,6 +252,20 @@ func TestLibp2pIntegration_XXFallback(t *testing.T) { time.Sleep(time.Second) } +func TestNewTransportGenerator(t *testing.T) { + kp := GenerateKeypair() + + ctx := context.Background() + h, err := libp2p.New(ctx, + libp2p.Security(ID, + NewTransportConstructor(NoiseKeyPair(kp), UseNoisePipes))) + + if err != nil { + t.Fatalf("unable to create libp2p host with NewTransportConstructor: %v", err) + } + _ = h.Close() +} + func handleStream(stream net.Stream) { defer func() { if err := stream.Close(); err != nil { diff --git a/p2p/security/noise/options.go b/p2p/security/noise/options.go new file mode 100644 index 0000000000..e4a0c155a9 --- /dev/null +++ b/p2p/security/noise/options.go @@ -0,0 +1,44 @@ +package noise + +// UseNoisePipes configures the Noise transport to use the Noise Pipes pattern. +// Noise Pipes attempts to use the more efficient IK handshake pattern when +// dialing a remote peer, if that peer's static Noise key is known. If this +// is unsuccessful, the transport will fallback to using the default XX pattern. +// +// Note that the fallback does not add any additional round-trips vs. simply +// using XX in the first place, however there is a slight processing overhead +// due to the initial decryption attempt of the IK message. +func UseNoisePipes(cfg *config) { + cfg.NoisePipesSupport = true +} + +// NoiseKeyPair configures the Noise transport to use the given Noise static +// keypair. This is distinct from the libp2p Host's identity keypair and is +// used only for Noise. If this option is not provided, a new Noise static +// keypair will be generated when the transport is initialized. +// +// This option is most useful when Noise Pipes is enabled, as longer static +// key lifetimes may lead to more successful IK handshake attempts. +// +// If you do use this option with a key that's been saved to disk, you must +// take care to store the key securely! +func NoiseKeyPair(kp *Keypair) Option { + return func(cfg *config) { + cfg.NoiseKeypair = kp + } +} + +type config struct { + NoiseKeypair *Keypair + NoisePipesSupport bool +} + +type Option func(cfg *config) + +func (cfg *config) applyOptions(opts ...Option) { + for _, opt := range opts { + if opt != nil { + opt(cfg) + } + } +} diff --git a/p2p/security/noise/transport.go b/p2p/security/noise/transport.go index 0a97e546bc..abbce66b75 100644 --- a/p2p/security/noise/transport.go +++ b/p2p/security/noise/transport.go @@ -42,26 +42,81 @@ type Transport struct { NoiseKeypair *Keypair } -// NewTransport creates a new noise transport and can be configured to use noise pipes and a given -// noise ed25519 keypair -func NewTransport(localID peer.ID, privkey crypto.PrivKey, noisePipesSupport bool, kp *Keypair) *Transport { +type transportConstructor func(crypto.PrivKey) (*Transport, error) + +// NewTransportConstructor returns a function that will construct a new Noise transport +// using the given Options. The returned function may be provided as a libp2p.Security +// option when configuring a libp2p Host using libp2p.New: +// +// host := libp2p.New( +// libp2p.Security(noise.ID, noise.NewTransportConstructor())) +// +// The transport can be configured by passing in Options. +// +// To enable the Noise Pipes pattern (which can be more efficient when reconnecting +// to a known peer), pass in the UseNoisePipes Option: +// +// NewTransportConstructor(UseNoisePipes) +// +// To use a specific Noise keypair, pass in the NoiseKeyPair(kp) option, where +// kp is a noise.Keypair struct. This is most useful when using Noise Pipes, whose +// efficiency gains rely on the static Noise key being known in advance. Persisting +// the Noise keypair across process restarts makes it more likely that other peers +// will be able to use the more efficient IK handshake pattern. +// +// NewTransportConstructor(UseNoisePipes, NoiseKeypair(keypairLoadedFromDisk)) +func NewTransportConstructor(options ...Option) transportConstructor { + return func(privKey crypto.PrivKey) (*Transport, error) { + return NewTransport(privKey, options...) + } +} + +// NewTransport creates a new Noise transport using the given private key as its +// libp2p identity key. This function may be used when you want a transport +// instance and know the libp2p Host's identity key before the Host is initialized. +// When configuring a go-libp2p Host using libp2p.New, it's simpler to use +// NewTransportConstructor instead, which will receive the identity key when the Host +// is initialized. +// +// NewTransport supports all the same Options as NewTransportConstructor. +// +// To configure a go-libp2p Host to use the newly created transport, pass it into +// libp2p.New wrapped in a libp2p.Security Option. You will also need to +// make sure to set the libp2p.Identity option so that the Host uses the same +// identity key: +// +// privkey := loadPrivateKeyFromSomewhere() +// noiseTpt := noise.NewTransport(privkey) +// host := libp2p.New( +// libp2p.Identity(privkey), +// libp2p.Security(noise.ID, noiseTpt)) +func NewTransport(privkey crypto.PrivKey, options ...Option) (*Transport, error) { + localID, err := peer.IDFromPrivateKey(privkey) + if err != nil { + return nil, err + } + + cfg := config{} + cfg.applyOptions(options...) + + kp := cfg.NoiseKeypair if kp == nil { kp = GenerateKeypair() } // the static key cache is only useful if Noise Pipes is enabled var keyCache *KeyCache - if noisePipesSupport { + if cfg.NoisePipesSupport { keyCache = NewKeyCache() } return &Transport{ LocalID: localID, PrivateKey: privkey, - NoisePipesSupport: noisePipesSupport, + NoisePipesSupport: cfg.NoisePipesSupport, NoiseKeypair: kp, NoiseStaticKeyCache: keyCache, - } + }, nil } // SecureInbound runs noise handshake as the responder From 8ab00e7201b2e26340e1d8772a610e7a2bb200cb Mon Sep 17 00:00:00 2001 From: Yusef Napora Date: Wed, 11 Dec 2019 17:21:58 -0500 Subject: [PATCH 2/2] rename constructors --- p2p/security/noise/integration_test.go | 10 +++++----- p2p/security/noise/transport.go | 25 +++++++++++++------------ 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/p2p/security/noise/integration_test.go b/p2p/security/noise/integration_test.go index c67b6d01a4..87b43e0de4 100644 --- a/p2p/security/noise/integration_test.go +++ b/p2p/security/noise/integration_test.go @@ -39,7 +39,7 @@ func makeNode(t *testing.T, seed int64, port int, kp *Keypair) (host.Host, error t.Fatal(err) } - tpt, err := NewTransport(priv, NoiseKeyPair(kp)) + tpt, err := New(priv, NoiseKeyPair(kp)) if err != nil { t.Fatal(err) } @@ -66,7 +66,7 @@ func makeNodePipes(t *testing.T, seed int64, port int, rpid peer.ID, rpubkey [32 t.Fatal(err) } - tpt, err := NewTransport(priv, UseNoisePipes, NoiseKeyPair(kp)) + tpt, err := New(priv, UseNoisePipes, NoiseKeyPair(kp)) if err != nil { t.Fatal(err) } @@ -252,16 +252,16 @@ func TestLibp2pIntegration_XXFallback(t *testing.T) { time.Sleep(time.Second) } -func TestNewTransportGenerator(t *testing.T) { +func TestConstrucingWithMaker(t *testing.T) { kp := GenerateKeypair() ctx := context.Background() h, err := libp2p.New(ctx, libp2p.Security(ID, - NewTransportConstructor(NoiseKeyPair(kp), UseNoisePipes))) + Maker(NoiseKeyPair(kp), UseNoisePipes))) if err != nil { - t.Fatalf("unable to create libp2p host with NewTransportConstructor: %v", err) + t.Fatalf("unable to create libp2p host with Maker: %v", err) } _ = h.Close() } diff --git a/p2p/security/noise/transport.go b/p2p/security/noise/transport.go index abbce66b75..38656aa471 100644 --- a/p2p/security/noise/transport.go +++ b/p2p/security/noise/transport.go @@ -44,19 +44,20 @@ type Transport struct { type transportConstructor func(crypto.PrivKey) (*Transport, error) -// NewTransportConstructor returns a function that will construct a new Noise transport +// Maker returns a function that will construct a new Noise transport // using the given Options. The returned function may be provided as a libp2p.Security -// option when configuring a libp2p Host using libp2p.New: +// option when configuring a libp2p Host using libp2p.New, and is compatible with the +// "reflection magic" that libp2p.New uses to inject the private identity key: // // host := libp2p.New( -// libp2p.Security(noise.ID, noise.NewTransportConstructor())) +// libp2p.Security(noise.ID, noise.Maker())) // // The transport can be configured by passing in Options. // // To enable the Noise Pipes pattern (which can be more efficient when reconnecting // to a known peer), pass in the UseNoisePipes Option: // -// NewTransportConstructor(UseNoisePipes) +// Maker(UseNoisePipes) // // To use a specific Noise keypair, pass in the NoiseKeyPair(kp) option, where // kp is a noise.Keypair struct. This is most useful when using Noise Pipes, whose @@ -64,21 +65,21 @@ type transportConstructor func(crypto.PrivKey) (*Transport, error) // the Noise keypair across process restarts makes it more likely that other peers // will be able to use the more efficient IK handshake pattern. // -// NewTransportConstructor(UseNoisePipes, NoiseKeypair(keypairLoadedFromDisk)) -func NewTransportConstructor(options ...Option) transportConstructor { +// Maker(UseNoisePipes, NoiseKeypair(keypairLoadedFromDisk)) +func Maker(options ...Option) transportConstructor { return func(privKey crypto.PrivKey) (*Transport, error) { - return NewTransport(privKey, options...) + return New(privKey, options...) } } -// NewTransport creates a new Noise transport using the given private key as its +// New creates a new Noise transport using the given private key as its // libp2p identity key. This function may be used when you want a transport // instance and know the libp2p Host's identity key before the Host is initialized. // When configuring a go-libp2p Host using libp2p.New, it's simpler to use -// NewTransportConstructor instead, which will receive the identity key when the Host +// Maker instead, which will receive the identity key when the Host // is initialized. // -// NewTransport supports all the same Options as NewTransportConstructor. +// New supports all the same Options as noise.Maker. // // To configure a go-libp2p Host to use the newly created transport, pass it into // libp2p.New wrapped in a libp2p.Security Option. You will also need to @@ -86,11 +87,11 @@ func NewTransportConstructor(options ...Option) transportConstructor { // identity key: // // privkey := loadPrivateKeyFromSomewhere() -// noiseTpt := noise.NewTransport(privkey) +// noiseTpt := noise.New(privkey) // host := libp2p.New( // libp2p.Identity(privkey), // libp2p.Security(noise.ID, noiseTpt)) -func NewTransport(privkey crypto.PrivKey, options ...Option) (*Transport, error) { +func New(privkey crypto.PrivKey, options ...Option) (*Transport, error) { localID, err := peer.IDFromPrivateKey(privkey) if err != nil { return nil, err