github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/whisper/whisperv6/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 whisperv6
    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.KeySym != nil && watcher.KeyAsym != nil {
    57  		return "", fmt.Errorf("filters must choose between symmetric and asymmetric keys")
    58  	}
    59  
    60  	if watcher.Messages == nil {
    61  		watcher.Messages = make(map[common.Hash]*ReceivedMessage)
    62  	}
    63  
    64  	id, err := GenerateRandomID()
    65  	if err != nil {
    66  		return "", err
    67  	}
    68  
    69  	fs.mutex.Lock()
    70  	defer fs.mutex.Unlock()
    71  
    72  	if fs.watchers[id] != nil {
    73  		return "", fmt.Errorf("failed to generate unique ID")
    74  	}
    75  
    76  	if watcher.expectsSymmetricEncryption() {
    77  		watcher.SymKeyHash = crypto.Keccak256Hash(watcher.KeySym)
    78  	}
    79  
    80  	fs.watchers[id] = watcher
    81  	return id, err
    82  }
    83  
    84  func (fs *Filters) Uninstall(id string) bool {
    85  	fs.mutex.Lock()
    86  	defer fs.mutex.Unlock()
    87  	if fs.watchers[id] != nil {
    88  		delete(fs.watchers, id)
    89  		return true
    90  	}
    91  	return false
    92  }
    93  
    94  func (fs *Filters) Get(id string) *Filter {
    95  	fs.mutex.RLock()
    96  	defer fs.mutex.RUnlock()
    97  	return fs.watchers[id]
    98  }
    99  
   100  func (fs *Filters) NotifyWatchers(env *Envelope, p2pMessage bool) {
   101  	var msg *ReceivedMessage
   102  
   103  	fs.mutex.RLock()
   104  	defer fs.mutex.RUnlock()
   105  
   106  	i := -1 // only used for logging info
   107  	for _, watcher := range fs.watchers {
   108  		i++
   109  		if p2pMessage && !watcher.AllowP2P {
   110  			log.Trace(fmt.Sprintf("msg [%x], filter [%d]: p2p messages are not allowed", env.Hash(), i))
   111  			continue
   112  		}
   113  
   114  		var match bool
   115  		if msg != nil {
   116  			match = watcher.MatchMessage(msg)
   117  		} else {
   118  			match = watcher.MatchEnvelope(env)
   119  			if match {
   120  				msg = env.Open(watcher)
   121  				if msg == nil {
   122  					log.Trace("processing message: failed to open", "message", env.Hash().Hex(), "filter", i)
   123  				}
   124  			} else {
   125  				log.Trace("processing message: does not match", "message", env.Hash().Hex(), "filter", i)
   126  			}
   127  		}
   128  
   129  		if match && msg != nil {
   130  			log.Trace("processing message: decrypted", "hash", env.Hash().Hex())
   131  			if watcher.Src == nil || IsPubKeyEqual(msg.Src, watcher.Src) {
   132  				watcher.Trigger(msg)
   133  			}
   134  		}
   135  	}
   136  }
   137  
   138  func (f *Filter) processEnvelope(env *Envelope) *ReceivedMessage {
   139  	if f.MatchEnvelope(env) {
   140  		msg := env.Open(f)
   141  		if msg != nil {
   142  			return msg
   143  		} else {
   144  			log.Trace("processing envelope: failed to open", "hash", env.Hash().Hex())
   145  		}
   146  	} else {
   147  		log.Trace("processing envelope: does not match", "hash", env.Hash().Hex())
   148  	}
   149  	return nil
   150  }
   151  
   152  func (f *Filter) expectsAsymmetricEncryption() bool {
   153  	return f.KeyAsym != nil
   154  }
   155  
   156  func (f *Filter) expectsSymmetricEncryption() bool {
   157  	return f.KeySym != nil
   158  }
   159  
   160  func (f *Filter) Trigger(msg *ReceivedMessage) {
   161  	f.mutex.Lock()
   162  	defer f.mutex.Unlock()
   163  
   164  	if _, exist := f.Messages[msg.EnvelopeHash]; !exist {
   165  		f.Messages[msg.EnvelopeHash] = msg
   166  	}
   167  }
   168  
   169  func (f *Filter) Retrieve() (all []*ReceivedMessage) {
   170  	f.mutex.Lock()
   171  	defer f.mutex.Unlock()
   172  
   173  	all = make([]*ReceivedMessage, 0, len(f.Messages))
   174  	for _, msg := range f.Messages {
   175  		all = append(all, msg)
   176  	}
   177  
   178  	f.Messages = make(map[common.Hash]*ReceivedMessage) // delete old messages
   179  	return all
   180  }
   181  
   182  // MatchMessage checks if the filter matches an already decrypted
   183  // message (i.e. a Message that has already been handled by
   184  // MatchEnvelope when checked by a previous filter)
   185  func (f *Filter) MatchMessage(msg *ReceivedMessage) bool {
   186  	if f.PoW > 0 && msg.PoW < f.PoW {
   187  		return false
   188  	}
   189  
   190  	if f.expectsAsymmetricEncryption() && msg.isAsymmetricEncryption() {
   191  		return IsPubKeyEqual(&f.KeyAsym.PublicKey, msg.Dst) && f.MatchTopic(msg.Topic)
   192  	} else if f.expectsSymmetricEncryption() && msg.isSymmetricEncryption() {
   193  		return f.SymKeyHash == msg.SymKeyHash && f.MatchTopic(msg.Topic)
   194  	}
   195  	return false
   196  }
   197  
   198  // MatchEvelope checks if it's worth decrypting the message. If
   199  // it returns `true`, client code is expected to attempt decrypting
   200  // the message and subsequently call MatchMessage.
   201  func (f *Filter) MatchEnvelope(envelope *Envelope) bool {
   202  	if f.PoW > 0 && envelope.pow < f.PoW {
   203  		return false
   204  	}
   205  
   206  	return f.MatchTopic(envelope.Topic)
   207  }
   208  
   209  func (f *Filter) MatchTopic(topic TopicType) bool {
   210  	if len(f.Topics) == 0 {
   211  		// any topic matches
   212  		return true
   213  	}
   214  
   215  	for _, bt := range f.Topics {
   216  		if matchSingleTopic(topic, bt) {
   217  			return true
   218  		}
   219  	}
   220  	return false
   221  }
   222  
   223  func matchSingleTopic(topic TopicType, bt []byte) bool {
   224  	if len(bt) > 4 {
   225  		bt = bt[:4]
   226  	}
   227  
   228  	for j, b := range bt {
   229  		if topic[j] != b {
   230  			return false
   231  		}
   232  	}
   233  	return true
   234  }
   235  
   236  func IsPubKeyEqual(a, b *ecdsa.PublicKey) bool {
   237  	if !ValidatePublicKey(a) {
   238  		return false
   239  	} else if !ValidatePublicKey(b) {
   240  		return false
   241  	}
   242  	// the curve is always the same, just compare the points
   243  	return a.X.Cmp(b.X) == 0 && a.Y.Cmp(b.Y) == 0
   244  }