Create whisperv6 patch versions of 0004-whisper-notifications.patch and 0009-whisper-envelopes-tracing.patch. Closes #637

This commit is contained in:
Pedro Pombeiro 2018-02-09 18:38:23 +01:00
parent 9976018978
commit 857b72e9fd
No known key found for this signature in database
GPG key ID: A65DEB11E4BBC647
8 changed files with 647 additions and 15 deletions

2
Gopkg.lock generated
View file

@ -109,7 +109,7 @@
"whisper/notifications",
"whisper/whisperv5"
]
revision = "09f08d50335df2d8c9b9f062b18f0ebd3a84133d"
revision = "5b2cc44bf2b32bb482def02d7c8fa32ba08d0bf4"
source = "https://github.com/status-im/go-ethereum.git"
[[projects]]

View file

@ -0,0 +1,324 @@
diff --git a/whisper/notifications/utils.go b/whisper/notifications/utils.go
index 106752186..cca3fba71 100644
--- a/whisper/notifications/utils.go
+++ b/whisper/notifications/utils.go
@@ -1,11 +1,12 @@
package notifications
import (
+ "crypto/sha256"
"crypto/sha512"
"errors"
- "crypto/sha256"
crand "crypto/rand"
+
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
"golang.org/x/crypto/pbkdf2"
)
@@ -24,7 +25,7 @@ func makeSessionKey() ([]byte, error) {
}
key := buf[:keyLen]
- derived, err := deriveKeyMaterial(key, whisper.EnvelopeVersion)
+ derived, err := deriveKeyMaterial(key, whisper.ProtocolVersion)
if err != nil {
return nil, err
} else if !validateSymmetricKey(derived) {
@@ -63,7 +64,7 @@ func deriveKeyMaterial(key []byte, version uint64) (derivedKey []byte, err error
}
// MakeTopic returns Whisper topic *as bytes array* by generating cryptographic key from the provided password
-func MakeTopicAsBytes(password []byte) ([]byte) {
+func MakeTopicAsBytes(password []byte) []byte {
topic := make([]byte, int(whisper.TopicLength))
x := pbkdf2.Key(password, password, 8196, 128, sha512.New)
for i := 0; i < len(x); i++ {
diff --git a/whisper/whisperv6/api.go b/whisper/whisperv6/api.go
index 8ae2882e1..7c97f0680 100644
--- a/whisper/whisperv6/api.go
+++ b/whisper/whisperv6/api.go
@@ -319,6 +319,16 @@ func (api *PublicWhisperAPI) Post(ctx context.Context, req NewMessage) (bool, er
return true, api.w.Send(env)
}
+// UninstallFilter is alias for Unsubscribe
+func (api *PublicWhisperAPI) UninstallFilter(id string) {
+ api.w.Unsubscribe(id)
+}
+
+// Unsubscribe disables and removes an existing filter.
+func (api *PublicWhisperAPI) Unsubscribe(id string) {
+ api.w.Unsubscribe(id)
+}
+
//go:generate gencodec -type Criteria -field-override criteriaOverride -out gen_criteria_json.go
// Criteria holds various filter options for inbound messages.
diff --git a/whisper/whisperv6/doc.go b/whisper/whisperv6/doc.go
index d5d7fed60..5ad660616 100644
--- a/whisper/whisperv6/doc.go
+++ b/whisper/whisperv6/doc.go
@@ -35,6 +35,8 @@ package whisperv6
import (
"fmt"
"time"
+
+ "github.com/ethereum/go-ethereum/p2p"
)
// Whisper protocol parameters
@@ -67,7 +69,7 @@ const (
MaxMessageSize = uint32(10 * 1024 * 1024) // maximum accepted size of a message.
DefaultMaxMessageSize = uint32(1024 * 1024)
- DefaultMinimumPoW = 0.2
+ DefaultMinimumPoW = 0.001
padSizeLimit = 256 // just an arbitrary number, could be changed without breaking the protocol
messageQueueLimit = 1024
@@ -95,3 +97,15 @@ type MailServer interface {
Archive(env *Envelope)
DeliverMail(whisperPeer *Peer, request *Envelope)
}
+
+// NotificationServer represents a notification server,
+// capable of screening incoming envelopes for special
+// topics, and once located, subscribe client nodes as
+// recipients to notifications (push notifications atm)
+type NotificationServer interface {
+ // Start initializes notification sending loop
+ Start(server *p2p.Server) error
+
+ // Stop stops notification sending loop, releasing related resources
+ Stop() error
+}
diff --git a/whisper/whisperv6/whisper.go b/whisper/whisperv6/whisper.go
index d75ad04ac..54d7d0f24 100644
--- a/whisper/whisperv6/whisper.go
+++ b/whisper/whisperv6/whisper.go
@@ -85,7 +85,8 @@ type Whisper struct {
statsMu sync.Mutex // guard stats
stats Statistics // Statistics of whisper node
- mailServer MailServer // MailServer interface
+ mailServer MailServer // MailServer interface
+ notificationServer NotificationServer
}
// New creates a Whisper client ready to communicate through the Ethereum P2P network.
@@ -209,6 +210,11 @@ func (whisper *Whisper) RegisterServer(server MailServer) {
whisper.mailServer = server
}
+// RegisterNotificationServer registers notification server with Whisper
+func (whisper *Whisper) RegisterNotificationServer(server NotificationServer) {
+ whisper.notificationServer = server
+}
+
// Protocols returns the whisper sub-protocols ran by this particular client.
func (whisper *Whisper) Protocols() []p2p.Protocol {
return []p2p.Protocol{whisper.protocol}
@@ -380,9 +386,9 @@ func (whisper *Whisper) NewKeyPair() (string, error) {
return "", fmt.Errorf("failed to generate valid key")
}
- id, err := GenerateRandomID()
+ id, err := toDeterministicID(common.ToHex(crypto.FromECDSAPub(&key.PublicKey)), keyIDSize)
if err != nil {
- return "", fmt.Errorf("failed to generate ID: %s", err)
+ return "", err
}
whisper.keyMu.Lock()
@@ -397,11 +403,16 @@ func (whisper *Whisper) NewKeyPair() (string, error) {
// DeleteKeyPair deletes the specified key if it exists.
func (whisper *Whisper) DeleteKeyPair(key string) bool {
+ deterministicID, err := toDeterministicID(key, keyIDSize)
+ if err != nil {
+ return false
+ }
+
whisper.keyMu.Lock()
defer whisper.keyMu.Unlock()
- if whisper.privateKeys[key] != nil {
- delete(whisper.privateKeys, key)
+ if whisper.privateKeys[deterministicID] != nil {
+ delete(whisper.privateKeys, deterministicID)
return true
}
return false
@@ -409,31 +420,73 @@ func (whisper *Whisper) DeleteKeyPair(key string) bool {
// AddKeyPair imports a asymmetric private key and returns it identifier.
func (whisper *Whisper) AddKeyPair(key *ecdsa.PrivateKey) (string, error) {
- id, err := GenerateRandomID()
+ id, err := makeDeterministicID(common.ToHex(crypto.FromECDSAPub(&key.PublicKey)), keyIDSize)
if err != nil {
- return "", fmt.Errorf("failed to generate ID: %s", err)
+ return "", err
+ }
+ if whisper.HasKeyPair(id) {
+ return id, nil // no need to re-inject
}
whisper.keyMu.Lock()
whisper.privateKeys[id] = key
whisper.keyMu.Unlock()
+ log.Info("Whisper identity added", "id", id, "pubkey", common.ToHex(crypto.FromECDSAPub(&key.PublicKey)))
return id, nil
}
+// SelectKeyPair adds cryptographic identity, and makes sure
+// that it is the only private key known to the node.
+func (whisper *Whisper) SelectKeyPair(key *ecdsa.PrivateKey) error {
+ id, err := makeDeterministicID(common.ToHex(crypto.FromECDSAPub(&key.PublicKey)), keyIDSize)
+ if err != nil {
+ return err
+ }
+
+ whisper.keyMu.Lock()
+ defer whisper.keyMu.Unlock()
+
+ whisper.privateKeys = make(map[string]*ecdsa.PrivateKey) // reset key store
+ whisper.privateKeys[id] = key
+
+ log.Info("Whisper identity selected", "id", id, "key", common.ToHex(crypto.FromECDSAPub(&key.PublicKey)))
+ return nil
+}
+
+// DeleteKeyPairs removes all cryptographic identities known to the node
+func (whisper *Whisper) DeleteKeyPairs() error {
+ whisper.keyMu.Lock()
+ defer whisper.keyMu.Unlock()
+
+ whisper.privateKeys = make(map[string]*ecdsa.PrivateKey)
+
+ return nil
+}
+
// HasKeyPair checks if the the whisper node is configured with the private key
// of the specified public pair.
func (whisper *Whisper) HasKeyPair(id string) bool {
+ deterministicID, err := toDeterministicID(id, keyIDSize)
+ if err != nil {
+ return false
+ }
+
whisper.keyMu.RLock()
defer whisper.keyMu.RUnlock()
- return whisper.privateKeys[id] != nil
+ return whisper.privateKeys[deterministicID] != nil
}
// GetPrivateKey retrieves the private key of the specified identity.
func (whisper *Whisper) GetPrivateKey(id string) (*ecdsa.PrivateKey, error) {
+ deterministicID, err := toDeterministicID(id, keyIDSize)
+ if err != nil {
+ return nil, err
+ }
+
whisper.keyMu.RLock()
defer whisper.keyMu.RUnlock()
- key := whisper.privateKeys[id]
+ key := whisper.privateKeys[deterministicID]
if key == nil {
return nil, fmt.Errorf("invalid id")
}
@@ -465,6 +518,23 @@ func (whisper *Whisper) GenerateSymKey() (string, error) {
return id, nil
}
+// AddSymKey stores the key with a given id.
+func (whisper *Whisper) AddSymKey(id string, key []byte) (string, error) {
+ deterministicID, err := toDeterministicID(id, keyIDSize)
+ if err != nil {
+ return "", err
+ }
+
+ whisper.keyMu.Lock()
+ defer whisper.keyMu.Unlock()
+
+ if whisper.symKeys[deterministicID] != nil {
+ return "", fmt.Errorf("key already exists: %v", id)
+ }
+ whisper.symKeys[deterministicID] = key
+ return deterministicID, nil
+}
+
// AddSymKeyDirect stores the key, and returns its id.
func (whisper *Whisper) AddSymKeyDirect(key []byte) (string, error) {
if len(key) != aesKeyLength {
@@ -599,7 +669,7 @@ func (whisper *Whisper) Send(envelope *Envelope) error {
// Start implements node.Service, starting the background data propagation thread
// of the Whisper protocol.
-func (whisper *Whisper) Start(*p2p.Server) error {
+func (whisper *Whisper) Start(stack *p2p.Server) error {
log.Info("started whisper v." + ProtocolVersionStr)
go whisper.update()
@@ -608,6 +678,12 @@ func (whisper *Whisper) Start(*p2p.Server) error {
go whisper.processQueue()
}
+ if whisper.notificationServer != nil {
+ if err := whisper.notificationServer.Start(stack); err != nil {
+ return err
+ }
+ }
+
return nil
}
@@ -615,6 +691,13 @@ func (whisper *Whisper) Start(*p2p.Server) error {
// of the Whisper protocol.
func (whisper *Whisper) Stop() error {
close(whisper.quit)
+
+ if whisper.notificationServer != nil {
+ if err := whisper.notificationServer.Stop(); err != nil {
+ return err
+ }
+ }
+
log.Info("whisper stopped")
return nil
}
@@ -1035,6 +1118,33 @@ func GenerateRandomID() (id string, err error) {
return id, err
}
+// makeDeterministicID generates a deterministic ID, based on a given input
+func makeDeterministicID(input string, keyLen int) (id string, err error) {
+ buf := pbkdf2.Key([]byte(input), nil, 4096, keyLen, sha256.New)
+ if !validateDataIntegrity(buf, keyIDSize) {
+ return "", fmt.Errorf("error in GenerateDeterministicID: failed to generate key")
+ }
+ id = common.Bytes2Hex(buf)
+ return id, err
+}
+
+// toDeterministicID reviews incoming id, and transforms it to format
+// expected internally be private key store. Originally, public keys
+// were used as keys, now random keys are being used. And in order to
+// make it easier to consume, we now allow both random IDs and public
+// keys to be passed.
+func toDeterministicID(id string, expectedLen int) (string, error) {
+ if len(id) != (expectedLen * 2) { // we received hex key, so number of chars in id is doubled
+ var err error
+ id, err = makeDeterministicID(id, expectedLen)
+ if err != nil {
+ return "", err
+ }
+ }
+
+ return id, nil
+}
+
func isFullNode(bloom []byte) bool {
if bloom == nil {
return true

View file

@ -0,0 +1,109 @@
diff --git a/whisper/whisperv6/doc.go b/whisper/whisperv6/doc.go
index 5ad660616..9659e6c46 100644
--- a/whisper/whisperv6/doc.go
+++ b/whisper/whisperv6/doc.go
@@ -109,3 +109,40 @@ type NotificationServer interface {
// Stop stops notification sending loop, releasing related resources
Stop() error
}
+
+type envelopeSource int
+
+const (
+ _ = iota
+ // peerSource indicates a source as a regular peer.
+ peerSource envelopeSource = iota
+ // p2pSource indicates that envelop was received from a trusted peer.
+ p2pSource
+)
+
+// EnvelopeMeta keeps metadata of received envelopes.
+type EnvelopeMeta struct {
+ Hash string
+ Topic TopicType
+ Size uint32
+ Source envelopeSource
+ IsNew bool
+ Peer string
+}
+
+// SourceString converts source to string.
+func (m *EnvelopeMeta) SourceString() string {
+ switch m.Source {
+ case peerSource:
+ return "peer"
+ case p2pSource:
+ return "p2p"
+ default:
+ return "unknown"
+ }
+}
+
+// EnvelopeTracer tracks received envelopes.
+type EnvelopeTracer interface {
+ Trace(*EnvelopeMeta)
+}
diff --git a/whisper/whisperv6/whisper.go b/whisper/whisperv6/whisper.go
index 54d7d0f24..ce9405dff 100644
--- a/whisper/whisperv6/whisper.go
+++ b/whisper/whisperv6/whisper.go
@@ -87,6 +87,7 @@ type Whisper struct {
mailServer MailServer // MailServer interface
notificationServer NotificationServer
+ envelopeTracer EnvelopeTracer // Service collecting envelopes metadata
}
// New creates a Whisper client ready to communicate through the Ethereum P2P network.
@@ -215,6 +216,12 @@ func (whisper *Whisper) RegisterNotificationServer(server NotificationServer) {
whisper.notificationServer = server
}
+// RegisterEnvelopeTracer registers an EnveloperTracer to collect information
+// about received envelopes.
+func (whisper *Whisper) RegisterEnvelopeTracer(tracer EnvelopeTracer) {
+ whisper.envelopeTracer = tracer
+}
+
// Protocols returns the whisper sub-protocols ran by this particular client.
func (whisper *Whisper) Protocols() []p2p.Protocol {
return []p2p.Protocol{whisper.protocol}
@@ -756,6 +763,7 @@ func (whisper *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {
trouble := false
for _, env := range envelopes {
+ whisper.traceEnvelope(env, !whisper.isEnvelopeCached(env.Hash()), peerSource, p)
cached, err := whisper.add(env)
if err != nil {
trouble = true
@@ -810,6 +818,7 @@ func (whisper *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {
return errors.New("invalid direct message")
}
whisper.postEvent(&envelope, true)
+ whisper.traceEnvelope(&envelope, false, p2pSource, p)
}
case p2pRequestCode:
// Must be processed if mail server is implemented. Otherwise ignore.
@@ -906,6 +915,22 @@ func (whisper *Whisper) add(envelope *Envelope) (bool, error) {
return true, nil
}
+// traceEnvelope collects basic metadata about an envelope and sender peer.
+func (whisper *Whisper) traceEnvelope(envelope *Envelope, isNew bool, source envelopeSource, peer *Peer) {
+ if whisper.envelopeTracer == nil {
+ return
+ }
+
+ whisper.envelopeTracer.Trace(&EnvelopeMeta{
+ Hash: envelope.Hash().String(),
+ Topic: BytesToTopic(envelope.Topic[:]),
+ Size: uint32(envelope.size()),
+ Source: source,
+ IsNew: isNew,
+ Peer: peer.peer.Info().ID,
+ })
+}
+
// postEvent queues the message for further processing.
func (whisper *Whisper) postEvent(envelope *Envelope, isP2P bool) {
if isP2P {

View file

@ -27,6 +27,8 @@ Instructions for creating a patch from the command line:
- [`0009-whisper-envelopes-tracing.patch`](./0009-whisper-envelopes-tracing.patch) — adds Whisper envelope tracing (need to be reviewed and documented)
- [`0010-geth-17-fix-npe-in-filter-system.patch`](./0010-geth-17-fix-npe-in-filter-system.patch) - Temp patch for 1.7.x to fix a NPE in the filter system.
- [`0011-geth-17-whisperv6-70fbc87.patch`](./0011-geth-17-whisperv6-70fbc87.patch) - Temp patch for 1.7.x to update whisper v6 to the upstream version at the `70fbc87` SHA1.
- [`0013-whisperv6-notifications.patch`](./0013-whisperv6-notifications.patch) — adds Whisper v6 notifications (need to be reviewed and documented)
- [`0014-whisperv6-envelopes-tracing.patch`](./0014-whisperv6-envelopes-tracing.patch) — adds Whisper v6 envelope tracing (need to be reviewed and documented)
# Updating upstream version

View file

@ -1,11 +1,12 @@
package notifications
import (
"crypto/sha256"
"crypto/sha512"
"errors"
"crypto/sha256"
crand "crypto/rand"
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
"golang.org/x/crypto/pbkdf2"
)
@ -24,7 +25,7 @@ func makeSessionKey() ([]byte, error) {
}
key := buf[:keyLen]
derived, err := deriveKeyMaterial(key, whisper.EnvelopeVersion)
derived, err := deriveKeyMaterial(key, whisper.ProtocolVersion)
if err != nil {
return nil, err
} else if !validateSymmetricKey(derived) {
@ -63,7 +64,7 @@ func deriveKeyMaterial(key []byte, version uint64) (derivedKey []byte, err error
}
// MakeTopic returns Whisper topic *as bytes array* by generating cryptographic key from the provided password
func MakeTopicAsBytes(password []byte) ([]byte) {
func MakeTopicAsBytes(password []byte) []byte {
topic := make([]byte, int(whisper.TopicLength))
x := pbkdf2.Key(password, password, 8196, 128, sha512.New)
for i := 0; i < len(x); i++ {

View file

@ -322,6 +322,16 @@ func (api *PublicWhisperAPI) Post(ctx context.Context, req NewMessage) (bool, er
return true, api.w.Send(env)
}
// UninstallFilter is alias for Unsubscribe
func (api *PublicWhisperAPI) UninstallFilter(id string) {
api.w.Unsubscribe(id)
}
// Unsubscribe disables and removes an existing filter.
func (api *PublicWhisperAPI) Unsubscribe(id string) {
api.w.Unsubscribe(id)
}
//go:generate gencodec -type Criteria -field-override criteriaOverride -out gen_criteria_json.go
// Criteria holds various filter options for inbound messages.

View file

@ -35,6 +35,8 @@ package whisperv6
import (
"fmt"
"time"
"github.com/ethereum/go-ethereum/p2p"
)
// Whisper protocol parameters
@ -67,7 +69,7 @@ const (
MaxMessageSize = uint32(10 * 1024 * 1024) // maximum accepted size of a message.
DefaultMaxMessageSize = uint32(1024 * 1024)
DefaultMinimumPoW = 0.2
DefaultMinimumPoW = 0.001
padSizeLimit = 256 // just an arbitrary number, could be changed without breaking the protocol
messageQueueLimit = 1024
@ -95,3 +97,52 @@ type MailServer interface {
Archive(env *Envelope)
DeliverMail(whisperPeer *Peer, request *Envelope)
}
// NotificationServer represents a notification server,
// capable of screening incoming envelopes for special
// topics, and once located, subscribe client nodes as
// recipients to notifications (push notifications atm)
type NotificationServer interface {
// Start initializes notification sending loop
Start(server *p2p.Server) error
// Stop stops notification sending loop, releasing related resources
Stop() error
}
type envelopeSource int
const (
_ = iota
// peerSource indicates a source as a regular peer.
peerSource envelopeSource = iota
// p2pSource indicates that envelop was received from a trusted peer.
p2pSource
)
// EnvelopeMeta keeps metadata of received envelopes.
type EnvelopeMeta struct {
Hash string
Topic TopicType
Size uint32
Source envelopeSource
IsNew bool
Peer string
}
// SourceString converts source to string.
func (m *EnvelopeMeta) SourceString() string {
switch m.Source {
case peerSource:
return "peer"
case p2pSource:
return "p2p"
default:
return "unknown"
}
}
// EnvelopeTracer tracks received envelopes.
type EnvelopeTracer interface {
Trace(*EnvelopeMeta)
}

View file

@ -85,7 +85,9 @@ type Whisper struct {
statsMu sync.Mutex // guard stats
stats Statistics // Statistics of whisper node
mailServer MailServer // MailServer interface
mailServer MailServer // MailServer interface
notificationServer NotificationServer
envelopeTracer EnvelopeTracer // Service collecting envelopes metadata
}
// New creates a Whisper client ready to communicate through the Ethereum P2P network.
@ -209,6 +211,17 @@ func (whisper *Whisper) RegisterServer(server MailServer) {
whisper.mailServer = server
}
// RegisterNotificationServer registers notification server with Whisper
func (whisper *Whisper) RegisterNotificationServer(server NotificationServer) {
whisper.notificationServer = server
}
// RegisterEnvelopeTracer registers an EnveloperTracer to collect information
// about received envelopes.
func (whisper *Whisper) RegisterEnvelopeTracer(tracer EnvelopeTracer) {
whisper.envelopeTracer = tracer
}
// Protocols returns the whisper sub-protocols ran by this particular client.
func (whisper *Whisper) Protocols() []p2p.Protocol {
return []p2p.Protocol{whisper.protocol}
@ -380,9 +393,9 @@ func (whisper *Whisper) NewKeyPair() (string, error) {
return "", fmt.Errorf("failed to generate valid key")
}
id, err := GenerateRandomID()
id, err := toDeterministicID(common.ToHex(crypto.FromECDSAPub(&key.PublicKey)), keyIDSize)
if err != nil {
return "", fmt.Errorf("failed to generate ID: %s", err)
return "", err
}
whisper.keyMu.Lock()
@ -397,11 +410,16 @@ func (whisper *Whisper) NewKeyPair() (string, error) {
// DeleteKeyPair deletes the specified key if it exists.
func (whisper *Whisper) DeleteKeyPair(key string) bool {
deterministicID, err := toDeterministicID(key, keyIDSize)
if err != nil {
return false
}
whisper.keyMu.Lock()
defer whisper.keyMu.Unlock()
if whisper.privateKeys[key] != nil {
delete(whisper.privateKeys, key)
if whisper.privateKeys[deterministicID] != nil {
delete(whisper.privateKeys, deterministicID)
return true
}
return false
@ -409,31 +427,73 @@ func (whisper *Whisper) DeleteKeyPair(key string) bool {
// AddKeyPair imports a asymmetric private key and returns it identifier.
func (whisper *Whisper) AddKeyPair(key *ecdsa.PrivateKey) (string, error) {
id, err := GenerateRandomID()
id, err := makeDeterministicID(common.ToHex(crypto.FromECDSAPub(&key.PublicKey)), keyIDSize)
if err != nil {
return "", fmt.Errorf("failed to generate ID: %s", err)
return "", err
}
if whisper.HasKeyPair(id) {
return id, nil // no need to re-inject
}
whisper.keyMu.Lock()
whisper.privateKeys[id] = key
whisper.keyMu.Unlock()
log.Info("Whisper identity added", "id", id, "pubkey", common.ToHex(crypto.FromECDSAPub(&key.PublicKey)))
return id, nil
}
// SelectKeyPair adds cryptographic identity, and makes sure
// that it is the only private key known to the node.
func (whisper *Whisper) SelectKeyPair(key *ecdsa.PrivateKey) error {
id, err := makeDeterministicID(common.ToHex(crypto.FromECDSAPub(&key.PublicKey)), keyIDSize)
if err != nil {
return err
}
whisper.keyMu.Lock()
defer whisper.keyMu.Unlock()
whisper.privateKeys = make(map[string]*ecdsa.PrivateKey) // reset key store
whisper.privateKeys[id] = key
log.Info("Whisper identity selected", "id", id, "key", common.ToHex(crypto.FromECDSAPub(&key.PublicKey)))
return nil
}
// DeleteKeyPairs removes all cryptographic identities known to the node
func (whisper *Whisper) DeleteKeyPairs() error {
whisper.keyMu.Lock()
defer whisper.keyMu.Unlock()
whisper.privateKeys = make(map[string]*ecdsa.PrivateKey)
return nil
}
// HasKeyPair checks if the the whisper node is configured with the private key
// of the specified public pair.
func (whisper *Whisper) HasKeyPair(id string) bool {
deterministicID, err := toDeterministicID(id, keyIDSize)
if err != nil {
return false
}
whisper.keyMu.RLock()
defer whisper.keyMu.RUnlock()
return whisper.privateKeys[id] != nil
return whisper.privateKeys[deterministicID] != nil
}
// GetPrivateKey retrieves the private key of the specified identity.
func (whisper *Whisper) GetPrivateKey(id string) (*ecdsa.PrivateKey, error) {
deterministicID, err := toDeterministicID(id, keyIDSize)
if err != nil {
return nil, err
}
whisper.keyMu.RLock()
defer whisper.keyMu.RUnlock()
key := whisper.privateKeys[id]
key := whisper.privateKeys[deterministicID]
if key == nil {
return nil, fmt.Errorf("invalid id")
}
@ -465,6 +525,23 @@ func (whisper *Whisper) GenerateSymKey() (string, error) {
return id, nil
}
// AddSymKey stores the key with a given id.
func (whisper *Whisper) AddSymKey(id string, key []byte) (string, error) {
deterministicID, err := toDeterministicID(id, keyIDSize)
if err != nil {
return "", err
}
whisper.keyMu.Lock()
defer whisper.keyMu.Unlock()
if whisper.symKeys[deterministicID] != nil {
return "", fmt.Errorf("key already exists: %v", id)
}
whisper.symKeys[deterministicID] = key
return deterministicID, nil
}
// AddSymKeyDirect stores the key, and returns its id.
func (whisper *Whisper) AddSymKeyDirect(key []byte) (string, error) {
if len(key) != aesKeyLength {
@ -599,7 +676,7 @@ func (whisper *Whisper) Send(envelope *Envelope) error {
// Start implements node.Service, starting the background data propagation thread
// of the Whisper protocol.
func (whisper *Whisper) Start(*p2p.Server) error {
func (whisper *Whisper) Start(stack *p2p.Server) error {
log.Info("started whisper v." + ProtocolVersionStr)
go whisper.update()
@ -608,6 +685,12 @@ func (whisper *Whisper) Start(*p2p.Server) error {
go whisper.processQueue()
}
if whisper.notificationServer != nil {
if err := whisper.notificationServer.Start(stack); err != nil {
return err
}
}
return nil
}
@ -615,6 +698,13 @@ func (whisper *Whisper) Start(*p2p.Server) error {
// of the Whisper protocol.
func (whisper *Whisper) Stop() error {
close(whisper.quit)
if whisper.notificationServer != nil {
if err := whisper.notificationServer.Stop(); err != nil {
return err
}
}
log.Info("whisper stopped")
return nil
}
@ -673,6 +763,7 @@ func (whisper *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {
trouble := false
for _, env := range envelopes {
whisper.traceEnvelope(env, !whisper.isEnvelopeCached(env.Hash()), peerSource, p)
cached, err := whisper.add(env)
if err != nil {
trouble = true
@ -723,6 +814,7 @@ func (whisper *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {
return errors.New("invalid direct message")
}
whisper.postEvent(&envelope, true)
whisper.traceEnvelope(&envelope, false, p2pSource, p)
}
case p2pRequestCode:
// Must be processed if mail server is implemented. Otherwise ignore.
@ -819,6 +911,22 @@ func (whisper *Whisper) add(envelope *Envelope) (bool, error) {
return true, nil
}
// traceEnvelope collects basic metadata about an envelope and sender peer.
func (whisper *Whisper) traceEnvelope(envelope *Envelope, isNew bool, source envelopeSource, peer *Peer) {
if whisper.envelopeTracer == nil {
return
}
whisper.envelopeTracer.Trace(&EnvelopeMeta{
Hash: envelope.Hash().String(),
Topic: BytesToTopic(envelope.Topic[:]),
Size: uint32(envelope.size()),
Source: source,
IsNew: isNew,
Peer: peer.peer.Info().ID,
})
}
// postEvent queues the message for further processing.
func (whisper *Whisper) postEvent(envelope *Envelope, isP2P bool) {
if isP2P {
@ -1031,6 +1139,33 @@ func GenerateRandomID() (id string, err error) {
return id, err
}
// makeDeterministicID generates a deterministic ID, based on a given input
func makeDeterministicID(input string, keyLen int) (id string, err error) {
buf := pbkdf2.Key([]byte(input), nil, 4096, keyLen, sha256.New)
if !validateDataIntegrity(buf, keyIDSize) {
return "", fmt.Errorf("error in GenerateDeterministicID: failed to generate key")
}
id = common.Bytes2Hex(buf)
return id, err
}
// toDeterministicID reviews incoming id, and transforms it to format
// expected internally be private key store. Originally, public keys
// were used as keys, now random keys are being used. And in order to
// make it easier to consume, we now allow both random IDs and public
// keys to be passed.
func toDeterministicID(id string, expectedLen int) (string, error) {
if len(id) != (expectedLen * 2) { // we received hex key, so number of chars in id is doubled
var err error
id, err = makeDeterministicID(id, expectedLen)
if err != nil {
return "", err
}
}
return id, nil
}
func isFullNode(bloom []byte) bool {
if bloom == nil {
return true