Documentation
¶
Overview ¶
Package noise pkg/dmsg/noise/dh.go c1-net-dmsg
Package noise pkg/dmsg/noise/dh_pool_native.go c1-net-dmsg
Package noise pkg/dmsg/noise/net.go c1-net-dmsg
Package noise pkg/dmsg/noise/noise.go c1-net-dmsg
Package noise pkg/dmsg/noise/nonce_window.go c1-net-dmsg
Package noise pkg/dmsg/noise/pqhybrid.go c1-net-dmsg
Self-contained post-quantum hybrid key-agreement layer (ML-KEM-768) that composes with the classical Noise_KK handshake. See docs/design/pq-hybrid-noise-handshake.md.
This file is JUST the crypto: ephemeral ML-KEM keygen / encapsulate / decapsulate, and the HKDF combiner that mixes the classical Noise session key with the ML-KEM shared secret. It deliberately does NOT touch the live handshake — wiring the payloads + capability negotiation into the handshake is a separate, flag-gated change so this layer can be reviewed and tested in isolation. Everything here is Go stdlib (crypto/mlkem + crypto/hkdf), no new dependency.
Package noise pkg/dmsg/noise/read_writer.go c1-net-dmsg
Package noise pkg/dmsg/noise/rpcdialer.go c1-net-dmsg
RPCClientDialer is split out of net.go behind //go:build !tinygo because it imports net/rpc, which transitively pulls net/http — broken on the TinyGo js target. It is a server-side redial helper (a visor exposing an RPC server to a remote), never used by a dmsg client, so excluding it from TinyGo builds costs nothing. See docs/design/tinygo-dmsg-client.md.
Index ¶
- Constants
- Variables
- func InitiatorHandshake(ns *Noise, r *bufio.Reader, w io.Writer) error
- func ReadRawFrame(r *bufio.Reader) (p []byte, err error)
- func ResponderHandshake(ns *Noise, r *bufio.Reader, w io.Writer) error
- func WriteRawFrame(w io.Writer, p []byte) ([]byte, error)
- type Addr
- type CacheStats
- type Config
- type Conn
- type Decoder
- type Listener
- type Noise
- func (ns *Noise) ChannelBinding() []byte
- func (ns *Noise) DecryptUnsafe(ciphertext []byte) ([]byte, error)
- func (ns *Noise) DecryptWithNonceMap(nm NonceMap, ciphertext []byte) ([]byte, error)
- func (ns *Noise) DecryptWithNonceWindow(nw *NonceWindow, ciphertext []byte) ([]byte, error)
- func (ns *Noise) EncryptUnsafe(plaintext []byte) []byte
- func (ns *Noise) GetDecNonce() uint64
- func (ns *Noise) GetEncNonce() uint64
- func (ns *Noise) HandshakeFinished() bool
- func (ns *Noise) LocalStatic() cipher.PubKey
- func (ns *Noise) MakeHandshakeMessage() (res []byte, err error)
- func (ns *Noise) OpenWithNonce(nonce uint64, ciphertext []byte) ([]byte, error)
- func (ns *Noise) PQActive() bool
- func (ns *Noise) ProcessHandshakeMessage(msg []byte) (err error)
- func (ns *Noise) RemoteStatic() cipher.PubKey
- func (ns *Noise) SealWithNonce(nonce uint64, plaintext []byte) []byte
- func (ns *Noise) WriterTurn() bool
- type NonceMap
- type NonceWindow
- type RPCClientDialer
- type ReadWriter
- func (rw *ReadWriter) Buffered() int
- func (rw *ReadWriter) ChannelBinding() []byte
- func (rw *ReadWriter) Decoder() (*Decoder, []byte)
- func (rw *ReadWriter) Handshake(hsTimeout time.Duration) error
- func (rw *ReadWriter) LocalStatic() cipher.PubKey
- func (rw *ReadWriter) Read(p []byte) (int, error)
- func (rw *ReadWriter) RemoteStatic() cipher.PubKey
- func (rw *ReadWriter) Unread() []byte
- func (rw *ReadWriter) Write(p []byte) (n int, err error)
- type Secp256k1
Constants ¶
const MaxWriteSize = maxPayloadSize
MaxWriteSize is the largest amount for a single write.
const NonceWindowSize = 1024
NonceWindowSize is the number of recent nonces tracked for replay prevention. Must be a multiple of 64 for the bitmap implementation.
Variables ¶
var ( // ErrAlreadyServing is returned when an operation fails due to an operation // that is currently running. ErrAlreadyServing = errors.New("already serving") // HandshakeXK is the XK handshake pattern. // legend: s(static) e(ephemeral) // <- s // ... // -> e, es // <- e, ee // -> s, se HandshakeXK = noise.HandshakeXK // HandshakeKK is the KK handshake pattern. // legend: s(static) e(ephemeral) // -> s // <- s // ... // -> e, es, ss // <- e, ee, se HandshakeKK = noise.HandshakeKK // AcceptHandshakeTimeout determines how long a noise hs should take. AcceptHandshakeTimeout = time.Second * 10 )
var ErrInvalidCipherText = errors.New("noise decrypt unsafe: ciphertext cannot be less than 8 bytes")
ErrInvalidCipherText occurs when a ciphertext is received which is too short in size.
var ErrReadDetached = errors.New("noise: reads moved to a decoder")
ErrReadDetached is returned by Read once the stream has moved to a Decoder.
Functions ¶
func InitiatorHandshake ¶
InitiatorHandshake performs a noise handshake as an initiator.
func ReadRawFrame ¶
ReadRawFrame attempts to read a raw frame from a buffered reader.
func ResponderHandshake ¶
ResponderHandshake performs a noise handshake as a responder.
Types ¶
type CacheStats ¶
CacheStats is a snapshot of the noise DH cache counters. Hits/Misses are counted on every call to (Secp256k1).DH; Evictions is incremented each time a single entry is dropped to make room (one per eviction, not per overflowed insertion).
func GetCacheStats ¶
func GetCacheStats() CacheStats
GetCacheStats returns a snapshot of the noise DH cache counters. Safe to call concurrently with DH.
type Config ¶
type Config struct {
LocalPK cipher.PubKey // Local instance static public key.
LocalSK cipher.SecKey // Local instance static secret key.
RemotePK cipher.PubKey // Remote instance static public key.
Initiator bool // Whether the local instance initiates the connection.
}
Config hold noise parameters.
type Conn ¶
Conn wraps a net.Conn and encrypts the connection with noise.
func (*Conn) ChannelBinding ¶
ChannelBinding returns the completed handshake's channel binding — see Noise.ChannelBinding. Lets a caller key a sibling channel (e.g. the faithful-UDP datagram route, #2607) off this encrypted route's session.
func (*Conn) Decoder ¶ added in v1.3.100
Decoder moves the rest of the stream to a Decoder, as ReadWriter.Decoder.
func (*Conn) RemoteAddr ¶
RemoteAddr returns the remote address of the connection.
type Decoder ¶ added in v1.3.100
type Decoder struct {
// contains filtered or unexported fields
}
Decoder decrypts noise frames from a stream pushed to it in pieces of any size, for a reader driven by arriving data instead of a goroutine in Read.
type Listener ¶
Listener accepts incoming connections and encrypts with noise.
func WrapListener ¶
func WrapListener(lis net.Listener, pk cipher.PubKey, sk cipher.SecKey, init bool, pattern noise.HandshakePattern) *Listener
WrapListener wraps a listener and encrypts incoming connections with noise.
type Noise ¶
type Noise struct {
// contains filtered or unexported fields
}
Noise handles the handshake and the frame's cryptography. All operations on Noise are not guaranteed to be thread-safe.
func KKAndSecp256k1 ¶
KKAndSecp256k1 creates a new Noise with:
- KK pattern for handshake.
- Secp256k1 for the curve.
func New ¶
func New(pattern noise.HandshakePattern, config Config) (*Noise, error)
New creates a new Noise with:
- provided pattern for handshake.
- Secp256k1 for the curve.
func XKAndSecp256k1 ¶
XKAndSecp256k1 creates a new Noise with:
- XK pattern for handshake.
- Secp256 for the curve.
func (*Noise) ChannelBinding ¶
ChannelBinding returns a value that uniquely identifies the completed handshake session — the Noise handshake hash. Both peers derive an identical value, and it is bound to the full transcript (static keys + ephemerals + any PQ hybrid payload), so it is safe to use as keying material for a sibling channel keyed off the same session. The faithful-UDP path (#2607) HKDFs this into a per-datagram AEAD master key so the datagram sibling of a route shares the reliable route's authenticated session without a second handshake.
Only meaningful once HandshakeFinished() is true; before that the binding is still over a partial transcript.
func (*Noise) DecryptUnsafe ¶
DecryptUnsafe decrypts ciphertext without interlocking, should only be used with external lock.
func (*Noise) DecryptWithNonceMap ¶
DecryptWithNonceMap is equivalent to DecryptNonce, instead it uses NonceMap to track nonces instead of a counter. Deprecated: Use DecryptWithNonceWindow instead.
func (*Noise) DecryptWithNonceWindow ¶
func (ns *Noise) DecryptWithNonceWindow(nw *NonceWindow, ciphertext []byte) ([]byte, error)
DecryptWithNonceWindow decrypts ciphertext using a sliding window for nonce tracking. Unlike DecryptWithNonceMap, memory usage is bounded to O(NonceWindowSize) regardless of how many messages are decrypted over the session's lifetime.
func (*Noise) EncryptUnsafe ¶
EncryptUnsafe encrypts plaintext without interlocking, should only be used with external lock.
func (*Noise) GetDecNonce ¶
GetDecNonce returns underlying decNonce.
func (*Noise) GetEncNonce ¶
GetEncNonce returns underlying encNonce.
func (*Noise) HandshakeFinished ¶
HandshakeFinished indicate whether handshake was completed.
func (*Noise) LocalStatic ¶
LocalStatic returns the local static public key.
func (*Noise) MakeHandshakeMessage ¶
MakeHandshakeMessage generates handshake message for a current handshake state. The handshake payload carries the post-quantum offer/response (ML-KEM public key from the initiator, ciphertext from the responder); see pqOfferPayload.
func (*Noise) OpenWithNonce ¶
OpenWithNonce decrypts a frame sealed by SealWithNonce, using the EXPLICIT nonce (the frame's stream sequence). It does NOT touch decNonce and imposes no ordering requirement, so out-of-order arrival across mux legs is fine. Replay is handled one layer up by the reorder buffer (a sequence at or below the delivered frontier is dropped). Returns an error if the handshake has not produced a transport cipher yet or the tag fails to verify.
func (*Noise) PQActive ¶
PQActive reports whether the session negotiated the post-quantum hybrid (both peers exchanged ML-KEM material and the shared secret was mixed into the transport keys). False means a classical-only handshake (e.g. an older peer).
func (*Noise) ProcessHandshakeMessage ¶
ProcessHandshakeMessage processes a received handshake message and appends the payload.
func (*Noise) RemoteStatic ¶
RemoteStatic returns the remote static public key. Returns an empty public key if the peer static key is invalid.
func (*Noise) SealWithNonce ¶
SealWithNonce encrypts plaintext under the transport-phase cipher using an EXPLICIT nonce supplied by the caller, and does NOT touch the internal encNonce counter. This is the per-frame AEAD primitive for the mux inverse- multiplexer: each mux DATA frame is sealed with its own stream sequence number as the nonce, so frames can be decrypted INDEPENDENTLY and OUT OF ORDER (no stateful cipher to desync). The caller MUST guarantee nonce uniqueness per key — the mux sequence is assigned exactly once per frame, so it is unique by construction. The 8-byte nonce is NOT prepended (unlike EncryptUnsafe): it is carried in the mux packet's own sequence field. Returns nil if the handshake has not produced a transport cipher yet.
func (*Noise) WriterTurn ¶
WriterTurn reports whether the NEXT handshake message is ours to WRITE (via MakeHandshakeMessage) rather than to read. The initiator writes the even-indexed handshake messages and reads the odd ones; the responder is the mirror. Callers use this to avoid MakeHandshakeMessage out of turn, which the underlying state machine rejects ("unexpected call to WriteMessage should be ReadMessage"). Returns false once the handshake is finished.
type NonceMap ¶
type NonceMap map[uint64]struct{}
NonceMap is a map of used nonces. Deprecated: Use NonceWindow instead for bounded memory usage.
type NonceWindow ¶
type NonceWindow struct {
// contains filtered or unexported fields
}
NonceWindow tracks nonces using a sliding window to prevent replay attacks without unbounded memory growth. It stores the highest nonce seen and a bitmap of the last NonceWindowSize nonces. Nonces older than the window are assumed to be replays.
This replaces the unbounded NonceMap which grew forever on long-lived sessions (e.g., setup-node handling thousands of streams).
func (*NonceWindow) Check ¶
func (nw *NonceWindow) Check(nonce uint64) error
Check returns nil if the nonce is valid (not a replay, not too old). If valid, the nonce is recorded in the window.
type RPCClientDialer ¶
type RPCClientDialer struct {
// contains filtered or unexported fields
}
RPCClientDialer attempts to redial to a remotely served RPCClient. It exposes an RPCServer to the remote server. The connection is encrypted via noise.
func NewRPCClientDialer ¶
func NewRPCClientDialer(addr string, pattern noise.HandshakePattern, config Config) *RPCClientDialer
NewRPCClientDialer creates a new RPCClientDialer.
func (*RPCClientDialer) Close ¶
func (d *RPCClientDialer) Close() (err error)
Close closes the handler.
type ReadWriter ¶
type ReadWriter struct {
// contains filtered or unexported fields
}
ReadWriter implements noise encrypted read writer.
func NewReadWriter ¶
func NewReadWriter(rw io.ReadWriter, ns *Noise) *ReadWriter
NewReadWriter constructs a new ReadWriter.
func (*ReadWriter) Buffered ¶
func (rw *ReadWriter) Buffered() int
Buffered returns the number of bytes that can be read from the buffer rawInput.
func (*ReadWriter) ChannelBinding ¶
func (rw *ReadWriter) ChannelBinding() []byte
ChannelBinding returns the completed handshake's channel binding — see Noise.ChannelBinding. Meaningful only after Handshake has returned.
func (*ReadWriter) Decoder ¶ added in v1.3.100
func (rw *ReadWriter) Decoder() (*Decoder, []byte)
Decoder moves the rest of the stream to a Decoder and returns it with the plaintext rw had decrypted but not yet returned. rw must not be read after.
func (*ReadWriter) Handshake ¶
func (rw *ReadWriter) Handshake(hsTimeout time.Duration) error
Handshake performs a Noise handshake using the provided io.ReadWriter.
func (*ReadWriter) LocalStatic ¶
func (rw *ReadWriter) LocalStatic() cipher.PubKey
LocalStatic returns the local static public key.
func (*ReadWriter) RemoteStatic ¶
func (rw *ReadWriter) RemoteStatic() cipher.PubKey
RemoteStatic returns the remote static public key.
func (*ReadWriter) Unread ¶
func (rw *ReadWriter) Unread() []byte
Unread returns a copy of the bytes the handshake reader has buffered past the final handshake message. In an XK handshake the initiator may start writing session frames the moment it has sent its last message, so those frames can land in the responder's read buffer together with that message. They are valid post-handshake data and must be handed to the session transport, not discarded.
type Secp256k1 ¶
type Secp256k1 struct {
// contains filtered or unexported fields
}
Secp256k1 implements `noise.DHFunc`. The zero value computes every DH. Built with the handshake's own static secret key and the peer's static public key (see New), it caches the one DH that repeats between the same two peers: static-static.