github.com/dotlike13/wemix30_go@v1.8.23/swarm/pss/keystore.go (about) 1 // Copyright 2019 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum 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 go-ethereum 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 go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package pss 18 19 import ( 20 "crypto/ecdsa" 21 "errors" 22 "fmt" 23 "sync" 24 25 "github.com/ethereum/go-ethereum/common" 26 "github.com/ethereum/go-ethereum/crypto" 27 "github.com/ethereum/go-ethereum/metrics" 28 "github.com/ethereum/go-ethereum/swarm/log" 29 whisper "github.com/ethereum/go-ethereum/whisper/whisperv6" 30 ) 31 32 type KeyStore struct { 33 w *whisper.Whisper // key and encryption backend 34 35 mx sync.RWMutex 36 pubKeyPool map[string]map[Topic]*pssPeer // mapping of hex public keys to peer address by topic. 37 symKeyPool map[string]map[Topic]*pssPeer // mapping of symkeyids to peer address by topic. 38 symKeyDecryptCache []*string // fast lookup of symkeys recently used for decryption; last used is on top of stack 39 symKeyDecryptCacheCursor int // modular cursor pointing to last used, wraps on symKeyDecryptCache array 40 } 41 42 func loadKeyStore() *KeyStore { 43 return &KeyStore{ 44 w: whisper.New(&whisper.DefaultConfig), 45 46 pubKeyPool: make(map[string]map[Topic]*pssPeer), 47 symKeyPool: make(map[string]map[Topic]*pssPeer), 48 symKeyDecryptCache: make([]*string, defaultSymKeyCacheCapacity), 49 } 50 } 51 52 func (ks *KeyStore) isSymKeyStored(key string) bool { 53 ks.mx.RLock() 54 defer ks.mx.RUnlock() 55 var ok bool 56 _, ok = ks.symKeyPool[key] 57 return ok 58 } 59 60 func (ks *KeyStore) isPubKeyStored(key string) bool { 61 ks.mx.RLock() 62 defer ks.mx.RUnlock() 63 var ok bool 64 _, ok = ks.pubKeyPool[key] 65 return ok 66 } 67 68 func (ks *KeyStore) getPeerSym(symkeyid string, topic Topic) (*pssPeer, bool) { 69 ks.mx.RLock() 70 defer ks.mx.RUnlock() 71 psp, ok := ks.symKeyPool[symkeyid][topic] 72 return psp, ok 73 } 74 75 func (ks *KeyStore) getPeerPub(pubkeyid string, topic Topic) (*pssPeer, bool) { 76 ks.mx.RLock() 77 defer ks.mx.RUnlock() 78 psp, ok := ks.pubKeyPool[pubkeyid][topic] 79 return psp, ok 80 } 81 82 // Links a peer ECDSA public key to a topic. 83 // This is required for asymmetric message exchange on the given topic. 84 // The value in `address` will be used as a routing hint for the public key / topic association. 85 func (ks *KeyStore) SetPeerPublicKey(pubkey *ecdsa.PublicKey, topic Topic, address PssAddress) error { 86 if err := validateAddress(address); err != nil { 87 return err 88 } 89 pubkeybytes := crypto.FromECDSAPub(pubkey) 90 if len(pubkeybytes) == 0 { 91 return fmt.Errorf("invalid public key: %v", pubkey) 92 } 93 pubkeyid := common.ToHex(pubkeybytes) 94 psp := &pssPeer{ 95 address: address, 96 } 97 ks.mx.Lock() 98 if _, ok := ks.pubKeyPool[pubkeyid]; !ok { 99 ks.pubKeyPool[pubkeyid] = make(map[Topic]*pssPeer) 100 } 101 ks.pubKeyPool[pubkeyid][topic] = psp 102 ks.mx.Unlock() 103 log.Trace("added pubkey", "pubkeyid", pubkeyid, "topic", topic, "address", address) 104 return nil 105 } 106 107 // adds a symmetric key to the pss key pool, and optionally adds the key to the 108 // collection of keys used to attempt symmetric decryption of incoming messages 109 func (ks *KeyStore) addSymmetricKeyToPool(keyid string, topic Topic, address PssAddress, addtocache bool, protected bool) { 110 psp := &pssPeer{ 111 address: address, 112 protected: protected, 113 } 114 ks.mx.Lock() 115 if _, ok := ks.symKeyPool[keyid]; !ok { 116 ks.symKeyPool[keyid] = make(map[Topic]*pssPeer) 117 } 118 ks.symKeyPool[keyid][topic] = psp 119 ks.mx.Unlock() 120 if addtocache { 121 ks.symKeyDecryptCacheCursor++ 122 ks.symKeyDecryptCache[ks.symKeyDecryptCacheCursor%cap(ks.symKeyDecryptCache)] = &keyid 123 } 124 } 125 126 // Returns all recorded topic and address combination for a specific public key 127 func (ks *KeyStore) GetPublickeyPeers(keyid string) (topic []Topic, address []PssAddress, err error) { 128 ks.mx.RLock() 129 defer ks.mx.RUnlock() 130 for t, peer := range ks.pubKeyPool[keyid] { 131 topic = append(topic, t) 132 address = append(address, peer.address) 133 } 134 return topic, address, nil 135 } 136 137 func (ks *KeyStore) getPeerAddress(keyid string, topic Topic) (PssAddress, error) { 138 ks.mx.RLock() 139 defer ks.mx.RUnlock() 140 if peers, ok := ks.pubKeyPool[keyid]; ok { 141 if t, ok := peers[topic]; ok { 142 return t.address, nil 143 } 144 } 145 return nil, fmt.Errorf("peer with pubkey %s, topic %x not found", keyid, topic) 146 } 147 148 // Attempt to decrypt, validate and unpack a symmetrically encrypted message. 149 // If successful, returns the unpacked whisper ReceivedMessage struct 150 // encapsulating the decrypted message, and the whisper backend id 151 // of the symmetric key used to decrypt the message. 152 // It fails if decryption of the message fails or if the message is corrupted. 153 func (ks *KeyStore) processSym(envelope *whisper.Envelope) (*whisper.ReceivedMessage, string, PssAddress, error) { 154 metrics.GetOrRegisterCounter("pss.process.sym", nil).Inc(1) 155 156 for i := ks.symKeyDecryptCacheCursor; i > ks.symKeyDecryptCacheCursor-cap(ks.symKeyDecryptCache) && i > 0; i-- { 157 symkeyid := ks.symKeyDecryptCache[i%cap(ks.symKeyDecryptCache)] 158 symkey, err := ks.w.GetSymKey(*symkeyid) 159 if err != nil { 160 continue 161 } 162 recvmsg, err := envelope.OpenSymmetric(symkey) 163 if err != nil { 164 continue 165 } 166 if !recvmsg.ValidateAndParse() { 167 return nil, "", nil, errors.New("symmetrically encrypted message has invalid signature or is corrupt") 168 } 169 var from PssAddress 170 ks.mx.RLock() 171 if ks.symKeyPool[*symkeyid][Topic(envelope.Topic)] != nil { 172 from = ks.symKeyPool[*symkeyid][Topic(envelope.Topic)].address 173 } 174 ks.mx.RUnlock() 175 ks.symKeyDecryptCacheCursor++ 176 ks.symKeyDecryptCache[ks.symKeyDecryptCacheCursor%cap(ks.symKeyDecryptCache)] = symkeyid 177 return recvmsg, *symkeyid, from, nil 178 } 179 return nil, "", nil, errors.New("could not decrypt message") 180 } 181 182 // Attempt to decrypt, validate and unpack an asymmetrically encrypted message. 183 // If successful, returns the unpacked whisper ReceivedMessage struct 184 // encapsulating the decrypted message, and the byte representation of 185 // the public key used to decrypt the message. 186 // It fails if decryption of message fails, or if the message is corrupted. 187 func (ks *Pss) processAsym(envelope *whisper.Envelope) (*whisper.ReceivedMessage, string, PssAddress, error) { 188 metrics.GetOrRegisterCounter("pss.process.asym", nil).Inc(1) 189 190 recvmsg, err := envelope.OpenAsymmetric(ks.privateKey) 191 if err != nil { 192 return nil, "", nil, fmt.Errorf("could not decrypt message: %s", err) 193 } 194 // check signature (if signed), strip padding 195 if !recvmsg.ValidateAndParse() { 196 return nil, "", nil, errors.New("invalid message") 197 } 198 pubkeyid := common.ToHex(crypto.FromECDSAPub(recvmsg.Src)) 199 var from PssAddress 200 ks.mx.RLock() 201 if ks.pubKeyPool[pubkeyid][Topic(envelope.Topic)] != nil { 202 from = ks.pubKeyPool[pubkeyid][Topic(envelope.Topic)].address 203 } 204 ks.mx.RUnlock() 205 return recvmsg, pubkeyid, from, nil 206 } 207 208 // Symkey garbage collection 209 // a key is removed if: 210 // - it is not marked as protected 211 // - it is not in the incoming decryption cache 212 func (ks *Pss) cleanKeys() (count int) { 213 for keyid, peertopics := range ks.symKeyPool { 214 var expiredtopics []Topic 215 for topic, psp := range peertopics { 216 if psp.protected { 217 continue 218 } 219 220 var match bool 221 for i := ks.symKeyDecryptCacheCursor; i > ks.symKeyDecryptCacheCursor-cap(ks.symKeyDecryptCache) && i > 0; i-- { 222 cacheid := ks.symKeyDecryptCache[i%cap(ks.symKeyDecryptCache)] 223 if *cacheid == keyid { 224 match = true 225 } 226 } 227 if !match { 228 expiredtopics = append(expiredtopics, topic) 229 } 230 } 231 for _, topic := range expiredtopics { 232 ks.mx.Lock() 233 delete(ks.symKeyPool[keyid], topic) 234 log.Trace("symkey cleanup deletion", "symkeyid", keyid, "topic", topic, "val", ks.symKeyPool[keyid]) 235 ks.mx.Unlock() 236 count++ 237 } 238 } 239 return count 240 } 241 242 // Automatically generate a new symkey for a topic and address hint 243 func (ks *KeyStore) GenerateSymmetricKey(topic Topic, address PssAddress, addToCache bool) (string, error) { 244 keyid, err := ks.w.GenerateSymKey() 245 if err == nil { 246 ks.addSymmetricKeyToPool(keyid, topic, address, addToCache, false) 247 } 248 return keyid, err 249 } 250 251 // Returns a symmetric key byte sequence stored in the whisper backend by its unique id. 252 // Passes on the error value from the whisper backend. 253 func (ks *KeyStore) GetSymmetricKey(symkeyid string) ([]byte, error) { 254 return ks.w.GetSymKey(symkeyid) 255 } 256 257 // Links a peer symmetric key (arbitrary byte sequence) to a topic. 258 // 259 // This is required for symmetrically encrypted message exchange on the given topic. 260 // 261 // The key is stored in the whisper backend. 262 // 263 // If addtocache is set to true, the key will be added to the cache of keys 264 // used to attempt symmetric decryption of incoming messages. 265 // 266 // Returns a string id that can be used to retrieve the key bytes 267 // from the whisper backend (see pss.GetSymmetricKey()) 268 func (ks *KeyStore) SetSymmetricKey(key []byte, topic Topic, address PssAddress, addtocache bool) (string, error) { 269 if err := validateAddress(address); err != nil { 270 return "", err 271 } 272 return ks.setSymmetricKey(key, topic, address, addtocache, true) 273 } 274 275 func (ks *KeyStore) setSymmetricKey(key []byte, topic Topic, address PssAddress, addtocache bool, protected bool) (string, error) { 276 keyid, err := ks.w.AddSymKeyDirect(key) 277 if err == nil { 278 ks.addSymmetricKeyToPool(keyid, topic, address, addtocache, protected) 279 } 280 return keyid, err 281 }