// Watch installs a new message handler to run in case a matching packet arrives // from the whisper network. func (self *Whisper) Watch(options Filter) int { filter := filterer{ to: string(crypto.FromECDSAPub(options.To)), from: string(crypto.FromECDSAPub(options.From)), matcher: newTopicMatcher(options.Topics...), fn: func(data interface{}) { options.Fn(data.(*Message)) }, } return self.filters.Install(filter) }
// createFilter creates a message filter to check against installed handlers. func createFilter(message *Message, topics []Topic) filter.Filter { matcher := make([][]Topic, len(topics)) for i, topic := range topics { matcher[i] = []Topic{topic} } return filterer{ to: string(crypto.FromECDSAPub(message.To)), from: string(crypto.FromECDSAPub(message.Recover())), matcher: newTopicMatcher(matcher...), } }
// NewWhisperMessage converts an internal message into an API version. func NewWhisperMessage(message *Message) WhisperMessage { return WhisperMessage{ ref: message, Payload: common.ToHex(message.Payload), From: common.ToHex(crypto.FromECDSAPub(message.Recover())), To: common.ToHex(crypto.FromECDSAPub(message.To)), Sent: message.Sent.Unix(), TTL: int64(message.TTL / time.Second), Hash: common.ToHex(message.Hash.Bytes()), } }
// makeAuthMsg creates the initiator handshake message. func (h *encHandshake) makeAuthMsg(prv *ecdsa.PrivateKey, token []byte) (*authMsgV4, error) { rpub, err := h.remoteID.Pubkey() if err != nil { return nil, fmt.Errorf("bad remoteID: %v", err) } h.remotePub = ecies.ImportECDSAPublic(rpub) // Generate random initiator nonce. h.initNonce = make([]byte, shaLen) if _, err := rand.Read(h.initNonce); err != nil { return nil, err } // Generate random keypair to for ECDH. h.randomPrivKey, err = ecies.GenerateKey(rand.Reader, crypto.S256(), nil) if err != nil { return nil, err } // Sign known message: static-shared-secret ^ nonce token, err = h.staticSharedSecret(prv) if err != nil { return nil, err } signed := xor(token, h.initNonce) signature, err := crypto.Sign(signed, h.randomPrivKey.ExportECDSA()) if err != nil { return nil, err } msg := new(authMsgV4) copy(msg.Signature[:], signature) copy(msg.InitiatorPubkey[:], crypto.FromECDSAPub(&prv.PublicKey)[1:]) copy(msg.Nonce[:], h.initNonce) msg.Version = 4 return msg, nil }
// NewIdentity generates a new cryptographic identity for the client, and injects // it into the known identities for message decryption. func (s *PublicWhisperAPI) NewIdentity() (string, error) { if s.w == nil { return "", whisperOffLineErr } identity := s.w.NewIdentity() return common.ToHex(crypto.FromECDSAPub(&identity.PublicKey)), nil }
// NewIdentity generates a new cryptographic identity for the client, and injects // it into the known identities for message decryption. func (self *Whisper) NewIdentity() *ecdsa.PrivateKey { key, err := crypto.GenerateKey() if err != nil { panic(err) } self.keys[string(crypto.FromECDSAPub(&key.PublicKey))] = key return key }
func TestHandshakeForwardCompatibility(t *testing.T) { var ( keyA, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") keyB, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") pubA = crypto.FromECDSAPub(&keyA.PublicKey)[1:] pubB = crypto.FromECDSAPub(&keyB.PublicKey)[1:] ephA, _ = crypto.HexToECDSA("869d6ecf5211f1cc60418a13b9d870b22959d0c16f02bec714c960dd2298a32d") ephB, _ = crypto.HexToECDSA("e238eb8e04fee6511ab04c6dd3c89ce097b11f25d584863ac2b6d5b35b1847e4") ephPubA = crypto.FromECDSAPub(&ephA.PublicKey)[1:] ephPubB = crypto.FromECDSAPub(&ephB.PublicKey)[1:] nonceA = unhex("7e968bba13b6c50e2c4cd7f241cc0d64d1ac25c7f5952df231ac6a2bda8ee5d6") nonceB = unhex("559aead08264d5795d3909718cdd05abd49572e84fe55590eef31a88a08fdffd") _, _, _, _ = pubA, pubB, ephPubA, ephPubB authSignature = unhex("299ca6acfd35e3d72d8ba3d1e2b60b5561d5af5218eb5bc182045769eb4226910a301acae3b369fffc4a4899d6b02531e89fd4fe36a2cf0d93607ba470b50f7800") _ = authSignature ) makeAuth := func(test handshakeAuthTest) *authMsgV4 { msg := &authMsgV4{Version: test.wantVersion, Rest: test.wantRest, gotPlain: test.isPlain} copy(msg.Signature[:], authSignature) copy(msg.InitiatorPubkey[:], pubA) copy(msg.Nonce[:], nonceA) return msg } makeAck := func(test handshakeAckTest) *authRespV4 { msg := &authRespV4{Version: test.wantVersion, Rest: test.wantRest} copy(msg.RandomPubkey[:], ephPubB) copy(msg.Nonce[:], nonceB) return msg } // check auth msg parsing for _, test := range eip8HandshakeAuthTests { r := bytes.NewReader(unhex(test.input)) msg := new(authMsgV4) ciphertext, err := readHandshakeMsg(msg, encAuthMsgLen, keyB, r) if err != nil { t.Errorf("error for input %x:\n %v", unhex(test.input), err) continue } if !bytes.Equal(ciphertext, unhex(test.input)) { t.Errorf("wrong ciphertext for input %x:\n %x", unhex(test.input), ciphertext) } want := makeAuth(test) if !reflect.DeepEqual(msg, want) { t.Errorf("wrong msg for input %x:\ngot %s\nwant %s", unhex(test.input), spew.Sdump(msg), spew.Sdump(want)) } } // check auth resp parsing for _, test := range eip8HandshakeRespTests { input := unhex(test.input) r := bytes.NewReader(input) msg := new(authRespV4) ciphertext, err := readHandshakeMsg(msg, encAuthRespLen, keyA, r) if err != nil { t.Errorf("error for input %x:\n %v", input, err) continue } if !bytes.Equal(ciphertext, input) { t.Errorf("wrong ciphertext for input %x:\n %x", input, err) } want := makeAck(test) if !reflect.DeepEqual(msg, want) { t.Errorf("wrong msg for input %x:\ngot %s\nwant %s", input, spew.Sdump(msg), spew.Sdump(want)) } } // check derivation for (Auth₂, Ack₂) on recipient side var ( hs = &encHandshake{ initiator: false, respNonce: nonceB, randomPrivKey: ecies.ImportECDSA(ephB), } authCiphertext = unhex(eip8HandshakeAuthTests[1].input) authRespCiphertext = unhex(eip8HandshakeRespTests[1].input) authMsg = makeAuth(eip8HandshakeAuthTests[1]) wantAES = unhex("80e8632c05fed6fc2a13b0f8d31a3cf645366239170ea067065aba8e28bac487") wantMAC = unhex("2ea74ec5dae199227dff1af715362700e989d889d7a493cb0639691efb8e5f98") wantFooIngressHash = unhex("0c7ec6340062cc46f5e9f1e3cf86f8c8c403c5a0964f5df0ebd34a75ddc86db5") ) if err := hs.handleAuthMsg(authMsg, keyB); err != nil { t.Fatalf("handleAuthMsg: %v", err) } derived, err := hs.secrets(authCiphertext, authRespCiphertext) if err != nil { t.Fatalf("secrets: %v", err) } if !bytes.Equal(derived.AES, wantAES) { t.Errorf("aes-secret mismatch:\ngot %x\nwant %x", derived.AES, wantAES) } if !bytes.Equal(derived.MAC, wantMAC) { t.Errorf("mac-secret mismatch:\ngot %x\nwant %x", derived.MAC, wantMAC) } io.WriteString(derived.IngressMAC, "foo") fooIngressHash := derived.IngressMAC.Sum(nil) if !bytes.Equal(fooIngressHash, wantFooIngressHash) { t.Errorf("ingress-mac('foo') mismatch:\ngot %x\nwant %x", fooIngressHash, wantFooIngressHash) } }
// GetIdentity retrieves the private key of the specified public identity. func (self *Whisper) GetIdentity(key *ecdsa.PublicKey) *ecdsa.PrivateKey { return self.keys[string(crypto.FromECDSAPub(key))] }
// 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 { return self.keys[string(crypto.FromECDSAPub(key))] != nil }
// NewIdentity generates a new cryptographic identity for the client, and injects // it into the known identities for message decryption. func (self *Whisper) NewIdentity() string { identity := self.Whisper.NewIdentity() return common.ToHex(crypto.FromECDSAPub(&identity.PublicKey)) }