Skip to content

Commit 12ca3b1

Browse files
gluk256zelig
authored andcommitted
swarm/pss: refactoring (ethereum#19110)
* swarm/pss: split pss and keystore * swarm/pss: moved whisper to keystore * swarm/pss: goimports fixed
1 parent 4f85c2b commit 12ca3b1

File tree

3 files changed

+293
-256
lines changed

3 files changed

+293
-256
lines changed

swarm/pss/keystore.go

Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
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+
}

swarm/pss/protocol.go

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,6 @@ func ToP2pMsg(msg []byte) (p2p.Msg, error) {
228228
// to link the peer to.
229229
// The key must exist in the pss store prior to adding the peer.
230230
func (p *Protocol) AddPeer(peer *p2p.Peer, topic Topic, asymmetric bool, key string) (p2p.MsgReadWriter, error) {
231-
var ok bool
232231
rw := &PssReadWriter{
233232
Pss: p.Pss,
234233
rw: make(chan p2p.Msg),
@@ -242,20 +241,14 @@ func (p *Protocol) AddPeer(peer *p2p.Peer, topic Topic, asymmetric bool, key str
242241
rw.sendFunc = p.Pss.SendSym
243242
}
244243
if asymmetric {
245-
p.Pss.pubKeyPoolMu.Lock()
246-
_, ok = p.Pss.pubKeyPool[key]
247-
p.Pss.pubKeyPoolMu.Unlock()
248-
if !ok {
244+
if !p.Pss.isPubKeyStored(key) {
249245
return nil, fmt.Errorf("asym key does not exist: %s", key)
250246
}
251247
p.RWPoolMu.Lock()
252248
p.pubKeyRWPool[key] = rw
253249
p.RWPoolMu.Unlock()
254250
} else {
255-
p.Pss.symKeyPoolMu.Lock()
256-
_, ok = p.Pss.symKeyPool[key]
257-
p.Pss.symKeyPoolMu.Unlock()
258-
if !ok {
251+
if !p.Pss.isSymKeyStored(key) {
259252
return nil, fmt.Errorf("symkey does not exist: %s", key)
260253
}
261254
p.RWPoolMu.Lock()

0 commit comments

Comments
 (0)