-
Notifications
You must be signed in to change notification settings - Fork 0
/
session.go
125 lines (107 loc) · 3.45 KB
/
session.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package v5wire
import (
"crypto/ecdsa"
crand "crypto/rand"
"encoding/binary"
"github.com/entropyio/go-entropy/common/crypto"
"github.com/entropyio/go-entropy/common/mclock"
"github.com/entropyio/go-entropy/server/p2p/enode"
"github.com/hashicorp/golang-lru/simplelru"
"time"
)
const handshakeTimeout = time.Second
// The SessionCache keeps negotiated encryption keys and
// state for in-progress handshakes in the Discovery v5 wire protocol.
type SessionCache struct {
sessions *simplelru.LRU
handshakes map[sessionID]*Whoareyou
clock mclock.Clock
// hooks for overriding randomness.
nonceGen func(uint32) (Nonce, error)
maskingIVGen func([]byte) error
ephemeralKeyGen func() (*ecdsa.PrivateKey, error)
}
// sessionID identifies a session or handshake.
type sessionID struct {
id enode.ID
addr string
}
// session contains session information
type session struct {
writeKey []byte
readKey []byte
nonceCounter uint32
}
// keysFlipped returns a copy of s with the read and write keys flipped.
func (s *session) keysFlipped() *session {
return &session{s.readKey, s.writeKey, s.nonceCounter}
}
func NewSessionCache(maxItems int, clock mclock.Clock) *SessionCache {
cache, err := simplelru.NewLRU(maxItems, nil)
if err != nil {
panic("can't create session cache")
}
return &SessionCache{
sessions: cache,
handshakes: make(map[sessionID]*Whoareyou),
clock: clock,
nonceGen: generateNonce,
maskingIVGen: generateMaskingIV,
ephemeralKeyGen: crypto.GenerateKey,
}
}
func generateNonce(counter uint32) (n Nonce, err error) {
binary.BigEndian.PutUint32(n[:4], counter)
_, err = crand.Read(n[4:])
return n, err
}
func generateMaskingIV(buf []byte) error {
_, err := crand.Read(buf)
return err
}
// nextNonce creates a nonce for encrypting a message to the given session.
func (sc *SessionCache) nextNonce(s *session) (Nonce, error) {
s.nonceCounter++
return sc.nonceGen(s.nonceCounter)
}
// session returns the current session for the given node, if any.
func (sc *SessionCache) session(id enode.ID, addr string) *session {
item, ok := sc.sessions.Get(sessionID{id, addr})
if !ok {
return nil
}
return item.(*session)
}
// readKey returns the current read key for the given node.
func (sc *SessionCache) readKey(id enode.ID, addr string) []byte {
if s := sc.session(id, addr); s != nil {
return s.readKey
}
return nil
}
// storeNewSession stores new encryption keys in the cache.
func (sc *SessionCache) storeNewSession(id enode.ID, addr string, s *session) {
sc.sessions.Add(sessionID{id, addr}, s)
}
// getHandshake gets the handshake challenge we previously sent to the given remote node.
func (sc *SessionCache) getHandshake(id enode.ID, addr string) *Whoareyou {
return sc.handshakes[sessionID{id, addr}]
}
// storeSentHandshake stores the handshake challenge sent to the given remote node.
func (sc *SessionCache) storeSentHandshake(id enode.ID, addr string, challenge *Whoareyou) {
challenge.sent = sc.clock.Now()
sc.handshakes[sessionID{id, addr}] = challenge
}
// deleteHandshake deletes handshake data for the given node.
func (sc *SessionCache) deleteHandshake(id enode.ID, addr string) {
delete(sc.handshakes, sessionID{id, addr})
}
// handshakeGC deletes timed-out handshakes.
func (sc *SessionCache) handshakeGC() {
deadline := sc.clock.Now().Add(-handshakeTimeout)
for key, challenge := range sc.handshakes {
if challenge.sent < deadline {
delete(sc.handshakes, key)
}
}
}