diff --git a/whisper/whisperv2/whisper.go b/whisper/whisperv2/whisper.go index 61c36918d..908346999 100644 --- a/whisper/whisperv2/whisper.go +++ b/whisper/whisperv2/whisper.go @@ -134,6 +134,13 @@ func (self *Whisper) NewIdentity() *ecdsa.PrivateKey { return key } +// AddIdentity adds identity into the known identities list (for message decryption). +func (self *Whisper) AddIdentity(key *ecdsa.PrivateKey) { + self.keysMu.Lock() + self.keys[string(crypto.FromECDSAPub(&key.PublicKey))] = key + self.keysMu.Unlock() +} + // HasIdentity checks if the the whisper node is configured with the private key // of the specified public pair. func (self *Whisper) HasIdentity(key *ecdsa.PublicKey) bool { diff --git a/whisper/whisperv5/api.go b/whisper/whisperv5/api.go index 96c4b0e6c..e3c2f4a97 100644 --- a/whisper/whisperv5/api.go +++ b/whisper/whisperv5/api.go @@ -313,6 +313,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/whisperv5/whisper.go b/whisper/whisperv5/whisper.go index 85849ccce..c39e8b3e0 100644 --- a/whisper/whisperv5/whisper.go +++ b/whisper/whisperv5/whisper.go @@ -250,9 +256,9 @@ func (w *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 } w.keyMu.Lock() @@ -265,45 +271,94 @@ func (w *Whisper) NewKeyPair() (string, error) { return id, nil } -// DeleteKeyPair deletes the specified key if it exists. -func (w *Whisper) DeleteKeyPair(key string) bool { +// AddIdentity adds cryptographic identity into the known +// identities list (for message decryption). +func (w *Whisper) AddKeyPair(key *ecdsa.PrivateKey) (string, error) { + id, err := makeDeterministicID(common.ToHex(crypto.FromECDSAPub(&key.PublicKey)), keyIdSize) + if err != nil { + return "", err + } + if w.HasKeyPair(id) { + return id, nil // no need to re-inject + } + w.keyMu.Lock() defer w.keyMu.Unlock() - if w.privateKeys[key] != nil { - delete(w.privateKeys, key) - return true - } - return false + w.privateKeys[id] = key + log.Info("Whisper identity added", "id", id, "pubkey", common.ToHex(crypto.FromECDSAPub(&key.PublicKey))) + + return id, nil } -// AddKeyPair imports a asymmetric private key and returns it identifier. -func (w *Whisper) AddKeyPair(key *ecdsa.PrivateKey) (string, error) { - id, err := GenerateRandomID() +// SelectKeyPair adds cryptographic identity, and makes sure +// that it is the only private key known to the node. +func (w *Whisper) SelectKeyPair(key *ecdsa.PrivateKey) error { + id, err := makeDeterministicID(common.ToHex(crypto.FromECDSAPub(&key.PublicKey)), keyIdSize) if err != nil { - return "", fmt.Errorf("failed to generate ID: %s", err) + return err } w.keyMu.Lock() + defer w.keyMu.Unlock() + + w.privateKeys = make(map[string]*ecdsa.PrivateKey) // reset key store w.privateKeys[id] = key - w.keyMu.Unlock() - return id, nil + 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 (w *Whisper) DeleteKeyPairs() error { + w.keyMu.Lock() + defer w.keyMu.Unlock() + + w.privateKeys = make(map[string]*ecdsa.PrivateKey) + + return nil +} + +// DeleteKeyPair deletes the specified key if it exists. +func (w *Whisper) DeleteKeyPair(id string) bool { + deterministicID, err := toDeterministicID(id, keyIdSize) + if err != nil { + return false + } + + w.keyMu.Lock() + defer w.keyMu.Unlock() + + if w.privateKeys[deterministicID] != nil { + delete(w.privateKeys, deterministicID) + return true + } + return false } // HasKeyPair checks if the the whisper node is configured with the private key // of the specified public pair. func (w *Whisper) HasKeyPair(id string) bool { + deterministicID, err := toDeterministicID(id, keyIdSize) + if err != nil { + return false + } + w.keyMu.RLock() defer w.keyMu.RUnlock() - return w.privateKeys[id] != nil + return w.privateKeys[deterministicID] != nil } // GetPrivateKey retrieves the private key of the specified identity. func (w *Whisper) GetPrivateKey(id string) (*ecdsa.PrivateKey, error) { + deterministicID, err := toDeterministicID(id, keyIdSize) + if err != nil { + return nil, err + } + w.keyMu.RLock() defer w.keyMu.RUnlock() - key := w.privateKeys[id] + key := w.privateKeys[deterministicID] if key == nil { return nil, fmt.Errorf("invalid id") } @@ -336,6 +391,23 @@ func (w *Whisper) GenerateSymKey() (string, error) { return id, nil } +// AddSymKey stores the key with a given id. +func (w *Whisper) AddSymKey(id string, key []byte) (string, error) { + deterministicID, err := toDeterministicID(id, keyIdSize) + if err != nil { + return "", err + } + + w.keyMu.Lock() + defer w.keyMu.Unlock() + + if w.symKeys[deterministicID] != nil { + return "", fmt.Errorf("key already exists: %v", id) + } + w.symKeys[deterministicID] = key + return deterministicID, nil +} + // AddSymKeyDirect stores the key, and returns its id. func (w *Whisper) AddSymKeyDirect(key []byte) (string, error) { if len(key) != aesKeyLength { @@ -856,3 +941,30 @@ func GenerateRandomID() (id string, err error) { id = common.Bytes2Hex(buf) 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 !validateSymmetricKey(buf) { + 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 +}