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  }