github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/whisper/whisperv5/filter.go (about)

     1  // Copyright 2016 The Spectrum Authors
     2  // This file is part of the Spectrum library.
     3  //
     4  // The Spectrum 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 Spectrum 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 Spectrum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package whisperv5
    18  
    19  import (
    20  	"crypto/ecdsa"
    21  	"fmt"
    22  	"sync"
    23  
    24  	"github.com/SmartMeshFoundation/Spectrum/common"
    25  	"github.com/SmartMeshFoundation/Spectrum/crypto"
    26  	"github.com/SmartMeshFoundation/Spectrum/log"
    27  )
    28  
    29  type Filter struct {
    30  	Src        *ecdsa.PublicKey  // Sender of the message
    31  	KeyAsym    *ecdsa.PrivateKey // Private Key of recipient
    32  	KeySym     []byte            // Key associated with the Topic
    33  	Topics     [][]byte          // Topics to filter messages with
    34  	PoW        float64           // Proof of work as described in the Whisper spec
    35  	AllowP2P   bool              // Indicates whether this filter is interested in direct peer-to-peer messages
    36  	SymKeyHash common.Hash       // The Keccak256Hash of the symmetric key, needed for optimization
    37  
    38  	Messages map[common.Hash]*ReceivedMessage
    39  	mutex    sync.RWMutex
    40  }
    41  
    42  type Filters struct {
    43  	watchers map[string]*Filter
    44  	whisper  *Whisper
    45  	mutex    sync.RWMutex
    46  }
    47  
    48  func NewFilters(w *Whisper) *Filters {
    49  	return &Filters{
    50  		watchers: make(map[string]*Filter),
    51  		whisper:  w,
    52  	}
    53  }
    54  
    55  func (fs *Filters) Install(watcher *Filter) (string, error) {
    56  	if watcher.Messages == nil {
    57  		watcher.Messages = make(map[common.Hash]*ReceivedMessage)
    58  	}
    59  
    60  	id, err := GenerateRandomID()
    61  	if err != nil {
    62  		return "", err
    63  	}
    64  
    65  	fs.mutex.Lock()
    66  	defer fs.mutex.Unlock()
    67  
    68  	if fs.watchers[id] != nil {
    69  		return "", fmt.Errorf("failed to generate unique ID")
    70  	}
    71  
    72  	if watcher.expectsSymmetricEncryption() {
    73  		watcher.SymKeyHash = crypto.Keccak256Hash(watcher.KeySym)
    74  	}
    75  
    76  	fs.watchers[id] = watcher
    77  	return id, err
    78  }
    79  
    80  func (fs *Filters) Uninstall(id string) bool {
    81  	fs.mutex.Lock()
    82  	defer fs.mutex.Unlock()
    83  	if fs.watchers[id] != nil {
    84  		delete(fs.watchers, id)
    85  		return true
    86  	}
    87  	return false
    88  }
    89  
    90  func (fs *Filters) Get(id string) *Filter {
    91  	fs.mutex.RLock()
    92  	defer fs.mutex.RUnlock()
    93  	return fs.watchers[id]
    94  }
    95  
    96  func (fs *Filters) NotifyWatchers(env *Envelope, p2pMessage bool) {
    97  	var msg *ReceivedMessage
    98  
    99  	fs.mutex.RLock()
   100  	defer fs.mutex.RUnlock()
   101  
   102  	i := -1 // only used for logging info
   103  	for _, watcher := range fs.watchers {
   104  		i++
   105  		if p2pMessage && !watcher.AllowP2P {
   106  			log.Trace(fmt.Sprintf("msg [%x], filter [%d]: p2p messages are not allowed", env.Hash(), i))
   107  			continue
   108  		}
   109  
   110  		var match bool
   111  		if msg != nil {
   112  			match = watcher.MatchMessage(msg)
   113  		} else {
   114  			match = watcher.MatchEnvelope(env)
   115  			if match {
   116  				msg = env.Open(watcher)
   117  				if msg == nil {
   118  					log.Trace("processing message: failed to open", "message", env.Hash().Hex(), "filter", i)
   119  				}
   120  			} else {
   121  				log.Trace("processing message: does not match", "message", env.Hash().Hex(), "filter", i)
   122  			}
   123  		}
   124  
   125  		if match && msg != nil {
   126  			log.Trace("processing message: decrypted", "hash", env.Hash().Hex())
   127  			if watcher.Src == nil || IsPubKeyEqual(msg.Src, watcher.Src) {
   128  				watcher.Trigger(msg)
   129  			}
   130  		}
   131  	}
   132  }
   133  
   134  func (f *Filter) processEnvelope(env *Envelope) *ReceivedMessage {
   135  	if f.MatchEnvelope(env) {
   136  		msg := env.Open(f)
   137  		if msg != nil {
   138  			return msg
   139  		} else {
   140  			log.Trace("processing envelope: failed to open", "hash", env.Hash().Hex())
   141  		}
   142  	} else {
   143  		log.Trace("processing envelope: does not match", "hash", env.Hash().Hex())
   144  	}
   145  	return nil
   146  }
   147  
   148  func (f *Filter) expectsAsymmetricEncryption() bool {
   149  	return f.KeyAsym != nil
   150  }
   151  
   152  func (f *Filter) expectsSymmetricEncryption() bool {
   153  	return f.KeySym != nil
   154  }
   155  
   156  func (f *Filter) Trigger(msg *ReceivedMessage) {
   157  	f.mutex.Lock()
   158  	defer f.mutex.Unlock()
   159  
   160  	if _, exist := f.Messages[msg.EnvelopeHash]; !exist {
   161  		f.Messages[msg.EnvelopeHash] = msg
   162  	}
   163  }
   164  
   165  func (f *Filter) Retrieve() (all []*ReceivedMessage) {
   166  	f.mutex.Lock()
   167  	defer f.mutex.Unlock()
   168  
   169  	all = make([]*ReceivedMessage, 0, len(f.Messages))
   170  	for _, msg := range f.Messages {
   171  		all = append(all, msg)
   172  	}
   173  
   174  	f.Messages = make(map[common.Hash]*ReceivedMessage) // delete old messages
   175  	return all
   176  }
   177  
   178  func (f *Filter) MatchMessage(msg *ReceivedMessage) bool {
   179  	if f.PoW > 0 && msg.PoW < f.PoW {
   180  		return false
   181  	}
   182  
   183  	if f.expectsAsymmetricEncryption() && msg.isAsymmetricEncryption() {
   184  		return IsPubKeyEqual(&f.KeyAsym.PublicKey, msg.Dst) && f.MatchTopic(msg.Topic)
   185  	} else if f.expectsSymmetricEncryption() && msg.isSymmetricEncryption() {
   186  		return f.SymKeyHash == msg.SymKeyHash && f.MatchTopic(msg.Topic)
   187  	}
   188  	return false
   189  }
   190  
   191  func (f *Filter) MatchEnvelope(envelope *Envelope) bool {
   192  	if f.PoW > 0 && envelope.pow < f.PoW {
   193  		return false
   194  	}
   195  
   196  	if f.expectsAsymmetricEncryption() && envelope.isAsymmetric() {
   197  		return f.MatchTopic(envelope.Topic)
   198  	} else if f.expectsSymmetricEncryption() && envelope.IsSymmetric() {
   199  		return f.MatchTopic(envelope.Topic)
   200  	}
   201  	return false
   202  }
   203  
   204  func (f *Filter) MatchTopic(topic TopicType) bool {
   205  	if len(f.Topics) == 0 {
   206  		// any topic matches
   207  		return true
   208  	}
   209  
   210  	for _, bt := range f.Topics {
   211  		if matchSingleTopic(topic, bt) {
   212  			return true
   213  		}
   214  	}
   215  	return false
   216  }
   217  
   218  func matchSingleTopic(topic TopicType, bt []byte) bool {
   219  	if len(bt) > 4 {
   220  		bt = bt[:4]
   221  	}
   222  
   223  	for j, b := range bt {
   224  		if topic[j] != b {
   225  			return false
   226  		}
   227  	}
   228  	return true
   229  }
   230  
   231  func IsPubKeyEqual(a, b *ecdsa.PublicKey) bool {
   232  	if !ValidatePublicKey(a) {
   233  		return false
   234  	} else if !ValidatePublicKey(b) {
   235  		return false
   236  	}
   237  	// the curve is always the same, just compare the points
   238  	return a.X.Cmp(b.X) == 0 && a.Y.Cmp(b.Y) == 0
   239  }