Documentation
¶
Overview ¶
Package ntcp2 provides NTCP2-specific implementations for the Noise Protocol Framework supporting I2P's NTCP2 transport protocol with router identity and session management.
Index ¶
- Constants
- func DeriveSipHashKeys(askMaster, handshakeHash []byte) (sipKeysAB [2]uint64, sipIVAB uint64, sipKeysBA [2]uint64, sipIVBA uint64, ...)
- type AESObfuscationModifier
- func (aom *AESObfuscationModifier) Clone() handshake.HandshakeModifier
- func (aom *AESObfuscationModifier) Close() error
- func (aom *AESObfuscationModifier) ModifyInbound(phase handshake.HandshakePhase, data []byte) ([]byte, error)
- func (aom *AESObfuscationModifier) ModifyOutbound(phase handshake.HandshakePhase, data []byte) ([]byte, error)
- func (aom *AESObfuscationModifier) Name() string
- type Acceptor
- type Addr
- type Config
- func (nc *Config) Clone() *Config
- func (nc *Config) SipHashModifier() *SipHashLengthModifier
- func (nc *Config) ToConnConfig() (*noise.ConnConfig, error)
- func (nc *Config) Validate() error
- func (nc *Config) WithAESObfuscation(enabled bool, customIV []byte) *Config
- func (nc *Config) WithFrameSettings(maxSize int, paddingEnabled bool, minPadding, maxPadding int) *Config
- func (nc *Config) WithHandshakeRetries(retries int) *Config
- func (nc *Config) WithHandshakeTimeout(timeout time.Duration) *Config
- func (nc *Config) WithLocalRouterInfo(ri []byte) *Config
- func (nc *Config) WithModifiers(modifiers ...handshake.HandshakeModifier) *Config
- func (nc *Config) WithPattern(pattern string) *Config
- func (nc *Config) WithReadTimeout(timeout time.Duration) *Config
- func (nc *Config) WithRemoteRouterHash(hash data.Hash) *Config
- func (nc *Config) WithRemoteStaticKey(key []byte) *Config
- func (nc *Config) WithReplayDetector(detector ReplayDetector) *Config
- func (nc *Config) WithRetryBackoff(backoff time.Duration) *Config
- func (nc *Config) WithSipHashLength(enabled bool, k1, k2 uint64) *Config
- func (nc *Config) WithStaticKey(key []byte) *Config
- func (nc *Config) WithStrictRouterInfoVerification(strict bool) *Config
- func (nc *Config) WithWriteTimeout(timeout time.Duration) *Config
- type Conn
- func DialNTCP2(network, addr string, config *Config) (*Conn, error)
- func DialNTCP2WithHandshake(network, addr string, config *Config) (*Conn, error)
- func DialNTCP2WithHandshakeContext(ctx context.Context, network, addr string, config *Config) (*Conn, error)
- func NewNTCP2Conn(noiseConn *noise.NoiseConn, localAddr, remoteAddr *Addr) (*Conn, error)
- func WrapNTCP2Conn(conn net.Conn, config *Config) (*Conn, error)
- func (nc *Conn) Close() error
- func (c *Conn) Handshake(ctx context.Context) error
- func (nc *Conn) HandshakeHash() []byte
- func (nc *Conn) LocalAddr() net.Addr
- func (nc *Conn) NonceExhaustionImminent() bool
- func (nc *Conn) PeerMessage3Payload() []byte
- func (nc *Conn) PeerRouterInfoBytes() []byte
- func (nc *Conn) PeerStaticKey() []byte
- func (nc *Conn) PropagatePeerStaticKey()
- func (nc *Conn) PropagateSipHash() error
- func (nc *Conn) Read(b []byte) (int, error)
- func (nc *Conn) Rekey() error
- func (nc *Conn) RemoteAddr() net.Addr
- func (nc *Conn) Role() string
- func (nc *Conn) RouterHash() data.Hash
- func (nc *Conn) SetDeadline(t time.Time) error
- func (nc *Conn) SetLengthObfuscator(slm *SipHashLengthModifier)
- func (nc *Conn) SetNTCP2Config(cfg *Config)
- func (nc *Conn) SetReadDeadline(t time.Time) error
- func (nc *Conn) SetWriteDeadline(t time.Time) error
- func (nc *Conn) UnderlyingConn() *noise.NoiseConn
- func (nc *Conn) Write(b []byte) (int, error)
- type ConnIface
- type Dialer
- type Listener
- type NTCP2PaddingModifier
- func NewNTCP2PaddingModifier(name string, minPadding, maxPadding int, useAEADPadding bool) (*NTCP2PaddingModifier, error)
- func NewNTCP2PaddingModifierForTesting(name string, minPadding, maxPadding int, useAEADPadding bool) (*NTCP2PaddingModifier, error)
- func NewNTCP2PaddingModifierWithRatio(name string, minPadding, maxPadding int, useAEADPadding bool, ...) (*NTCP2PaddingModifier, error)
- func (npm *NTCP2PaddingModifier) Clone() handshake.HandshakeModifier
- func (npm *NTCP2PaddingModifier) Close() error
- func (npm *NTCP2PaddingModifier) EstimatePaddingSize(dataLen int) int
- func (npm *NTCP2PaddingModifier) GetPaddingLimits() (int, int)
- func (npm *NTCP2PaddingModifier) GetPaddingRatio() float64
- func (npm *NTCP2PaddingModifier) IsAEADMode() bool
- func (npm *NTCP2PaddingModifier) ModifyInbound(phase handshake.HandshakePhase, data []byte) ([]byte, error)
- func (npm *NTCP2PaddingModifier) ModifyOutbound(phase handshake.HandshakePhase, data []byte) ([]byte, error)
- func (npm *NTCP2PaddingModifier) Name() string
- func (npm *NTCP2PaddingModifier) SetPaddingLimits(minPadding, maxPadding int) error
- func (npm *NTCP2PaddingModifier) SetPaddingRatio(ratio float64) error
- func (npm *NTCP2PaddingModifier) ValidateAEADFrame(data []byte) bool
- type ReplayCache
- type ReplayDetector
- type SipHashLengthModifier
Constants ¶
const ( // RouterHashSize is the size of the I2P router identity hash in bytes. RouterHashSize = 32 // StaticKeySize is the size of a Curve25519 static key in bytes. StaticKeySize = 32 // IVSize is the size of the AES-CBC initialization vector in bytes. IVSize = 16 // PaddingBlockType is the I2P NTCP2 padding block type identifier. PaddingBlockType = 254 // MaxBlockDataSize is the maximum data size for a single I2P NTCP2 block (bytes). MaxBlockDataSize = 65516 // MaxFrameSize is the maximum size of an NTCP2 data frame (bytes). MaxFrameSize = 65535 // SpecMaxFrameSize is the absolute maximum frame size allowed by the I2P spec (uint16 max). // validateFrameConfiguration uses this to reject user-provided values that exceed the wire limit. SpecMaxFrameSize = 65535 // MinDataPhaseFrameSize is the minimum valid data-phase frame size per the I2P spec. // The spec states the deobfuscated size range is 16–65535. The minimum corresponds // to a ChaChaPoly ciphertext containing only the 16-byte Poly1305 MAC tag (empty payload). MinDataPhaseFrameSize = 16 // BlockHeaderSize is the size of an I2P block header: [type:1][size:2]. BlockHeaderSize = 3 // SipHashIVSize is the size of the SipHash IV in bytes (uint64 = 8 bytes). SipHashIVSize = 8 // NTCP2ProtocolName is the full Noise protocol name for NTCP2 as defined by the I2P spec. // This is passed to InitializeSymmetric() via the ProtocolName field on noise.Config, // producing the correct KDF output for interoperability with other I2P implementations. NTCP2ProtocolName = "Noise_XKaesobfse+hs2+hs3_25519_ChaChaPoly_SHA256" // NTCP2Pattern is the base Noise pattern used by NTCP2. NTCP2Pattern = "XK" // DefaultMaxFrameSize is the default maximum frame size (16KB). DefaultMaxFrameSize = 16384 // DefaultMaxPaddingSize is the default maximum padding size in bytes. DefaultMaxPaddingSize = 64 // MaxNTCP2HandshakePadding is the maximum allowed cleartext padding size // during the NTCP2 handshake (bytes). Per spec §4.3, padding is "0..223 bytes" // in practice. We set a conservative limit of 1024 to allow for future spec // changes while preventing DoS via unbounded allocation on malicious padLen. MaxNTCP2HandshakePadding = 1024 // MaxNTCP2Message3Part2Len is the maximum allowed size for message 3 part 2 // (Alice's RouterInfo block plus optional padding/options). Per spec, RouterInfo // is typically < 2 KB; we allow up to 8192 bytes (8 KB) for legitimate RouterInfo // plus generous padding headroom, while preventing DoS via unbounded allocation // on malicious m3p2Len. This limit applies to the AEAD ciphertext size. MaxNTCP2Message3Part2Len = 8192 // MaxPaddingRatio is the maximum padding ratio per I2P NTCP2 spec (4.4 fixed-point). MaxPaddingRatio = 15.9375 // Poly1305Overhead is the ChaChaPoly AEAD authentication tag size in bytes. Poly1305Overhead = 16 // MaxNonce is the nonce limit per the Noise Protocol spec and I2P NTCP2 spec. // Connections MUST be terminated before the nonce reaches 2^64 - 2. // Using 2^64 - 2 = 18446744073709551614. MaxNonce uint64 = 18446744073709551614 // AEADErrorMaxJunkBytes is the maximum number of random bytes to read // on an AEAD authentication failure for probing resistance. Per the spec: // "random number of bytes (range TBD)" — we use 1024 as a reasonable upper bound. // // INVARIANT: AEADErrorMaxJunkBytes MUST be a power of two. // The bitmask in handleAEADError (val & (AEADErrorMaxJunkBytes - 1)) only // produces a uniform distribution when this constant is a power of two. // Changing it to a non-power-of-two value will introduce modulo bias. AEADErrorMaxJunkBytes = 1024 // AEADErrorTimeoutMin is the minimum duration to wait while reading random // bytes on an AEAD authentication failure. Per the spec: "random timeout // (range TBD)" — randomized over [1s, 3s] to avoid timing fingerprints. AEADErrorTimeoutMin = 1 * time.Second // AEADErrorTimeoutMax is the maximum duration to wait while reading random // bytes on an AEAD authentication failure. AEADErrorTimeoutMax = 3 * time.Second // NonceRekeyThreshold is the nonce value at which the connection should // be considered approaching exhaustion. Since Noise Rekey() does not reset // the nonce counter, the correct response is to establish a new connection. // Set to MaxNonce - 1000 to provide advance warning. NonceRekeyThreshold = MaxNonce - 1000 )
const ClockSkewTolerance = 60 * time.Second
ClockSkewTolerance is the maximum allowed difference between local and remote clocks for NTCP2 handshake timestamp validation. Per the I2P spec, connections with clock skew exceeding this value should be rejected.
const DefaultHandshakeRetries = 3
DefaultHandshakeRetries is the default number of handshake retry attempts.
const DefaultHandshakeTimeoutSeconds = 30
DefaultHandshakeTimeoutSeconds is the default handshake timeout in seconds.
const FrameLengthFieldSize = 2
FrameLengthFieldSize is the size of the SipHash-obfuscated length field.
Variables ¶
This section is empty.
Functions ¶
func DeriveSipHashKeys ¶ added in v0.1.6
func DeriveSipHashKeys(askMaster, handshakeHash []byte) ( sipKeysAB [2]uint64, sipIVAB uint64, sipKeysBA [2]uint64, sipIVBA uint64, err error, )
DeriveSipHashKeys derives per-direction SipHash-2-4 keys and initial IVs from the Noise handshake hash and ask_master secret per the I2P NTCP2 spec.
The derivation follows the spec's 5-step HMAC-SHA256 chain:
Step 1: temp_key = HMAC-SHA256(key=ask_master, data=h || "siphash") Step 2: sip_master = HMAC-SHA256(key=temp_key, data=byte(0x01)) Step 3: temp_key = HMAC-SHA256(key=sip_master, data=zerolen) Step 4: sipkeys_ab = HMAC-SHA256(key=temp_key, data=byte(0x01))[0:24] Step 5: sipkeys_ba = HMAC-SHA256(key=temp_key, data=sipkeys_ab[0:32] || byte(0x02))[0:24]
NOTE: Step 5 uses the full 32-byte HMAC output from step 4 as its input prefix, not just the 24 bytes extracted for sipkeys_ab. This matches the i2pd reference implementation (m_Sipkeysab is 32 bytes; m_Sipkeysab[32]=2 before HMAC call).
Each 24-byte output is split into (sipk1, sipk2, sipiv) as little-endian uint64s.
Parameters:
- askMaster: the ask_master secret from the Noise handshake (32 bytes)
- handshakeHash: the handshake hash (h) from the completed Noise session (32 bytes)
Returns:
- sipKeysAB: [2]uint64{sipk1, sipk2} for direction A→B (initiator→responder)
- sipIVAB: initial IV for A→B SipHash length obfuscation chain
- sipKeysBA: [2]uint64{sipk1, sipk2} for direction B→A (responder→initiator)
- sipIVBA: initial IV for B→A SipHash length obfuscation chain
- err: non-nil if derivation fails
Types ¶
type AESObfuscationModifier ¶
type AESObfuscationModifier struct {
// contains filtered or unexported fields
}
AESObfuscationModifier implements NTCP2's AES-based ephemeral key obfuscation. This modifier encrypts/decrypts the X and Y ephemeral keys in messages 1 and 2 using AES-256-CBC with the router hash as key and published IV.
Per the NTCP2 spec, the AES state (last ciphertext block) from message 1 encryption is carried forward as the IV for message 2 encryption.
After AES decryption, received keys are validated per the NTCP2 spec: X[31]&0x80 must be 0 (Curve25519 requirement). Invalid keys cause message rejection.
func NewAESObfuscationModifier ¶
func NewAESObfuscationModifier(name string, routerHash, iv []byte) (*AESObfuscationModifier, error)
NewAESObfuscationModifier creates a new AES obfuscation modifier for NTCP2. routerHash must be 32 bytes (RH_B), iv must be 16 bytes from network database.
func (*AESObfuscationModifier) Clone ¶ added in v0.1.6
func (aom *AESObfuscationModifier) Clone() handshake.HandshakeModifier
Clone creates a deep copy of the AESObfuscationModifier with independent state. This implements handshake.ModifierCloner to support safe Config.Clone() operations. The cloned modifier starts with a fresh state (no aesState carried over).
func (*AESObfuscationModifier) Close ¶ added in v0.1.6
func (aom *AESObfuscationModifier) Close() error
Close zeroes the AES key material and IV stored in the modifier to prevent sensitive data from lingering in memory after the connection is closed. This method is safe for concurrent use.
func (*AESObfuscationModifier) ModifyInbound ¶
func (aom *AESObfuscationModifier) ModifyInbound(phase handshake.HandshakePhase, data []byte) ([]byte, error)
ModifyInbound removes AES obfuscation from ephemeral keys in handshake messages.
The modifier chain receives the full Noise message (e.g. 64 bytes for msg1/msg2: 32-byte ephemeral key + AEAD payload). Only the first 32 bytes (the ephemeral key) are AES-CBC decrypted; the remainder is passed through unchanged.
func (*AESObfuscationModifier) ModifyOutbound ¶
func (aom *AESObfuscationModifier) ModifyOutbound(phase handshake.HandshakePhase, data []byte) ([]byte, error)
ModifyOutbound applies AES obfuscation to ephemeral keys in handshake messages. For message 1: encrypts X key with RH_B and published IV For message 2: encrypts Y key with RH_B and AES state from message 1
The modifier chain receives the full Noise message (e.g. 64 bytes for msg1/msg2: 32-byte ephemeral key + AEAD payload). Only the first 32 bytes (the ephemeral key) are AES-CBC encrypted; the remainder is passed through unchanged.
func (*AESObfuscationModifier) Name ¶
func (aom *AESObfuscationModifier) Name() string
Name returns the modifier name for logging and debugging.
type Acceptor ¶ added in v0.1.6
type Acceptor interface {
net.Listener
// AcceptWithHandshake accepts the next inbound connection and performs
// the NTCP2 handshake before returning. The ctx governs handshake
// cancellation for each accepted connection.
AcceptWithHandshake(ctx context.Context) (ConnIface, error)
}
Acceptor accepts inbound NTCP2 connections on a listener. It extends net.Listener with AcceptWithHandshake, which performs the NTCP2 handshake as part of the accept loop. *Listener satisfies this interface.
type Addr ¶ added in v0.1.6
type Addr struct {
// contains filtered or unexported fields
}
Addr implements net.Addr for NTCP2 transport connections. It provides I2P-specific addressing information including router identity, destination hash, and session parameters for the NTCP2 protocol.
func NewNTCP2Addr ¶
NewNTCP2Addr creates a new NTCP2Addr with the specified TCP address and router hash. routerHash is the I2P router identity hash. role should be either "initiator" or "responder".
func (*Addr) IdentHash ¶ added in v0.1.6
IdentHash returns the router identity hash as a fixed-size [32]byte array.
func (*Addr) Network ¶ added in v0.1.6
Network returns "ntcp2" to identify this as an NTCP2 transport address. This implements the net.Addr interface requirement.
func (*Addr) RouterHash ¶ added in v0.1.6
RouterHash returns the router identity hash.
func (*Addr) SetRouterHash ¶ added in v0.1.6
SetRouterHash updates the router identity hash. This is used to update a placeholder zero hash after the Noise handshake reveals the remote peer's static key.
func (*Addr) String ¶ added in v0.1.6
String returns a string representation of the NTCP2 address. Format: "ntcp2://[router_hash]/[role]/[tcp_address][?dest=dest_hash]" Router hash and optional parameters are base64 encoded for readability.
func (*Addr) UnderlyingAddr ¶ added in v0.1.6
UnderlyingAddr returns the underlying TCP network address.
type Config ¶ added in v0.1.6
type Config struct {
// Pattern is the Noise protocol pattern for NTCP2
// Default: "XK" (standard NTCP2 pattern)
Pattern string
// Initiator indicates if this connection is the handshake initiator
// For listeners, this is always false
Initiator bool
// BobRouterHash is Bob's (the responder's) router hash.
// Per the I2P NTCP2 spec, both Alice (initiator) and Bob (responder)
// use RH_B as the AES-256-CBC key for ephemeral key obfuscation.
// For the responder, this is their own router hash.
// For the initiator, this is the remote peer's router hash.
// Required for NTCP2 addressing and AES obfuscation.
BobRouterHash data.Hash
// StaticKey is the long-term static key for this peer (32 bytes for Curve25519)
StaticKey []byte
// RemoteRouterHash is the remote peer's router identity hash.
// Required for outbound connections, optional for listeners.
RemoteRouterHash *data.Hash
// RemoteStaticKey is the remote peer's Curve25519 static public key (32 bytes)
// Required for initiator connections using XK pattern (pre-message: ← s)
// Distinct from RemoteRouterHash (which is SHA-256 of RouterIdentity)
RemoteStaticKey []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: 3 attempts (0 = no retries, -1 = 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
// EnableAESObfuscation enables AES-based ephemeral key obfuscation
// Default: true (recommended for production)
EnableAESObfuscation bool
// ObfuscationIV is the 16-byte IV for AES obfuscation
// If nil, will be derived from router hash (recommended)
ObfuscationIV []byte
// EnableSipHashLength enables SipHash-based frame length obfuscation
// Default: true (recommended for production)
EnableSipHashLength bool
// SipHashKeys are the k1, k2 keys for SipHash length obfuscation
// If empty, will be derived during handshake
SipHashKeys [2]uint64
// Modifiers is a list of additional handshake modifiers for custom obfuscation
// These are applied in addition to NTCP2's standard modifiers
// Default: empty (no additional modifiers)
Modifiers []handshake.HandshakeModifier
// MaxFrameSize is the maximum size of NTCP2 data frames
// Default: 16384 bytes (16KB)
MaxFrameSize int
// FramePaddingEnabled enables random padding in NTCP2 frames
// Default: true (recommended for traffic analysis resistance)
FramePaddingEnabled bool
// MinPaddingSize is the minimum padding size for frames
// Default: 0 bytes
MinPaddingSize int
// MaxPaddingSize is the maximum padding size for frames
// Default: 64 bytes
MaxPaddingSize int
// LocalRouterInfo contains Alice's serialized RouterInfo bytes.
// Required for outbound NTCP2 connections: its length determines m3p2Len
// in the message 1 options block and it is sent as the encrypted payload
// in message 3 part 2.
LocalRouterInfo []byte
// StrictRouterInfoVerification, when true, causes the initiator handshake
// to return a hard error if the configured LocalRouterInfo does not
// advertise the static key that will be sent in the Noise handshake msg1.
// Defaults to false so that existing tests using synthetic RouterInfo
// bytes continue to work. Set to true in production to catch
// misconfiguration before a silent i2pd interop failure (frame #0 EOF).
StrictRouterInfoVerification bool
// ReplayDetector checks for replayed ephemeral keys during handshakes.
// If nil, replay detection is disabled (not recommended for production).
// Shared across all responder connections using this config (typically
// instantiated once per listener). Initiator connections ignore this field.
ReplayDetector ReplayDetector
// contains filtered or unexported fields
}
Config contains configuration for creating NTCP2 connections and listeners. It follows the builder pattern for optional configuration and validation, similar to the main ConnConfig but with NTCP2-specific parameters.
func NewNTCP2Config ¶
NewNTCP2Config creates a new NTCP2Config with sensible defaults. bobRouterHash is Bob's (the responder's) router hash (RH_B). Per the NTCP2 spec, both initiator and responder use RH_B for AES ephemeral key obfuscation. For a responder, pass your own router hash; for an initiator, pass the remote peer's router hash. initiator indicates whether this connection will initiate the handshake.
func (*Config) Clone ¶ added in v0.1.6
Clone creates a deep copy of this NTCP2Config that is safe to use independently (e.g., for per-connection configs on the listener path). The atomic.Pointer[SipHashLengthModifier] field is NOT copied — the returned config has a fresh zero-value atomic, which is correct because the PostHandshakeHook will populate it after the handshake.
IMPORTANT: The Modifiers slice is shallow-copied — individual modifier interface values (pointers to AESObfuscationModifier, SipHashLengthModifier, etc.) are shared between the original and clone. Since modifiers carry mutable state (AES cipher state, SipHash IVs), the clone MUST NOT be used concurrently with the original for handshakes. Callers MUST call ToConnConfig() on the clone before use, which creates fresh modifier instances with independent state. The listener path does this correctly. Direct use of Clone() without ToConnConfig() will cause data races and corrupt handshake state if both configs are active concurrently.
func (*Config) SipHashModifier ¶ added in v0.1.6
func (nc *Config) SipHashModifier() *SipHashLengthModifier
SipHashModifier returns the SipHash length modifier created during ToConnConfig().
PRECONDITION: This method returns nil until the PostHandshakeHook has executed during Handshake(). Callers MUST NOT call this method before the handshake completes, as the modifier's keys are derived from the handshake state. Calling PropagateSipHash() before the handshake will return an error rather than silently no-op.
Returns nil if SipHash length obfuscation is disabled or ToConnConfig() hasn't been called. Each call to ToConnConfig() creates a fresh modifier instance, so configs can be safely reused for multiple connections without sharing IV state.
func (*Config) ToConnConfig ¶ added in v0.1.6
func (nc *Config) ToConnConfig() (*noise.ConnConfig, error)
ToConnConfig converts NTCP2Config to a standard ConnConfig for use with NoiseConn. This includes setting up NTCP2-specific modifiers based on the configuration. A PostHandshakeHook is automatically registered when SipHash length obfuscation is enabled — the hook captures the handshake hash for future SipHash key derivation.
func (*Config) WithAESObfuscation ¶ added in v0.1.6
WithAESObfuscation enables or disables AES-based ephemeral key obfuscation. When enabled with a custom IV, the IV must be exactly 16 bytes; invalid IV lengths are ignored. Call Validate() to check all fields before use. Note: Options negotiation (padding limits as 4.4 fixed-point, dummy traffic, delay parameters) is the responsibility of the higher-level router transport.
func (*Config) WithFrameSettings ¶ added in v0.1.6
func (nc *Config) WithFrameSettings(maxSize int, paddingEnabled bool, minPadding, maxPadding int) *Config
WithFrameSettings configures NTCP2 frame handling parameters. maxSize sets the maximum frame size (default: 16384 bytes). paddingEnabled enables random padding (default: true). minPadding and maxPadding set the padding size range (default: 0-64 bytes).
func (*Config) WithHandshakeRetries ¶ added in v0.1.6
WithHandshakeRetries sets the number of handshake retry attempts. Use 0 for no retries, -1 for infinite retries.
func (*Config) WithHandshakeTimeout ¶ added in v0.1.6
WithHandshakeTimeout sets the handshake timeout.
func (*Config) WithLocalRouterInfo ¶ added in v0.1.6
WithLocalRouterInfo sets Alice's serialized RouterInfo bytes for outbound NTCP2 connections. The length is used to populate m3p2Len in the message 1 options block; the bytes are encrypted and sent as message 3 part 2.
func (*Config) WithModifiers ¶ added in v0.1.6
func (nc *Config) WithModifiers(modifiers ...handshake.HandshakeModifier) *Config
WithModifiers sets additional handshake modifiers for custom obfuscation. These are applied in addition to NTCP2's standard modifiers.
func (*Config) WithPattern ¶ added in v0.1.6
WithPattern sets the Noise protocol pattern. For NTCP2, this should typically remain "XK".
func (*Config) WithReadTimeout ¶ added in v0.1.6
WithReadTimeout sets the read timeout for post-handshake operations.
func (*Config) WithRemoteRouterHash ¶ added in v0.1.6
WithRemoteRouterHash sets the remote peer's router identity. hash must be 32 bytes. Required for outbound connections. Invalid lengths are ignored; call Validate() to check all fields before use.
func (*Config) WithRemoteStaticKey ¶ added in v0.1.6
WithRemoteStaticKey sets the remote peer's Curve25519 static public key. key must be exactly 32 bytes. Required for initiator connections using the XK pattern, where the initiator must know the responder's static key as a pre-message (← s). This is distinct from RemoteRouterHash (which is SHA-256 of the RouterIdentity). Invalid lengths are ignored; call Validate() to check all fields before use.
func (*Config) WithReplayDetector ¶ added in v0.1.6
func (nc *Config) WithReplayDetector(detector ReplayDetector) *Config
WithReplayDetector sets the replay detector for responder handshakes. If nil, replay detection is disabled (not recommended for production). The detector is shared across all responder connections using this config (typically instantiated once per listener). Initiator connections ignore this field.
func (*Config) WithRetryBackoff ¶ added in v0.1.6
WithRetryBackoff sets the base delay between retry attempts.
func (*Config) WithSipHashLength ¶ added in v0.1.6
WithSipHashLength enables or disables SipHash-based frame length obfuscation. When enabled with custom keys, both k1 and k2 must be provided.
func (*Config) WithStaticKey ¶ added in v0.1.6
WithStaticKey sets the static key for this connection. key must be 32 bytes for Curve25519. Invalid lengths are ignored; call Validate() to check all fields before use.
func (*Config) WithStrictRouterInfoVerification ¶ added in v0.1.6
WithStrictRouterInfoVerification enables or disables hard-error mode for the local RouterInfo / static-key mismatch check. Set to true in production to catch misconfiguration before a silent i2pd interop failure.
type Conn ¶ added in v0.1.6
type Conn struct {
// OnAEADError is an optional callback invoked during AEAD failure handling,
// after the probing-resistance junk-read phase but before the TCP RST.
// The router transport layer can use this to send a termination block
// (type 4, reason 4 = "AEAD failure") before the connection is killed.
// The callback receives the underlying net.Conn for direct writing;
// the NTCP2Conn's broken flag is already set, so normal Write() is blocked.
// If nil, no termination block is sent (current behaviour).
OnAEADError func(conn net.Conn)
// contains filtered or unexported fields
}
Conn implements net.Conn for NTCP2 transport connections. It wraps a NoiseConn with NTCP2-specific addressing and protocol handling.
This package implements the Noise XK handshake with NTCP2 extensions (AES ephemeral key obfuscation, SipHash frame length obfuscation, and cleartext padding). It provides framed I/O (SipHash-obfuscated length prefix + ChaChaPoly AEAD) and probing resistance on AEAD failures.
Higher-level concerns — I2NP message parsing, block framing (types 0–9), termination blocks, options negotiation, timestamp validation, replay caches, and version detection — belong in the router transport layer (github.com/go-i2p/go-i2p/lib/transport/ntcp).
func DialNTCP2 ¶
DialNTCP2 creates a connection to the given address and wraps it with NTCP2Conn. This is a convenience function that combines net.Dial, NoiseConn creation, and NTCP2 wrapping. For more control over the underlying connection, use net.Dial followed by NewNoiseConn and NewNTCP2Conn.
func DialNTCP2WithHandshake ¶
DialNTCP2WithHandshake creates a connection and performs the NTCP2 handshake automatically. This is a convenience function that combines DialNTCP2 and handshake execution.
func DialNTCP2WithHandshakeContext ¶
func DialNTCP2WithHandshakeContext(ctx context.Context, network, addr string, config *Config) (*Conn, error)
DialNTCP2WithHandshakeContext creates a connection and performs the NTCP2 handshake with context. The context can be used to cancel the dial or handshake operations.
func NewNTCP2Conn ¶
NewNTCP2Conn creates a new NTCP2Conn wrapping the provided NoiseConn. The NoiseConn must already be configured with appropriate NTCP2 modifiers.
func WrapNTCP2Conn ¶
WrapNTCP2Conn wraps an existing net.Conn with NTCP2Conn. This function creates the necessary Noise wrapper and NTCP2 addressing.
func (*Conn) Close ¶ added in v0.1.6
Close implements net.Conn.Close. Closes the underlying Noise connection, zeroes key material, and cleans up resources. Close is idempotent — calling it multiple times is safe.
func (*Conn) Handshake ¶ added in v0.1.6
Handshake performs the NTCP2 XK three-way handshake with correct wire framing.
The standard Noise framing adds a 2-byte length prefix to every handshake message. The NTCP2 spec explicitly forbids length prefixes on messages 1, 2, and 3. This method writes and reads raw Noise bytes directly over the TCP socket to produce the correct on-wire format:
- Message 1 (64 bytes): [AES-obfuscated e (32B)] [EncryptAndHash(options) (16B)] [tag (16B)]
- Message 2 (64 bytes): [AES-obfuscated e (32B)] [EncryptAndHash(options) (16B)] [tag (16B)]
- Message 3 (48 + m3p2Len bytes): [EncryptAndHash(s) (48B)] [EncryptAndHash(RI block) (m3p2Len B)]
Cleartext padding: Alice sends none (padLen=0). Bob's padding is read, MixHash'd, and discarded per the NTCP2 spec §4.4.
Handshake must not be called concurrently on the same connection.
func (*Conn) HandshakeHash ¶ added in v0.1.6
HandshakeHash returns the Noise handshake hash (h) from the completed session. This is needed by the router transport layer to derive data-phase keys via DeriveSipHashKeys(ask_master, h) for SipHash frame length obfuscation. Returns nil if the handshake has not been initiated.
func (*Conn) LocalAddr ¶ added in v0.1.6
LocalAddr implements net.Conn.LocalAddr. Returns the NTCP2-specific local address.
func (*Conn) NonceExhaustionImminent ¶ added in v0.1.6
NonceExhaustionImminent returns true if either the read or write nonce counter has reached NonceRekeyThreshold, indicating that the connection is approaching the maximum nonce limit and should be replaced.
The Noise Protocol's Rekey() operation does NOT reset nonce counters, so the correct response to imminent exhaustion is to establish a new connection rather than attempt a rekey.
func (*Conn) PeerMessage3Payload ¶ added in v0.1.6
PeerMessage3Payload returns the decrypted plaintext of NTCP2 message 3 part 2 received from the remote peer. This is meaningful only on the responder side of a completed inbound handshake; it returns nil for initiator connections and before Handshake() succeeds.
The payload is the I2NP block frame as transmitted by Alice. Per the NTCP2 spec it contains a RouterInfo block (type 2) and may contain optional padding (type 254) and options (type 1) blocks. The router transport layer is responsible for parsing this and storing Alice's RouterInfo in the local NetDB / peer cache so that direct replies (e.g. ShortTunnelBuildReply for 1-hop outbound tunnels) can be routed back to her NTCP2 address.
The returned slice is a defensive copy and may be modified freely.
func (*Conn) PeerRouterInfoBytes ¶ added in v0.1.6
PeerRouterInfoBytes is a convenience wrapper around PeerMessage3Payload that locates the RouterInfo block (type 2) inside the message-3 part-2 frame and returns just the inner RouterInfo bytes (with the 1-byte flag field stripped). Returns nil if no payload was captured, the payload is malformed, or no RouterInfo block is present.
Block frame layout (per NTCP2 spec §5):
byte 0 : block type bytes 1-2 : block size (uint16, big-endian) — number of bytes that follow bytes 3+ : block data (size bytes)
For the RouterInfo block (type 2) the first data byte is a flag field; the remaining bytes are the serialized RouterInfo.
func (*Conn) PeerStaticKey ¶ added in v0.1.6
PeerStaticKey returns the remote peer's Noise static public key (32 bytes). This is available after the handshake completes and can be used by the router transport layer (github.com/go-i2p/go-i2p/lib/transport/ntcp) to compute the full router hash via SHA-256(RouterIdentity) using github.com/go-i2p/common/router_identity.
func (*Conn) PropagatePeerStaticKey ¶ added in v0.1.6
func (nc *Conn) PropagatePeerStaticKey()
PropagatePeerStaticKey extracts the remote peer's Noise static public key from the completed handshake and updates the remote NTCP2 address's router hash. This must be called after a successful Handshake() to replace the placeholder zero hash that was used before the peer's identity was known (e.g., on inbound/responder connections).
If the peer static key is not available (handshake not completed) or the remote address already has a non-zero router hash, this is a no-op.
func (*Conn) PropagateSipHash ¶ added in v0.1.6
PropagateSipHash copies the SipHash modifier from the stored NTCP2Config (populated by the PostHandshakeHook during Handshake) into this connection's lengthObfuscator. Call this immediately after a successful Handshake().
Returns an error if: - No NTCP2 config is stored (SetNTCP2Config was not called) - The SipHash modifier is nil (PostHandshakeHook has not yet executed or SipHash is disabled)
This prevents silent no-op behavior when PropagateSipHash is called before the handshake completes.
func (*Conn) Read ¶ added in v0.1.6
Read implements net.Conn.Read. When a length obfuscator is set, reads use NTCP2 framed I/O:
- Return any buffered plaintext from a previous frame first
- Read exactly 2 bytes from the underlying TCP connection
- XOR with SipHash inbound mask to recover the frame length
- Read exactly frameLength bytes of ciphertext from TCP
- Decrypt the ciphertext via the Noise cipher state
- Copy plaintext into the caller's buffer; buffer any remainder
When no length obfuscator is set, delegates directly to NoiseConn.Read.
func (*Conn) Rekey ¶ added in v0.1.6
Rekey triggers a rekey operation on the underlying Noise connection. This delegates to NoiseConn.Rekey(), which advances the encryption key material per the Noise Protocol specification.
This method allows NTCP2Conn to satisfy a Rekeyer interface:
type Rekeyer interface { Rekey() error }
func (*Conn) RemoteAddr ¶ added in v0.1.6
RemoteAddr implements net.Conn.RemoteAddr. Returns the NTCP2-specific remote address.
func (*Conn) RouterHash ¶ added in v0.1.6
RouterHash returns the router hash from the remote address. This is I2P-specific functionality for NTCP2 connections.
func (*Conn) SetDeadline ¶ added in v0.1.6
SetDeadline implements net.Conn.SetDeadline. Sets read and write deadlines on the underlying connection.
func (*Conn) SetLengthObfuscator ¶ added in v0.1.6
func (nc *Conn) SetLengthObfuscator(slm *SipHashLengthModifier)
SetLengthObfuscator sets the SipHash length obfuscator for data-phase framing. When set, Read/Write will use framed I/O with SipHash-obfuscated length prefixes. This is safe to call concurrently with Read/Write (uses atomic.Pointer).
func (*Conn) SetNTCP2Config ¶ added in v0.1.6
SetNTCP2Config stores a reference to the originating NTCP2Config so that PropagateSipHash can copy PostHandshakeHook-derived keys after handshake. This is safe to call concurrently with PropagateSipHash (uses atomic.Pointer).
func (*Conn) SetReadDeadline ¶ added in v0.1.6
SetReadDeadline implements net.Conn.SetReadDeadline. Sets the read deadline on the underlying connection.
func (*Conn) SetWriteDeadline ¶ added in v0.1.6
SetWriteDeadline implements net.Conn.SetWriteDeadline. Sets the write deadline on the underlying connection.
func (*Conn) UnderlyingConn ¶ added in v0.1.6
UnderlyingConn returns the underlying NoiseConn for advanced operations. This allows access to Noise-specific functionality when needed.
func (*Conn) Write ¶ added in v0.1.6
Write implements net.Conn.Write. When a length obfuscator is set, writes use NTCP2 framed I/O:
- Encrypt the plaintext via the Noise cipher state
- Compute the ciphertext length as a uint16
- XOR with SipHash outbound mask to obfuscate the length
- Write [2-byte obfuscated length][ciphertext] to the underlying TCP connection
When no length obfuscator is set, delegates directly to NoiseConn.Write. Large writes are transparently split into multiple frames of at most MaxFrameSize minus Poly1305Overhead bytes of plaintext each.
type ConnIface ¶ added in v0.1.6
type ConnIface interface {
net.Conn
// Handshake performs the NTCP2 handshake. Must be called before Read/Write.
Handshake(ctx context.Context) error
}
ConnIface is the minimal interface satisfied by *Conn. Dialer.DialContext and Acceptor.AcceptWithHandshake return ConnIface so that callers can substitute test doubles without importing the concrete *Conn type. Where NTCP2-specific methods (e.g. PropagateSipHash, RouterHash) are needed, use a type assertion: conn.(*Conn).PropagateSipHash().
type Dialer ¶ added in v0.1.6
type Dialer interface {
// DialContext dials an outbound NTCP2 connection to network/addr using
// config, performs the NTCP2 handshake, and returns the established Conn.
// The ctx governs handshake cancellation.
DialContext(ctx context.Context, network, addr string, config *Config) (ConnIface, error)
}
Dialer establishes outbound NTCP2 connections with handshake. The package-level DialNTCP2WithHandshakeContext provides this behaviour; wrap it via NewDialer() to obtain a Dialer value suitable for dependency injection and test substitution.
type Listener ¶ added in v0.1.6
type Listener struct {
// contains filtered or unexported fields
}
Listener implements net.Listener for accepting NTCP2 transport connections. It accepts raw TCP connections from the underlying listener, wraps each in a NoiseConn created via NTCP2Config.ToConnConfig() (which sets the correct CipherSuite, ProtocolName, and Modifiers), and then wraps that in an NTCP2Conn.
func ListenNTCP2 ¶
ListenNTCP2 creates a listener on the given address and wraps it with NTCP2Listener. This is a convenience function that combines net.Listen and NewNTCP2Listener. For more control over the underlying listener, use net.Listen followed by NewNTCP2Listener.
func NewNTCP2Listener ¶
NewNTCP2Listener creates a new NTCP2Listener that wraps the underlying TCP listener. The listener will accept connections and wrap them in NTCP2Conn instances configured as responders with NTCP2-specific addressing and protocol handling.
func WrapNTCP2Listener ¶
WrapNTCP2Listener wraps an existing net.Listener with NTCP2Listener. This is an alias for NewNTCP2Listener for consistency with the transport API.
func (*Listener) Accept ¶ added in v0.1.6
Accept waits for and returns the next connection to the listener. The returned connection is wrapped in an NTCP2Conn configured as a responder with the full NTCP2 cipher suite, protocol name, and modifiers.
The returned connection has NOT yet performed the Noise handshake. RemoteAddr().(*Addr).RouterHash is a zero value until Handshake(ctx) and PropagatePeerStaticKey() complete. Most callers should use AcceptWithHandshake (if available) or call Handshake explicitly after Accept.
func (*Listener) AcceptWithHandshake ¶ added in v0.1.6
AcceptWithHandshake waits for the next connection and automatically performs the NTCP2 handshake. This mirrors DialNTCP2WithHandshakeContext for the responder side.
type NTCP2PaddingModifier ¶
type NTCP2PaddingModifier struct {
// contains filtered or unexported fields
}
NTCP2PaddingModifier implements production-grade NTCP2-specific padding strategies. Supports I2P NTCP2 specification requirements including: - Cleartext padding for messages 1 and 2 (outside AEAD frames) - AEAD padding for message 3 and data phase (inside AEAD frames with type 254) - Cryptographically secure random padding distribution - Configurable padding ratios for traffic analysis resistance
Padding computation and I2P block wire format operations are provided by the shared handshake.PaddingEngine. NTCP2-specific concerns (AEAD removal with trailing-block scan, frame validation) remain in this type.
All exported methods are safe for concurrent use.
func NewNTCP2PaddingModifier ¶
func NewNTCP2PaddingModifier(name string, minPadding, maxPadding int, useAEADPadding bool) (*NTCP2PaddingModifier, error)
NewNTCP2PaddingModifier creates a new production-grade NTCP2 padding modifier.
Parameters:
- name: identifier for logging and debugging
- minPadding: minimum padding bytes (0-65516)
- maxPadding: maximum padding bytes (>= minPadding, 0-65516)
- useAEADPadding: false for messages 1-2 (cleartext), true for message 3+ (AEAD)
The modifier uses cryptographically secure random padding by default. Padding sizes follow I2P NTCP2 specification guidelines.
func NewNTCP2PaddingModifierForTesting ¶
func NewNTCP2PaddingModifierForTesting(name string, minPadding, maxPadding int, useAEADPadding bool) (*NTCP2PaddingModifier, error)
NewNTCP2PaddingModifierForTesting creates a modifier with deterministic padding for testing. This should NEVER be used in production as it compromises security.
func NewNTCP2PaddingModifierWithRatio ¶
func NewNTCP2PaddingModifierWithRatio(name string, minPadding, maxPadding int, useAEADPadding bool, paddingRatio float64) (*NTCP2PaddingModifier, error)
NewNTCP2PaddingModifierWithRatio creates a new NTCP2 padding modifier with a specific padding ratio.
Parameters:
- name: identifier for logging and debugging
- minPadding: minimum padding bytes (0-65516)
- maxPadding: maximum padding bytes (>= minPadding, 0-65516)
- useAEADPadding: false for messages 1-2 (cleartext), true for message 3+ (AEAD)
- paddingRatio: ratio of padding to data (0.0 to 15.9375 as per I2P NTCP2 spec)
A paddingRatio of 0.0 means no ratio-based padding (uses min/max only). A paddingRatio of 1.0 means 100% padding (double the message size).
func (*NTCP2PaddingModifier) Clone ¶ added in v0.1.6
func (npm *NTCP2PaddingModifier) Clone() handshake.HandshakeModifier
Clone creates a deep copy of the NTCP2PaddingModifier with independent state. This implements handshake.ModifierCloner to support safe Config.Clone() operations.
func (*NTCP2PaddingModifier) Close ¶ added in v0.1.6
func (npm *NTCP2PaddingModifier) Close() error
Close is a no-op for NTCP2PaddingModifier because it holds no sensitive key material. It satisfies the HandshakeModifier lifecycle contract.
func (*NTCP2PaddingModifier) EstimatePaddingSize ¶
func (npm *NTCP2PaddingModifier) EstimatePaddingSize(dataLen int) int
EstimatePaddingSize estimates the padding size for a given data length. Useful for pre-allocating buffers and bandwidth calculations.
func (*NTCP2PaddingModifier) GetPaddingLimits ¶
func (npm *NTCP2PaddingModifier) GetPaddingLimits() (int, int)
GetPaddingLimits returns the current min/max padding limits.
func (*NTCP2PaddingModifier) GetPaddingRatio ¶
func (npm *NTCP2PaddingModifier) GetPaddingRatio() float64
GetPaddingRatio returns the current padding ratio.
func (*NTCP2PaddingModifier) IsAEADMode ¶
func (npm *NTCP2PaddingModifier) IsAEADMode() bool
IsAEADMode returns true if this modifier is configured for AEAD padding (message 3+).
func (*NTCP2PaddingModifier) ModifyInbound ¶
func (npm *NTCP2PaddingModifier) ModifyInbound(phase handshake.HandshakePhase, data []byte) ([]byte, error)
ModifyInbound removes NTCP2-specific padding.
PhaseFinal (message 3) is skipped — padding in message 3 is inside the encrypted payload and parsed by the block-format layer, not the modifier.
func (*NTCP2PaddingModifier) ModifyOutbound ¶
func (npm *NTCP2PaddingModifier) ModifyOutbound(phase handshake.HandshakePhase, data []byte) ([]byte, error)
ModifyOutbound adds NTCP2-specific padding based on message phase.
PhaseFinal (message 3) is explicitly skipped because the handshake code manages message 3 padding at the plaintext level — it must be included in m3p2Len which is committed in message 1 before the modifier runs. AEAD padding is only applied during PhaseData (post-handshake frames).
func (*NTCP2PaddingModifier) Name ¶
func (npm *NTCP2PaddingModifier) Name() string
Name returns the modifier name for logging and debugging.
func (*NTCP2PaddingModifier) SetPaddingLimits ¶
func (npm *NTCP2PaddingModifier) SetPaddingLimits(minPadding, maxPadding int) error
SetPaddingLimits updates the padding limits for dynamic adjustment. Supports I2P NTCP2 options negotiation during data phase.
func (*NTCP2PaddingModifier) SetPaddingRatio ¶
func (npm *NTCP2PaddingModifier) SetPaddingRatio(ratio float64) error
SetPaddingRatio updates the padding ratio for dynamic adjustment during connection. This supports I2P NTCP2 options negotiation where padding parameters can be updated.
func (*NTCP2PaddingModifier) ValidateAEADFrame ¶
func (npm *NTCP2PaddingModifier) ValidateAEADFrame(data []byte) bool
ValidateAEADFrame validates that a frame contains properly formatted AEAD blocks. Returns true if the frame structure is valid according to I2P NTCP2 spec.
type ReplayCache ¶ added in v0.1.6
type ReplayCache struct {
// contains filtered or unexported fields
}
ReplayCache is a thread-safe, bounded, TTL-based cache for detecting replayed NTCP2 handshake ephemeral keys. It is shared across all listener goroutines within a single router instance.
The cache stores the first 32 bytes of each message 1 (the ephemeral key X) and rejects duplicates within the TTL window.
ReplayCache implements the ReplayDetector interface.
func NewReplayCache ¶ added in v0.1.6
func NewReplayCache() *ReplayCache
NewReplayCache creates a new replay cache and starts a background cleanup goroutine. Call Close() when the cache is no longer needed.
func (*ReplayCache) CheckAndAdd ¶ added in v0.1.6
func (rc *ReplayCache) CheckAndAdd(ephemeralKey [32]byte) bool
CheckAndAdd checks whether an ephemeral key has been seen before. If the key is new, it is added to the cache and false is returned (not a replay). If the key has been seen within the TTL window, true is returned (replay detected).
This is the primary method called by the listener before processing message 1.
func (*ReplayCache) Close ¶ added in v0.1.6
func (rc *ReplayCache) Close()
Close stops the background cleanup goroutine and releases resources. Close is idempotent — calling it more than once is safe and will not panic.
func (*ReplayCache) Size ¶ added in v0.1.6
func (rc *ReplayCache) Size() int
Size returns the current number of entries in the cache.
type ReplayDetector ¶ added in v0.1.6
type ReplayDetector interface {
// CheckAndAdd returns true if the ephemeral key has been seen before (replay).
// If the key is new, it is added to the cache and false is returned.
CheckAndAdd(ephemeralKey [32]byte) bool
// Size returns the current number of entries in the replay cache.
Size() int
// Close releases resources (stops cleanup goroutine if any).
Close()
}
ReplayDetector checks for replayed ephemeral keys during NTCP2 handshakes. Implementations maintain a TTL-based cache of recently seen ephemeral keys to prevent handshake replay attacks.
The cache should automatically evict entries older than the configured TTL (typically derived from ClockSkewTolerance). Implementations must be safe for concurrent use.
type SipHashLengthModifier ¶
type SipHashLengthModifier = pkgsiphash.LengthModifier
SipHashLengthModifier implements NTCP2's SipHash-2-4 length obfuscation for data phase frame lengths. The canonical implementation lives in handshake/siphash; this alias makes the type directly accessible from the ntcp2 package without an extra import.
func NewSipHashLengthModifier ¶
func NewSipHashLengthModifier(name string, sipKeys [2]uint64, initialIV uint64) *SipHashLengthModifier
NewSipHashLengthModifier creates a new SipHash length obfuscation modifier with shared keys for both directions.
func NewSipHashLengthModifierDirectional ¶ added in v0.1.6
func NewSipHashLengthModifierDirectional(name string, outKeys, inKeys [2]uint64, outIV, inIV uint64) *SipHashLengthModifier
NewSipHashLengthModifierDirectional creates a SipHash length obfuscation modifier with per-direction keys as required by the NTCP2 spec.