client

package
v0.0.73-rc3 Latest Latest
Warning

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

Go to latest
Published: Apr 24, 2026 License: AGPL-3.0 Imports: 51 Imported by: 0

Documentation

Overview

SPDX-FileCopyrightText: © 2023 David Stainton SPDX-License-Identifier: AGPL-3.0-only

Client2 is the new low level client for Katzenpost mix networks.

Introduction

Client2 uses a privilege separated design where many applications use a thin client library to connect to a single client daemon which multiplexes their connections to the mixnet Gateway node.

A Katzenpost mixnet client has several responsibilities at minimum:

* compose Sphinx packets * decrypt SURB replies * send and receive PQ Noise protocol messages * keep up to date with the latest PKI document

Overview

Applications will be integrated with Katzenpost using the thin client library which gives them the capability to talk with the client daemon and in that way interact with the mix network. The reason we call it a thin client library is because it does not do any mixnet related cryptography since that is already handled by the client daemon. In particular, the PKI document is stripped by the daemon before it's passed on to the thin clients. Likewise, thin clients don't decrypt SURB replies or compose Sphinx packets, instead all the PQ Noise, Sphinx and PKI related cryptography are handled by the daemon.

For more details, please see our Thin client design document: https://katzenpost.network/docs/specs/thin_client.html

SPDX-FileCopyrightText: (c) 2026 David Stainton. SPDX-License-Identifier: AGPL-3.0-only

SPDX-FileCopyrightText: (c) 2024, 2025 David Stainton. SPDX-License-Identifier: AGPL-3.0-only

SPDX-FileCopyrightText: © 2023 David Stainton SPDX-License-Identifier: AGPL-3.0-only

Index

Constants

View Source
const (
	// MessageIDLength is the length of a message ID in bytes.
	MessageIDLength = 16

	// RoundTripTimeSlop is the slop added to the expected packet
	// round trip timeout threshold.
	RoundTripTimeSlop = (20 * time.Second)
)
View Source
const (
	// ChannelIDField is the CBOR field name for channel ID.
	ChannelIDField = "channel_id"
	// PayloadField is the CBOR field name for payload.
	PayloadField = "payload"
	// MessageIDField is the CBOR field name for message ID.
	MessageIDField = "message_id"
	// ErrField is the CBOR field name for error (omitempty).
	ErrField = "err,omitempty"
)

CBOR field constants

View Source
const (
	// AppIDLength is the length of the application ID in bytes.
	AppIDLength = 16
)
View Source
const CopyPollInterval = 5 * time.Second

CopyPollInterval is the delay between courier polls while a Copy command is still InProgress. Small enough that a small Copy finishes a round-trip or two early, large enough that a long Copy doesn't hammer the courier.

View Source
const (
	// EchoService is the standardized service string for the echo service.
	EchoService = "echo"
)

Service constants

View Source
const (
	// HaltingState indicates the client is halting.
	HaltingState = "halting"
)

State constants

Variables

View Source
var (
	// ErrNotConnected is the error returned when an operation fails due to the
	// client not currently being connected to the Gateway.
	ErrNotConnected = errors.New("client/conn: not connected to the Gateway")

	// ErrShutdown is the error returned when the connection is closed due to
	// a call to Shutdown().
	ErrShutdown = errors.New("shutdown requested")
)
View Source
var (
	PublishDeadline = vServer.PublishConsensusDeadline

	// WarpedEpoch is a build time flag that accelerates the recheckInterval
	WarpedEpoch = "false"
)

Functions

func GetRandomCourier added in v0.0.73

func GetRandomCourier(doc *cpki.Document) (*[hash.HashSize]byte, []byte, error)

func IntoThinResponse added in v0.0.73

func IntoThinResponse(r *Response) *thin.Response

func NewListener added in v0.0.73

func NewListener(client *Client, rates *Rates, egressCh chan *Request, logBackend *log.Backend, onAppDisconnectFn func(*[AppIDLength]byte)) (*listener, error)

New creates a new listener.

Types

type ARQAction added in v0.0.73

type ARQAction int

ARQAction represents the action to take after an ARQ state transition.

const (
	// ARQActionSendNewSURB means send a new SURB to continue the protocol.
	ARQActionSendNewSURB ARQAction = iota
	// ARQActionComplete means the operation completed successfully.
	ARQActionComplete
	// ARQActionHandlePayload means the payload reply should be processed.
	ARQActionHandlePayload
	// ARQActionError means the reply contained an error code.
	ARQActionError
	// ARQActionIgnore means the reply should be ignored (terminal state).
	ARQActionIgnore
)

type ARQMessage added in v0.0.73

type ARQMessage struct {
	// MessageType distinguishes between envelope operations and copy commands.
	// This determines how the reply is processed.
	MessageType ARQMessageType

	// AppID identifies the application sending/receiving the message/reply.
	AppID *[AppIDLength]byte

	// QueryID is used for correlating replies with the original request.
	QueryID *[thin.QueryIDLength]byte

	// EnvelopeHash is the persistent identifier for this message.
	// Used to cancel resending via CancelResendingEncryptedMessage.
	// For copy commands, this is a hash of the WriteCap.
	EnvelopeHash *[32]byte

	// DestinationIdHash is 32 byte hash of the destination Courier's identity public key.
	DestinationIdHash *[32]byte

	// RecipientQueueID is the Courier queue identity.
	RecipientQueueID []byte

	// Payload is the MessageCiphertext (CourierQuery bytes) to send.
	Payload []byte

	// SURBID is the current SURB identifier.
	SURBID *[sConstants.SURBIDLength]byte

	// SURBDecryptionKeys is the SURB decryption keys for the current send.
	SURBDecryptionKeys []byte

	// Retransmissions counts the number of times this has been resent (for logging).
	Retransmissions uint32

	// SentAt contains the time the message was last sent.
	SentAt time.Time

	// ReplyETA is the expected round trip time to receive a response.
	ReplyETA time.Duration

	// EnvelopeDescriptor contains the key material to decrypt replies.
	EnvelopeDescriptor []byte

	// IsRead indicates whether this is a read operation (true) or write (false).
	IsRead bool

	// State tracks the current state in the stop-and-wait ARQ protocol.
	State ARQState

	// ReadCap is the read capability for BACAP decryption (only for read operations).
	ReadCap *bacap.ReadCap

	// MessageBoxIndex is the current message box index being operated on (only for read operations).
	MessageBoxIndex []byte

	// NoRetryOnBoxIDNotFound disables automatic retries on BoxIDNotFound for read operations.
	// When true, BoxIDNotFound is returned immediately. When false (default), reads retry
	// up to 10 times to handle replication lag.
	NoRetryOnBoxIDNotFound bool

	// NoIdempotentBoxAlreadyExists disables treating BoxAlreadyExists as idempotent success
	// for write operations. When true, BoxAlreadyExists is returned as an error.
	// When false (default), BoxAlreadyExists is treated as success (the write already happened).
	NoIdempotentBoxAlreadyExists bool
}

ARQMessage is used by ARQ for automatic retransmission of Pigeonhole messages. It retries forever until cancelled via CancelResendingEncryptedMessage or successful.

type ARQMessageType added in v0.0.73

type ARQMessageType uint8

ARQMessageType distinguishes between different types of ARQ messages.

const (
	// ARQMessageTypeEnvelope is for encrypted read/write envelope operations.
	ARQMessageTypeEnvelope ARQMessageType = 0

	// ARQMessageTypeCopyCommand is for copy command operations.
	ARQMessageTypeCopyCommand ARQMessageType = 1
)

type ARQState added in v0.0.73

type ARQState uint8

ARQState represents the state of an ARQ message in the stop-and-wait protocol.

const (
	// ARQStateWaitingForACK is the initial state when a query is sent.
	// The client is waiting for an ACK from the courier.
	ARQStateWaitingForACK ARQState = 0

	// ARQStateACKReceived indicates that an ACK has been received from the courier.
	// For read queries, the client now needs to send another SURB to receive the payload.
	// For write queries, this is the terminal state.
	ARQStateACKReceived ARQState = 1

	// ARQStatePayloadReceived indicates that the payload has been received.
	// This is the terminal state for read queries.
	ARQStatePayloadReceived ARQState = 2
)

type ARQTransitionResult added in v0.0.73

type ARQTransitionResult struct {
	NewState     ARQState
	Action       ARQAction
	ShouldRemove bool
	ErrorCode    uint8
}

ARQTransitionResult is the output of the ARQ state machine transition.

type CachedDoc added in v0.0.73

type CachedDoc struct {
	Doc  *cpki.Document
	Blob []byte
}

type Client

type Client struct {
	worker.Worker
	sync.RWMutex

	PKIClient cpki.Client

	// DialContextFn is the optional alternative Dialer.DialContext function
	// to be used when creating outgoing network connections.
	DialContextFn func(ctx context.Context, network, address string) (net.Conn, error)
	// contains filtered or unexported fields
}

Client manages startup, shutdow, creating new connections and reconnecting.

func New

func New(cfg *config.Config, logBackend *log.Backend) (*Client, error)

New creates a new Client with the provided configuration.

func (*Client) ClockSkew added in v0.0.73

func (c *Client) ClockSkew() time.Duration

ClockSkew returns the current best guess difference between the client's system clock and the network's global clock, rounded to the nearest second, as measured against the provider during the handshake process. Calls to this routine should not be made until the first `ClientConfig.OnConnFn(true)` callback.

func (*Client) ComposeSphinxPacket added in v0.0.73

func (c *Client) ComposeSphinxPacket(request *Request) (pkt []byte, surbkey []byte, rtt time.Duration, err error)

ComposeSphinxPacket is used to compose Sphinx packets.

func (*Client) ComposeSphinxPacketForQuery added in v0.0.73

func (c *Client) ComposeSphinxPacketForQuery(request *thin.SendChannelQuery, surbID *[sConstants.SURBIDLength]byte) (pkt []byte, surbkey []byte, rtt time.Duration, err error)

ComposeSphinxPacketForQuery is used to compose Sphinx packets for channel queries.

func (*Client) CurrentDocument added in v0.0.73

func (c *Client) CurrentDocument() ([]byte, *cpki.Document)

CurrentDocument returns the current pki.Document, or nil iff one does not exist. The caller MUST NOT modify the returned object in any way.

func (*Client) ForceFetch added in v0.0.73

func (c *Client) ForceFetch()

ForceFetch attempts to force an otherwise idle client to attempt to fetch the contents of the user's spool. This call has no effect if a connection is not established or if the connection is already in the middle of a fetch cycle, and should be considered a best effort operation.

func (*Client) ForceFetchPKI added in v0.0.73

func (c *Client) ForceFetchPKI()

ForceFetchPKI attempts to force client's pkiclient to wake and fetch consensus documents immediately.

func (*Client) GetDocumentByEpoch added in v0.0.73

func (c *Client) GetDocumentByEpoch(epoch uint64) *cpki.Document

func (*Client) GetPollInterval added in v0.0.73

func (c *Client) GetPollInterval() time.Duration

XXX This will go away once we get rid of polling.

func (*Client) SendChannelQuery added in v0.0.73

func (c *Client) SendChannelQuery(sendQuery *thin.SendChannelQuery, surbID *[sConstants.SURBIDLength]byte) (surbKey []byte, rtt time.Duration, err error)

SendChannelQuery

func (*Client) SendCiphertext added in v0.0.73

func (c *Client) SendCiphertext(request *Request) ([]byte, time.Duration, error)

SendCiphertext sends the ciphertext b to the recipient/provider, with a SURB identified by surbID, and returns the SURB decryption key and total round trip delay. Blocks until packet is sent on the wire.

func (*Client) SendPacket added in v0.0.73

func (c *Client) SendPacket(pkt []byte) error

func (*Client) SetPollInterval added in v0.0.73

func (c *Client) SetPollInterval(interval time.Duration)

XXX This will go away once we get rid of polling.

func (*Client) Shutdown

func (c *Client) Shutdown()

Shutdown cleanly shuts down a given Client instance.

func (*Client) Start added in v0.0.73

func (c *Client) Start() error

func (*Client) WaitForCurrentDocument added in v0.0.73

func (c *Client) WaitForCurrentDocument()

type ConnectError added in v0.0.73

type ConnectError struct {
	// Err is the original error that caused the connect attempt to fail.
	Err error
}

ConnectError is the error used to indicate that a connect attempt has failed.

func (*ConnectError) Error added in v0.0.73

func (e *ConnectError) Error() string

Error implements the error interface.

type ConsensusGetter added in v0.0.73

type ConsensusGetter interface {
	GetConsensus(ctx context.Context, epoch uint64) (*commands.Consensus2, error)
}

type Daemon added in v0.0.73

type Daemon struct {
	worker.Worker
	// contains filtered or unexported fields
}

func NewDaemon added in v0.0.73

func NewDaemon(cfg *config.Config) (*Daemon, error)

func (*Daemon) Shutdown added in v0.0.73

func (d *Daemon) Shutdown()

Shutdown cleanly shuts down a given Server instance.

func (*Daemon) Start added in v0.0.73

func (d *Daemon) Start() error

type DisconnectedSession added in v0.0.73

type DisconnectedSession struct {
	AppID         *[AppIDLength]byte
	Token         [16]byte
	DisconnectAt  time.Time
	CleanupTimer  *time.Timer
	QueuedReplies []*Response
}

type EnvelopeDescriptor added in v0.0.73

type EnvelopeDescriptor struct {
	// Epoch is the Katzenpost epoch in which the ReplyIndex is valid.
	Epoch uint64

	// ReplicaNums are the replica numbers used for this envelope.
	ReplicaNums [2]uint8

	// EnvelopeKey is the Private NIKE Key used with our MKEM scheme.
	EnvelopeKey []byte
}

EnvelopeDescriptor supplies us with everthing we need to decrypt an encrypted envelope reply from a storage replica via the courier. The assumption is that we have access to the PKI document for the Epoch in which the envelope was sent.

func EnvelopeDescriptorFromBytes added in v0.0.73

func EnvelopeDescriptorFromBytes(blob []byte) (*EnvelopeDescriptor, error)

EnvelopeDescriptorFromBytes uses CBOR to deserialize the EnvelopeDescriptor.

func (*EnvelopeDescriptor) Bytes added in v0.0.73

func (e *EnvelopeDescriptor) Bytes() ([]byte, error)

Bytes uses CBOR to serialize the EnvelopeDescriptor.

type PKIError added in v0.0.73

type PKIError struct {
	// Err is the original PKI error.
	Err error
}

PKIError is the error used to indicate PKI related failures.

func (*PKIError) Error added in v0.0.73

func (e *PKIError) Error() string

Error implements the error interface.

type ProtocolError added in v0.0.73

type ProtocolError struct {
	// Err is the original error that triggered connection termination.
	Err error
}

ProtocolError is the error used to indicate that the connection was closed due to wire protocol related reasons.

func (*ProtocolError) Error added in v0.0.73

func (e *ProtocolError) Error() string

Error implements the error interface.

type Rates added in v0.0.73

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

type Request added in v0.0.73

type Request struct {
	// AppID must be a unique identity for the client application
	// that is sending this Request.
	AppID *[AppIDLength]byte

	NewKeypair *thin.NewKeypair

	EncryptRead *thin.EncryptRead

	EncryptWrite *thin.EncryptWrite

	StartResendingEncryptedMessage *thin.StartResendingEncryptedMessage

	CancelResendingEncryptedMessage *thin.CancelResendingEncryptedMessage

	StartResendingCopyCommand *thin.StartResendingCopyCommand

	CancelResendingCopyCommand *thin.CancelResendingCopyCommand

	NextMessageBoxIndex *thin.NextMessageBoxIndex

	GetMessageBoxIndexCounter *thin.GetMessageBoxIndexCounter

	CreateCourierEnvelopesFromPayload *thin.CreateCourierEnvelopesFromPayload

	CreateCourierEnvelopesFromPayloads *thin.CreateCourierEnvelopesFromPayloads

	CreateCourierEnvelopesFromTombstoneRange *thin.CreateCourierEnvelopesFromTombstoneRange

	SessionToken *thin.SessionToken

	ThinClose *thin.ThinClose

	SendLoopDecoy *SendLoopDecoy

	SendMessage *thin.SendMessage

	// ResendARQ carries a SURB ID that the ARQ timer wants to retransmit.
	// Emitted by the listener's scheduler (never from a thin client), so
	// resends travel through the same fair, Poisson-gated path as fresh
	// sends. egressWorker routes it to arqDoResend.
	ResendARQ *[sphinxConstants.SURBIDLength]byte
}

func FromThinRequest added in v0.0.73

func FromThinRequest(r *thin.Request, appid *[AppIDLength]byte) *Request

type Response added in v0.0.73

type Response struct {
	// AppID must be a unique identity for the client application
	// that is receiving this Response.
	AppID *[AppIDLength]byte

	SessionTokenReply *thin.SessionTokenReply

	ShutdownEvent *thin.ShutdownEvent

	ConnectionStatusEvent *thin.ConnectionStatusEvent

	NewPKIDocumentEvent *thin.NewPKIDocumentEvent

	MessageSentEvent *thin.MessageSentEvent

	MessageReplyEvent *thin.MessageReplyEvent

	MessageIDGarbageCollected *thin.MessageIDGarbageCollected

	NewKeypairReply *thin.NewKeypairReply

	EncryptReadReply *thin.EncryptReadReply

	EncryptWriteReply *thin.EncryptWriteReply

	StartResendingEncryptedMessageReply *thin.StartResendingEncryptedMessageReply

	CancelResendingEncryptedMessageReply *thin.CancelResendingEncryptedMessageReply

	StartResendingCopyCommandReply *thin.StartResendingCopyCommandReply

	CancelResendingCopyCommandReply *thin.CancelResendingCopyCommandReply

	NextMessageBoxIndexReply *thin.NextMessageBoxIndexReply

	GetMessageBoxIndexCounterReply *thin.GetMessageBoxIndexCounterReply

	CreateCourierEnvelopesFromPayloadReply *thin.CreateCourierEnvelopesFromPayloadReply

	CreateCourierEnvelopesFromPayloadsReply *thin.CreateCourierEnvelopesFromPayloadsReply

	CreateCourierEnvelopesFromTombstoneRangeReply *thin.CreateCourierEnvelopesFromTombstoneRangeReply
}

type SendLoopDecoy added in v0.0.73

type SendLoopDecoy struct {
}

type TimerQueue

type TimerQueue struct {
	worker.Worker
	// contains filtered or unexported fields
}

func NewTimerQueue

func NewTimerQueue(action func(interface{})) *TimerQueue

func (*TimerQueue) Cancel added in v0.0.73

func (t *TimerQueue) Cancel(value interface{}) bool

Cancel removes the first queued entry whose Value is equal to the supplied value (Go == comparison, which for pointer values is pointer identity), and returns true if an entry was removed. Entries already popped by the worker are not cancellable; callers that need to defend against the popped-but-action-not-yet-run race must handle that at the action callback.

func (*TimerQueue) Len added in v0.0.73

func (t *TimerQueue) Len() int

func (*TimerQueue) Peek added in v0.0.73

func (t *TimerQueue) Peek() *queue.Entry

func (*TimerQueue) Pop added in v0.0.73

func (t *TimerQueue) Pop() interface{}

func (*TimerQueue) Push

func (t *TimerQueue) Push(priority uint64, value interface{})

func (*TimerQueue) Start added in v0.0.35

func (t *TimerQueue) Start()

Directories

Path Synopsis
Package config implements the configuration for the Katzenpost client.
Package config implements the configuration for the Katzenpost client.
Package proxy implements the support for an upstream (outgoing) proxy.
Package proxy implements the support for an upstream (outgoing) proxy.
Package thin provides a lightweight client API for the Katzenpost mixnet.
Package thin provides a lightweight client API for the Katzenpost mixnet.
transport
Package transport defines the thin-client-side transport abstraction used by the Go thin-client to dial the kpclientd daemon.
Package transport defines the thin-client-side transport abstraction used by the Go thin-client to dial the kpclientd daemon.
Package transport defines the daemon-side transport abstraction used by kpclientd to accept thin-client connections.
Package transport defines the daemon-side transport abstraction used by kpclientd to accept thin-client connections.

Jump to

Keyboard shortcuts

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