noise

package module
v0.1.55 Latest Latest
Warning

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

Go to latest
Published: May 8, 2026 License: MIT Imports: 15 Imported by: 0

README

go-noise

A wrapper library around the flynn/noise package that provides net.Conn, net.Listener, and net.Addr interfaces for the Noise Protocol Framework. Designed for implementing I2P's NTCP2 and SSU2 transport protocols with extensible handshake modification capabilities.

Notes on scope:

go-i2p theoretically has strictly scoped packages for low-level operations. This package is intended to be the package where specific modifications are applied to Noise protocol handshakes. It is currently slightly jumbled with go-i2p/crypto on the lower levels, and go-i2p/go-i2p on the upper levels.

This package MAY use any of the following libraries, and SHOULD use them where possible.

This package MUST NOT use any of the following libraries.

  • go-i2p/go-i2p: I2P router implementation
  • I2P Common datastructures are not allowed in the root, unmodified noise directory.

Features

  • Configurable Noise Patterns: Support for all standard Noise Protocol patterns
  • net.Conn Interface: Compatible with Go's standard network interfaces
  • Error Handling: Contextual error information using samber/oops
  • Thread-Safe: Concurrent connection handling with synchronization - Read/Write operations can be called concurrently, Close() is idempotent, and state access is atomic
  • Memory Management: Structured buffer management

Supported Noise Patterns

The library supports all standard Noise Protocol patterns with both short names and full specification:

  • One-way patterns: N, K, X
  • Interactive patterns: NN, NK, NX, XN, XK, XX, KN, KK, KX, IN, IK, IX
  • Full pattern names: Noise_XX_25519_AESGCM_SHA256, etc.

Quick Start

package main

import (
    "context"
    "fmt"
    "net"
    "time"
    
    "github.com/go-i2p/go-noise"
)

func main() {
    // Create configuration for XX pattern
    config := noise.NewConnConfig("XX", true).
        WithHandshakeTimeout(10 * time.Second).
        WithReadTimeout(5 * time.Second)
    
    // Wrap an existing connection
    tcpConn, err := net.Dial("tcp", "localhost:8080")
    if err != nil {
        panic(err)
    }
    defer tcpConn.Close()
    
    // Create Noise connection
    noiseConn, err := noise.NewNoiseConn(tcpConn, config)
    if err != nil {
        panic(err)
    }
    defer noiseConn.Close()
    
    // Perform handshake
    ctx := context.Background()
    if err := noiseConn.Handshake(ctx); err != nil {
        panic(err)
    }
    
    // Use as regular net.Conn
    _, err = noiseConn.Write([]byte("Hello, Noise!"))
    if err != nil {
        panic(err)
    }
    
    buffer := make([]byte, 1024)
    n, err := noiseConn.Read(buffer)
    if err != nil {
        panic(err)
    }
    
    fmt.Printf("Received: %s\n", buffer[:n])
}

Configuration

ConnConfig Options
config := noise.NewConnConfig("XX", true).
    WithStaticKey(staticKey).              // 32-byte Curve25519 private key
    WithHandshakeTimeout(30*time.Second).  // Handshake timeout
    WithReadTimeout(5*time.Second).        // Read operation timeout
    WithWriteTimeout(5*time.Second)        // Write operation timeout
Pattern Selection

Choose the appropriate pattern based on your security requirements:

  • XX: Mutual authentication with ephemeral keys
  • IK: Initiator knows responder's static key
  • XK: Initiator knows responder's static key, responder authenticates
  • NK: Responder has known static key, one-way authentication
  • NN: No authentication, only encryption (not recommended for production)

Connection State Management

The library provides connection state tracking and metrics:

// Check connection state
state := noiseConn.GetConnectionState()
switch state {
case noise.StateInit:
    fmt.Println("Connection created, handshake not started")
case noise.StateHandshaking:
    fmt.Println("Handshake in progress")
case noise.StateEstablished:
    fmt.Println("Handshake complete, ready for secure communication")
case noise.StateClosed:
    fmt.Println("Connection closed")
}

// Get connection metrics
bytesRead, bytesWritten, handshakeDuration := noiseConn.GetConnectionMetrics()
fmt.Printf("Transferred %d bytes read, %d bytes written\n", bytesRead, bytesWritten)
fmt.Printf("Handshake completed in %v\n", handshakeDuration)
State Transitions

Connections follow this lifecycle:

  1. Init → Created with NewNoiseConn()
  2. HandshakingHandshake() called
  3. Established → Handshake completed successfully
  4. ClosedClose() called or connection failed
Monitoring

The library tracks:

  • Handshake Duration: Time taken to complete the Noise handshake
  • Bytes Read/Written: Total data transferred (plaintext, not encrypted)
  • Connection Lifecycle: Creation time and state transitions

Implementation Status

Core Noise and NTCP2 implementations completed. SSU2 implementation in beta.

Packages

Core: Noise Protocol Transport

The root package provides net.Conn, net.Listener, and net.Addr wrappers for the Noise Protocol Framework, used by I2P's NTCP2 transport.

ratchet/ — ECIES-X25519-AEAD-Ratchet

The ratchet package provides the ECIES-X25519-AEAD-Ratchet cryptographic engine for I2P's end-to-end garlic encryption:

  • Garlic sessions: Double-ratchet encrypt/decrypt with DH ratchet rotation
  • Build record crypto: ChaCha20-Poly1305 and ECIES for tunnel build records
  • Interface-based: All APIs defined via interfaces for testability

See ratchet/README.md for detailed API documentation.

ntcp2/ — NTCP2 Transport Helpers

The ntcp2 package provides NTCP2-specific connection handling, replay detection (ReplayCache), and KDF utilities.

Architecture

NoiseConn (net.Conn)
├── Config (pattern, keys, timeouts)
├── HandshakeState (flynn/noise)
├── CipherState (post-handshake encryption)
├── NoiseAddr (net.Addr with pattern info)
└── Underlying net.Conn (TCP, UDP, etc.)

Dependencies

  • flynn/noise v1.1.0: Core Noise Protocol implementation
  • go-i2p/logger: Structured logging support
  • samber/oops v1.19.0: Rich error context

Logging

This library uses go-i2p/logger for structured logging across all packages. Logging is controlled via environment variables:

Variable Values Description
DEBUG_I2P debug, warn, error Sets log level. Unset = no output.
WARNFAIL_I2P true Promotes warnings to fatal errors (useful for CI).

Each package has a dedicated log.go file that initializes a package-level logger:

var log = logger.GetGoI2PLogger()

Every log call includes structured pkg and func fields for filtering and tracing:

log.WithFields(logger.Fields{
    "pkg":     "noise",
    "func":    "NewConnConfig",
    "pattern": config.Pattern,
}).Debug("Creating new connection config")

log.WithFields(logger.Fields{
    "pkg":  "ntcp2",
    "func": "Handshake",
}).WithError(err).Error("Handshake failed")

Available pkg values: noise, handshake, internal, replaycache, ntcp2, pool, ratchet, ssu2.

Running Tests with Logging
# Default (no log output)
go test ./...

# With debug logging
DEBUG_I2P=debug go test ./...

# With warn-fail mode (warnings become fatal)
WARNFAIL_I2P=true DEBUG_I2P=debug go test ./...

Testing

go test -v ./...

Current test coverage includes unit and integration tests for core functionality across all components:

  • Handshake pattern parsing and validation
  • Configuration validation
  • NoiseAddr interface compliance
  • NoiseConn read/write operations and error handling
  • NTCP2Addr I2P-specific addressing functionality
  • NTCP2Conn net.Conn interface compliance and error wrapping
  • Handshake modifier chaining and transformations
  • Error handling scenarios across all components

Contributing

This library follows Go best practices:

  • Functions under 30 lines with single responsibility
  • Explicit error handling (no ignored returns)
  • Self-documenting code with clear naming
  • Unit and integration testing with coverage monitoring

License

MIT License

Status

Development Status: Core functionality, handshake modification system, connection pooling, and listener features implemented. SSU2 transport in beta.

Documentation

Overview

Package noise provides a high-level wrapper around the go-i2p/noise package implementing net.Conn, net.Listener, and net.Addr interfaces for the Noise Protocol Framework. It supports extensible handshake modification for implementing I2P's NTCP2 and SSU2 transport protocols.

Index

Constants

View Source
const (
	// StateInit represents a newly created connection
	StateInit = internal.StateInit
	// StateHandshaking represents a connection performing handshake
	StateHandshaking = internal.StateHandshaking
	// StateEstablished represents a connection with completed handshake
	StateEstablished = internal.StateEstablished
	// StateClosed represents a closed connection
	StateClosed = internal.StateClosed
)

Connection state constants for public API

Variables

This section is empty.

Functions

func GetGlobalConnPool

func GetGlobalConnPool() *pool.ConnPool

GetGlobalConnPool returns the current global connection pool

func GracefulShutdown

func GracefulShutdown() error

GracefulShutdown initiates graceful shutdown of all global components. This includes the global connection pool and all registered connections/listeners.

func SetGlobalConnPool

func SetGlobalConnPool(p *pool.ConnPool)

SetGlobalConnPool sets a custom connection pool for transport functions

func SetGlobalShutdownManager

func SetGlobalShutdownManager(sm *ShutdownManager)

SetGlobalShutdownManager sets a custom shutdown manager for transport functions. The previous shutdown manager will be shut down gracefully.

Types

type ConnConfig

type ConnConfig struct {
	// Pattern is the Noise protocol pattern (e.g., "Noise_XX_25519_AESGCM_SHA256")
	Pattern string

	// Initiator indicates if this connection is the handshake initiator
	Initiator bool

	// StaticKey is the long-term static key for this peer (32 bytes for Curve25519)
	StaticKey []byte

	// RemoteKey is the remote peer's static public key (32 bytes for Curve25519)
	// Required for some patterns, optional for others
	RemoteKey []byte

	// HandshakeTimeout is the maximum time to wait for handshake completion
	// Default: 30 seconds
	HandshakeTimeout time.Duration

	// ReadTimeout is the timeout for read operations after handshake
	// Default: no timeout (0)
	ReadTimeout time.Duration

	// WriteTimeout is the timeout for write operations after handshake
	// Default: no timeout (0)
	WriteTimeout time.Duration

	// HandshakeRetries is the number of handshake retry attempts.
	// Default: 0 (no retries). Set to a positive value and use
	// HandshakeWithRetry() to enable retry semantics with exponential
	// backoff. Handshake() always performs a single attempt regardless
	// of this setting. Use -1 for infinite retries.
	HandshakeRetries int

	// RetryBackoff is the base delay between retry attempts
	// Actual delay uses exponential backoff: delay = RetryBackoff * (2^attempt)
	// Default: 1 second
	RetryBackoff time.Duration

	// Modifiers is a list of handshake modifiers for obfuscation and padding
	// Modifiers are applied in order during outbound processing and in reverse
	// order during inbound processing. Default: empty (no modifiers)
	Modifiers []handshake.HandshakeModifier

	// CipherSuite specifies the Noise cipher suite to use for the handshake.
	// If nil, defaults to DH25519 + CipherAESGCM + SHA256.
	// Protocols like NTCP2 require DH25519 + CipherChaChaPoly + SHA256.
	CipherSuite noise.CipherSuite

	// ProtocolName overrides the auto-generated Noise protocol name used
	// for InitializeSymmetric(). When set, this exact byte string is used
	// instead of constructing "Noise_" + Pattern.Name + "_" + CipherSuite.Name().
	// Required for protocols like NTCP2 that use non-standard protocol names
	// (e.g., "Noise_XKaesobfse+hs2+hs3_25519_ChaChaPoly_SHA256").
	// If nil/empty, the standard Noise protocol name is constructed as usual.
	ProtocolName []byte

	// AdditionalSymmetricKeyLabels specifies labels for Additional Symmetric
	// Key (ASK) derivation at Split() time, per Noise spec §10.3. Each label
	// produces a 32-byte key derived from the chaining key. The derived keys
	// are available via NoiseConn.AdditionalSymmetricKeys() after the
	// handshake completes.
	//
	// For NTCP2, this should be set to [][]byte{[]byte("ask")} to derive
	// the ask_master used for SipHash key derivation.
	AdditionalSymmetricKeyLabels [][]byte

	// PostHandshakeHook is an optional callback invoked after the Noise
	// handshake completes successfully but before the connection transitions
	// to the Established state. This allows protocol layers (e.g., NTCP2)
	// to derive additional key material from the handshake hash, set up
	// data-phase obfuscators, or perform any post-handshake validation.
	//
	// If the hook returns an error, the handshake is considered failed and
	// the connection reverts to the Init state.
	PostHandshakeHook func(*NoiseConn) error
	// contains filtered or unexported fields
}

ConnConfig contains configuration for creating a NoiseConn. It follows the builder pattern for optional configuration and validation.

func NewConnConfig

func NewConnConfig(pattern string, initiator bool) *ConnConfig

NewConnConfig creates a new ConnConfig with sensible defaults.

func (*ConnConfig) AddModifier

func (c *ConnConfig) AddModifier(modifier handshake.HandshakeModifier) *ConnConfig

AddModifier appends a single modifier to the existing modifier list.

func (*ConnConfig) ClearModifiers

func (c *ConnConfig) ClearModifiers() *ConnConfig

ClearModifiers removes all modifiers from the configuration.

func (*ConnConfig) GetModifierChain

func (c *ConnConfig) GetModifierChain() *handshake.ModifierChain

GetModifierChain returns a ModifierChain containing all configured modifiers. Returns nil if no modifiers are configured. The chain is lazily initialized and cached; subsequent calls return the same instance until the modifier list is mutated via WithModifiers, AddModifier, or ClearModifiers.

func (*ConnConfig) Validate

func (c *ConnConfig) Validate() error

Validate checks if the configuration is valid and complete. Returns an error with context if validation fails.

func (*ConnConfig) WithHandshakeRetries

func (c *ConnConfig) WithHandshakeRetries(retries int) *ConnConfig

WithHandshakeRetries sets the number of handshake retry attempts used by HandshakeWithRetry. Handshake() always performs a single attempt regardless of this setting. Use 0 for no retries, -1 for infinite retries.

func (*ConnConfig) WithHandshakeTimeout

func (c *ConnConfig) WithHandshakeTimeout(timeout time.Duration) *ConnConfig

WithHandshakeTimeout sets the handshake timeout.

func (*ConnConfig) WithModifiers

func (c *ConnConfig) WithModifiers(modifiers ...handshake.HandshakeModifier) *ConnConfig

WithModifiers sets the handshake modifiers for obfuscation and padding. Modifiers are applied in the order provided for outbound data and in reverse order for inbound data.

func (*ConnConfig) WithReadTimeout

func (c *ConnConfig) WithReadTimeout(timeout time.Duration) *ConnConfig

WithReadTimeout sets the read timeout for post-handshake operations.

func (*ConnConfig) WithRemoteKey

func (c *ConnConfig) WithRemoteKey(key []byte) *ConnConfig

WithRemoteKey sets the remote peer's static public key. key must be 32 bytes for Curve25519.

func (*ConnConfig) WithRetryBackoff

func (c *ConnConfig) WithRetryBackoff(backoff time.Duration) *ConnConfig

WithRetryBackoff sets the base delay between retry attempts. Actual delay uses exponential backoff: delay = backoff * (2^attempt).

func (*ConnConfig) WithStaticKey

func (c *ConnConfig) WithStaticKey(key []byte) *ConnConfig

WithStaticKey sets the static key for this connection. key must be 32 bytes for Curve25519.

func (*ConnConfig) WithWriteTimeout

func (c *ConnConfig) WithWriteTimeout(timeout time.Duration) *ConnConfig

WithWriteTimeout sets the write timeout for post-handshake operations.

type ConnState

type ConnState = internal.ConnState

ConnState represents the state of a NoiseConn

type ListenerConfig

type ListenerConfig struct {
	// Pattern is the Noise protocol pattern (e.g., "Noise_XX_25519_AESGCM_SHA256")
	Pattern string

	// StaticKey is the long-term static key for this listener (32 bytes for Curve25519)
	StaticKey []byte

	// HandshakeTimeout is the maximum time to wait for handshake completion
	// Default: 30 seconds
	HandshakeTimeout time.Duration

	// ReadTimeout is the timeout for read operations after handshake
	// Default: no timeout (0)
	ReadTimeout time.Duration

	// WriteTimeout is the timeout for write operations after handshake
	// Default: no timeout (0)
	WriteTimeout time.Duration

	// Modifiers is a list of handshake modifiers for obfuscation and padding.
	// Modifiers are applied in order during outbound processing and in reverse
	// order during inbound processing. Required for NTCP2 server-side
	// connections that need AES-CBC obfuscation and SipHash length obfuscation.
	// Default: empty (no modifiers)
	Modifiers []handshake.HandshakeModifier

	// PostHandshakeHook is an optional callback invoked after the Noise
	// handshake completes successfully but before the connection transitions
	// to the Established state. This allows protocol layers (e.g., NTCP2)
	// to derive additional key material from the handshake hash, set up
	// data-phase obfuscators, or perform post-handshake validation.
	//
	// If the hook returns an error, the handshake is considered failed and
	// the connection reverts to the Init state.
	PostHandshakeHook func(*NoiseConn) error

	// AdditionalSymmetricKeyLabels specifies labels for Additional Symmetric
	// Key (ASK) derivation at Split() time, per Noise spec §10.3. Each label
	// produces a 32-byte key derived from the chaining key. The derived keys
	// are available via NoiseConn.AdditionalSymmetricKeys() after the
	// handshake completes.
	//
	// For NTCP2, this should be set to [][]byte{[]byte("ask")} to derive
	// the ask_master used for SipHash key derivation.
	AdditionalSymmetricKeyLabels [][]byte

	// HandshakeRetries is the number of handshake retry attempts for accepted
	// connections. 0 means no retries. Default: 0.
	HandshakeRetries int

	// RetryBackoff is the base delay between retry attempts for accepted
	// connections. Actual delay uses exponential backoff. Default: 1 second.
	RetryBackoff time.Duration
}

ListenerConfig contains configuration for creating a NoiseListener. It follows the builder pattern for optional configuration and validation.

func NewListenerConfig

func NewListenerConfig(pattern string) *ListenerConfig

NewListenerConfig creates a new ListenerConfig with sensible defaults.

func (*ListenerConfig) Validate

func (lc *ListenerConfig) Validate() error

Validate checks if the configuration is valid.

func (*ListenerConfig) WithAdditionalSymmetricKeyLabels added in v0.1.52

func (lc *ListenerConfig) WithAdditionalSymmetricKeyLabels(labels [][]byte) *ListenerConfig

WithAdditionalSymmetricKeyLabels sets labels for ASK derivation at Split() time. For NTCP2, use [][]byte{[]byte("ask")}.

func (*ListenerConfig) WithHandshakeRetries added in v0.1.52

func (lc *ListenerConfig) WithHandshakeRetries(retries int) *ListenerConfig

WithHandshakeRetries sets the number of handshake retry attempts for accepted connections. 0 means no retries.

func (*ListenerConfig) WithHandshakeTimeout

func (lc *ListenerConfig) WithHandshakeTimeout(timeout time.Duration) *ListenerConfig

WithHandshakeTimeout sets the handshake timeout.

func (*ListenerConfig) WithModifiers added in v0.1.52

func (lc *ListenerConfig) WithModifiers(modifiers ...handshake.HandshakeModifier) *ListenerConfig

WithModifiers sets the handshake modifiers for accepted connections. Modifiers are applied in the order provided for outbound data and in reverse order for inbound data. Required for NTCP2 server-side connections.

func (*ListenerConfig) WithPostHandshakeHook added in v0.1.52

func (lc *ListenerConfig) WithPostHandshakeHook(hook func(*NoiseConn) error) *ListenerConfig

WithPostHandshakeHook sets a callback invoked after the Noise handshake completes but before the connection transitions to the Established state.

func (*ListenerConfig) WithReadTimeout

func (lc *ListenerConfig) WithReadTimeout(timeout time.Duration) *ListenerConfig

WithReadTimeout sets the read timeout for accepted connections.

func (*ListenerConfig) WithRetryBackoff added in v0.1.52

func (lc *ListenerConfig) WithRetryBackoff(backoff time.Duration) *ListenerConfig

WithRetryBackoff sets the base delay between retry attempts for accepted connections. Actual delay uses exponential backoff.

func (*ListenerConfig) WithStaticKey

func (lc *ListenerConfig) WithStaticKey(key []byte) *ListenerConfig

WithStaticKey sets the static key for this listener. key must be 32 bytes for Curve25519.

func (*ListenerConfig) WithWriteTimeout

func (lc *ListenerConfig) WithWriteTimeout(timeout time.Duration) *ListenerConfig

WithWriteTimeout sets the write timeout for accepted connections.

type NoiseAddr

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

NoiseAddr implements net.Addr for Noise Protocol connections. It wraps an underlying net.Addr and adds Noise-specific addressing information.

func NewNoiseAddr

func NewNoiseAddr(underlying net.Addr, pattern, role string) *NoiseAddr

NewNoiseAddr creates a new NoiseAddr wrapping an underlying network address. pattern should be a valid Noise protocol pattern (e.g., "Noise_XX_25519_AESGCM_SHA256"). role should be either "initiator" or "responder".

func (*NoiseAddr) Network

func (na *NoiseAddr) Network() string

Network returns the network type, prefixed with "noise+" to indicate Noise wrapping. For example, "noise+tcp" for Noise over TCP or "noise+udp" for Noise over UDP.

func (*NoiseAddr) Pattern

func (na *NoiseAddr) Pattern() string

Pattern returns the Noise protocol pattern.

func (*NoiseAddr) Role

func (na *NoiseAddr) Role() string

Role returns the role (initiator or responder).

func (*NoiseAddr) String

func (na *NoiseAddr) String() string

String returns a string representation of the Noise address. Format: "noise://[pattern]/[role]/[underlying_address]" Example: "noise://Noise_XX_25519_AESGCM_SHA256/initiator/192.168.1.1:8080"

func (*NoiseAddr) Underlying

func (na *NoiseAddr) Underlying() net.Addr

Underlying returns the wrapped network address. This allows access to the original address when needed.

type NoiseConn

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

NoiseConn implements net.Conn with Noise Protocol encryption. It wraps an underlying net.Conn and provides encrypted communication following the Noise Protocol Framework specification.

Thread Safety: NoiseConn is safe for concurrent use by multiple goroutines with the following guarantees:

  • Read() and Write() can be called concurrently from different goroutines
  • Close() can be called concurrently with other operations and will be idempotent
  • GetConnectionState() and GetConnectionMetrics() are safe for concurrent access
  • Handshake() operations are serialized - only one handshake can occur at a time
  • All operations that check connection state are atomic and consistent

Synchronization is achieved through multiple mutexes:

  • stateMutex: Protects connection state transitions (RWMutex for read-heavy access)
  • handshakeMutex: Serializes handshake operations
  • closeMutex: Protects close operations from concurrent execution
  • Internal metrics mutex: Protects connection metrics updates

func DialNoise

func DialNoise(network, addr string, config *ConnConfig) (*NoiseConn, error)

DialNoise creates a connection to the given address and wraps it with NoiseConn. This is a convenience function that combines net.Dial and NewNoiseConn. For more control over the underlying connection, use net.Dial followed by NewNoiseConn.

func DialNoiseWithHandshake

func DialNoiseWithHandshake(network, addr string, config *ConnConfig) (*NoiseConn, error)

DialNoiseWithHandshake creates a connection to the given address, wraps it with NoiseConn, and performs the handshake with retry logic. This is the recommended high-level function for establishing Noise connections with automatic retry capabilities.

func DialNoiseWithHandshakeContext

func DialNoiseWithHandshakeContext(ctx context.Context, network, addr string, config *ConnConfig) (*NoiseConn, error)

DialNoiseWithHandshakeContext creates a connection with context support for cancellation. It combines dialing, NoiseConn creation, and handshake with retry in a single operation.

func DialNoiseWithPool

func DialNoiseWithPool(network, addr string, config *ConnConfig) (*NoiseConn, error)

DialNoiseWithPool creates a connection to the given address, checking the pool first. If a suitable connection is available in the pool, it will be reused. Otherwise, a new connection is created. The connection will be automatically returned to the pool when the NoiseConn is closed.

func DialNoiseWithPoolAndHandshake

func DialNoiseWithPoolAndHandshake(network, addr string, config *ConnConfig) (*NoiseConn, error)

DialNoiseWithPoolAndHandshake creates a connection with pool support and handshake retry. It checks the pool first, creates new if needed, and performs handshake with retry logic.

func DialNoiseWithPoolAndHandshakeContext

func DialNoiseWithPoolAndHandshakeContext(ctx context.Context, network, addr string, config *ConnConfig) (*NoiseConn, error)

DialNoiseWithPoolAndHandshakeContext combines pool checking, dialing, and handshake with context. It reuses a pooled raw TCP connection when available (the pool keys by conn.RemoteAddr().String(), which equals addr), wraps it in a new NoiseConn, and performs the Noise handshake. Falls back to a fresh dial if the pool is empty or the pooled connection fails the handshake.

func NewNoiseConn

func NewNoiseConn(underlying net.Conn, config *ConnConfig) (*NoiseConn, error)

NewNoiseConn creates a new NoiseConn wrapping the underlying connection. The handshake must be completed before using Read/Write operations.

func WrapConn

func WrapConn(conn net.Conn, config *ConnConfig) (*NoiseConn, error)

WrapConn wraps an existing net.Conn with NoiseConn. This is an alias for NewNoiseConn for consistency with the transport API.

func (*NoiseConn) AdditionalSymmetricKeys added in v0.1.52

func (nc *NoiseConn) AdditionalSymmetricKeys() [][]byte

AdditionalSymmetricKeys returns the Additional Symmetric Key (ASK) values derived during the handshake Split(), per Noise spec §10.3. Returns nil if no labels were configured or the handshake hasn't completed. The returned keys correspond 1:1 to the configured AdditionalSymmetricKeyLabels.

func (*NoiseConn) ChannelBinding added in v0.1.52

func (nc *NoiseConn) ChannelBinding() []byte

ChannelBinding returns the handshake hash (h) from the completed Noise session. This is the hash of all handshake transcript data and can be used for:

  • Channel binding (tying an application-layer credential to the Noise session)
  • Deriving additional key material via HKDF (e.g., NTCP2's SipHash keys)

Returns nil if the handshake has not been initiated.

Thread Safety: This method is safe for concurrent use. The underlying HandshakeState.ChannelBinding() is mutex-protected.

func (*NoiseConn) Close

func (nc *NoiseConn) Close() error

Close closes the connection.

Thread Safety: This method is safe for concurrent use and is idempotent. Multiple goroutines can call Close simultaneously - only the first call will perform the actual close operation, subsequent calls will return nil. The close mutex ensures atomic close operations.

func (*NoiseConn) CompleteHandshake added in v0.1.52

func (nc *NoiseConn) CompleteHandshake()

CompleteHandshake marks the connection as fully established after the final handshake message has been processed. Call this once after the last WriteHandshakeMsgToBytes / ReadHandshakeMsgFromBytes call.

func (*NoiseConn) Decrypt added in v0.1.52

func (nc *NoiseConn) Decrypt(encrypted []byte) ([]byte, error)

Decrypt decrypts ciphertext data using the connection's cipher state without reading from the underlying connection. This allows callers to separate decryption from wire-level framing (e.g., for NTCP2's SipHash-obfuscated length prefix).

The connection must have completed the Noise handshake. Thread Safety: Same guarantees as Read().

func (*NoiseConn) Encrypt added in v0.1.52

func (nc *NoiseConn) Encrypt(data []byte) ([]byte, error)

Encrypt encrypts plaintext data using the connection's cipher state without writing to the underlying connection. This allows callers to separate encryption from wire-level framing (e.g., for NTCP2's SipHash-obfuscated length prefix).

The connection must have completed the Noise handshake. Thread Safety: Same guarantees as Write().

func (*NoiseConn) FailHandshake added in v0.1.52

func (nc *NoiseConn) FailHandshake()

FailHandshake resets the handshake state machine so that a fresh attempt can be made (or so that close/cleanup code sees a consistent state).

func (*NoiseConn) GetConnectionMetrics

func (nc *NoiseConn) GetConnectionMetrics() (bytesRead, bytesWritten int64, handshakeDuration time.Duration)

GetConnectionMetrics returns the current connection statistics

func (*NoiseConn) GetConnectionState

func (nc *NoiseConn) GetConnectionState() ConnState

GetConnectionState returns the current connection state

Thread Safety: This method is safe for concurrent use. It uses a read lock on the state mutex, allowing multiple goroutines to read the state simultaneously while preventing inconsistent reads during state transitions.

func (*NoiseConn) GetHandshakeHash added in v0.1.52

func (nc *NoiseConn) GetHandshakeHash() []byte

GetHandshakeHash returns the hash of the handshake transcript from the underlying noise.HandshakeState. This can be used by protocol layers to derive post-handshake key material (e.g. SipHash keys for NTCP2 data phase).

func (*NoiseConn) GetModifierChain added in v0.1.52

func (nc *NoiseConn) GetModifierChain() *handshake.ModifierChain

GetModifierChain returns the HandshakeModifier chain from the config. Returns nil if no modifiers are configured. NTCP2 framed I/O uses this to apply PhaseData transforms (padding, obfuscation) around Encrypt/Decrypt.

func (*NoiseConn) Handshake

func (nc *NoiseConn) Handshake(ctx context.Context) error

Handshake performs a single Noise Protocol handshake attempt. This must be called before using Read/Write operations.

Handshake always performs exactly one attempt and does not use the HandshakeRetries or RetryBackoff configuration fields. To perform a handshake with automatic retries and exponential backoff, use HandshakeWithRetry instead.

Thread Safety: This method is safe for concurrent use but handshake operations are serialized. Only one handshake can be in progress at a time per connection. If multiple goroutines call Handshake concurrently, they will be queued and execute sequentially. If the handshake is already complete, subsequent calls will return immediately without error.

func (*NoiseConn) HandshakeWithRetry

func (nc *NoiseConn) HandshakeWithRetry(ctx context.Context) error

HandshakeWithRetry performs a handshake with retry logic based on configuration. It uses the HandshakeRetries and RetryBackoff fields from ConnConfig to control the number of attempts and exponential backoff delay between retries. If HandshakeRetries is 0 (the default), this method behaves identically to Handshake() — a single attempt with no retries. It respects context cancellation between retry attempts.

func (*NoiseConn) LocalAddr

func (nc *NoiseConn) LocalAddr() net.Addr

LocalAddr returns the local network address.

func (*NoiseConn) MixHashData added in v0.1.52

func (nc *NoiseConn) MixHashData(data []byte)

MixHashData mixes additional data into the Noise symmetric hash state. This is required by the NTCP2 spec to authenticate cleartext padding bytes that appear after AEAD frames in messages 1 and 2 (§4.4.1, §4.4.2). Call this after reading and discarding cleartext padding, passing the raw padding bytes so that the hash state stays in sync with the peer.

func (*NoiseConn) PeerStatic added in v0.1.52

func (nc *NoiseConn) PeerStatic() []byte

PeerStatic returns the static public key provided by the remote peer during the Noise handshake. Returns nil if the handshake has not completed or if the handshake pattern does not transmit a static key.

Thread Safety: This method is safe for concurrent use. The underlying HandshakeState.PeerStatic() is mutex-protected.

func (*NoiseConn) Read

func (nc *NoiseConn) Read(b []byte) (int, error)

Read reads data from the connection. If the handshake is not complete, it will return an error.

Thread Safety: This method is safe for concurrent use. Multiple goroutines can call Read simultaneously; calls are serialized via readMutex to protect the receive cipher state nonce counter.

func (*NoiseConn) ReadHandshakeMsgFromBytes added in v0.1.52

func (nc *NoiseConn) ReadHandshakeMsgFromBytes(phase handshake.HandshakePhase, wireData []byte) ([]byte, error)

ReadHandshakeMsgFromBytes processes raw inbound Noise handshake bytes, applies the modifier chain, runs the Noise ReadMessage, and returns the decrypted payload. The caller must supply exactly the bytes that form the wire message (no length prefix).

The caller is responsible for calling StartHandshake before the first call and CompleteHandshake after the final message.

func (*NoiseConn) RecvCipherState added in v0.1.52

func (nc *NoiseConn) RecvCipherState() *noise.CipherState

RecvCipherState returns the receive-direction CipherState for direct access by protocol layers. Returns nil before the handshake produces cipher states.

func (*NoiseConn) Rekey added in v0.1.52

func (nc *NoiseConn) Rekey() error

Rekey triggers a rekey operation on the underlying cipher state. This advances the encryption key material per the Noise Protocol specification (encrypts 32 zero bytes with nonce 2^64-1, takes first 32 bytes as new key).

Rekey requires the handshake to be complete (connection in Established state). It is safe for concurrent use; the underlying CipherState.Rekey() is mutex-protected.

func (*NoiseConn) RemoteAddr

func (nc *NoiseConn) RemoteAddr() net.Addr

RemoteAddr returns the remote network address.

func (*NoiseConn) RunPostHandshakeHook added in v0.1.52

func (nc *NoiseConn) RunPostHandshakeHook() error

RunPostHandshakeHook executes the PostHandshakeHook configured on this connection, if one is set. Protocol-specific handshake implementations (e.g. NTCP2Conn.Handshake) must call this before CompleteHandshake so that post-handshake key derivation (e.g. SipHash keys) runs correctly.

Returns nil if no hook is configured.

func (*NoiseConn) SendCipherState added in v0.1.52

func (nc *NoiseConn) SendCipherState() *noise.CipherState

SendCipherState returns the send-direction CipherState for direct access by protocol layers (e.g., NTCP2 SipHash key derivation). Returns nil before the handshake produces cipher states.

func (*NoiseConn) SetDeadline

func (nc *NoiseConn) SetDeadline(t time.Time) error

SetDeadline sets the read and write deadlines.

func (*NoiseConn) SetReadDeadline

func (nc *NoiseConn) SetReadDeadline(t time.Time) error

SetReadDeadline sets the read deadline.

func (*NoiseConn) SetShutdownManager

func (nc *NoiseConn) SetShutdownManager(sm *ShutdownManager)

SetShutdownManager sets the shutdown manager for this connection. If a shutdown manager is set, the connection will be automatically registered for graceful shutdown coordination.

func (*NoiseConn) SetWriteDeadline

func (nc *NoiseConn) SetWriteDeadline(t time.Time) error

SetWriteDeadline sets the write deadline.

func (*NoiseConn) StartHandshake added in v0.1.52

func (nc *NoiseConn) StartHandshake()

StartHandshake transitions the connection to the handshaking state and records the handshake start time. Call this once before the first WriteHandshakeMsgToBytes or ReadHandshakeMsgFromBytes call.

func (*NoiseConn) Underlying added in v0.1.52

func (nc *NoiseConn) Underlying() net.Conn

Underlying returns the underlying net.Conn for direct wire access. This is needed for protocols like NTCP2 that add framing (e.g., SipHash-obfuscated length prefixes) between the TCP connection and the encrypted Noise frames.

Callers should use Encrypt/Decrypt for crypto and write/read the resulting bytes to/from this connection with their own framing.

func (*NoiseConn) Write

func (nc *NoiseConn) Write(b []byte) (int, error)

Write writes data to the connection. If the handshake is not complete, it will return an error.

Thread Safety: This method is safe for concurrent use. Multiple goroutines can call Write simultaneously; calls are serialized via writeMutex to protect the send cipher state nonce counter.

func (*NoiseConn) WriteHandshakeMsgToBytes added in v0.1.52

func (nc *NoiseConn) WriteHandshakeMsgToBytes(phase handshake.HandshakePhase, payload []byte) ([]byte, error)

WriteHandshakeMsgToBytes executes one Noise handshake outbound step with the given payload, runs it through the modifier chain, and returns the raw wire bytes WITHOUT any length-prefix framing. This is used by protocol-specific handshake implementations (e.g. NTCP2) that require exact control over wire framing.

The caller is responsible for calling StartHandshake before the first call and CompleteHandshake after the final message.

func (*NoiseConn) ZeroKeys added in v0.1.52

func (nc *NoiseConn) ZeroKeys()

ZeroKeys securely zeroes the send and receive cipher state key material. This delegates to the upstream CipherState.ZeroKey() which overwrites the key bytes with zeros and marks the cipher states as invalid.

After calling ZeroKeys, the connection can no longer encrypt or decrypt data. Any subsequent Read/Write calls will fail.

type NoiseListener

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

NoiseListener implements net.Listener for accepting Noise Protocol connections. It wraps an underlying net.Listener and provides encrypted connections following the Noise Protocol Framework specification.

func ListenNoise

func ListenNoise(network, addr string, config *ListenerConfig) (*NoiseListener, error)

ListenNoise creates a listener on the given address and wraps it with NoiseListener. This is a convenience function that combines net.Listen and NewNoiseListener. For more control over the underlying listener, use net.Listen followed by NewNoiseListener.

func NewNoiseListener

func NewNoiseListener(underlying net.Listener, config *ListenerConfig) (*NoiseListener, error)

NewNoiseListener creates a new NoiseListener that wraps the underlying listener. The listener will accept connections and wrap them in NoiseConn instances configured as responders (non-initiators) using the provided configuration.

func WrapListener

func WrapListener(listener net.Listener, config *ListenerConfig) (*NoiseListener, error)

WrapListener wraps an existing net.Listener with NoiseListener. This is an alias for NewNoiseListener for consistency with the transport API.

func (*NoiseListener) Accept

func (nl *NoiseListener) Accept() (net.Conn, error)

Accept waits for and returns the next connection to the listener. The returned connection is wrapped in a NoiseConn configured as a responder. This method is safe for concurrent use by multiple goroutines.

func (*NoiseListener) Addr

func (nl *NoiseListener) Addr() net.Addr

Addr returns the listener's network address. This is a NoiseAddr that wraps the underlying listener's address.

func (*NoiseListener) Close

func (nl *NoiseListener) Close() error

Close closes the listener and prevents new connections from being accepted. Any blocked Accept operations will be unblocked and return errors.

func (*NoiseListener) SetShutdownManager

func (nl *NoiseListener) SetShutdownManager(sm *ShutdownManager)

SetShutdownManager sets the shutdown manager for this listener. If a shutdown manager is set, the listener will be automatically registered for graceful shutdown coordination.

type ShutdownManager

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

ShutdownManager coordinates graceful shutdown of noise components. It provides context-based cancellation and ensures proper resource cleanup with configurable timeouts for graceful vs forceful shutdown.

func GetGlobalShutdownManager

func GetGlobalShutdownManager() *ShutdownManager

GetGlobalShutdownManager returns the current global shutdown manager.

func NewShutdownManager

func NewShutdownManager(timeout time.Duration) *ShutdownManager

NewShutdownManager creates a new shutdown manager with the given timeout. If timeout is 0, a default of 30 seconds is used.

func (*ShutdownManager) Context

func (sm *ShutdownManager) Context() context.Context

Context returns the shutdown context for monitoring shutdown signals. Components can use this context to detect when shutdown has been initiated.

func (*ShutdownManager) RegisterConnection

func (sm *ShutdownManager) RegisterConnection(conn *NoiseConn)

RegisterConnection adds a connection to be managed during shutdown. The connection will be gracefully closed during shutdown.

func (*ShutdownManager) RegisterListener

func (sm *ShutdownManager) RegisterListener(listener *NoiseListener)

RegisterListener adds a listener to be managed during shutdown. The listener will be gracefully closed during shutdown.

func (*ShutdownManager) Shutdown

func (sm *ShutdownManager) Shutdown() error

Shutdown initiates graceful shutdown of all managed components. It closes listeners first, waits for connections to drain, then forcefully closes remaining connections after the timeout period.

func (*ShutdownManager) UnregisterConnection

func (sm *ShutdownManager) UnregisterConnection(conn *NoiseConn)

UnregisterConnection removes a connection from shutdown management. This should be called when a connection is closed normally.

func (*ShutdownManager) UnregisterListener

func (sm *ShutdownManager) UnregisterListener(listener *NoiseListener)

UnregisterListener removes a listener from shutdown management. This should be called when a listener is closed normally.

func (*ShutdownManager) Wait

func (sm *ShutdownManager) Wait()

Wait blocks until shutdown is complete. This can be used to wait for shutdown to finish after calling Shutdown().

Directories

Path Synopsis
examples
basic command
Example: Basic usage of the go-noise library with configurable patterns and complete handshakes
Example: Basic usage of the go-noise library with configurable patterns and complete handshakes
echoclient command
Example: Echo Client using Noise Protocol with complete handshake
Example: Echo Client using Noise Protocol with complete handshake
echoserver command
Example: Echo Server using Noise Protocol with complete handshake
Example: Echo Server using Noise Protocol with complete handshake
listener command
Example: NoiseListener demonstration with complete handshake
Example: NoiseListener demonstration with complete handshake
modifiers command
ntcp2 command
Example: NTCP2 addressing and connection demonstration for I2P router addressing
Example: NTCP2 addressing and connection demonstration for I2P router addressing
ntcp2-config command
Example: NTCP2Config builder pattern demonstration for I2P transport configuration This example shows how to create and configure NTCP2Config objects using the builder pattern with proper argument handling and validation.
Example: NTCP2Config builder pattern demonstration for I2P transport configuration This example shows how to create and configure NTCP2Config objects using the builder pattern with proper argument handling and validation.
ntcp2-listener command
Example: NTCP2Listener demonstration for I2P transport This example shows how to create and use an NTCP2Listener for accepting I2P NTCP2 transport connections with router identity management.
Example: NTCP2Listener demonstration for I2P transport This example shows how to create and use an NTCP2Listener for accepting I2P NTCP2 transport connections with router identity management.
ntcp2-shared
Package shared provides NTCP2-specific utilities for go-noise examples
Package shared provides NTCP2-specific utilities for go-noise examples
pool command
Example: Connection pooling demonstration with complete handshakes
Example: Connection pooling demonstration with complete handshakes
retry command
Example: Handshake retry mechanisms with complete connections
Example: Handshake retry mechanisms with complete connections
shared
Package shared provides common utilities for go-noise examples
Package shared provides common utilities for go-noise examples
shutdown command
Example: Graceful shutdown demonstration with complete handshakes
Example: Graceful shutdown demonstration with complete handshakes
state command
Example: Connection state management demonstration with complete handshakes
Example: Connection state management demonstration with complete handshakes
transport command
Example: Transport wrapping demonstration with complete handshakes
Example: Transport wrapping demonstration with complete handshakes
Package handshake provides a generic modifier framework for transforming handshake data during Noise protocol exchanges.
Package handshake provides a generic modifier framework for transforming handshake data during Noise protocol exchanges.
Package internal provides shared helpers and secure utilities used across go-noise sub-packages, including state management and cryptographic zeroing.
Package internal provides shared helpers and secure utilities used across go-noise sub-packages, including state management and cryptographic zeroing.
replaycache
Package replaycache provides a thread-safe, bounded, TTL-based cache for detecting replayed [32]byte keys.
Package replaycache provides a thread-safe, bounded, TTL-based cache for detecting replayed [32]byte keys.
Package ntcp2 provides NTCP2-specific implementations for the Noise Protocol Framework supporting I2P's NTCP2 transport protocol with router identity and session management.
Package ntcp2 provides NTCP2-specific implementations for the Noise Protocol Framework supporting I2P's NTCP2 transport protocol with router identity and session management.
Package pool provides a connection pool for reusing Noise-encrypted connections across multiple Dial operations, reducing handshake overhead for repeated peers.
Package pool provides a connection pool for reusing Noise-encrypted connections across multiple Dial operations, reducing handshake overhead for repeated peers.
Package ratchet provides ECIES-X25519-AEAD-Ratchet cryptographic primitives.
Package ratchet provides ECIES-X25519-AEAD-Ratchet cryptographic primitives.
Package ssu2 provides SSU2-specific implementations for the Noise Protocol Framework supporting I2P's SSU2 transport protocol with UDP-based connections and NAT traversal.
Package ssu2 provides SSU2-specific implementations for the Noise Protocol Framework supporting I2P's SSU2 transport protocol with UDP-based connections and NAT traversal.

Jump to

Keyboard shortcuts

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