parcel: An ultra-lightweight, cross-platform, end-to-end encrypted, terminal-based group messaging application
Linux, BSD, Darwin/macOS, Windows, and iOS (kind of) on x86-64, x86, AArch32, and AArch64 architectures.
After cloning the repository, simply
make installto build and install both parcel and parceld under /usr/local/bin.
Optionally, PREFIX can be set in order to change the install location.
make PREFIX=~/.parcel installBoth the parcel client and daemon run without modification on iOS devices using ish.
The simplest way to build parcel on Windows is using w64devkit.
The pre-built mini release variant is just over 50 MB and contains everything needed to build parcel from source.
Once cloned, simply
make installNote that, by default, the binaries will be installed at %HOMEPATH%\parcel
Alternatively, download prebuilt binaries from Github.
Please note, however, that these builds are cross-compiled for x86_64 Windows from an AArch64 MacBook Pro and only minimally tested using Wine through Rosetta 2 on an unsupported version of macOS.
Print usage information with -h:
usage: parcel [-lhd] [-a ADDR] [-p PORT] [-u NAME]
-a ADDR server address (www.example.com, 111.222.333.444)
-p PORT server port (default: 2315)
-u NAME username displayed alongside sent messages
-l use computer login as username
-h print this usage information
If the target server is on the default port, then the only required arguments
are server address and username. Passing the flag -l on launch sets the
username as the computer login.
If a required argument is not provided, then it is prompted at startup.
Parcel encrypts and decrypts message data using AES128.
Messages are authenticated using CMAC (OMAC1) to guarantee message authenticity and data integrity. The CMAC tag authenticates ciphertext rather than plaintext, allowing the message to be authenticated prior to decryption.
The parcel daemon, parceld, generates a random 32-byte control key at startup.
After establishing a secured channel, this key is shared with the client and
used to decrypt TYPE_CTRL messages from the daemon.
The control key is used only once, with TYPE_CTRL messages containing the next
control keys as part of its encrypted contents.
Upon recieving and decrypting a control message, the parcel clients perform a multi-party
Elliptic-curve Diffie-Hellman key exchange using Curve25519, at which point all clients posess a new shared key. A new group key is derived whenever the number of clients changes.
But why though?
parcel uses standard
ANSI X3.64 (ISO 6429)
escape sequences for in-band signaling. Despite having been standardized since 1979,
they lacked proper support in Windows until the Windows 10 Anniversery Update in 2016.
If you're running this version or newer, parcel should properly configure the console automatically. In the unlikely case that parcel is unable to configure the console, you can try adding the following registry key to globally enable Virtual Terminal Processing.
REG ADD HKCU\CONSOLE /f /v VirtualTerminalLevel /t REG_DWORD /d 1If an earlier version of Windows is being used or if parcel is being run from a different shell, the extra console configuration will fail silently and parcel will continue as normal.
The number of active clients supported by parceld is determined by
FD_SETSIZE. As a result, parceld supports a maximum of 64 active clients
when running on Windows.
Windows support for UTF-8 has been improving in recent years, with Windows
Version 1903 introducing the ability to
set UTF-8 as an active process's codepage.
Parcel takes advantage of this and embeds the required XML to set the process
codepage directly into the application binary. Additionally, the console's
codepage is set to UTF-8 at runtime, although this only works for console
output... because despite UTF-8 becoming standarized in 1993, there remains no
way to read UTF-8 input in Windows. To combat this, UTF-16 input is read in one
character at a time using ReadConsoleW() and encoded as UTF-8 with
WideCharToMultiByte(). It might not be ideal but gets the job done.
All commands start with a slash followed by the command itself.
| Command | Description |
|---|---|
/list |
List available commands |
/x |
Exit the server and close parcel |
/username |
Change username |
/encinfo |
Display active session and control keys |
/file |
Send a file |
/clear |
Clear the screen |
/version |
Display application version |
Commands can be parsed once enough has been typed to become unambiguous, i.e.,
the username command can be entered as /u, clear can be /c, etcetera.
Commands are interactive only, no need to supply arguments.
All wire messages are encapsulated in a cable structure for transmission between client and daemon. The cable provides a framing layer that allows the receiver to know exactly how much data to expect.
The cable structure consists of two components:
magic contains the 6-byte magic number parcel for validation.
length contains the 8-byte total length of the cable (including the 14-byte
header and the entire wire payload).
The complete transmission format is:
[magic (6 bytes) | length (8 bytes) | wire (variable)]
The wire consists of an authentication section, header, and data payload.
Authentication Section (48 bytes):
mac_outer contains the 16-byte MAC of the entire wire (inner MAC, IV, header,
and data sections).
mac_inner contains the 16-byte MAC of the wire header only, preventing length
oracle attacks.
iv contains the 16-byte Initialization Vector required for cipher block
chaining. Sent as plaintext, as it needs only to be random- not secret.
Header (16 bytes):
magic contains the 6-byte magic number "-wire-".
wire_len contains the 8-byte total length of the entire wire.
alignment is a 1-byte field indicating padding bytes added to align data with
the AES block size.
type is a 1-byte field indicating the message type (see Wire Types below).
Data:
data contains one or more 16-byte chunks of encrypted data.
The complete wire structure is:
[mac_outer (16) | mac_inner (16) | iv (16) | magic (6) | wire_len (8) | alignment (1) | type (1) | data (variable)]
The following message types are defined:
Standard text messages sent between clients. Contains UTF-8 encoded chat messages.
File transfer messages. The data section contains a wire_file_message struct
with the filename, file size, and file data.
Status messages automatically sent by clients to inform others of connection state changes (join, leave, username changes).
Control messages sent only by the daemon to trigger group key exchange operations.
The data section contains a wire_ctrl_message struct with a control function,
function arguments, and the renewed control key. These messages trigger a GDHKE
(Group Diffie-Hellman Key Exchange) sequence to update session keys.
Session key distribution messages used during the multi-party key exchange protocol. Contains public keys or derived key material for establishing the shared session key.
Internal types used for initialization and error handling. Not transmitted over the network.
