github.com/core-coin/go-core/v2@v2.1.9/p2p/discover/v5wire/session.go (about) 1 // Copyright 2020 by the Authors 2 // This file is part of the go-core library. 3 // 4 // The go-core 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-core 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-core library. If not, see <http://www.gnu.org/licenses/>. 16 17 package v5wire 18 19 import ( 20 crand "crypto/rand" 21 "encoding/binary" 22 "io" 23 "time" 24 25 "github.com/hashicorp/golang-lru/simplelru" 26 27 "github.com/core-coin/go-core/v2/common/mclock" 28 "github.com/core-coin/go-core/v2/crypto" 29 "github.com/core-coin/go-core/v2/p2p/enode" 30 ) 31 32 const handshakeTimeout = time.Second 33 34 // The SessionCache keeps negotiated encryption keys and 35 // state for in-progress handshakes in the Discovery v5 wire protocol. 36 type SessionCache struct { 37 sessions *simplelru.LRU 38 handshakes map[sessionID]*Whoareyou 39 clock mclock.Clock 40 41 // hooks for overriding randomness. 42 nonceGen func(uint32) (Nonce, error) 43 maskingIVGen func([]byte) error 44 ephemeralKeyGen func(read io.Reader) (*crypto.PrivateKey, error) 45 } 46 47 // sessionID identifies a session or handshake. 48 type sessionID struct { 49 id enode.ID 50 addr string 51 } 52 53 // session contains session information 54 type session struct { 55 writeKey []byte 56 readKey []byte 57 nonceCounter uint32 58 } 59 60 // keysFlipped returns a copy of s with the read and write keys flipped. 61 func (s *session) keysFlipped() *session { 62 return &session{s.readKey, s.writeKey, s.nonceCounter} 63 } 64 65 func NewSessionCache(maxItems int, clock mclock.Clock) *SessionCache { 66 cache, err := simplelru.NewLRU(maxItems, nil) 67 if err != nil { 68 panic("can't create session cache") 69 } 70 return &SessionCache{ 71 sessions: cache, 72 handshakes: make(map[sessionID]*Whoareyou), 73 clock: clock, 74 nonceGen: generateNonce, 75 maskingIVGen: generateMaskingIV, 76 ephemeralKeyGen: crypto.GenerateKey, 77 } 78 } 79 80 func generateNonce(counter uint32) (n Nonce, err error) { 81 binary.BigEndian.PutUint32(n[:4], counter) 82 _, err = crand.Read(n[4:]) 83 return n, err 84 } 85 86 func generateMaskingIV(buf []byte) error { 87 _, err := crand.Read(buf) 88 return err 89 } 90 91 // nextNonce creates a nonce for encrypting a message to the given session. 92 func (sc *SessionCache) nextNonce(s *session) (Nonce, error) { 93 s.nonceCounter++ 94 return sc.nonceGen(s.nonceCounter) 95 } 96 97 // session returns the current session for the given node, if any. 98 func (sc *SessionCache) session(id enode.ID, addr string) *session { 99 item, ok := sc.sessions.Get(sessionID{id, addr}) 100 if !ok { 101 return nil 102 } 103 return item.(*session) 104 } 105 106 // readKey returns the current read key for the given node. 107 func (sc *SessionCache) readKey(id enode.ID, addr string) []byte { 108 if s := sc.session(id, addr); s != nil { 109 return s.readKey 110 } 111 return nil 112 } 113 114 // storeNewSession stores new encryption keys in the cache. 115 func (sc *SessionCache) storeNewSession(id enode.ID, addr string, s *session) { 116 sc.sessions.Add(sessionID{id, addr}, s) 117 } 118 119 // getHandshake gets the handshake challenge we previously sent to the given remote node. 120 func (sc *SessionCache) getHandshake(id enode.ID, addr string) *Whoareyou { 121 return sc.handshakes[sessionID{id, addr}] 122 } 123 124 // storeSentHandshake stores the handshake challenge sent to the given remote node. 125 func (sc *SessionCache) storeSentHandshake(id enode.ID, addr string, challenge *Whoareyou) { 126 challenge.sent = sc.clock.Now() 127 sc.handshakes[sessionID{id, addr}] = challenge 128 } 129 130 // deleteHandshake deletes handshake data for the given node. 131 func (sc *SessionCache) deleteHandshake(id enode.ID, addr string) { 132 delete(sc.handshakes, sessionID{id, addr}) 133 } 134 135 // handshakeGC deletes timed-out handshakes. 136 func (sc *SessionCache) handshakeGC() { 137 deadline := sc.clock.Now().Add(-handshakeTimeout) 138 for key, challenge := range sc.handshakes { 139 if challenge.sent < deadline { 140 delete(sc.handshakes, key) 141 } 142 } 143 }