github.com/aidoskuneen/adk-node@v0.0.0-20220315131952-2e32567cb7f4/p2p/discover/v5wire/session.go (about)

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