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