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 ¶
**Please see the ThinClient section of this documentation** to learn about Katzenpost client integration. The vast majority of applications will use the ThinClient type and not any other part of this library.
Client2 uses a privilege separated design where many applications use a thin client library to connect to the client2 daemon which multiplexes their connections to the mixnet entry node.
A Katzenpost mixnet client has several responsibilities at minimum:
* compose Sphinx packets * decrypt SURB replies * send and receive Noise protocol messages * keep up to date with the latest PKI document
Overview ¶
Client2 is essentially a long running daemon process that listens on an abstract unix domain socket for incoming thin client library connections. Many client applications can use the same client2 daemon. Those connections are multiplexed into the daemon's single connection to the mix network.
Therefore applications will be integrated with Katzenpost using the thin client library which gives them the capability to talk with the client2 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 client2 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 that Noise, Sphinx and PKI related cryptography is handled by the daemon.
Thin client and daemon protocol ¶
Note that the thin client daemon protocol listens on an abstract unix domain of type `SOCK_SEQPACKET` which is defined as:
**SOCK_SEQPACKET** (since Linux 2.6.4), is a connection-oriented socket that preserves message boundaries and delivers messages in the order that they were sent.
In golang this is referred to by the "unixpacket" network string.
## Client socket naming convention
Thin clients MUST randomize their abstract unix domain socket name otherwise the static name will prevent multiplexing because the kernel requires that the connection be between uniquely nameed socket pairs. The Katzenpost reference implementation of the thin client library selects a socket name with four random hex digits appended to the end of the name like so:
@katzenpost_golang_thin_client_DEADBEEF
## Daemon socket naming convention
The client2 daemon listens on an abstract unix domain socket with the following name:
@katzenpost
## Protocol description
Upon connecting to the daemon socket the client must wait for two messages. The first message received must contain a connection status and the second message must contain a new PKI document event. This marks the end of the initial connection sequence. Note that this PKI document is stripped of all cryptographic signatures.
In the next protocol phase, the client may send `Request` messages to the daemon in order to cause the daemon to encapsulate the given payload in a Sphinx packet and send it to the entry node. Likewise the daemon my send the client `Response` messages at any time during this protocol phase. These `Response` messages may indicated a connection status change, a new PKI document or a message sent or reply event.
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
- Variables
- func CreateChannelReadRequest(channelID [thin.ChannelIDLength]byte, statefulReader *bacap.StatefulReader, ...) (*pigeonhole.CourierEnvelope, nike.PrivateKey, error)
- func CreateChannelReadRequestWithBoxID(channelID [thin.ChannelIDLength]byte, boxID *[bacap.BoxIDSize]byte, ...) (*pigeonhole.CourierEnvelope, nike.PrivateKey, error)
- func CreateChannelWriteRequest(statefulWriter *bacap.StatefulWriter, payload []byte, doc *cpki.Document, ...) (*pigeonhole.CourierEnvelope, nike.PrivateKey, error)
- func CreateChannelWriteRequestPrepareOnly(statefulWriter *bacap.StatefulWriter, payload []byte, doc *cpki.Document, ...) (*pigeonhole.CourierEnvelope, nike.PrivateKey, error)
- func GetRandomCourier(doc *cpki.Document) (*[hash.HashSize]byte, []byte)
- func IntoThinResponse(r *Response) *thin.Response
- func NewListener(client *Client, rates *Rates, egressCh chan *Request, logBackend *log.Backend, ...) (*listener, error)
- func NewPigeonholeChannel() (*bacap.StatefulWriter, *bacap.ReadCap, *bacap.WriteCap)
- type ARQMessage
- type CachedDoc
- type ChannelDescriptor
- type Client
- func (c *Client) ClockSkew() time.Duration
- func (c *Client) ComposeSphinxPacket(request *Request) (pkt []byte, surbkey []byte, rtt time.Duration, err error)
- func (c *Client) CurrentDocument() ([]byte, *cpki.Document)
- func (c *Client) ForceFetch()
- func (c *Client) ForceFetchPKI()
- func (c *Client) GetDocumentByEpoch(epoch uint64) *cpki.Document
- func (c *Client) GetPollInterval() time.Duration
- func (c *Client) SendCiphertext(request *Request) ([]byte, time.Duration, error)
- func (c *Client) SendPacket(pkt []byte) error
- func (c *Client) SetPollInterval(interval time.Duration)
- func (c *Client) Shutdown()
- func (c *Client) Start() error
- func (c *Client) WaitForCurrentDocument()
- type ConnectError
- type ConsensusGetter
- type Daemon
- type EnvelopeDescriptor
- type ExpDist
- type NewReplyHandlerParams
- type PKIError
- type ProtocolError
- type Rates
- type Request
- type Response
- type StoredEnvelopeData
- type TimerQueue
Constants ¶
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 = (3 * time.Minute) + (45 * time.Second) MaxRetransmissions = 3 )
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
const (
// AppIDLength is the length of the application ID in bytes.
AppIDLength = 16
)
const (
// EchoService is the standardized service string for the echo service.
EchoService = "echo"
)
Service constants
const (
// HaltingState indicates the client is halting.
HaltingState = "halting"
)
State constants
Variables ¶
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") )
var ( PublishDeadline = vServer.PublishConsensusDeadline // WarpedEpoch is a build time flag that accelerates the recheckInterval WarpedEpoch = "false" )
Functions ¶
func CreateChannelReadRequest ¶ added in v0.0.50
func CreateChannelReadRequest(channelID [thin.ChannelIDLength]byte, statefulReader *bacap.StatefulReader, doc *cpki.Document) (*pigeonhole.CourierEnvelope, nike.PrivateKey, error)
func CreateChannelReadRequestWithBoxID ¶ added in v0.0.50
func CreateChannelReadRequestWithBoxID(channelID [thin.ChannelIDLength]byte, boxID *[bacap.BoxIDSize]byte, doc *cpki.Document) (*pigeonhole.CourierEnvelope, nike.PrivateKey, error)
func CreateChannelWriteRequest ¶ added in v0.0.50
func CreateChannelWriteRequest( statefulWriter *bacap.StatefulWriter, payload []byte, doc *cpki.Document, geometry *pigeonholeGeo.Geometry) (*pigeonhole.CourierEnvelope, nike.PrivateKey, error)
func CreateChannelWriteRequestPrepareOnly ¶ added in v0.0.50
func CreateChannelWriteRequestPrepareOnly( statefulWriter *bacap.StatefulWriter, payload []byte, doc *cpki.Document, geometry *pigeonholeGeo.Geometry) (*pigeonhole.CourierEnvelope, nike.PrivateKey, error)
CreateChannelWriteRequestPrepareOnly prepares a write request WITHOUT advancing StatefulWriter state. This allows for deferred state advancement until courier acknowledgment.
func GetRandomCourier ¶ added in v0.0.50
func IntoThinResponse ¶
func NewListener ¶
func NewListener(client *Client, rates *Rates, egressCh chan *Request, logBackend *log.Backend, onAppDisconnectFn func(*[AppIDLength]byte)) (*listener, error)
New creates a new listener.
func NewPigeonholeChannel ¶ added in v0.0.50
Types ¶
type ARQMessage ¶
type ARQMessage struct {
// AppID identifies the application sending/receiving the message/reply.
AppID *[AppIDLength]byte
// MessageID is the unique message identifier
MessageID *[MessageIDLength]byte
// DestinationIdHash is 32 byte hash of the destination Provider's
// identity public key.
DestinationIdHash *[32]byte
// RecipientQueueID is the queue identity which will receive the message.
RecipientQueueID []byte
// Payload is the message payload
Payload []byte
// SURBID is the SURB identifier.
SURBID *[sConstants.SURBIDLength]byte
// SURBDecryptionKeys is the SURB decryption keys
SURBDecryptionKeys []byte
// Retransmissions counts the number of times the message has been retransmitted.
Retransmissions uint32
// SentAt contains the time the message was sent.
SentAt time.Time
// ReplyETA is the expected round trip time to receive a response.
ReplyETA time.Duration
}
ARQMessage is used by ARQ.
type ChannelDescriptor ¶ added in v0.0.50
type ChannelDescriptor struct {
// AppID tracks which thin client owns this channel for cleanup purposes
AppID *[AppIDLength]byte
StatefulWriter *bacap.StatefulWriter
StatefulWriterLock sync.Mutex
StatefulReader *bacap.StatefulReader
StatefulReaderLock sync.Mutex
EnvelopeDescriptors map[[hash.HashSize]byte]*EnvelopeDescriptor
EnvelopeDescriptorsLock sync.RWMutex
StoredEnvelopes map[[thin.MessageIDLength]byte]*StoredEnvelopeData
StoredEnvelopesLock sync.RWMutex
}
ChannelDescriptor describes a pigeonhole channel and supplies us with everthing we need to read or write to the channel.
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 (*Client) ClockSkew ¶
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 ¶
func (c *Client) ComposeSphinxPacket(request *Request) (pkt []byte, surbkey []byte, rtt time.Duration, err error)
ComposeSphinxPacket is used to compose Sphinx packets.
func (*Client) CurrentDocument ¶
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 ¶
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 ¶
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.50
func (*Client) GetPollInterval ¶
XXX This will go away once we get rid of polling.
func (*Client) SendCiphertext ¶
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 ¶
func (*Client) SetPollInterval ¶
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) WaitForCurrentDocument ¶
func (c *Client) WaitForCurrentDocument()
type ConnectError ¶
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 ¶
func (e *ConnectError) Error() string
Error implements the error interface.
type ConsensusGetter ¶
type Daemon ¶
type EnvelopeDescriptor ¶ added in v0.0.50
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
// EnvelopeKeys is tyhe 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.
type ExpDist ¶
ExpDist provides a pseudorandom ticker with an average and maximum delay The channel returned by OutCh() is written at an average rate specified with UpdateRate(average, max uint64), in units of milliseconds.
func NewExpDist ¶
func NewExpDist() *ExpDist
NewExpDist returns an ExpDist with running worker routine.
func (*ExpDist) OutCh ¶
func (e *ExpDist) OutCh() <-chan struct{}
OutCh returns channel that receives at the rate specified by UpdateRate
func (*ExpDist) UpdateConnectionStatus ¶
UpdateConnectionStatus(true) starts sending to OutCh and UpdateConnectionStatus(false) stops sending to OutCh
func (*ExpDist) UpdateRate ¶
UpdateRate is a value in milliseconds and specifies the average and maximum delay between writes to the channel returned by OutCh.
type NewReplyHandlerParams ¶ added in v0.0.50
type NewReplyHandlerParams struct {
AppID *[AppIDLength]byte
MessageID *[MessageIDLength]byte
ChannelID uint16
ChannelDesc *ChannelDescriptor
EnvHash *[hash.HashSize]byte
IsReader bool
IsWriter bool
Conn *incomingConn
ReplyIndex uint8
}
NewReplyHandlerParams groups parameters for reply handler functions
type PKIError ¶
type PKIError struct {
// Err is the original PKI error.
Err error
}
PKIError is the error used to indicate PKI related failures.
type ProtocolError ¶
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 ¶
func (e *ProtocolError) Error() string
Error implements the error interface.
type Request ¶
type Request struct {
CreateWriteChannel *thin.CreateWriteChannel
CreateReadChannel *thin.CreateReadChannel
WriteChannel *thin.WriteChannel
ReadChannel *thin.ReadChannel
CloseChannel *thin.CloseChannel
SendMessage *thin.SendMessage
SendARQMessage *thin.SendARQMessage
SendLoopDecoy *thin.SendLoopDecoy
SendDropDecoy *thin.SendDropDecoy
ThinClose *thin.ThinClose
// AppID must be a unique identity for the client application
// that is sending this Request.
AppID *[AppIDLength]byte
}
func FromThinRequest ¶
func FromThinRequest(r *thin.Request, appid *[AppIDLength]byte) *Request
type Response ¶
type Response struct {
// AppID must be a unique identity for the client application
// that is receiving this Response.
AppID *[AppIDLength]byte
ShutdownEvent *thin.ShutdownEvent
ConnectionStatusEvent *thin.ConnectionStatusEvent
NewPKIDocumentEvent *thin.NewPKIDocumentEvent
MessageSentEvent *thin.MessageSentEvent
MessageReplyEvent *thin.MessageReplyEvent
MessageIDGarbageCollected *thin.MessageIDGarbageCollected
CreateWriteChannelReply *thin.CreateWriteChannelReply
CreateReadChannelReply *thin.CreateReadChannelReply
WriteChannelReply *thin.WriteChannelReply
ReadChannelReply *thin.ReadChannelReply
}
type StoredEnvelopeData ¶ added in v0.0.50
type StoredEnvelopeData struct {
Envelope *pigeonhole.CourierEnvelope
BoxID *[bacap.BoxIDSize]byte
}
StoredEnvelopeData contains the envelope and associated box ID for reuse
type TimerQueue ¶
func NewTimerQueue ¶
func NewTimerQueue(action func(interface{})) *TimerQueue
func (*TimerQueue) Len ¶
func (t *TimerQueue) Len() int
func (*TimerQueue) Peek ¶
func (t *TimerQueue) Peek() *queue.Entry
func (*TimerQueue) Pop ¶
func (t *TimerQueue) Pop() interface{}
func (*TimerQueue) Push ¶
func (t *TimerQueue) Push(priority uint64, value interface{})
func (*TimerQueue) Start ¶
func (t *TimerQueue) Start()
Source Files
¶
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. |