Files
go-i2p/lib/transport/ntcp/README.md
2025-05-15 19:17:42 -04:00

15 KiB

ntcp

-- import "github.com/go-i2p/go-i2p/lib/transport/ntcp"

ntcp.svg

Usage

const (
	NOISE_DH_CURVE25519 = 1

	NOISE_CIPHER_CHACHAPOLY = 1
	NOISE_CIPHER_AESGCM     = 2

	NOISE_HASH_SHA256 = 3

	NOISE_PATTERN_XK = 11

	MaxPayloadSize = math.MaxUint16 - 16 - uint16Size /*data len*/
)
const (
	// Message 1 - SessionRequest
	NTCP2_MSG1_SIZE   = 64
	NTCP2_MSG1_HEADER = 0x00

	// Message 2 - SessionCreated
	NTCP2_MSG2_SIZE   = 64
	NTCP2_MSG2_HEADER = 0x01

	// Message 3 - SessionConfirmed
	NTCP2_MSG3_HEADER = 0x02

	// Timeout for handshake operations
	NTCP2_HANDSHAKE_TIMEOUT = 15 * time.Second
)

Constants for NTCP2 handshake

const (
	MaxPaddingSize    = 64
	MinPaddingSize    = 1
	DefaultMinSize    = 128
	DefaultMinPadding = 1
	DefaultMaxExtra   = 30
)
const (
	NTCP_PROTOCOL_VERSION = 2
	NTCP_PROTOCOL_NAME    = "NTCP2"
	NTCP_MESSAGE_MAX_SIZE = 65537
)

func CalculatePaddingLength

func CalculatePaddingLength(contentSize int, minSize int, minPadding int, maxExtraPadding int) int

CalculatePaddingLength determines padding length based on content size and randomness

func GenerateRandomPadding

func GenerateRandomPadding(length int) ([]byte, error)

GenerateRandomPadding creates a byte slice of random data with the given length

func Intn

func Intn(n int) int

Intn generates a random integer in the range [0, n) This is a secure alternative to math/rand.Intn It uses crypto/rand to generate a cryptographically secure random number Which might be dumb and or pointless for padding.

func ReadAndValidatePadding

func ReadAndValidatePadding(conn net.Conn, paddingLen int) error

ReadAndValidatePadding reads padding from a connection and validates its length

type AEADOperator

type AEADOperator interface {
	// EncryptWithAssociatedData encrypts data using the provided key and associated data
	EncryptWithAssociatedData(key, data, associatedData []byte, nonceCounter uint64) ([]byte, error)

	// DecryptWithAssociatedData decrypts data using the provided key and associated data
	DecryptWithAssociatedData(key, data, associatedData []byte, nonceCounter uint64) ([]byte, error)

	// EncryptWithDerivedKey encrypts data, deriving the key from raw key material first
	EncryptWithDerivedKey(keyMaterial, data, associatedData []byte, nonceCounter uint64) ([]byte, error)

	// DecryptWithDerivedKey decrypts data, deriving the key from raw key material first
	DecryptWithDerivedKey(keyMaterial, data, associatedData []byte, nonceCounter uint64) ([]byte, error)
}

AEADOperator defines the interface for AEAD operations in the NTCP2 protocol

type NTCP2Session

type NTCP2Session struct {
	*noise.NoiseSession
	*NTCP2Transport

	// Processors for handling handshake messages
	Processors map[messages.MessageType]handshake.HandshakeMessageProcessor
}

NTCP2Session extends the base noise.NoiseSession with NTCP2-specific functionality

func NewNTCP2Session

func NewNTCP2Session(routerInfo router_info.RouterInfo) (*NTCP2Session, error)

NewNTCP2Session creates a new NTCP2 session using the existing noise implementation

func (*NTCP2Session) AddDelayForSecurity

func (c *NTCP2Session) AddDelayForSecurity()

addDelayForSecurity adds a small random delay to resist probing

func (*NTCP2Session) CreateHandshakeProcessors

func (s *NTCP2Session) CreateHandshakeProcessors()

CreateHandshakeProcessors initializes all the handshake message processors

func (*NTCP2Session) DecryptOptionsBlock

func (c *NTCP2Session) DecryptOptionsBlock(encryptedOptions []byte, obfuscatedX []byte, deobfuscatedX []byte) ([]byte, error)

DecryptOptionsBlock decrypts the options block from a SessionRequest message

func (*NTCP2Session) DecryptWithAssociatedData

func (c *NTCP2Session) DecryptWithAssociatedData(
	key []byte,
	data []byte,
	associatedData []byte,
	nonceCounter uint64,
) ([]byte, error)

DecryptWithAssociatedData decrypts data using ChaCha20-Poly1305 with the provided key and associated data

func (*NTCP2Session) DecryptWithDerivedKey

func (c *NTCP2Session) DecryptWithDerivedKey(
	keyMaterial []byte,
	data []byte,
	associatedData []byte,
	nonceCounter uint64,
) ([]byte, error)

DecryptWithDerivedKey decrypts data, deriving the key from raw key material first

func (*NTCP2Session) DeobfuscateEphemeral

func (s *NTCP2Session) DeobfuscateEphemeral(obfuscatedEphemeralKey []byte) ([]byte, error)

DeobfuscateEphemeral reverses the key obfuscation

func (*NTCP2Session) DeriveSessionKeys

func (c *NTCP2Session) DeriveSessionKeys(sharedSecret []byte, ephemeralKey []byte) error

DeriveSessionKeys derives all required keys for a session using existing X25519 shared secret This replaces scattered key derivation across session files

func (*NTCP2Session) EncryptWithAssociatedData

func (c *NTCP2Session) EncryptWithAssociatedData(
	key []byte,
	data []byte,
	associatedData []byte,
	nonceCounter uint64,
) ([]byte, error)

EncryptWithAssociatedData encrypts data using ChaCha20-Poly1305 with the provided key and associated data

func (*NTCP2Session) EncryptWithDerivedKey

func (c *NTCP2Session) EncryptWithDerivedKey(
	keyMaterial []byte,
	data []byte,
	associatedData []byte,
	nonceCounter uint64,
) ([]byte, error)

EncryptWithDerivedKey encrypts data, deriving the key from raw key material first

func (*NTCP2Session) GetProcessor

func (s *NTCP2Session) GetProcessor(messageType messages.MessageType) (handshake.HandshakeMessageProcessor, error)

GetProcessor returns the appropriate processor for a message type

func (*NTCP2Session) ObfuscateEphemeral

func (s *NTCP2Session) ObfuscateEphemeral(ephemeralKey []byte) ([]byte, error)

ObfuscateEphemeral implements NTCP2's key obfuscation using AES-256-CBC

func (*NTCP2Session) PerformAEADOperation

func (c *NTCP2Session) PerformAEADOperation(
	keyMaterial []byte,
	data []byte,
	associatedData []byte,
	nonceCounter uint64,
	encrypt bool,
) ([]byte, error)

PerformAEADOperation handles both encryption and decryption using ChaCha20-Poly1305

func (*NTCP2Session) PerformAEADWithDerivedKey

func (c *NTCP2Session) PerformAEADWithDerivedKey(
	keyMaterial []byte,
	data []byte,
	associatedData []byte,
	nonceCounter uint64,
	encrypt bool,
) ([]byte, error)

PerformAEADWithDerivedKey performs AEAD operation, deriving the key from raw key material first

func (*NTCP2Session) PerformIncomingHandshake

func (s *NTCP2Session) PerformIncomingHandshake(conn net.Conn) error

PerformIncomingHandshake conducts the NTCP2 handshake as the responder (server). It performs the server side of the 3-message handshake sequence: 1. Receives and processes SessionRequest (Message 1) 2. Creates and sends SessionCreated (Message 2) 3. Receives and processes SessionConfirmed (Message 3) After successful completion, the session is established and ready for data exchange.

func (*NTCP2Session) PerformOutboundHandshake

func (s *NTCP2Session) PerformOutboundHandshake(conn net.Conn) error

PerformOutboundHandshake conducts the NTCP2 handshake as the initiator (client). It performs the full 3-message handshake sequence: 1. Creates and sends SessionRequest (Message 1) 2. Receives and processes SessionCreated (Message 2) 3. Creates and sends SessionConfirmed (Message 3) After successful completion, the session is established and ready for data exchange.

func (*NTCP2Session) ProcessEphemeralKey

func (c *NTCP2Session) ProcessEphemeralKey(obfuscatedX []byte, hs *handshake.HandshakeState) ([]byte, error)

processEphemeralKey deobfuscates and validates the ephemeral key

func (*NTCP2Session) ReadAndValidatePadding

func (c *NTCP2Session) ReadAndValidatePadding(conn net.Conn, paddingLen int) error

readAndValidatePadding reads the padding from the connection

func (*NTCP2Session) ReadEphemeralKey

func (c *NTCP2Session) ReadEphemeralKey(conn net.Conn) ([]byte, error)

readEphemeralKey reads the ephemeral key (X) from the connection

func (*NTCP2Session) ValidateTimestamp

func (s *NTCP2Session) ValidateTimestamp(timestamp time.Time) error

Add this method to NTCP2Session ValidateTimestamp validates a timestamp is within acceptable range

type NTCP2Transport

type NTCP2Transport struct {
	*noise.NoiseTransport
	*sntp.RouterTimestamper
}

NTCP2Transport is an ntcp2 transport implementing transport.NTCP2Transport interface

func NewNTCP2Transport

func NewNTCP2Transport(routerInfo *router_info.RouterInfo) (*NTCP2Transport, error)

func (*NTCP2Transport) Accept

func (t *NTCP2Transport) Accept() (net.Conn, error)

func (*NTCP2Transport) Address

func (t *NTCP2Transport) Address() (*router_address.RouterAddress, error)

func (*NTCP2Transport) Compatible

func (t *NTCP2Transport) Compatible(routerInfo router_info.RouterInfo) bool

func (*NTCP2Transport) GetSession

func (t *NTCP2Transport) GetSession(routerInfo router_info.RouterInfo) (transport.TransportSession, error)

func (*NTCP2Transport) LocalStaticKey

func (s *NTCP2Transport) LocalStaticKey() ([32]byte, error)

LocalStaticKey is equal to the NTCP2 static public key, found in our router info

func (*NTCP2Transport) Name

func (t *NTCP2Transport) Name() string

type SessionConfirmedProcessor

type SessionConfirmedProcessor struct {
	*NTCP2Session
}

func (*SessionConfirmedProcessor) CreateMessage

func (s *SessionConfirmedProcessor) CreateMessage(hs *handshake.HandshakeState) (messages.Message, error)

CreateMessage implements handshake.HandshakeMessageProcessor.

func (*SessionConfirmedProcessor) EncryptPayload

func (s *SessionConfirmedProcessor) EncryptPayload(msg messages.Message, obfuscatedKey []byte, hs *handshake.HandshakeState) ([]byte, error)

EncryptPayload implements handshake.HandshakeMessageProcessor.

func (*SessionConfirmedProcessor) GetPadding

func (s *SessionConfirmedProcessor) GetPadding(msg messages.Message) []byte

GetPadding implements handshake.HandshakeMessageProcessor.

func (*SessionConfirmedProcessor) MessageType

func (s *SessionConfirmedProcessor) MessageType() messages.MessageType

MessageType implements handshake.HandshakeMessageProcessor.

func (*SessionConfirmedProcessor) ObfuscateKey

func (s *SessionConfirmedProcessor) ObfuscateKey(msg messages.Message, hs *handshake.HandshakeState) ([]byte, error)

ObfuscateKey implements handshake.HandshakeMessageProcessor.

func (*SessionConfirmedProcessor) ProcessMessage

func (s *SessionConfirmedProcessor) ProcessMessage(message messages.Message, hs *handshake.HandshakeState) error

ProcessMessage implements handshake.HandshakeMessageProcessor.

func (*SessionConfirmedProcessor) ReadMessage

func (s *SessionConfirmedProcessor) ReadMessage(conn net.Conn, hs *handshake.HandshakeState) (messages.Message, error)

ReadMessage implements handshake.HandshakeMessageProcessor.

type SessionCreatedProcessor

type SessionCreatedProcessor struct {
	*NTCP2Session
}

func (*SessionCreatedProcessor) CreateMessage

func (s *SessionCreatedProcessor) CreateMessage(hs *handshake.HandshakeState) (messages.Message, error)

CreateMessage implements handshake.HandshakeMessageProcessor.

func (*SessionCreatedProcessor) EncryptPayload

func (s *SessionCreatedProcessor) EncryptPayload(
	msg messages.Message,
	obfuscatedKey []byte,
	hs *handshake.HandshakeState,
) ([]byte, error)

EncryptPayload implements handshake.HandshakeMessageProcessor.

func (*SessionCreatedProcessor) GetPadding

func (s *SessionCreatedProcessor) GetPadding(msg messages.Message) []byte

GetPadding retrieves padding from a message

func (*SessionCreatedProcessor) MessageType

func (s *SessionCreatedProcessor) MessageType() messages.MessageType

MessageType implements handshake.HandshakeMessageProcessor.

func (*SessionCreatedProcessor) ObfuscateKey

func (s *SessionCreatedProcessor) ObfuscateKey(msg messages.Message, hs *handshake.HandshakeState) ([]byte, error)

ObfuscateKey should follow the same pattern as in SessionRequestProcessor

func (*SessionCreatedProcessor) ProcessMessage

func (s *SessionCreatedProcessor) ProcessMessage(message messages.Message, hs *handshake.HandshakeState) error

ProcessMessage implements handshake.HandshakeMessageProcessor.

func (*SessionCreatedProcessor) ReadMessage

func (s *SessionCreatedProcessor) ReadMessage(conn net.Conn, hs *handshake.HandshakeState) (messages.Message, error)

ReadMessage implements handshake.HandshakeMessageProcessor.

type SessionRequestProcessor

type SessionRequestProcessor struct {
	*NTCP2Session
}

SessionRequestProcessor implements NTCP2 Message 1 (SessionRequest): 1. Create session request message with options block (version, padding length, etc.) 2. Set timeout deadline for the connection 3. Obfuscate ephemeral key (X) using AES with Bob's router hash as key 4. Encrypt options block using ChaCha20-Poly1305 5. Assemble final message: obfuscated X + encrypted options + padding 6. Write complete message to connection

SessionRequestProcessor processes incoming NTCP2 Message 1 (SessionRequest): 1. Read and buffer the fixed-length ephemeral key portion (X) 2. Deobfuscate X using AES with local router hash as key 3. Validate the ephemeral key (X) is a valid Curve25519 point 4. Read the ChaCha20-Poly1305 encrypted options block 5. Derive KDF for handshake message 1 using X and local static key 6. Decrypt and authenticate the options block 7. Extract and validate handshake parameters (timestamp, version, padding length) 8. Read and validate any padding bytes 9. Check timestamp for acceptable clock skew (±60 seconds?)

func (*SessionRequestProcessor) CreateMessage

func (s *SessionRequestProcessor) CreateMessage(hs *handshake.HandshakeState) (messages.Message, error)

CreateMessage implements HandshakeMessageProcessor.

func (*SessionRequestProcessor) EncryptPayload

func (p *SessionRequestProcessor) EncryptPayload(
	message messages.Message,
	obfuscatedKey []byte,
	hs *handshake.HandshakeState,
) ([]byte, error)

EncryptPayload encrypts the payload portion of the message

func (*SessionRequestProcessor) GetPadding

func (p *SessionRequestProcessor) GetPadding(message messages.Message) []byte

GetPadding retrieves padding from a message

func (*SessionRequestProcessor) MessageType

func (s *SessionRequestProcessor) MessageType() messages.MessageType

MessageType implements handshake.HandshakeMessageProcessor.

func (*SessionRequestProcessor) ObfuscateKey

func (p *SessionRequestProcessor) ObfuscateKey(message messages.Message, hs *handshake.HandshakeState) ([]byte, error)

ObfuscateKey obfuscates the ephemeral key for transmission

func (*SessionRequestProcessor) ProcessMessage

func (s *SessionRequestProcessor) ProcessMessage(message messages.Message, hs *handshake.HandshakeState) error

ProcessMessage implements handshake.HandshakeMessageProcessor.

func (*SessionRequestProcessor) ReadMessage

func (p *SessionRequestProcessor) ReadMessage(conn net.Conn, hs *handshake.HandshakeState) (messages.Message, error)

ReadMessage reads a SessionRequest message from the connection

ntcp

github.com/go-i2p/go-i2p/lib/transport/ntcp

go-i2p template file