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 }