session

package
v0.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 9, 2025 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package session implements session management and encryption for discv5.

The session module handles:

  • WHOAREYOU challenge/response handshakes
  • Session key derivation using ECDH + HKDF
  • AES-GCM encryption and decryption
  • Session caching and lifecycle management
  • Nonce tracking to prevent replay attacks

Index

Constants

View Source
const DefaultMaxSessions = 1000

DefaultMaxSessions is the default maximum number of cached sessions.

View Source
const DefaultSessionLifetime = 30 * time.Minute

DefaultSessionLifetime is the default lifetime for sessions (30 minutes).

Variables

This section is empty.

Functions

func DecryptMessage

func DecryptMessage(key, nonce, authData, ciphertext []byte) ([]byte, error)

DecryptMessage decrypts a message using AES-128-GCM.

The decryption uses:

  • Key: 16-byte session key (initiator or recipient key)
  • Nonce: 12-byte nonce from packet header
  • Additional Data: Complete packet header (IV + masked static header + masked authdata)

The ciphertext must include the 16-byte authentication tag at the end.

According to discv5 spec, authData for GCM is the entire header data before the message.

Example:

plaintext, err := DecryptMessage(key, nonce, headerData, ciphertext)

func EncryptMessage

func EncryptMessage(key, nonce, authData, plaintext []byte) ([]byte, error)

EncryptMessage encrypts a message using AES-128-GCM.

The encryption uses:

  • Key: 16-byte session key (initiator or recipient key)
  • Nonce: 12-byte nonce from packet header
  • Additional Data: Complete packet header (IV + masked static header + masked authdata)

Returns the ciphertext with appended authentication tag (16 bytes).

According to discv5 spec, authData for GCM is the entire header data before the message. For ordinary packets, this is typically 71 bytes (16 IV + 23 header + 32 authdata).

Example:

ciphertext, err := EncryptMessage(key, nonce, headerData, plaintext)

func GenerateEphemeralKey

func GenerateEphemeralKey() (*ecdsa.PrivateKey, error)

GenerateEphemeralKey generates a new ephemeral ECDSA key pair.

Ephemeral keys are used once per session and discarded after the session keys are derived.

Example:

ephemeralKey, err := GenerateEphemeralKey()
if err != nil {
    return err
}

Types

type Cache

type Cache struct {
	// contains filtered or unexported fields
}

Cache manages active sessions with peers.

The cache provides:

  • Fast session lookup by node ID and address
  • Automatic session expiration and cleanup
  • LRU eviction when the cache is full
  • Thread-safe concurrent access

func NewCache

func NewCache(maxSessions int, sessionLifetime time.Duration, logger logrus.FieldLogger) *Cache

NewCache creates a new session cache.

Parameters:

  • maxSessions: Maximum number of sessions to cache (0 = unlimited)
  • sessionLifetime: How long sessions remain valid (0 = default 30min)
  • logger: Optional logger for debug messages

Example:

cache := NewCache(1000, 30*time.Minute, logger)
defer cache.Close()

func (*Cache) CleanupExpired

func (c *Cache) CleanupExpired() int

CleanupExpired removes all expired sessions from the cache.

This should be called periodically by a background goroutine.

Returns the number of sessions removed.

Example:

count := cache.CleanupExpired()
log.Printf("Removed %d expired sessions", count)

func (*Cache) Close

func (c *Cache) Close() error

Close cleans up the cache and releases resources.

func (*Cache) Count

func (c *Cache) Count() int

Count returns the number of cached sessions.

func (*Cache) Delete

func (c *Cache) Delete(nodeID node.ID)

Delete removes a session from the cache.

Example:

cache.Delete(remoteNodeID)

func (*Cache) Get

func (c *Cache) Get(nodeID node.ID) *Session

Get retrieves a session by node ID.

Returns nil if no session exists or if the session has expired.

Example:

session := cache.Get(remoteNodeID)
if session != nil {
    // Use session for encryption
}

func (*Cache) GetByAddr

func (c *Cache) GetByAddr(addr *net.UDPAddr) *Session

GetByAddr retrieves a session by network address.

This is slower than GetByID since it requires scanning all sessions. Use GetByID when possible.

func (*Cache) GetNodeByID

func (c *Cache) GetNodeByID(nodeID node.ID) *node.Node

GetNodeByID retrieves a node by ID from the session cache.

Returns nil if no session exists or if the session has no node reference.

This allows the session cache to act as a local node lookup table.

func (*Cache) GetStats

func (c *Cache) GetStats() Stats

GetStats returns statistics about cached sessions.

func (*Cache) Lifetime

func (c *Cache) Lifetime() time.Duration

Lifetime returns the configured session lifetime.

func (*Cache) Put

func (c *Cache) Put(session *Session)

Put stores a session in the cache.

If the cache is full, the least recently used session is evicted. If a session already exists for this node ID, it is replaced.

Example:

cache.Put(session)

type Session

type Session struct {
	// RemoteID is the node ID of the remote peer
	RemoteID node.ID

	// RemoteAddr is the network address of the remote peer
	RemoteAddr *net.UDPAddr

	// Node is the full node information (ENR, etc.)
	// This allows protocol operations to access node data without a separate table lookup
	Node *node.Node

	// Keys contains the encryption keys for this session
	Keys *SessionKeys

	// IsInitiator indicates if we initiated this session
	IsInitiator bool

	// CreatedAt is when the session was established
	CreatedAt time.Time

	// ExpiresAt is when the session expires
	ExpiresAt time.Time

	// LastUsed is the last time this session was used
	LastUsed time.Time
	// contains filtered or unexported fields
}

Session represents an active encrypted session with a peer.

Each session has:

  • Unique session keys derived from ECDH
  • Creation and expiration timestamps
  • Role (initiator or recipient)
  • Nonce tracking for replay protection
  • Node reference for protocol operations

func NewSession

func NewSession(
	remoteID node.ID,
	remoteAddr *net.UDPAddr,
	keys *SessionKeys,
	isInitiator bool,
	lifetime time.Duration,
) *Session

NewSession creates a new session.

Parameters:

  • remoteID: Node ID of the remote peer
  • remoteAddr: Network address of the remote peer
  • keys: Derived session keys
  • isInitiator: True if we initiated the session
  • lifetime: How long the session is valid (default 30 minutes)

Example:

session := NewSession(remoteID, remoteAddr, keys, true, 30*time.Minute)

func (*Session) Age

func (s *Session) Age() time.Duration

Age returns how long ago the session was created.

func (*Session) DecryptionKey

func (s *Session) DecryptionKey() []byte

DecryptionKey returns the key to use for decrypting incoming messages.

The key is the opposite of the encryption key.

func (*Session) EncryptionKey

func (s *Session) EncryptionKey() []byte

EncryptionKey returns the key to use for encrypting outgoing messages.

The key depends on whether we are the session initiator or recipient.

func (*Session) GetNode

func (s *Session) GetNode() *node.Node

GetNode returns the node reference for this session.

Returns nil if no node has been set.

func (*Session) IdleTime

func (s *Session) IdleTime() time.Duration

IdleTime returns how long ago the session was last used.

func (*Session) IsExpired

func (s *Session) IsExpired() bool

IsExpired checks if the session has expired.

Sessions expire after their lifetime (default 12 hours) or can be manually expired by the session manager.

func (*Session) SetNode

func (s *Session) SetNode(n *node.Node)

SetNode updates the node reference for this session.

This is typically called after a handshake when we receive the remote node's ENR.

func (*Session) String

func (s *Session) String() string

String returns a human-readable representation of the session.

func (*Session) TimeUntilExpiry

func (s *Session) TimeUntilExpiry() time.Duration

TimeUntilExpiry returns how long until the session expires.

Returns 0 if already expired.

func (*Session) Touch

func (s *Session) Touch()

Touch updates the last used timestamp.

This is called whenever the session is used for encryption or decryption.

func (*Session) UpdateAddr

func (s *Session) UpdateAddr(addr *net.UDPAddr)

UpdateAddr updates the remote address for this session.

This is called when we detect that a node has moved to a different IP address.

type SessionKeys

type SessionKeys struct {
	// InitiatorKey is used by the session initiator for encryption
	InitiatorKey []byte

	// RecipientKey is used by the session recipient for encryption
	RecipientKey []byte

	// AuthRespKey is used for authenticating handshake responses
	AuthRespKey []byte
}

SessionKeys contains the derived encryption keys for a session.

Keys are derived from the ECDH shared secret using HKDF-SHA256 with protocol-specific context strings.

func DeriveSessionKeys

func DeriveSessionKeys(
	secret []byte,
	initiatorNodeID node.ID,
	recipientNodeID node.ID,
	challengeData []byte,
) (*SessionKeys, error)

DeriveSessionKeys derives session keys from an ECDH shared secret.

The key derivation follows the discv5 specification:

  1. Compute ECDH shared secret from ephemeral keys
  2. Use HKDF-SHA256 to derive three keys: - initiator-key (16 bytes) - recipient-key (16 bytes) - auth-resp-key (16 bytes)

Parameters:

  • secret: ECDH shared secret
  • initiatorNodeID: Node ID of the session initiator
  • recipientNodeID: Node ID of the session recipient
  • challengeData: Challenge data from WHOAREYOU packet (idnonce)

Example:

secret, _ := crypto.ComputeECDH(localPrivKey, remotePubKey)
keys, err := DeriveSessionKeys(secret, localNodeID, remoteNodeID, challenge)

type Stats

type Stats struct {
	Total   int
	Expired int
	Active  int
}

Stats returns statistics about the cache.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL