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 }