github.com/samgwo/go-ethereum@v1.8.2-0.20180302101319-49bcb5fbd55e/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  }