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