v5_session.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. // Copyright 2020 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. package discover
  17. import (
  18. crand "crypto/rand"
  19. "github.com/ethereum/go-ethereum/common/mclock"
  20. "github.com/ethereum/go-ethereum/p2p/enode"
  21. "github.com/hashicorp/golang-lru/simplelru"
  22. )
  23. // The sessionCache keeps negotiated encryption keys and
  24. // state for in-progress handshakes in the Discovery v5 wire protocol.
  25. type sessionCache struct {
  26. sessions *simplelru.LRU
  27. handshakes map[sessionID]*whoareyouV5
  28. clock mclock.Clock
  29. }
  30. // sessionID identifies a session or handshake.
  31. type sessionID struct {
  32. id enode.ID
  33. addr string
  34. }
  35. // session contains session information
  36. type session struct {
  37. writeKey []byte
  38. readKey []byte
  39. nonceCounter uint32
  40. }
  41. func newSessionCache(maxItems int, clock mclock.Clock) *sessionCache {
  42. cache, err := simplelru.NewLRU(maxItems, nil)
  43. if err != nil {
  44. panic("can't create session cache")
  45. }
  46. return &sessionCache{
  47. sessions: cache,
  48. handshakes: make(map[sessionID]*whoareyouV5),
  49. clock: clock,
  50. }
  51. }
  52. // nextNonce creates a nonce for encrypting a message to the given session.
  53. func (sc *sessionCache) nextNonce(id enode.ID, addr string) []byte {
  54. n := make([]byte, gcmNonceSize)
  55. crand.Read(n)
  56. return n
  57. }
  58. // session returns the current session for the given node, if any.
  59. func (sc *sessionCache) session(id enode.ID, addr string) *session {
  60. item, ok := sc.sessions.Get(sessionID{id, addr})
  61. if !ok {
  62. return nil
  63. }
  64. return item.(*session)
  65. }
  66. // readKey returns the current read key for the given node.
  67. func (sc *sessionCache) readKey(id enode.ID, addr string) []byte {
  68. if s := sc.session(id, addr); s != nil {
  69. return s.readKey
  70. }
  71. return nil
  72. }
  73. // writeKey returns the current read key for the given node.
  74. func (sc *sessionCache) writeKey(id enode.ID, addr string) []byte {
  75. if s := sc.session(id, addr); s != nil {
  76. return s.writeKey
  77. }
  78. return nil
  79. }
  80. // storeNewSession stores new encryption keys in the cache.
  81. func (sc *sessionCache) storeNewSession(id enode.ID, addr string, r, w []byte) {
  82. sc.sessions.Add(sessionID{id, addr}, &session{
  83. readKey: r, writeKey: w,
  84. })
  85. }
  86. // getHandshake gets the handshake challenge we previously sent to the given remote node.
  87. func (sc *sessionCache) getHandshake(id enode.ID, addr string) *whoareyouV5 {
  88. return sc.handshakes[sessionID{id, addr}]
  89. }
  90. // storeSentHandshake stores the handshake challenge sent to the given remote node.
  91. func (sc *sessionCache) storeSentHandshake(id enode.ID, addr string, challenge *whoareyouV5) {
  92. challenge.sent = sc.clock.Now()
  93. sc.handshakes[sessionID{id, addr}] = challenge
  94. }
  95. // deleteHandshake deletes handshake data for the given node.
  96. func (sc *sessionCache) deleteHandshake(id enode.ID, addr string) {
  97. delete(sc.handshakes, sessionID{id, addr})
  98. }
  99. // handshakeGC deletes timed-out handshakes.
  100. func (sc *sessionCache) handshakeGC() {
  101. deadline := sc.clock.Now().Add(-handshakeTimeout)
  102. for key, challenge := range sc.handshakes {
  103. if challenge.sent < deadline {
  104. delete(sc.handshakes, key)
  105. }
  106. }
  107. }