github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/whisper/whisperv6/filter.go (about)

     1  // This file is part of the go-sberex library. The go-sberex library is 
     2  // free software: you can redistribute it and/or modify it under the terms 
     3  // of the GNU Lesser General Public License as published by the Free 
     4  // Software Foundation, either version 3 of the License, or (at your option)
     5  // any later version.
     6  //
     7  // The go-sberex library is distributed in the hope that it will be useful, 
     8  // but WITHOUT ANY WARRANTY; without even the implied warranty of
     9  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 
    10  // General Public License <http://www.gnu.org/licenses/> for more details.
    11  
    12  package whisperv6
    13  
    14  import (
    15  	"crypto/ecdsa"
    16  	"fmt"
    17  	"sync"
    18  
    19  	"github.com/Sberex/go-sberex/common"
    20  	"github.com/Sberex/go-sberex/crypto"
    21  	"github.com/Sberex/go-sberex/log"
    22  )
    23  
    24  // Filter represents a Whisper message filter
    25  type Filter struct {
    26  	Src        *ecdsa.PublicKey  // Sender of the message
    27  	KeyAsym    *ecdsa.PrivateKey // Private Key of recipient
    28  	KeySym     []byte            // Key associated with the Topic
    29  	Topics     [][]byte          // Topics to filter messages with
    30  	PoW        float64           // Proof of work as described in the Whisper spec
    31  	AllowP2P   bool              // Indicates whether this filter is interested in direct peer-to-peer messages
    32  	SymKeyHash common.Hash       // The Keccak256Hash of the symmetric key, needed for optimization
    33  
    34  	Messages map[common.Hash]*ReceivedMessage
    35  	mutex    sync.RWMutex
    36  }
    37  
    38  // Filters represents a collection of filters
    39  type Filters struct {
    40  	watchers map[string]*Filter
    41  	whisper  *Whisper
    42  	mutex    sync.RWMutex
    43  }
    44  
    45  // NewFilters returns a newly created filter collection
    46  func NewFilters(w *Whisper) *Filters {
    47  	return &Filters{
    48  		watchers: make(map[string]*Filter),
    49  		whisper:  w,
    50  	}
    51  }
    52  
    53  // Install will add a new filter to the filter collection
    54  func (fs *Filters) Install(watcher *Filter) (string, error) {
    55  	if watcher.KeySym != nil && watcher.KeyAsym != nil {
    56  		return "", fmt.Errorf("filters must choose between symmetric and asymmetric keys")
    57  	}
    58  
    59  	if watcher.Messages == nil {
    60  		watcher.Messages = make(map[common.Hash]*ReceivedMessage)
    61  	}
    62  
    63  	id, err := GenerateRandomID()
    64  	if err != nil {
    65  		return "", err
    66  	}
    67  
    68  	fs.mutex.Lock()
    69  	defer fs.mutex.Unlock()
    70  
    71  	if fs.watchers[id] != nil {
    72  		return "", fmt.Errorf("failed to generate unique ID")
    73  	}
    74  
    75  	if watcher.expectsSymmetricEncryption() {
    76  		watcher.SymKeyHash = crypto.Keccak256Hash(watcher.KeySym)
    77  	}
    78  
    79  	fs.watchers[id] = watcher
    80  	return id, err
    81  }
    82  
    83  // Uninstall will remove a filter whose id has been specified from
    84  // the filter collection
    85  func (fs *Filters) Uninstall(id string) bool {
    86  	fs.mutex.Lock()
    87  	defer fs.mutex.Unlock()
    88  	if fs.watchers[id] != nil {
    89  		delete(fs.watchers, id)
    90  		return true
    91  	}
    92  	return false
    93  }
    94  
    95  // Get returns a filter from the collection with a specific ID
    96  func (fs *Filters) Get(id string) *Filter {
    97  	fs.mutex.RLock()
    98  	defer fs.mutex.RUnlock()
    99  	return fs.watchers[id]
   100  }
   101  
   102  // NotifyWatchers notifies any filter that has declared interest
   103  // for the envelope's topic.
   104  func (fs *Filters) NotifyWatchers(env *Envelope, p2pMessage bool) {
   105  	var msg *ReceivedMessage
   106  
   107  	fs.mutex.RLock()
   108  	defer fs.mutex.RUnlock()
   109  
   110  	i := -1 // only used for logging info
   111  	for _, watcher := range fs.watchers {
   112  		i++
   113  		if p2pMessage && !watcher.AllowP2P {
   114  			log.Trace(fmt.Sprintf("msg [%x], filter [%d]: p2p messages are not allowed", env.Hash(), i))
   115  			continue
   116  		}
   117  
   118  		var match bool
   119  		if msg != nil {
   120  			match = watcher.MatchMessage(msg)
   121  		} else {
   122  			match = watcher.MatchEnvelope(env)
   123  			if match {
   124  				msg = env.Open(watcher)
   125  				if msg == nil {
   126  					log.Trace("processing message: failed to open", "message", env.Hash().Hex(), "filter", i)
   127  				}
   128  			} else {
   129  				log.Trace("processing message: does not match", "message", env.Hash().Hex(), "filter", i)
   130  			}
   131  		}
   132  
   133  		if match && msg != nil {
   134  			log.Trace("processing message: decrypted", "hash", env.Hash().Hex())
   135  			if watcher.Src == nil || IsPubKeyEqual(msg.Src, watcher.Src) {
   136  				watcher.Trigger(msg)
   137  			}
   138  		}
   139  	}
   140  }
   141  
   142  func (f *Filter) processEnvelope(env *Envelope) *ReceivedMessage {
   143  	if f.MatchEnvelope(env) {
   144  		msg := env.Open(f)
   145  		if msg != nil {
   146  			return msg
   147  		}
   148  
   149  		log.Trace("processing envelope: failed to open", "hash", env.Hash().Hex())
   150  	} else {
   151  		log.Trace("processing envelope: does not match", "hash", env.Hash().Hex())
   152  	}
   153  	return nil
   154  }
   155  
   156  func (f *Filter) expectsAsymmetricEncryption() bool {
   157  	return f.KeyAsym != nil
   158  }
   159  
   160  func (f *Filter) expectsSymmetricEncryption() bool {
   161  	return f.KeySym != nil
   162  }
   163  
   164  // Trigger adds a yet-unknown message to the filter's list of
   165  // received messages.
   166  func (f *Filter) Trigger(msg *ReceivedMessage) {
   167  	f.mutex.Lock()
   168  	defer f.mutex.Unlock()
   169  
   170  	if _, exist := f.Messages[msg.EnvelopeHash]; !exist {
   171  		f.Messages[msg.EnvelopeHash] = msg
   172  	}
   173  }
   174  
   175  // Retrieve will return the list of all received messages associated
   176  // to a filter.
   177  func (f *Filter) Retrieve() (all []*ReceivedMessage) {
   178  	f.mutex.Lock()
   179  	defer f.mutex.Unlock()
   180  
   181  	all = make([]*ReceivedMessage, 0, len(f.Messages))
   182  	for _, msg := range f.Messages {
   183  		all = append(all, msg)
   184  	}
   185  
   186  	f.Messages = make(map[common.Hash]*ReceivedMessage) // delete old messages
   187  	return all
   188  }
   189  
   190  // MatchMessage checks if the filter matches an already decrypted
   191  // message (i.e. a Message that has already been handled by
   192  // MatchEnvelope when checked by a previous filter)
   193  func (f *Filter) MatchMessage(msg *ReceivedMessage) bool {
   194  	if f.PoW > 0 && msg.PoW < f.PoW {
   195  		return false
   196  	}
   197  
   198  	if f.expectsAsymmetricEncryption() && msg.isAsymmetricEncryption() {
   199  		return IsPubKeyEqual(&f.KeyAsym.PublicKey, msg.Dst) && f.MatchTopic(msg.Topic)
   200  	} else if f.expectsSymmetricEncryption() && msg.isSymmetricEncryption() {
   201  		return f.SymKeyHash == msg.SymKeyHash && f.MatchTopic(msg.Topic)
   202  	}
   203  	return false
   204  }
   205  
   206  // MatchEnvelope checks if it's worth decrypting the message. If
   207  // it returns `true`, client code is expected to attempt decrypting
   208  // the message and subsequently call MatchMessage.
   209  func (f *Filter) MatchEnvelope(envelope *Envelope) bool {
   210  	if f.PoW > 0 && envelope.pow < f.PoW {
   211  		return false
   212  	}
   213  
   214  	return f.MatchTopic(envelope.Topic)
   215  }
   216  
   217  // MatchTopic checks that the filter captures a given topic.
   218  func (f *Filter) MatchTopic(topic TopicType) bool {
   219  	if len(f.Topics) == 0 {
   220  		// any topic matches
   221  		return true
   222  	}
   223  
   224  	for _, bt := range f.Topics {
   225  		if matchSingleTopic(topic, bt) {
   226  			return true
   227  		}
   228  	}
   229  	return false
   230  }
   231  
   232  func matchSingleTopic(topic TopicType, bt []byte) bool {
   233  	if len(bt) > TopicLength {
   234  		bt = bt[:TopicLength]
   235  	}
   236  
   237  	if len(bt) < TopicLength {
   238  		return false
   239  	}
   240  
   241  	for j, b := range bt {
   242  		if topic[j] != b {
   243  			return false
   244  		}
   245  	}
   246  	return true
   247  }
   248  
   249  // IsPubKeyEqual checks that two public keys are equal
   250  func IsPubKeyEqual(a, b *ecdsa.PublicKey) bool {
   251  	if !ValidatePublicKey(a) {
   252  		return false
   253  	} else if !ValidatePublicKey(b) {
   254  		return false
   255  	}
   256  	// the curve is always the same, just compare the points
   257  	return a.X.Cmp(b.X) == 0 && a.Y.Cmp(b.Y) == 0
   258  }