catshadow

package
v0.0.31 Latest Latest
Warning

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

Go to latest
Published: Mar 21, 2024 License: AGPL-3.0, AGPL-3.0 Imports: 36 Imported by: 1

README

.. image:: https://travis-ci.org/katzenpost/catshadow.svg?branch=master
  :target: https://travis-ci.org/katzenpost/catshadow

.. image:: https://godoc.org/github.com/katzenpost/katzenpost/catshadow?status.svg
  :target: https://godoc.org/github.com/katzenpost/katzenpost/catshadow


the catshadow client
====================

Catshadow is a mix network messaging system. This repository contains
a client library which can be used with a Katzenpost mix network. It
not only uses strong modern end to end encryption (Noise + Double
Ratchet), but it is also designed to reduce the amount of metadata
leaked onto the network.

This code is actively being developed and is intended
to be used with our Qt user interface, catchat:

* https://github.com/katzenpost/catchat


contact
=======

* IRC: irc.oftc.net #katzenpost <irc://irc.oftc.net/#katzenpost>
* Mailing List <https://lists.mixnetworks.org/listinfo/katzenpost>


disclaimer
==========

Katzenpost is still pre-alpha.  DO NOT DEPEND ON IT FOR STRONG SECURITY OR ANONYMITY.


testing
=======

optional docker tests
---------------------

To run the optional docker tests firstly, see our docker repo
and start your local dockerized mix network:

https://github.com/katzenpost/katzenpost/docker

A couple of minutes after startup run the tests like this:
::

   GORACE=history_size=7 go test -tags=docker_test -race -v -run Docker

This will run our docker based integration tests for the catshadow library.


design
======

It is my understanding that in terms of the analysis presented in this
blog post ( Brian Warner's **"Petmail mailbox-server delivery protocol"**
http://www.lothar.com/blog/53-petmail-delivery/ ),
the catshadow messaging system can be described as:

**S1, M0, R1, Rev0**

Here I rephrase the definitions of the above messaging system
properties: Catshadow clients can compare delivery tokens to determine
if they share contacts. However the message spool server cannot tell
which message came from which sender, not even that two messages came
from the same sender, nor can it determine how many senders might be
configured for each recipient. The recipient cannot use the transport
information to identify the sender. The recipient depends upon
information not visible to the mailbox server to identify the sender,
which means a legitimate (but annoying) sender could flood the server
without revealing which sender they are. Finally, the revocation
behavior is such that the recipient can revoke one or more senders
without involving the remaining senders.

Clients make use of a Sphinx SURB based protocol to retrieve messages
from their remote spool service. The mix network has several providers
which operate spool services which clients can interact with. The
spool service is in fact a seperate process which uses our CBOR/HTTP
over unix domain socket plugin system to communicate with the mix server.

Over time I plan on replacing the spool services with gradually more
sophisticated spool services until I finally have a replicating CRDT
based spool service which can help eliminate single points of failure
in this messaging system.

Clients make use of the PANDA protocol for exchanging spool identities
and the Signal Double Ratchet keys. That is, this messaging system creates
bidirectional metadata leakage resistant communications channels which
are composed with two unidirection channels. Each unidirectional channel
contains the required information to write to a correspondant's
remote message spool.

Katzenpost is a variant of the Loopix design and as such makes use of
the Poisson mix strategy and therefore must be properly tuned. Tuning
of the Poisson mix strategy has not been publicly solved yet but I
suspect the solution has something to do with a discrete network event
simulator and possibly some machine learning algorithms as
well. Perhaps we all should consider the tuning of this mixnet
messaging system as half of its design.

Another unfinished design area is: The Catshadow client periodically
polls the client's remote message spool where the intervals between
polling are the result of a Poisson process. Currently, tuning this
Poisson procress is left unfinished, however, I can state that the
goal in tuning this would be to reduce vulnerability to a long term
statistical disclosure attack where the passive adversary or
compromised Provider tries to link clients with their spool
service.


**"The Loopix Anonymity System"**:

https://www.usenix.org/system/files/conference/usenixsecurity17/sec17-piotrowska.pdf


the longer design overview
--------------------------

The design of this messaging is not yet fully specified but is
partially specified in these specification documents:

* https://github.com/katzenpost/katzenpost/blob/main/docs/drafts/client.md
* https://github.com/katzenpost/katzenpost/blob/main/docs/drafts/deaddrop.md
* https://github.com/katzenpost/katzenpost/blob/main/docs/drafts/decoy_traffic.md
* https://github.com/katzenpost/katzenpost/blob/main/docs/drafts/panda.md

Whereas all those specifications assume the existence of the core
Katzenpost specifications here which mostly covers the design of
the server infrastructure:

* https://github.com/katzenpost/katzenpost/blob/main/docs/specs/mixnet.md
* https://github.com/katzenpost/katzenpost/blob/main/docs/specs/wire-protocol.md
* https://github.com/katzenpost/katzenpost/blob/main/docs/specs/kaetzchen.md
* https://github.com/katzenpost/katzenpost/blob/main/docs/specs/sphinx_replay_detection.md
* https://github.com/katzenpost/katzenpost/blob/main/docs/specs/sphinx.md
* https://github.com/katzenpost/katzenpost/blob/main/docs/specs/oldspecs/end_to_end.md
* https://github.com/katzenpost/katzenpost/blob/main/docs/specs/pki.md
* https://github.com/katzenpost/katzenpost/blob/main/docs/specs/certificate.md

There is an older copy of our core Katzenpost specifications rendered
in Latex if you prefer to read it that way:
https://panoramix-project.eu/wp-content/uploads/2019/03/D7.2.pdf


code organization
=================

This repository contains a small amount of high level client
code. This client depends on lots of code in other Katzenpost
repositories including my fork of agl's PANDA and agl's Signal Double
Ratchet:

* https://github.com/katzenpost/katzenpost/doubleratchet
* https://github.com/katzenpost/katzenpost/panda
* https://github.com/katzenpost/channels
* https://github.com/katzenpost/katzenpost/memspool
* https://github.com/katzenpost/katzenpost/client
* https://github.com/katzenpost/katzenpost/minclient
* https://github.com/katzenpost/katzenpost/core

acknowledgments
===============

* I would like to thank Leif Ryge for feedback during the design of this
  client and many of its protocols.

* I would like to also thank Adam Langely for writing [Pond](https://github.com/agl/pond)
  which has very obviously inspired a few of our design choices and has provided some
  code that we use such as the PANDA cryptographic protocol and the Double Ratchet.


supported by
============

The development of the Catshadow Katzenpost client has been supported by:

* The Samsung Next Stack Zero grant
* NLnet and the NGI0 PET Fund paid for by the European Commission

.. image:: https://katzenpost.mixnetworks.org/_static/images/eu-flag-tiny.jpg


See **NLnet accouncement** https://nlnet.nl/project/katzenpost/index.html


See **Announcing the Samsung NEXT Stack Zero Grant recipients**.
https://samsungnext.com/whats-next/category/podcasts/decentralization-samsung-next-stack-zero-grant-recipients/




license
=======

AGPL: see LICENSE file for details.

Copyright (C) 2020  David Stainton.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

Documentation

Index

Constants

View Source
const (
	// MessageExpirationDuration is the duration of time after which messages will be removed.
	MessageExpirationDuration = 168 * time.Hour

	// MessageIDLen is the length of our message IDs which are used the keys in a map
	// to reference individual messages of a conversation.
	MessageIDLen = 4

	// GarbageCollectionInterval is the time interval between garbage collecting
	// old messages.
	GarbageCollectionInterval = 120 * time.Minute
)
View Source
const MaxQueueSize = 20
View Source
const ReadInboxLambdaPDivisor = 8

ReadInboxLambdaPDivisor is used to divide our LambdaP parameter to determine our new lambda parameter for our poisson process which is used in selecting time intervals between attempting to retreive messages from our remote Provider.

Variables

View Source
var (
	ErrTrialDecryptionFailed  = errors.New("Trial Decryption Failed")
	ErrInvalidPlaintextLength = errors.New("Plaintext has invalid payload length")
	ErrContactNotFound        = errors.New("Contact not found")
	ErrPendingKeyExchange     = errors.New("Cannot send to contact pending key exchange")
	ErrProviderNotFound       = errors.New("Cannot find provider")
	ErrBlobNotFound           = errors.New("Blob not found in store")
	ErrNoSpool                = errors.New("No Spool Found")
	ErrNotOnline              = errors.New("Client is not online")
	ErrNoCurrentDocument      = errors.New("No current document")
	ErrAlreadyHaveKeyExchange = errors.New("Already created KeyExchange with contact")
	ErrHalted                 = errors.New("Halted")
)
View Source
var (
	DecryptStateFailed = errors.New("failed to decrypted statefile")
)
View Source
var ErrQueueEmpty = errors.New("queue is empty")

ErrQueueEmpty is the error issued when the queue is empty.

View Source
var ErrQueueFull = errors.New("queue is full")

ErrQueueFull is the error issued when the queue is full.

Functions

func DoubleRatchetPayloadLength

func DoubleRatchetPayloadLength(geo *geo.Geometry) int

func LoadStateWriter

func LoadStateWriter(log *logging.Logger, stateFile string, passphrase []byte) (*StateWriter, *State, error)

LoadStateWriter decrypts the given stateFile and returns the State as well as a new StateWriter.

func NewContactExchangeBytes

func NewContactExchangeBytes(spoolWriteDescriptor *memspoolClient.SpoolWriteDescriptor, keyExchange []byte) ([]byte, error)

NewContactExchangeBytes returns serialized contact exchange information.

Types

type CBORState added in v0.0.31

type CBORState struct {
	SpoolReadDescriptor *client.CBORSpoolReadDescriptor
	Contacts            []*Contact
	Providers           []*pki.MixDescriptor
	Conversations       map[string]map[MessageID]*Message
	Blob                map[string][]byte
}

type Client

type Client struct {
	worker.Worker

	EventSink chan interface{}
	// contains filtered or unexported fields
}

Client is the mixnet client which interacts with other clients and services on the network.

func New

func New(logBackend *log.Backend, mixnetClient *client.Client, stateWorker *StateWriter, state *State) (*Client, error)

New creates a new Client instance given a mixnetClient, stateWorker and state. This constructor is used to load the previously saved state of a Client.

func NewClientAndRemoteSpool

func NewClientAndRemoteSpool(ctx context.Context, logBackend *log.Backend, mixnetClient *client.Client, stateWorker *StateWriter) (*Client, error)

NewClientAndRemoteSpool creates and connects a new Client and creates a new remote spool for collecting messages destined to this Client. The Client is associated with this remote spool and this state is preserved in the encrypted statefile, of course. This constructor of Client is used when creating a new Client as opposed to loading the previously saved state for an existing Client.

func (*Client) AddBlob

func (c *Client) AddBlob(id string, blob []byte) error

AddBlob adds a []byte blob identified by id string to the clients storage

func (*Client) ChangeExpiration

func (c *Client) ChangeExpiration(name string, expiration time.Duration) error

ChangeExpiration changes the message history expiration of a contact.

func (*Client) CreateRemoteSpool

func (c *Client) CreateRemoteSpool() error

CreateRemoteSpool creates a remote spool for collecting messages destined to this Client. This method blocks until the reply from the remote spool service is received or the round trip timeout is reached.

func (*Client) CreateRemoteSpoolOn

func (c *Client) CreateRemoteSpoolOn(provider string) error

CreateRemoteSpoolOn creates a remote spool for collecting messages destined to this Client. This method blocks until the reply from the remote spool service is received or the round trip timeout is reached.

func (*Client) DeleteBlob

func (c *Client) DeleteBlob(id string) error

DeleteBlob removes the blob identified by id string or error

func (*Client) DoubleRatchetPayloadLength

func (c *Client) DoubleRatchetPayloadLength() int

func (*Client) GetBlob

func (c *Client) GetBlob(id string) ([]byte, error)

GetBlob returns the blob identified by id string or error

func (*Client) GetContacts

func (c *Client) GetContacts() map[string]*Contact

GetContacts returns the contacts map.

func (*Client) GetExpiration

func (c *Client) GetExpiration(name string) (time.Duration, error)

GetExpiration returns the message expiration of a contact.

func (*Client) GetPKIDocument

func (c *Client) GetPKIDocument() (*pki.Document, error)

GetPKIDocument() returns the current pki.Document or error

func (*Client) GetSortedConversation

func (c *Client) GetSortedConversation(nickname string) Messages

GetSortedConversation returns Messages (a slice of *Message, sorted by Timestamp)

func (*Client) GetSpoolProviders

func (c *Client) GetSpoolProviders() ([]string, error)

GetSpoolProviders() returns the set of current spool providers in the pki.Document

func (*Client) NewContact

func (c *Client) NewContact(nickname string, sharedSecret []byte)

NewContact adds a new contact to the Client's state. This starts the PANDA protocol instance for this contact where intermediate states will be preserved in the encrypted statefile such that progress on the PANDA key exchange can be continued at a later time after program shutdown or restart.

func (*Client) Offline

func (c *Client) Offline() error

Offline() tells the client to disconnect from network services and blocks until the client has disconnected.

func (*Client) Online

func (c *Client) Online(ctx context.Context) error

Online() brings catshadow online or returns an error

func (*Client) RemoveContact

func (c *Client) RemoveContact(nickname string) error

RemoveContact removes a contact from the Client's state.

func (*Client) RenameContact

func (c *Client) RenameContact(oldname, newname string) error

RenameContact changes the name of a contact.

func (*Client) SendMessage

func (c *Client) SendMessage(nickname string, message []byte) MessageID

SendMessage sends a message to the Client contact with the given nickname.

func (*Client) Shutdown

func (c *Client) Shutdown()

Shutdown shuts down the client.

func (*Client) SpoolWriteDescriptor

func (c *Client) SpoolWriteDescriptor() *memspoolclient.SpoolWriteDescriptor

SpoolWriteDescriptor() returns the SpoolWriteDescriptor for this client or nil

func (*Client) Start

func (c *Client) Start()

Start starts the client worker goroutine and the read-inbox worker goroutine.

func (*Client) WipeConversation

func (c *Client) WipeConversation(nickname string) error

WipeConversation removes all messages between a contact

type Contact

type Contact struct {

	// Nickname is also unique locally.
	Nickname string

	// IsPending is true if the key exchange has not been completed.
	IsPending bool

	LastMessage *Message
	// contains filtered or unexported fields
}

Contact is a communications contact that we have bidirectional communication with.

func NewContact

func NewContact(nickname string, id uint64, secret []byte) (*Contact, error)

NewContact creates a new Contact or returns an error.

func (*Contact) Destroy

func (c *Contact) Destroy()

func (*Contact) ID

func (c *Contact) ID() uint64

ID returns the Contact ID.

func (*Contact) MarshalBinary

func (c *Contact) MarshalBinary() ([]byte, error)

MarshalBinary does what you expect and returns a serialized Contact.

func (*Contact) UnmarshalBinary

func (c *Contact) UnmarshalBinary(data []byte) error

UnmarshalBinary does what you expect and initializes the given Contact with deserialized Contact fields from the given binary blob.

type KeyExchangeCompletedEvent

type KeyExchangeCompletedEvent struct {
	// Nickname is the nickname of the contact with whom our key
	// exchange has been completed.
	Nickname string
	// Err is a key exchange error or is set to nil on success.
	Err error
}

KeyExchangeCompletedEvent is an event signaling the completion of a key exchange or failure if Err is non-nil.

type Message

type Message struct {
	Plaintext []byte
	Timestamp time.Time
	Outbound  bool
	Sent      bool
	Delivered bool
}

Message encapsulates message that is sent or received.

type MessageDeliveredEvent

type MessageDeliveredEvent struct {
	// Nickname is the nickname of the recipient of our delivered message.
	Nickname string

	// MessageID is the key in the conversation map referencing a specific message.
	MessageID MessageID
}

MessageDeliveredEvent is an event signaling that the message has been delivered to the remote spool.

type MessageID

type MessageID [MessageIDLen]byte

type MessageNotDeliveredEvent

type MessageNotDeliveredEvent struct {
	// Nickname is the nickname of the recipient of our delivered message.
	Nickname string

	// MessageID is the key in the conversation map referencing a specific message.
	MessageID MessageID

	// Err is an error with reason for failure
	Err error
}

MessageNotDeliveredEvent is an event signaling that the message has NOT been delivered to the remote spool, with Err.

type MessageNotSentEvent

type MessageNotSentEvent struct {
	// Nickname is the nickname of the recipient of our delivered message.
	Nickname string

	// MessageID is the key in the conversation map referencing a specific message.
	MessageID MessageID

	// Err is an error with reason for failure
	Err error
}

MessageNotSentEvent is an event signalling that the message was not sent.

type MessageReceivedEvent

type MessageReceivedEvent struct {
	// Nickname is the nickname from whom we received a message.
	Nickname string
	// Message is the message content which was received.
	Message []byte
	// Timestamp is the time the message was received.
	Timestamp time.Time
}

MessageReceivedEvent is the event signaling that a message was received.

type MessageSentEvent

type MessageSentEvent struct {
	// Nickname is the nickname of the recipient of our delivered message.
	Nickname string

	// MessageID is the key in the conversation map referencing a specific message.
	MessageID MessageID
}

MessageSentEvent is an event signaling that the message was sent.

type Messages

type Messages []*Message

func (Messages) Len

func (d Messages) Len() int

Len implements sort.Interface.

func (Messages) Less

func (d Messages) Less(i, j int) bool

Less is part of sort.Interface.

func (Messages) Swap

func (d Messages) Swap(i, j int)

Swap is part of sort.Interface.

type Queue

type Queue struct {
	sync.Mutex
	// contains filtered or unexported fields
}

Queue is our in-memory queue implementation used as our egress FIFO queue for messages sent by the client.

func (*Queue) MarshalBinary

func (q *Queue) MarshalBinary() ([]byte, error)

func (*Queue) Peek

func (q *Queue) Peek() (*queuedSpoolCommand, error)

Peek returns the next message ref from the queue without modifying the queue.

func (*Queue) Pop

func (q *Queue) Pop() (*queuedSpoolCommand, error)

Pop pops the next message ref off the queue and returns nil upon success, otherwise an error is returned.

func (*Queue) Push

func (q *Queue) Push(e *queuedSpoolCommand) error

Push pushes the given message ref onto the queue and returns nil on success, otherwise an error is returned.

func (*Queue) UnmarshalBinary

func (q *Queue) UnmarshalBinary(data []byte) error

type ReadMessageDescriptor

type ReadMessageDescriptor struct {
	// MessageID is the key in the conversation map referencing a specific message.
	MessageID MessageID
}

ReadMessageDescriptor is used to track Spool Read Responses

type SentMessageDescriptor

type SentMessageDescriptor struct {
	// Nickname is the contact nickname to whom a message was sent.
	Nickname string

	// MessageID is the key in the conversation map referencing a specific message.
	MessageID MessageID
}

SentMessageDescriptor is used to track Spool Write Responses

type State

type State struct {
	SpoolReadDescriptor *client.SpoolReadDescriptor
	Contacts            []*Contact
	Providers           []*pki.MixDescriptor
	Conversations       map[string]map[MessageID]*Message
	Blob                map[string][]byte
}

State is the struct type representing the Client's state which is encrypted and persisted to disk.

type StateWriter

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

StateWriter takes ownership of the Client's encrypted statefile and has a worker goroutine which writes updates to disk.

func NewStateWriter

func NewStateWriter(log *logging.Logger, stateFile string, passphrase []byte) (*StateWriter, error)

NewStateWriter is a constructor for StateWriter which is to be used when creating the statefile for the first time.

func (*StateWriter) Start

func (w *StateWriter) Start()

Start starts the StateWriter's worker goroutine.

Jump to

Keyboard shortcuts

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