github.com/anthdm/go-ethereum@v1.8.4-0.20180412101906-60516c83b011/whisper/whisperv5/api.go (about) 1 // Copyright 2016 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package whisperv5 18 19 import ( 20 "context" 21 "crypto/ecdsa" 22 "errors" 23 "fmt" 24 "sync" 25 "time" 26 27 "github.com/ethereum/go-ethereum/common" 28 "github.com/ethereum/go-ethereum/common/hexutil" 29 "github.com/ethereum/go-ethereum/crypto" 30 "github.com/ethereum/go-ethereum/log" 31 "github.com/ethereum/go-ethereum/p2p/discover" 32 "github.com/ethereum/go-ethereum/rpc" 33 ) 34 35 const ( 36 filterTimeout = 300 // filters are considered timeout out after filterTimeout seconds 37 ) 38 39 var ( 40 ErrSymAsym = errors.New("specify either a symmetric or an asymmetric key") 41 ErrInvalidSymmetricKey = errors.New("invalid symmetric key") 42 ErrInvalidPublicKey = errors.New("invalid public key") 43 ErrInvalidSigningPubKey = errors.New("invalid signing public key") 44 ErrTooLowPoW = errors.New("message rejected, PoW too low") 45 ErrNoTopics = errors.New("missing topic(s)") 46 ) 47 48 // PublicWhisperAPI provides the whisper RPC service that can be 49 // use publicly without security implications. 50 type PublicWhisperAPI struct { 51 w *Whisper 52 53 mu sync.Mutex 54 lastUsed map[string]time.Time // keeps track when a filter was polled for the last time. 55 } 56 57 // NewPublicWhisperAPI create a new RPC whisper service. 58 func NewPublicWhisperAPI(w *Whisper) *PublicWhisperAPI { 59 api := &PublicWhisperAPI{ 60 w: w, 61 lastUsed: make(map[string]time.Time), 62 } 63 return api 64 } 65 66 // Version returns the Whisper sub-protocol version. 67 func (api *PublicWhisperAPI) Version(ctx context.Context) string { 68 return ProtocolVersionStr 69 } 70 71 // Info contains diagnostic information. 72 type Info struct { 73 Memory int `json:"memory"` // Memory size of the floating messages in bytes. 74 Messages int `json:"messages"` // Number of floating messages. 75 MinPow float64 `json:"minPow"` // Minimal accepted PoW 76 MaxMessageSize uint32 `json:"maxMessageSize"` // Maximum accepted message size 77 } 78 79 // Info returns diagnostic information about the whisper node. 80 func (api *PublicWhisperAPI) Info(ctx context.Context) Info { 81 stats := api.w.Stats() 82 return Info{ 83 Memory: stats.memoryUsed, 84 Messages: len(api.w.messageQueue) + len(api.w.p2pMsgQueue), 85 MinPow: api.w.MinPow(), 86 MaxMessageSize: api.w.MaxMessageSize(), 87 } 88 } 89 90 // SetMaxMessageSize sets the maximum message size that is accepted. 91 // Upper limit is defined in whisperv5.MaxMessageSize. 92 func (api *PublicWhisperAPI) SetMaxMessageSize(ctx context.Context, size uint32) (bool, error) { 93 return true, api.w.SetMaxMessageSize(size) 94 } 95 96 // SetMinPow sets the minimum PoW for a message before it is accepted. 97 func (api *PublicWhisperAPI) SetMinPoW(ctx context.Context, pow float64) (bool, error) { 98 return true, api.w.SetMinimumPoW(pow) 99 } 100 101 // MarkTrustedPeer marks a peer trusted. , which will allow it to send historic (expired) messages. 102 // Note: This function is not adding new nodes, the node needs to exists as a peer. 103 func (api *PublicWhisperAPI) MarkTrustedPeer(ctx context.Context, enode string) (bool, error) { 104 n, err := discover.ParseNode(enode) 105 if err != nil { 106 return false, err 107 } 108 return true, api.w.AllowP2PMessagesFromPeer(n.ID[:]) 109 } 110 111 // NewKeyPair generates a new public and private key pair for message decryption and encryption. 112 // It returns an ID that can be used to refer to the keypair. 113 func (api *PublicWhisperAPI) NewKeyPair(ctx context.Context) (string, error) { 114 return api.w.NewKeyPair() 115 } 116 117 // AddPrivateKey imports the given private key. 118 func (api *PublicWhisperAPI) AddPrivateKey(ctx context.Context, privateKey hexutil.Bytes) (string, error) { 119 key, err := crypto.ToECDSA(privateKey) 120 if err != nil { 121 return "", err 122 } 123 return api.w.AddKeyPair(key) 124 } 125 126 // DeleteKeyPair removes the key with the given key if it exists. 127 func (api *PublicWhisperAPI) DeleteKeyPair(ctx context.Context, key string) (bool, error) { 128 if ok := api.w.DeleteKeyPair(key); ok { 129 return true, nil 130 } 131 return false, fmt.Errorf("key pair %s not found", key) 132 } 133 134 // HasKeyPair returns an indication if the node has a key pair that is associated with the given id. 135 func (api *PublicWhisperAPI) HasKeyPair(ctx context.Context, id string) bool { 136 return api.w.HasKeyPair(id) 137 } 138 139 // GetPublicKey returns the public key associated with the given key. The key is the hex 140 // encoded representation of a key in the form specified in section 4.3.6 of ANSI X9.62. 141 func (api *PublicWhisperAPI) GetPublicKey(ctx context.Context, id string) (hexutil.Bytes, error) { 142 key, err := api.w.GetPrivateKey(id) 143 if err != nil { 144 return hexutil.Bytes{}, err 145 } 146 return crypto.FromECDSAPub(&key.PublicKey), nil 147 } 148 149 // GetPublicKey returns the private key associated with the given key. The key is the hex 150 // encoded representation of a key in the form specified in section 4.3.6 of ANSI X9.62. 151 func (api *PublicWhisperAPI) GetPrivateKey(ctx context.Context, id string) (hexutil.Bytes, error) { 152 key, err := api.w.GetPrivateKey(id) 153 if err != nil { 154 return hexutil.Bytes{}, err 155 } 156 return crypto.FromECDSA(key), nil 157 } 158 159 // NewSymKey generate a random symmetric key. 160 // It returns an ID that can be used to refer to the key. 161 // Can be used encrypting and decrypting messages where the key is known to both parties. 162 func (api *PublicWhisperAPI) NewSymKey(ctx context.Context) (string, error) { 163 return api.w.GenerateSymKey() 164 } 165 166 // AddSymKey import a symmetric key. 167 // It returns an ID that can be used to refer to the key. 168 // Can be used encrypting and decrypting messages where the key is known to both parties. 169 func (api *PublicWhisperAPI) AddSymKey(ctx context.Context, key hexutil.Bytes) (string, error) { 170 return api.w.AddSymKeyDirect([]byte(key)) 171 } 172 173 // GenerateSymKeyFromPassword derive a key from the given password, stores it, and returns its ID. 174 func (api *PublicWhisperAPI) GenerateSymKeyFromPassword(ctx context.Context, passwd string) (string, error) { 175 return api.w.AddSymKeyFromPassword(passwd) 176 } 177 178 // HasSymKey returns an indication if the node has a symmetric key associated with the given key. 179 func (api *PublicWhisperAPI) HasSymKey(ctx context.Context, id string) bool { 180 return api.w.HasSymKey(id) 181 } 182 183 // GetSymKey returns the symmetric key associated with the given id. 184 func (api *PublicWhisperAPI) GetSymKey(ctx context.Context, id string) (hexutil.Bytes, error) { 185 return api.w.GetSymKey(id) 186 } 187 188 // DeleteSymKey deletes the symmetric key that is associated with the given id. 189 func (api *PublicWhisperAPI) DeleteSymKey(ctx context.Context, id string) bool { 190 return api.w.DeleteSymKey(id) 191 } 192 193 //go:generate gencodec -type NewMessage -field-override newMessageOverride -out gen_newmessage_json.go 194 195 // NewMessage represents a new whisper message that is posted through the RPC. 196 type NewMessage struct { 197 SymKeyID string `json:"symKeyID"` 198 PublicKey []byte `json:"pubKey"` 199 Sig string `json:"sig"` 200 TTL uint32 `json:"ttl"` 201 Topic TopicType `json:"topic"` 202 Payload []byte `json:"payload"` 203 Padding []byte `json:"padding"` 204 PowTime uint32 `json:"powTime"` 205 PowTarget float64 `json:"powTarget"` 206 TargetPeer string `json:"targetPeer"` 207 } 208 209 type newMessageOverride struct { 210 PublicKey hexutil.Bytes 211 Payload hexutil.Bytes 212 Padding hexutil.Bytes 213 } 214 215 // Post a message on the Whisper network. 216 func (api *PublicWhisperAPI) Post(ctx context.Context, req NewMessage) (bool, error) { 217 var ( 218 symKeyGiven = len(req.SymKeyID) > 0 219 pubKeyGiven = len(req.PublicKey) > 0 220 err error 221 ) 222 223 // user must specify either a symmetric or an asymmetric key 224 if (symKeyGiven && pubKeyGiven) || (!symKeyGiven && !pubKeyGiven) { 225 return false, ErrSymAsym 226 } 227 228 params := &MessageParams{ 229 TTL: req.TTL, 230 Payload: req.Payload, 231 Padding: req.Padding, 232 WorkTime: req.PowTime, 233 PoW: req.PowTarget, 234 Topic: req.Topic, 235 } 236 237 // Set key that is used to sign the message 238 if len(req.Sig) > 0 { 239 if params.Src, err = api.w.GetPrivateKey(req.Sig); err != nil { 240 return false, err 241 } 242 } 243 244 // Set symmetric key that is used to encrypt the message 245 if symKeyGiven { 246 if params.Topic == (TopicType{}) { // topics are mandatory with symmetric encryption 247 return false, ErrNoTopics 248 } 249 if params.KeySym, err = api.w.GetSymKey(req.SymKeyID); err != nil { 250 return false, err 251 } 252 if !validateSymmetricKey(params.KeySym) { 253 return false, ErrInvalidSymmetricKey 254 } 255 } 256 257 // Set asymmetric key that is used to encrypt the message 258 if pubKeyGiven { 259 params.Dst = crypto.ToECDSAPub(req.PublicKey) 260 if !ValidatePublicKey(params.Dst) { 261 return false, ErrInvalidPublicKey 262 } 263 } 264 265 // encrypt and sent message 266 whisperMsg, err := NewSentMessage(params) 267 if err != nil { 268 return false, err 269 } 270 271 env, err := whisperMsg.Wrap(params) 272 if err != nil { 273 return false, err 274 } 275 276 // send to specific node (skip PoW check) 277 if len(req.TargetPeer) > 0 { 278 n, err := discover.ParseNode(req.TargetPeer) 279 if err != nil { 280 return false, fmt.Errorf("failed to parse target peer: %s", err) 281 } 282 return true, api.w.SendP2PMessage(n.ID[:], env) 283 } 284 285 // ensure that the message PoW meets the node's minimum accepted PoW 286 if req.PowTarget < api.w.MinPow() { 287 return false, ErrTooLowPoW 288 } 289 290 return true, api.w.Send(env) 291 } 292 293 //go:generate gencodec -type Criteria -field-override criteriaOverride -out gen_criteria_json.go 294 295 // Criteria holds various filter options for inbound messages. 296 type Criteria struct { 297 SymKeyID string `json:"symKeyID"` 298 PrivateKeyID string `json:"privateKeyID"` 299 Sig []byte `json:"sig"` 300 MinPow float64 `json:"minPow"` 301 Topics []TopicType `json:"topics"` 302 AllowP2P bool `json:"allowP2P"` 303 } 304 305 type criteriaOverride struct { 306 Sig hexutil.Bytes 307 } 308 309 // Messages set up a subscription that fires events when messages arrive that match 310 // the given set of criteria. 311 func (api *PublicWhisperAPI) Messages(ctx context.Context, crit Criteria) (*rpc.Subscription, error) { 312 var ( 313 symKeyGiven = len(crit.SymKeyID) > 0 314 pubKeyGiven = len(crit.PrivateKeyID) > 0 315 err error 316 ) 317 318 // ensure that the RPC connection supports subscriptions 319 notifier, supported := rpc.NotifierFromContext(ctx) 320 if !supported { 321 return nil, rpc.ErrNotificationsUnsupported 322 } 323 324 // user must specify either a symmetric or an asymmetric key 325 if (symKeyGiven && pubKeyGiven) || (!symKeyGiven && !pubKeyGiven) { 326 return nil, ErrSymAsym 327 } 328 329 filter := Filter{ 330 PoW: crit.MinPow, 331 Messages: make(map[common.Hash]*ReceivedMessage), 332 AllowP2P: crit.AllowP2P, 333 } 334 335 if len(crit.Sig) > 0 { 336 filter.Src = crypto.ToECDSAPub(crit.Sig) 337 if !ValidatePublicKey(filter.Src) { 338 return nil, ErrInvalidSigningPubKey 339 } 340 } 341 342 for i, bt := range crit.Topics { 343 if len(bt) == 0 || len(bt) > 4 { 344 return nil, fmt.Errorf("subscribe: topic %d has wrong size: %d", i, len(bt)) 345 } 346 filter.Topics = append(filter.Topics, bt[:]) 347 } 348 349 // listen for message that are encrypted with the given symmetric key 350 if symKeyGiven { 351 if len(filter.Topics) == 0 { 352 return nil, ErrNoTopics 353 } 354 key, err := api.w.GetSymKey(crit.SymKeyID) 355 if err != nil { 356 return nil, err 357 } 358 if !validateSymmetricKey(key) { 359 return nil, ErrInvalidSymmetricKey 360 } 361 filter.KeySym = key 362 filter.SymKeyHash = crypto.Keccak256Hash(filter.KeySym) 363 } 364 365 // listen for messages that are encrypted with the given public key 366 if pubKeyGiven { 367 filter.KeyAsym, err = api.w.GetPrivateKey(crit.PrivateKeyID) 368 if err != nil || filter.KeyAsym == nil { 369 return nil, ErrInvalidPublicKey 370 } 371 } 372 373 id, err := api.w.Subscribe(&filter) 374 if err != nil { 375 return nil, err 376 } 377 378 // create subscription and start waiting for message events 379 rpcSub := notifier.CreateSubscription() 380 go func() { 381 // for now poll internally, refactor whisper internal for channel support 382 ticker := time.NewTicker(250 * time.Millisecond) 383 defer ticker.Stop() 384 385 for { 386 select { 387 case <-ticker.C: 388 if filter := api.w.GetFilter(id); filter != nil { 389 for _, rpcMessage := range toMessage(filter.Retrieve()) { 390 if err := notifier.Notify(rpcSub.ID, rpcMessage); err != nil { 391 log.Error("Failed to send notification", "err", err) 392 } 393 } 394 } 395 case <-rpcSub.Err(): 396 api.w.Unsubscribe(id) 397 return 398 case <-notifier.Closed(): 399 api.w.Unsubscribe(id) 400 return 401 } 402 } 403 }() 404 405 return rpcSub, nil 406 } 407 408 //go:generate gencodec -type Message -field-override messageOverride -out gen_message_json.go 409 410 // Message is the RPC representation of a whisper message. 411 type Message struct { 412 Sig []byte `json:"sig,omitempty"` 413 TTL uint32 `json:"ttl"` 414 Timestamp uint32 `json:"timestamp"` 415 Topic TopicType `json:"topic"` 416 Payload []byte `json:"payload"` 417 Padding []byte `json:"padding"` 418 PoW float64 `json:"pow"` 419 Hash []byte `json:"hash"` 420 Dst []byte `json:"recipientPublicKey,omitempty"` 421 } 422 423 type messageOverride struct { 424 Sig hexutil.Bytes 425 Payload hexutil.Bytes 426 Padding hexutil.Bytes 427 Hash hexutil.Bytes 428 Dst hexutil.Bytes 429 } 430 431 // ToWhisperMessage converts an internal message into an API version. 432 func ToWhisperMessage(message *ReceivedMessage) *Message { 433 msg := Message{ 434 Payload: message.Payload, 435 Padding: message.Padding, 436 Timestamp: message.Sent, 437 TTL: message.TTL, 438 PoW: message.PoW, 439 Hash: message.EnvelopeHash.Bytes(), 440 Topic: message.Topic, 441 } 442 443 if message.Dst != nil { 444 b := crypto.FromECDSAPub(message.Dst) 445 if b != nil { 446 msg.Dst = b 447 } 448 } 449 450 if isMessageSigned(message.Raw[0]) { 451 b := crypto.FromECDSAPub(message.SigToPubKey()) 452 if b != nil { 453 msg.Sig = b 454 } 455 } 456 457 return &msg 458 } 459 460 // toMessage converts a set of messages to its RPC representation. 461 func toMessage(messages []*ReceivedMessage) []*Message { 462 msgs := make([]*Message, len(messages)) 463 for i, msg := range messages { 464 msgs[i] = ToWhisperMessage(msg) 465 } 466 return msgs 467 } 468 469 // GetFilterMessages returns the messages that match the filter criteria and 470 // are received between the last poll and now. 471 func (api *PublicWhisperAPI) GetFilterMessages(id string) ([]*Message, error) { 472 api.mu.Lock() 473 f := api.w.GetFilter(id) 474 if f == nil { 475 api.mu.Unlock() 476 return nil, fmt.Errorf("filter not found") 477 } 478 api.lastUsed[id] = time.Now() 479 api.mu.Unlock() 480 481 receivedMessages := f.Retrieve() 482 messages := make([]*Message, 0, len(receivedMessages)) 483 for _, msg := range receivedMessages { 484 messages = append(messages, ToWhisperMessage(msg)) 485 } 486 487 return messages, nil 488 } 489 490 // DeleteMessageFilter deletes a filter. 491 func (api *PublicWhisperAPI) DeleteMessageFilter(id string) (bool, error) { 492 api.mu.Lock() 493 defer api.mu.Unlock() 494 495 delete(api.lastUsed, id) 496 return true, api.w.Unsubscribe(id) 497 } 498 499 // NewMessageFilter creates a new filter that can be used to poll for 500 // (new) messages that satisfy the given criteria. 501 func (api *PublicWhisperAPI) NewMessageFilter(req Criteria) (string, error) { 502 var ( 503 src *ecdsa.PublicKey 504 keySym []byte 505 keyAsym *ecdsa.PrivateKey 506 topics [][]byte 507 508 symKeyGiven = len(req.SymKeyID) > 0 509 asymKeyGiven = len(req.PrivateKeyID) > 0 510 511 err error 512 ) 513 514 // user must specify either a symmetric or an asymmetric key 515 if (symKeyGiven && asymKeyGiven) || (!symKeyGiven && !asymKeyGiven) { 516 return "", ErrSymAsym 517 } 518 519 if len(req.Sig) > 0 { 520 src = crypto.ToECDSAPub(req.Sig) 521 if !ValidatePublicKey(src) { 522 return "", ErrInvalidSigningPubKey 523 } 524 } 525 526 if symKeyGiven { 527 if keySym, err = api.w.GetSymKey(req.SymKeyID); err != nil { 528 return "", err 529 } 530 if !validateSymmetricKey(keySym) { 531 return "", ErrInvalidSymmetricKey 532 } 533 } 534 535 if asymKeyGiven { 536 if keyAsym, err = api.w.GetPrivateKey(req.PrivateKeyID); err != nil { 537 return "", err 538 } 539 } 540 541 if len(req.Topics) > 0 { 542 topics = make([][]byte, 0, len(req.Topics)) 543 for _, topic := range req.Topics { 544 topics = append(topics, topic[:]) 545 } 546 } 547 548 f := &Filter{ 549 Src: src, 550 KeySym: keySym, 551 KeyAsym: keyAsym, 552 PoW: req.MinPow, 553 AllowP2P: req.AllowP2P, 554 Topics: topics, 555 Messages: make(map[common.Hash]*ReceivedMessage), 556 } 557 558 id, err := api.w.Subscribe(f) 559 if err != nil { 560 return "", err 561 } 562 563 api.mu.Lock() 564 api.lastUsed[id] = time.Now() 565 api.mu.Unlock() 566 567 return id, nil 568 }