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 }