From 52a4260be5ecf1bed89e0eda32591c312d198d30 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ra=C3=BAl=20Kripalani?= <raul@protocol.ai>
Date: Thu, 16 Jan 2020 01:29:26 +0000
Subject: [PATCH] Introduce routability and protocol events; cache unmarshalled
 RSA keys (#105)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* event: Add autonat events (#25)

* add events for identify (#26)

* implement caching for rsaKey.Bytes()

* store marshalled protobuf in cache for RsaPublicKey.Bytes()

* fix(crypto): fix build when openssl is enabled

* add godocs to routability events.

Co-authored-by: Ɓukasz Magiera <magik6k@users.noreply.github.com>
Co-authored-by: Whyrusleeping <why@ipfs.io>
Co-authored-by: Adin Schmahmann <adin.schmahmann@gmail.com>
Co-authored-by: Steven Allen <steven@stebalien.com>
---
 crypto/key.go             | 14 +++++++++++++-
 crypto/key_not_openssl.go |  2 +-
 crypto/key_openssl.go     |  2 +-
 crypto/openssl_common.go  | 17 ++++++++++++++---
 crypto/rsa_go.go          | 19 +++++++++++++++----
 crypto/rsa_openssl.go     |  4 ++--
 event/identify.go         | 17 +++++++++++++++++
 event/routability.go      | 21 +++++++++++++++++++++
 8 files changed, 84 insertions(+), 12 deletions(-)
 create mode 100644 event/identify.go
 create mode 100644 event/routability.go

diff --git a/crypto/key.go b/crypto/key.go
index 3a52636..0903388 100644
--- a/crypto/key.go
+++ b/crypto/key.go
@@ -294,7 +294,19 @@ func PublicKeyFromProto(pmes *pb.PublicKey) (PubKey, error) {
 		return nil, ErrBadKeyType
 	}
 
-	return um(pmes.GetData())
+	data := pmes.GetData()
+
+	pk, err := um(data)
+	if err != nil {
+		return nil, err
+	}
+
+	switch tpk := pk.(type) {
+	case *RsaPublicKey:
+		tpk.cached, _ = pmes.Marshal()
+	}
+
+	return pk, nil
 }
 
 // MarshalPublicKey converts a public key object into a protobuf serialized
diff --git a/crypto/key_not_openssl.go b/crypto/key_not_openssl.go
index 6b5b6fc..fb1e36a 100644
--- a/crypto/key_not_openssl.go
+++ b/crypto/key_not_openssl.go
@@ -19,7 +19,7 @@ func KeyPairFromStdKey(priv crypto.PrivateKey) (PrivKey, PubKey, error) {
 
 	switch p := priv.(type) {
 	case *rsa.PrivateKey:
-		return &RsaPrivateKey{*p}, &RsaPublicKey{p.PublicKey}, nil
+		return &RsaPrivateKey{*p}, &RsaPublicKey{k: p.PublicKey}, nil
 
 	case *ecdsa.PrivateKey:
 		return &ECDSAPrivateKey{p}, &ECDSAPublicKey{&p.PublicKey}, nil
diff --git a/crypto/key_openssl.go b/crypto/key_openssl.go
index b948f3d..5b1e360 100644
--- a/crypto/key_openssl.go
+++ b/crypto/key_openssl.go
@@ -26,7 +26,7 @@ func KeyPairFromStdKey(priv crypto.PrivateKey) (PrivKey, PubKey, error) {
 			return nil, nil, err
 		}
 
-		return &opensslPrivateKey{pk}, &opensslPublicKey{pk}, nil
+		return &opensslPrivateKey{pk}, &opensslPublicKey{key: pk}, nil
 
 	case *ecdsa.PrivateKey:
 		return &ECDSAPrivateKey{p}, &ECDSAPublicKey{&p.PublicKey}, nil
diff --git a/crypto/openssl_common.go b/crypto/openssl_common.go
index 2466521..88807ca 100644
--- a/crypto/openssl_common.go
+++ b/crypto/openssl_common.go
@@ -3,6 +3,8 @@
 package crypto
 
 import (
+	"sync"
+
 	pb "github.com/libp2p/go-libp2p-core/crypto/pb"
 
 	openssl "github.com/libp2p/go-openssl"
@@ -13,6 +15,9 @@ import (
 
 type opensslPublicKey struct {
 	key openssl.PublicKey
+
+	cacheLk sync.Mutex
+	cached  []byte
 }
 
 type opensslPrivateKey struct {
@@ -32,7 +37,7 @@ func unmarshalOpensslPublicKey(b []byte) (opensslPublicKey, error) {
 	if err != nil {
 		return opensslPublicKey{}, err
 	}
-	return opensslPublicKey{sk}, nil
+	return opensslPublicKey{key: sk, cached: b}, nil
 }
 
 // Verify compares a signature against input data
@@ -52,7 +57,13 @@ func (pk *opensslPublicKey) Type() pb.KeyType {
 
 // Bytes returns protobuf bytes of a public key
 func (pk *opensslPublicKey) Bytes() ([]byte, error) {
-	return MarshalPublicKey(pk)
+	pk.cacheLk.Lock()
+	var err error
+	if pk.cached == nil {
+		pk.cached, err = MarshalPublicKey(pk)
+	}
+	pk.cacheLk.Unlock()
+	return pk.cached, err
 }
 
 func (pk *opensslPublicKey) Raw() ([]byte, error) {
@@ -76,7 +87,7 @@ func (sk *opensslPrivateKey) Sign(message []byte) ([]byte, error) {
 
 // GetPublic returns a public key
 func (sk *opensslPrivateKey) GetPublic() PubKey {
-	return &opensslPublicKey{sk.key}
+	return &opensslPublicKey{key: sk.key}
 }
 
 func (sk *opensslPrivateKey) Type() pb.KeyType {
diff --git a/crypto/rsa_go.go b/crypto/rsa_go.go
index d774991..f28a327 100644
--- a/crypto/rsa_go.go
+++ b/crypto/rsa_go.go
@@ -9,6 +9,7 @@ import (
 	"crypto/x509"
 	"errors"
 	"io"
+	"sync"
 
 	pb "github.com/libp2p/go-libp2p-core/crypto/pb"
 
@@ -23,6 +24,9 @@ type RsaPrivateKey struct {
 // RsaPublicKey is an rsa public key
 type RsaPublicKey struct {
 	k rsa.PublicKey
+
+	cacheLk sync.Mutex
+	cached  []byte
 }
 
 // GenerateRSAKeyPair generates a new rsa private and public key
@@ -35,7 +39,7 @@ func GenerateRSAKeyPair(bits int, src io.Reader) (PrivKey, PubKey, error) {
 		return nil, nil, err
 	}
 	pk := priv.PublicKey
-	return &RsaPrivateKey{sk: *priv}, &RsaPublicKey{pk}, nil
+	return &RsaPrivateKey{sk: *priv}, &RsaPublicKey{k: pk}, nil
 }
 
 // Verify compares a signature against input data
@@ -54,7 +58,13 @@ func (pk *RsaPublicKey) Type() pb.KeyType {
 
 // Bytes returns protobuf bytes of a public key
 func (pk *RsaPublicKey) Bytes() ([]byte, error) {
-	return MarshalPublicKey(pk)
+	pk.cacheLk.Lock()
+	var err error
+	if pk.cached == nil {
+		pk.cached, err = MarshalPublicKey(pk)
+	}
+	pk.cacheLk.Unlock()
+	return pk.cached, err
 }
 
 func (pk *RsaPublicKey) Raw() ([]byte, error) {
@@ -80,7 +90,7 @@ func (sk *RsaPrivateKey) Sign(message []byte) ([]byte, error) {
 
 // GetPublic returns a public key
 func (sk *RsaPrivateKey) GetPublic() PubKey {
-	return &RsaPublicKey{sk.sk.PublicKey}
+	return &RsaPublicKey{k: sk.sk.PublicKey}
 }
 
 func (sk *RsaPrivateKey) Type() pb.KeyType {
@@ -137,5 +147,6 @@ func UnmarshalRsaPublicKey(b []byte) (PubKey, error) {
 	if pk.N.BitLen() < MinRsaKeyBits {
 		return nil, ErrRsaKeyTooSmall
 	}
-	return &RsaPublicKey{*pk}, nil
+
+	return &RsaPublicKey{k: *pk}, nil
 }
diff --git a/crypto/rsa_openssl.go b/crypto/rsa_openssl.go
index 9b7a342..8e7fb74 100644
--- a/crypto/rsa_openssl.go
+++ b/crypto/rsa_openssl.go
@@ -29,12 +29,12 @@ func GenerateRSAKeyPair(bits int, _ io.Reader) (PrivKey, PubKey, error) {
 	if err != nil {
 		return nil, nil, err
 	}
-	return &RsaPrivateKey{opensslPrivateKey{key}}, &RsaPublicKey{opensslPublicKey{key}}, nil
+	return &RsaPrivateKey{opensslPrivateKey{key}}, &RsaPublicKey{opensslPublicKey{key: key}}, nil
 }
 
 // GetPublic returns a public key
 func (sk *RsaPrivateKey) GetPublic() PubKey {
-	return &RsaPublicKey{opensslPublicKey{sk.opensslPrivateKey.key}}
+	return &RsaPublicKey{opensslPublicKey{key: sk.opensslPrivateKey.key}}
 }
 
 // UnmarshalRsaPrivateKey returns a private key from the input x509 bytes
diff --git a/event/identify.go b/event/identify.go
new file mode 100644
index 0000000..7c4d189
--- /dev/null
+++ b/event/identify.go
@@ -0,0 +1,17 @@
+package event
+
+import "github.com/libp2p/go-libp2p-core/peer"
+
+// EvtPeerIdentificationCompleted is emitted when the initial identification round for a peer is completed.
+type EvtPeerIdentificationCompleted struct {
+	// Peer is the ID of the peer whose identification succeeded.
+	Peer peer.ID
+}
+
+// EvtPeerIdentificationFailed is emitted when the initial identification round for a peer failed.
+type EvtPeerIdentificationFailed struct {
+	// Peer is the ID of the peer whose identification failed.
+	Peer peer.ID
+	// Reason is the reason why identification failed.
+	Reason error
+}
diff --git a/event/routability.go b/event/routability.go
new file mode 100644
index 0000000..7a25d69
--- /dev/null
+++ b/event/routability.go
@@ -0,0 +1,21 @@
+package event
+
+// EvtLocalRoutabilityPrivate is an event struct to be emitted with the local's
+// node routability changes to PRIVATE (i.e. not routable from the Internet).
+//
+// This event is usually emitted by the AutoNAT subsystem.
+type EvtLocalRoutabilityPrivate struct{}
+
+// EvtLocalRoutabilityPublic is an event struct to be emitted with the local's
+// node routability changes to PUBLIC (i.e. appear to routable from the
+// Internet).
+//
+// This event is usually emitted by the AutoNAT subsystem.
+type EvtLocalRoutabilityPublic struct{}
+
+// EvtLocalRoutabilityUnknown is an event struct to be emitted with the local's
+// node routability changes to UNKNOWN (i.e. we were unable to make a
+// determination about our NAT status with enough confidence).
+//
+// This event is usually emitted by the AutoNAT subsystem.
+type EvtLocalRoutabilityUnknown struct{}