Update Log - maqp/tfc Wiki

TFC 1.21.09 Update log


TFC 1.21.08 Update log



TFC 1.21.07 Update Log


TFC 1.21.06 Update Log




TFC 1.21.04 Update Log




TFC 1.21.03 Update Log




Platform support

TFC 1.20.12 Update Log




Code quality

TFC 1.20.11 Update Log



Platform support

Code quality


TFC 1.20.10 Update Log




Code quality

TFC 1.20.07 Update Log


Platform support

Code quality

TFC 1.20.04 Update Log



Platform support


Code quality

TFC 1.20.03 Update Log


Platform support



Code quality


TFC 1.20.02 Update Log




Code quality


TFC 1.19.12 Update Log




Code quality

TFC 1.19.11 Update Log





TFC 1.19.10 Update Log





Code quality



Possibly bugs:

Tor requires accurate time information. If the system time has any offset (encountered at least with PureOS and Debian during testing), torsocks refuses to function properly and the TFC won't install.

To protect users' privacy, TFC is installed over Tor. Speed of Tor varies, so sometimes some component can time-out, and TFC won't install in those cases. There is no better fix than to try running the installer again.

TFC 1.19.08 Update Log




Code quality





TFC 1.19.04 Update Log




Code quality

TFC 1.19.03 Update Log





Code quality


Known bugs

TFC 1.19.01 Update Log








Code quality


TFC 1.17.08 Update log

TFC is looking good

To summarize

That being said, after four years, it is a great pleasure to announce the system is ready enough to have its major version number bumped to one. There's still room for improvement. In future TFC will hopefully ditch Pidgin as its underlying ciphertext delivery mechanism and switch to native Tor Onion Service routing. Routing via Tor Onion Services can already be done with Pidgin but it is not the default. Cipher choices also leave some room for improvement: X448 with ChaCha20-Poly1305 is something to aim towards if/once they become available.




The effect of this is all public keys are slightly longer than before, but always 51 chars long. Local testing still displays the key as one easy-to-copy string, but in normal use, the keys are displayed in 17 substrings of three chars (which feels like the right size for the humans' working memory), with lettering from A to Q that helps check position when typing the key:

     A   B   C   D   E   F   G   H   I   J   K   L   M   N   O   P   Q
    5Ka 52G yNz vjF nM4 2jw Duu rWo 7di zgi Y8g iiy yGd 78L cCx mwQ mWV

Code quality

Directory structure

TFC 0.17.04 Update log

Code quality

Crypto / Security

Hardware design




The trust chain works as follows: If the fingerprint in one-liner matches the one you know to be secure, imported 4096-bit RSA public signature verification key will also be authentic. With the authentic public key, only the installer install.sh with authentic digital signature is executed by the one-liner. The install.sh downloads TFC files and checks their SHA256 hashes. Among these files are the requirements.txt and requirements-nh.txt that contain pinned SHA512 hashes for dependencies downloaded over pip.

Installer creates virtual environments for Python3.6 (venv_tfc) and 3.5 (venv_nh) pip dependencies. This avoids littering the global Python environment.


TFC 0.16.10 | Update log


From this version onwards, TFC-NaCl is the only version that gets new feature updates. The reason for this is in-part time constraints, in-part the goal to unify the userbase and ultimately integrate cascading ciphers. The 'NaCl' name will be dropped starting with this version.


Encrypted databases and login screen

Tx.py and Rx.py now feature an animated login screen built on curses, mocking all the things cyber. When the programs are launched for the first time, the user enters and confirms a master password. This password will be combined with salt (created with /dev/urandom) in PBKDF2-HMAC-SHA256, the iteration count of which is ensured to be within 2000..4000ms range, but with at least 65536 iterations. This means slow systems will suffer during login time; Security comes first. While the salt and long key derivation time are currently best practices, they offer negligible protection against weak passwords. No magical crypto dust exists to protect from this so make sure to use strong passphrases and possibly 2FA with something like Yubikey, that remembers a static part of the passphrase.

The output of HKDF is the master key, the SHA3-256 hash of which is stored together with the iteration count and salt to file .{tx,rx}_login_data. Forgetting the password, tampering with, or deleting the login data file renders all stored data useless: be careful. Under the assumption, the password remains unbreakable by the adversary, encrypted databases help against following problems:

Wear leveling:

All data is encrypted before it touches the hard drive: These are increasingly becoming SSDs that don't overwrite data the way magnetic drives do.

Physical data exfiltration:

In the case where the adversary has not remotely exploited RxM with a keylogger that copies TFC master password, physical access where encrypted data is merely exfiltrated means attacker is unable to access sensitive data. Previously similar protection could be obtained with Full Disk Encryption (FDE) and ensuring the encrypted disk is not mounted. TFC now keeps data secure even if FDE drive is mounted but TFC is not running. Since encryption scheme is authenticated (XSalsa20-Poly1305), any tampering can be detected as authentication of encrypted data fails with the correct password.


In situations where TxM and RxM operating systems are left open (something user should never do) but when TFC master password hasn't been given, encryption of data prevents impersonating the user.

File metadata:

All fields in databases are padded prior to encryption. With groups and contacts, there is now a maximum number of contacts adjustable by the user. The database is padded accordingly to hide the length of data in fields, number of contacts, groups, and members of each group.

Encrypted PSK:s

When the user creates a new PSK, in addition to contact details, Tx.py generates a 256-bit salt and prompts the user to enter and confirm a password that protects the PSK during transit. An encryption key is derived from the password and salt using PBKDF2-HMAC-SHA256 over 65536 iterations, and PSK is encrypted with that key using XSalsa20-Poly1305. The salt, nonce, ciphertext, and tag are written to PSK file.

The recipient who adds PSK to their RxM now uses command /psk that replaces the old command /rxkey. Rx.py then parses salt, nonce, ct, and tag from PSK file, and then asks the user for the password to decrypt the PSK. If the user enters the correct password, the key and contact details are added to RxM, keyfile is shredded and the user is advised to physically destroy the transmission media.

Delivery of PSK password should be split over multiple media.

Encrypted SSH password

Tx.py now prompts the user to enter and confirm the RPi SSH password the first time the connection is established. The password length is protected by padding it to the next 255 chars before it's the master key using XSalsa20-Poly1305. The resulting ciphertext is stored into file .ssh_login_data.

Group trickle connection

The user can now select a group for trickle connection (must be created before trickle connection is enabled from Tx.py configuration).

Multi-account support for user

When the user adds a contact, they must now specify account of the user, associated with the account of the contact. When combined with Tails configuration for NH and Tor-hidden service XMPP server, this can effectively hide the metadata about group structure. For example, Alice could communicate to Bob and Charlie using separate accounts for both, and Bob and Charlie could do the same for the three. In such case, the contact list itself does not leak metadata about the structure of communicating parties' network.

All commands are now encrypted

The last unencrypted command (screen clearing) is now encrypted to prevent DOS of RxM in cases where NH is compromised. NH side screens are cleared with additional unencrypted command when trickle connection is not enabled.

Shell escaping

Enabled shell escaping for all shell-commands with variables. All inputs for shell commands come from the user by design and are thus trusted. Enabling it, however, prevents shell-injection of RxM by typing commands to Tx.py. This is unlikely an issue as any physical attacker can type commands on RxM screen.

Reset command

Added command /reset that resets Tx.py, Rx.py, and NH.py terminals. Reset also clears the ephemeral message history of session for active contact/group. All messages for that conversation that are not logged are lost for good; Use /clear for temporary screen clearing when you need a clean table or if you detect non-threatening shoulder surfing and /reset when the conversation needs to be removed.

Encrypted hash ratchet counter

The hash ratchet counter is no longer an unauthenticated plaintext counter, but an XSalsa20-Poly1305 encrypted, eight-byte value that is prepended to packet. This removes metadata datagrams in IM client without OTR-protocol would otherwise reveal about the number of sent packets, and removes the DoS attack vector in RxM from third parties (TFC is still vulnerable against frenemies, but they can be removed altogether from TFC.)


Chat 'windows'

Rx.py finally features separate windows or tabs for each conversation, including one for issued commands. If a message is received into an inactive window, either the message or a notification about the message is temporarily displayed. The duration of the notifications can be controlled with the setting new_msg_notify_dur. Information about commands is also temporarily displayed on the screen before moved into the command window. This helps keep conversations free from clutter.

Some warnings and notifications are not stored in the temporary message log and they disappear when the user changes window by selecting same or another conversation with /msg command, or by opening the command window with /cmd or //.

The user can check if they have messages on unread tabs by typing either command /unread or (single space).

Shorter public keys

Moved from 72 hex char public keys to Bitcoin Wallet Import Format where keys are Base58 encoded and come with integrated SHA256 based checksum. Base58 encoding reduces the length to 48-50 chars that do not look like one another (unlike in the case of Base64).

New group messaging

Group creation

When Alice creates a group, her Tx.py asks her if she wants to send an invitation to group members. The invite will contain a list of members she added to the group, and it is displayed by Rx.py of invited members. Joining users must manually create a group with the same name using their TxM, that will share the group with RxM.

If Bob creates the same group and sends a list of members he added to Alice and Charlie, Alice will get a notification "Bob has joined the group", while Charlie (who Alice did not add) will get an invitation to group. Alice and Charlie will see that Bob has added the other to the group, so they know to add each other as contacts and later, to the group.

Adding members to the group

When Alice adds new members to the group, her Tx.py asks if she wants to send a list of added members to existing members, so they can synchronize their side of the group. Members added to the group will receive the same invitation that members who are invited to the new group get.

Removing members from the group

When Alice removes Charlie and David from her group, her Tx.py asks if she wants to send the list of removed members to remaining members. That way, the remaining members are aware that she is no longer receiving or sending messages to these contacts. Removed members never receive notification about being removed. The only thing that happens is, Alice's Tx.py stops sending messages to removed contacts, and her Rx.py stops showing messages from these members directed to the group's conversation window. Removed users can still exchange private messages with Alice. To block this, Alice should block the contacts in IM client.

Leaving the group

When Alice removes the group, Tx.py asks if she wants to send members of the group a notification that she's no longer sending messages to group or receiving messages from them. If she sends the notification, Rx.py of receiving contacts also warns the contacts, that unless they themselves remove Alice from their group, their Tx.py will keep sending the group messages to Alice, who could regardless of group exit message, still be decrypting and reading the group messages. All Alice needs to do is join back to the group, and not send a notification about it.

Preventing the user from doing this is comparable to sender based control, something that is not just snake oil, but impossible to achieve with free and open source software. The only way to prevent the user from receiving and decrypting messages from contacts when the parties by design share keys, is not to send messages to the user in the first place.

The user can now enable logging and file reception for groups with commands

/logging on <group name>
/store on <group name>

By default, the latter allows everyone in the group to send a single file to the user. Automatic disabling of file reception can be disabled by setting auto_disable_store to False.

Significantly improved local testing

Local testing uses now uses Terminator, that is installed alongside TFC. Tx.py, Rx.py, NH.py (and dd.py) are launched from one of two desktop entries: TFC (Standard TFC local testing layout) TFC DD (TFC local testing layout with data diode simulators)

More sequenced key exchanges

Rx.py now displays public keys after the local key has been delivered to RxM. Screens are cleared for each step to make them easier to follow.

Fallback for Tkinter file prompts

When Tx.py/Rx.py is unable to show Tkinter dialogs (for example, when Tx.py is used as blacker over SSH from a local network that sits behind TxM-NH data diode), Tx.py no longer demands the use of default directories, but asks the user to type the desired path over CLI (tab-complete is supported).

Improved /names command

/names command no longer shows ID, but instead, account, nick, key exchange type (Curve25519 or PSK) and whether logging is enabled or disabled.

Easier contact choosing

The user can now directly select group from select contact command or use nick instead of account to select a contact. Tab complete is supported.

Program design

Support for arbitrary length commands

This means better synchronization with TxM and RxM data in the future. It works even with trickle connection.

Changed naming of variables

Most of the variables in configuration were changed to more descriptive. New static length naming is easier on eyes.

Improved user interface

TxM/RxM/NH configurations for *buntu configurations now feature desktop entries and Terminator profiles. Each installation features their own layout and profile. Previously existing profiles will not be overwritten.

Switched from os.system("clear") that caused issues with Ubuntu 16 scrollback to VT100 control codes that remove the problem.

VT100 control codes are now also used to remove repeating questions (removes clutter), to move key input guides below text, to auto-complete previous lines (nick, key exchange choices, y/n answers etc). Notifications about file transfer and random delays when long_packet_rand_d is enabled, also have much cleaner notifications.

Changed naming policy and functionality of database files

Group files: Group files have been removed. Instead, all groups and values in fields that contain a list of members and a log setting for the group are stored in a fully padded format to file .tx_groups or .rx_groups.

Keyfiles: The "Keys" folder and aside PSK delivered to contact, all keyfiles have been removed. Keys are instead stored in the contact database.

Logfiles: The "Logs" folder and log files have been removed. Instead, all messages for all contacts are stored in a padded state into the file .tx_logs or .rx_logs. The logged data is not the assembled data, but the plaintext form of each assembly packet. This prevents metadata leaks from observing the ciphertext. Messages are parsed from assembly packets when the log file is viewed with the command /history or exported with the command /export (requires confirmation from the user).

Database files: The database file is like group file, each field is padded separately to hide contact specific plaintext length. The contact database is padded with dummy contacts that contain dummy data. This protects from an adversary who gains physical access, from deducing metadata about the user. The database holds the following information: -account of contact -account of the user (TxM only) -nickname of user -key for sent messages -key for received messages (RxM only) -hash ratchet header key for sent messages -hash ratchet header key for received messages (RxM only) -hash ratchet counter for sent messages -hash ratchet counter for received messages (RxM only) -logging setting for contact -file reception setting for contact (RxM only) -window notification privacy setting for contact (RxM only)

The sampler program hwrng-nacl.py was renamed to hwrng.py.

Significantly improved and refactored code

New input_validation function that gracefully exits and gives more details: Example:

    ERROR! Second parameter of function 'g_mgmt_print()':
    Got <type 'list'> instead of <type 'str'>.

The code for group management was improved significantly by changing For-loops into set operations among other things (Tx.py and Rx.py).

Simplified data type carrying in Tx.py.

File packet loading and delivery time evaluation was completely rewritten.

Plaintext data fields are now separated using non-printable Unit Separator character (0x1F). This reduces overhead and greatly clarifies structure.

Account name string on Rx.py no longer carries trailing .ld / .rx, but separate origin parameter (u for user and c for contact) is passed to each function.

Removed need for exit queue by adding a periodical check if the child processes are alive, and exiting gracefully if they are not.

Added even more unittests. Running unittests now warn they remove all TFC user-data, and ask for confirmation before running.

Improved serial interfacing

Transparent serial auto-configuration

Removed need for interface configuration packets sent by Tx.py during launch. NH.py now uses any packet to configure serial interface directions before forwarding it to Pidgin and/or RxM.

Programs wait for adapters

During start, Tx.py, Rx.py, and NH.py wait until missing USB-to-RS232 adapters have been connected before proceeding.

Adapter disconnection handling

Tx.py, Rx.py, and NH.py no longer crash when USB-to-serial adapter is removed. Tx.py won't send further packets until the adapter is reinserted. Rx.py won't read packets until adapter has been reinserted. NH.py won't send packets to NH until both interfaces have been reinserted and the interface for TxM has been determined by any packet from TxM. All three programs can remap interfaces regardless of device name assigned to it by the OS (/dev/ttyUSB#, where # changes unpredictably). To ensure the mapping works, make sure there aren't additional USB-to-Serial adapters connected to any of the computers TFC uses.

Reactive crossover resolving

In the situation where cables are crossed over, e.g. when the user changes the data diodes to opposing serial interfaces on NH (something the unidirectional interfaces can't detect), NH assumes it's outputting to RxM, while in fact, it's sending messages to TxM. In such situations packets will be lost until NH.py learns about the crossover by receiving a packet from TxM; NH.py listens to both interfaces for TxM data, and it automatically changes the port to RxM to the other interface, resolving the issue.

An exception to packet loss is during start when NH.py doesn't yet know which interface connects to TxM. The process that outputs messages to RxM, therefore, waits until user sends a packet from TxM. When that happens, NH.py maps the interfaces, and the process then outputs sent and received packets from the queue to RxM in order.

Adjustable forward Error Correction with Reed-Solomon erasure codes

Depending on the quality of serial interfaces and data diodes, transmission errors might occur every now and then. TFC now uses Reed-Solomon error correction to detect and fix errors and missing data. A simple setting e_correction_ratio is provided to users. This number describes the maximum number of byte errors in transmission that can be recovered from.

Faster default baud rate

The reference data diode design appears to remain stable with baud rates of 19200, this doubles the speed.

Raspbian Jessie -style serial interfacing.

Changed the integrated serial interface of Raspbian to /dev/serial0.

NOTE: While this makes it possible to run TFC on Raspberry Pi 3, these devices have wireless network interface embedded within the processor. This makes Raspberry Pi 3 impossible to physically air-gap, thus using it is NOT secure.

Program specific updates


Improved sampling

Tx.py now shows the progress of key generation over SSH.

TFC no longer loads multiple 256-bit keys but instead specifies the amount of entropy that is required (varies between 256 and 768 bits). This speeds up key generation, and multiple SSH connections are no longer necessary.

Account validation

Tx.py now checks accounts against regular expressions. This is generally bad practice with emails and XMPP accounts, so the rule set is just


This check ensures the user does not use reserved account name for the dummy accounts that are used as padding in the database.

Streamlined NH bypass

Tx.py no longer asks for NH bypass during key exchange. This feature is mainly needed for local key delivery where remote exploitation of NH can be combined with a keylogger or visual collection. If root local key is compromised, all keys can be accessed from NH. Therefore, it's still useful to send encrypted local key directly from TxM to RxM. NH bypass messages now halt Tx.py only twice during the entire procedure (second time after confirmation code has been typed).

Improved file transmission

Tx.py no longer encodes sent file to temp file before reading it, but reads data into memory and converts it there. This helps when sending large files, but since file transfer is already slow due to design limitations, it won't do much good. Tx.py now uses zlib to compress (maximum compression setting) the file prior to encoding. This also saves some time depending on the entropy of sent data.

Tx.py no longer appends hash of file data, but prior to encoding generates a 256-bit key from /dev/urandom, that will be used to encrypt the file data. Encrypted data is then concatenated with the key. The benefit is, the decryption key will be sent to the recipient in the one or two last packets, meaning if the user e.g. cancels file transmission to a wrong recipient, the file can not be decrypted. Long messages are also encrypted with additional layer and trailing key.

The file packet estimation counter was completely rewritten and is now much more accurate. Delivery time estimation now scales up to 29d 23h 59m 59s.

Tx.py also shows more detailed information about the preprocessing of sent file e.g. loading, compression, encryption and delivery time evaluation).

Tx.py no longer allows sending files with /file /path/to/file. Instead, the file is selection is opened with the command /file, after which either GUI prompt (or if that's unavailable or disabled by setting disable_gui_dialog to False, tab-complete supported CLI prompt) is displayed.

Simplified contact adding

New contacts are from now on always added with the command /add. Depending on which parameters user adds, Tx.py will interactively ask other ones:

/add [email protected] [email protected] alice ecdhe*    asks to use HWRNG (if avail.)
/add [email protected] [email protected] alice           also asks key exchange
/add [email protected] [email protected]                 also asks nickname
/add [email protected]                                also asks user's account name
/add                                                 also asks contact's account name

Please note that you can't skip parameters!

Giving command

/add [email protected] [email protected] ecdhe

assumes Bob wants to set nick as ecdhe, and asks for the key exchange

*Key exchange can be



Support for multiple accounts

NH can now send messages from multiple Pidgin accounts to contacts.



Much cleaner message printing

Messages for contact are now dynamically wrapped to screen according to the window size. Nicknames are dynamically updated, and date changes are displayed between messages. Messages are indented in front of the time stamp. Together these make a huge difference to usability compared to previous versions:

Moved from confusing messaging layout

10:20 Me > Alice: Hi Alice
10:21      Alice: Hi Bob!
Changed [email protected] nick to alice.


10:20    Me: Hey Alice

10:21 Alice: Hey Bob!

10:22    -!- Changed [email protected] nick to alice.   // this message is displayed only temporarily

that's easier on eyes as recipient's nick isn't always next to the message. The previous style was required, as all messages sent to all contacts were displayed in the same window, and the user had to be able to tell to whom their message was delivered to. Me is no longer allowed as a nick: this is to prevent confusion about the author of the message. RxM-side log files use the same layout.

The messages displayed are now word-wrapped and long messages have extra room between prints.

Better public key display

When the user initiates new ECDHE key exchange, Rx.py displays latest public key received from contact during the session. The user no longer needs to scroll RxM terminal up to find the public key.

Local key backlog clearing

Rx.py now resets the terminal when the local key encryption key is correctly input. This removes backlogs with the intention of hiding the sensitive key decryption key. If local testing is enabled and the key was pasted to Rx.py from Tx.py, Rx.py clears the clipboard.



Renamed hwrng-nacl.py back to hwrng.py as CEV/OTP versions use their own file names, and as CEV/OTP versions are slowly removed/integrated.

Key size in parameter

hwrng.py allows three key sizes to be loaded from GPIO-HWRNG: 256, 512 and 768 bits.


Smarter configuration chooser

setup.py now detects if OS is Raspbian or Ubuntu derivative and allows the user to choose between four configurations. Unsupported OSs exit setup.

Selection # Raspbian Ubuntu
1 TxM TxM
2 RxM RxM
4 HWRNG Local testing

setup.py will also detect Tails installation configuration and initialize the NH configuration setup automatically. Note that Tails installation configuration is not run as sudo.

Automatic signature verification key and signature download

When setup.py is run, the TFC public key and the digital signature for setup.py are downloaded automatically. It is up to the user to choose whether to verify the signature first or after installation by importing the key and running

gpg --verify setup.py.asc setup.py

The user shouldn't trust software downloaded from the network is actually downloading the correct public key. The only way to trust the signature verification key is by having prior knowledge of the authentic fingerprint (never use the short version) from the authentic source, e.g. from face to face meeting with the developer or via trustworthy Web-of-Trust.

Moved from apt-get to apt

Apt shows download progress more clearly.

Shuffled dependency downloads

Apt-commands and library downloads are now shuffled with CSPRNG when selecting TxM/RxM configurations. As the window of opportunity to remotely exploit TxM starts the second user downloads setup.py, this only delays detection of installation of those users who have obtained setup.py via sneakernet or different Tor session, where they weren't deanonymized.

TxM/RxM/HWRNG configurations for TFC can now be installed with single line:


sudo echo && wget https://raw.githubusercontent.com/maqp/tfc/master/setup.py && sudo python setup.py 1


sudo echo && wget https://raw.githubusercontent.com/maqp/tfc/master/setup.py && sudo python setup.py 2


sudo echo && wget https://raw.githubusercontent.com/maqp/tfc/master/setup.py && sudo python setup.py 3

HWRNG (Raspbian) or Local testing (Ubuntu)

sudo echo && wget https://raw.githubusercontent.com/maqp/tfc/master/setup.py && sudo python setup.py 4

The reason sudo echo is run is to not waste time writing sudo password after setup.py has been downloaded. While sudo wget would be equally fast, running wget as root is not a good idea. setup.py should be run as sudo because that way it has the permission to run sudo ifconfig # down for all network interfaces. Ethernet cable should be disconnected once installer tells the user to do so. All wireless network cards must be removed before TFC is installed on TxM/RxM.

HWRNG installation configuration now automatically sets RPi static IP

TxM configuration for *buntu asks the user if they will connect to RPi HWRNG over SSH. If the user does this, setup.py will wait until at least one ethernet device is connected, and then asks the user to enter the one to be used (tab-complete supported). It then enables static IP for TxM and restarts the network interface.

Smaller changes

Setup.py now enables execution permissions on .py files so they can be run with

./<program name>.py

Setup.py now scales (if supported by the terminal) the terminal to fit all configurations in the main menu.

TFC NaCl 0.16.05 | Update log

Common changes



Opens a file prompt when /rxkey command is sent by TxM. This allows the user to load the PSK for the contact without turning off Rx.py.

Fixed issue where missing rx.<account>.e key on RxM crashes Rx.py when /logging on <account> command is issued from TxM.

Opens file prompts based on keyfile change commands.


Added preinstalled hashes of tested (not audited) crypto libraries.

Improved SSH configuration during installation.

Added NH installation configuration.

Reduced the window of compromise time to minimum: setup.py now first downloads apt-packets. It then downloads and verifies crypto libraries and TFC. Finally, the installer runs sudo ifconfig down on every interface excluding lo, listed by ifconfig -a, before extracting and building libraries.

Fixed issues with file permissions when running the installer as superuser.


Renamed hwrng.py to hwrng-nacl.py in case multiple versions of TFC (CEV/OTP) use same RPi to load entropy from HWRNG.


dd.py now automatically re-sizes terminal window to a proper size.

TFC NaCl 0.16.01 | Update log

Local key transmission: Bootstrapping security

In the initial bootstrap, Tx.py generates a 256-bit local key and 256-bit key encryption keys with separate processes, where 32-byte /dev/urandom string is hashed with SHA3-256 (simplesha3 library), that is then passed through 25k iteration PBKDF2-HMAC-SHA256. If Tx.py is run on a Raspbian (Raspberry Pi), the user can mix entropy from free hardware design HWRNG, connected to GPIO 4 of the SoC board into the PBKDF2 function. Entropy is sampled with 10Hz frequency and Von Neumann whitened during sampling. Once the Von Neumann samples have been collected, the 256-bit entropy is compressed using the SHA3-256 hash function. If Tx.py is run on a computer that has a direct ethernet connection to Raspberry Pi with GPIO HWRNG, Tx.py can SSH into that computer using Paramiko library, and query entropy with program hwrng.py.

The local key is padded to 254 bytes and encrypted with the key encryption key using PyCrypto library's XSalsa20 stream cipher (192-bit nonce), and 128-bit Poly1305 MAC. The signed ciphertext is transmitted to RxM through data diode. The user can choose whether to leave data diodes connected to NH and let signed ciphertext pass through, or whether they want to transmit keys directly from TxM to RxM by connecting serial interfaces that way. Such configuration prevents the adversary from obtaining the signed ciphertext from NH in case it has been compromised at this point.

RxM will then ask the user to manually type the 64 hex char key decryption key displayed by TxM. The key decryption key is concatenated with 8 hex char checksum, that is the truncated SHA3-256 hash of the key decryption key. Once the 72 char hex key has been typed to Rx.py, the program decrypts the local key, adds it to the database, and shows a two char hex code that lets TxM know local key delivery succeeded. This is a pre-requisite for the next step, where account data/keys are transmitted to RxM, encrypted with the local key.

If RxM does not receive the encrypted local key, the user can send the packet again by typing "replay" instead of the device code. The local key transmission can later be redone with command /localkey.

First contact

The user is guided when adding the first contact. Tx.py first prompts for an account, e.g. [email protected] and a nick. Empty nick uses nick Alice (automatically parsed from the account). Tx.py asks the user to confirm account and nick (choosing no jumps to the beginning of account creation). After this Tx.py prompts the user to choose key exchange method:

Use PSK instead of ECDHE (y/n)?

PSK is explained later so here we choose n (=no) to start ECDHE key exchange.

ECDHE Private key generation

Tx.py generates a Curve25519 ECDHE private key by with PyNaCl's PrivateKey.generate(), that has been modified to XOR in either bitstring of 256 zeroes, or if provided, 256 bits string of external entropy. Tx.py always provides the external entropy:

ext_ent = PBKDF2-HMAC-SHA256(SHA3-256(32-bytes from /dev/urandom, salt)).

If Raspbian with HWRNG is available, Tx.py will add the salt to PBKDF2-mix:

salt = SHA3-256( Von Neumann whitening (HWRNG samples))

The number of samples is dynamic and depends on results. This ensures SHA3 always takes in 256 Von Neumann whitened samples. If HWRNG produces bad entropy i.e. a chunk of just zeroes/ones, they are not added as samples.

PyNaCl's PrivateKey.generate()'s own entropy is loaded after external entropy is provided, thus even if external entropy adds zero entropy, it does not weaken the overall security, as in such case it would be the equal of XORing PyNaCl's entropy with bitstring of zeroes.

Once the final entropy has been generated, PyNaCl will use it to create a private key object.

ECDHE Public key delivery and verification of key authenticity

Tx.py generates the ECDHE public key from the private key object and transmits it to the recipient's RxM. Once the 64 hex char public key of contact has been received by Rx.py the program shows it concatenated with an eight char hex checksum like the one in the local key. The user must manually type the 72 hex char string to TxM. The manual process ensures no automated data channel from the network can infect TxM after setup.

Once the key has been successfully entered, Tx.py will ask the user to use Signal by Open Whisper Systems to verify the authenticity of contact's public key.

MITM against OTR session is possible if the adversary has exfiltrated respective keys from endpoints of users. If users find out during Signal call that the public keys have been generated by a man in the middle, they do not have to throw in the towel. Instead, users can read the public keys to each other over Signal, manually type in the new public key and effectively bypass the MITM in the network. Once the public key has been successfully typed, Tx.py will log the public keys for later, higher assurance verification is done face to face. This also enables retrospective detection of MITM, if for some reason users are unable to verify the authenticity of public keys during key exchange.

Shared secret derivation

TxM is the only device that receives no automatically input data after setup, so it is the only device trusted to generate secret keys. Tx.py will generate two symmetric keys (one for encryption, one for decryption) by concatenating ECDHE shared secret with one of the public keys and passing them through PBKDF2-HMAC-SHA256 (25 000 iterations). This is mandatory for the forward secrecy to work. Forward secrecy is obtained by passing the encryption keys through PBKDF2-HMAC-SHA256 (1 000 iterations) between every message. Were the ECDHE shared secret used as the symmetric key for communication in both directions, offset in key derivation could lead to decryption of collected ciphertexts by iterating physically compromised key, that lacks behind in PBKDF2 iterations.

Once the symmetric keys are generated, the account details are encrypted and signed with XSalsa20-Poly1305 using the local key and transmitted to RxM, where the contact is automatically added. While this packet doesn't differ in any way from encrypted command, the user can still choose to prevent NH from receiving the ciphertext by plugging the TxM's data diode directly to RxM once the public key of contact has been received to RxM. During this period any message sent by contacts is not received. Dropped packets do not cause de-synchronization of keys, as each packet contains the number of times Tx.py has iterated the initial symmetric encryption key with PBKDF2. This means both receiving devices can catch up with the Tx.py's key. If Rx.py detects dropped packets, it will display the catch-up progress. KeyID introduces a DoS attack vector where packets with great keyID block Rx.py from operating. This would, however, require a MITM attack against the OTR, or messing with the NH; Such DoS would blow the cover of the high-strength attacker. A more covert DoS can be mitigated from within the network or IM server, so this attack is an unlikely problem. Were this evaluation incorrect, an efficient fix would be to use a separate static MAC key that signs the keyID. For now, it is not implemented. It should be mentioned that this DoS attack can also be mitigated by frenemies, but they are easy to block from the IM client.

Additional accounts can be added using commands

/dh <account>( <nick>)


/add <account>( <nick>)

PSK keys

While TFC-NaCl is all about convenient public key crypto with exfiltration secure private keys, it's security will not hold in the long run. Quantum computers are making their way albeit slowly, and in the future, any symmetric key agreed using Curve25519 ECDHE will be broken. The only public key algorithm secure against Shor's quantum algorithm is McEliece with Goppa Codes. Long-term security would require users to type in 1 000 000 bit public keys, which is highly inconvenient regardless of encoding used. As an initial answer to quantum computing, TFC-NaCl retains the possibility to create pre-shared symmetric keys for quantum-secure 256-bit XSalsa20-Poly1305:

Answering yes to PSK question, in the beginning, will create symmetric keys with PBKDF-HMAC-SHA256 (25 000 iterations), using SHA3-256(urandom(32)) as password, and if HWRNG connected to Raspbian (SSH or not) is available, by using SHA3-512(VN(256-bit HWRNG entropy)) as salt.

The contact's account, nick and the symmetric key for local decryption are sent to RxM, encrypted with the local key (again, NH doesn't have to be in between if the user so prefers). Tx.py then prompts the user for his or her account name.

If Alice is adding [email protected]as contact, a copy of her symmetric key is generated for Bob, and conveniently placed inside folder "PSKs", under the name

[email protected] - Give this file to [email protected]

Alice must then take a never before used thumb drive, copy the PSK to that and give it to Bob in a face-to-face meeting. Bob must also have generated a PSK for Alice in advance. Once Alice has received the thumb drive from Bob, she plugs it into her RxM (NOT TxM), and copies the

[email protected] - Give this file to [email protected]

keyfile to folder "keys". She must then restart Rx.py. Rx.py automatically strips the trailing instruction from the keyfile and adds Bob as a contact. Since keys for encrypting messages to Bob are already installed, once Bob has copied keyfile generated by Alice to his RxM, they are ready to communicate.

To ensure forward secrecy and privacy of messages, both parties MUST ensure the thumb drive given by their contact is physically destroyed immediately after keys have been added.

If contacts have already been generated, new PSKs can be generated with the command

/psk <account>( <nick>)

Other updates over TFC 0.5.5:

New local testing IPC

Local testing of TFC with a single computer now uses multiprocessing sockets instead of files. This reduces i/o errors also means fewer files in TFC root directory.

Data diode simulators

Added a program (dd.py) that emulates data diodes on screen. The user needs to enable dd_sockets boolean in Tx.py and NH.py with the argument -d before this can take place. Each dd.py program is launched with a set of arguments so that they know which sockets to use and to what direction data visually flows between terminals. An example set of commands that launches TFC on *buntu is

gnome-terminal --title='TxM' --geometry=100x35+0+630  -x sh -c "python /dir/Tx.py -d -l"
gnome-terminal --title='NH'  --geometry=71x41+920+150 -x sh -c "python /dir/NH.py -d -l"
gnome-terminal --title='RxM' --geometry=100x20+0+0    -x sh -c "python /dir/Rx.py -l"
gnome-terminal --title='dd'  --geometry=25x12+740+630 -x sh -c "python /dir/dd.py txnhlr"
gnome-terminal --title='dd'  --geometry=25x12+740+425 -x sh -c "python /dir/dd.py nhrxlr"

Setting keyboard shortcut or alias for each of these makes local test startup fast.

Serial interface auto-config

User's device configuration is asked during installation. This will configure Tx.py, NH.py and Rx.py to look for either integrated ttyS# (or ttyAMA0in the case of Raspbian) serial interface, or for a USB interface ttyUSB# if the user decides to use those. The serial interface numbering is finally automatically detected, so if the user accidentally pulls out serial interface, the user doesn't have to manually edit the interface if OS remaps /dev/ttyUSB0to /dev/ttyUSB1.

NH interface auto-flip

Removed the need to run NH.py with -f flag to flip interfaces: Tx.py will send a serial interface configuration packet to NH.py during start. In the event NH.py has mapped TxM and RxM in opposite interfaces, initial listener processes detect Tx.py's configuration packet (or any packet for that matter), and map correct interfaces to TxM and RxM.

Organized files to folders

Moved location of group files, keyfiles, received files and logfiles to respective folders. Renamed txc.tfcand rxc.tfc to dotfiles .tx_contacts and .rx_contacts. The only TFC files visible in software root directory are .py files (and syslog.tfconce it's first generated).

Added some metadata about TFC to packet headers

In future, this prevents inter-operation with older versions of TFC. It allows fingerprinting of users (only if NH is remotely exploited or OTR is being MITM attacked / not used) but forces users to keep up with security updates. New protocol uses more simple and mature separation of payload components.

Re-designed trickle connection

input_process() now prepares all data to packets with length in the range [0, 253] and puts the data into message or file queue. The queue is periodically read by sender_process() that opens a thread that pads, encrypts, signs, adds headers and outputs messages. XSalsa20-Poly1305 algorithms are designed to run constant time. The system time in ms is recorded immediately before function starts; Once the function finishes, the timestamp is passed to function trickle_delay() for evaluation. This function will then sleep the difference between Thread runtime and value trickle_c_delay to hide platform speed. Platforms should not introduce problems, as older Raspberry Pi (generation 2), only takes about 400ms to encrypt a message, leaving 1600ms headroom for slower SoC boards.

Changed random sleep to use /dev/urandom instead of Python math.random. The CSPRNG prevents statistical attacks that might be able to calculate the internal state of math.random's Mersenne Twister state to detect whether communication takes place. When print_ct_stats boolean is enabled, Tx.py shows the statistics about how long the message/command output thread ran, how long constant time sleep was added, and how well the constant time matched trickle_c_delay: Average error in constant time delay was 2ms so the CSPRNG based random sleep will hide the slight differences effectively. Tx.py will gracefully exit if thread runtime exceeds trickle_c_delay.

The packet_delay used to prevent congestion / IM server spam with long messages is now also constant time, to prevent slower data transmission with slower devices. The default value is 500ms which should be enough to hide metadata about TxM device performance and make hardware fingerprinting harder. This also makes the prediction of file transfer duration easier.

Improved logging

Logging can now be enabled for individual contacts with command /logging {on,off} <account>. When logging is enabled, in addition to automatic logging of public keys, Tx.py will also log messages sent to contact. This allows the two users to manually audit whether malware has at some point infected RxM device(s) and substituted words in log files, by cross-comparing the clean TxM logfile of Alice and purported RxM logfile of Bob, and vice versa. Rx.py also stores information about dropped packets. Malware that replaces received messages with notification about the dropped packet is harder to detect; offline record keeping might be required.

Tweakable checksums for data diode error detection

Changed CRC32 checksums to truncated SHA-256 hashes (8 hex = 32 bits) so data diode error detection accuracy can be tweaked at will by changing variable checksum_len. The checksum doesn't have any effect on security: it only detects transmission errors inside data diode.

Newly written NH.py

Converted classes to functions to fit with functional programming style. The program is now much more structured and easier to audit. Added processes that exchange data via queues. DBus / purple should no longer output error messages, nor should they drop packets. NH multiprocessing now cleanly exits.

Re-designed file transmission

/file 1.txt still sends the file from TxM directory. Absolute paths can also be defined. Non-TFC filenames in Tx.py directory are now included in the tab-complete list. Command "/file" opens a file dialog for easy file selection. Tx.py will first encode file data to base64 and load it into memory. It'll evaluate and display an approximate amount of packets and time required for file transfer, based on delay settings.

File data is prepended with a header that contains the name, extension, file size and estimated number of packets and delivery time for transfer. This information is displayed to the recipient after the first packet of the file has been received. File reception must be enabled.

The user can control global file reception by setting boolean file_saving to True. /store {on,off} enables/disables file reception for all contacts /store on [email protected] enables file reception for just Alice

By default boolean setting a_close_f_recv is True. This means file reception for Alice is closed automatically after the file has been received. When the variable is false, file storage will remain open until the user enters /store off [email protected] or /store off

During trickle connection, messages and files can be canceled with commands /cm and /cf. Already sent data is out of control of the user. Rx.py will discard received data when it receives cancel packet / new message/file, but this is a convenience feature, NOT a security feature. Sender-based control is snake oil: Nothing prevents the receiver from modifying their Rx.py to show/log partially transmitted data. The only solution to this would be to offer closed source version of TFC, which would defeat all security FOSS software is designed to offer.

Since the base64 encoding of transmitted files depends on TxM of contact, a frenemy could still send malware to user's RxM. Thus, the entire manual decoding feature was removed. The file is no longer generated with subprocess. This prevents shell injection with custom file names that would have otherwise become possible.

File data during trickle connection is loaded from a separate queue that has lower priority than message queue. This means that users can still exchange messages during file transmission: file data is output to contact when there are no messages to output. File data is indistinguishable from messages to NH, IM client, IM server and any adversary who might observe data from these systems. During trickle connection, it is extremely hard to determine whether the user is sending noise data, messages or files. No guarantees can, unfortunately, be made. Timing attacks are prone to side channel attacks anywhere from NH/smartphone microphone to the user moving the mouse on NH. Python is also a high-level language, which has its own problems.

Command line arguments

Added arguments for Tx.py, Rx.py, and NH.py to control many settings from the command line. View arguments by launching the program with -h or --help flag.

Hard coded packet length

Packet length was hardcoded to 254 to ensure everyone uses the same value, and to minimize the chance of errors when sending encrypted commands.


Wrote unittests for Tx.py, Rx.py, and NH.py to reduce the number of bugs. Created custom error classes for some functions for this purpose as well. Unittesting is a work in progress, so while TFC is more stable than ever, no guarantees are made.

New startup banner animation

I want to thank Tom Wallroth for his awesome work. I had so much fun editing his project to fit print_banner(). I'll leave the details of style as a surprise, check it out.

Made edits to installer

Installer now uses SHA256 instead of SHA512. As collision resistance against quantum computers is still 128 bits. The reason for this was the simplified import of file hashes when making changes.

More authentication was added with Trust-on-first-use (that being me) hashes for most downloaded libraries (PIP has no authentication). Provided that you can verify the setup.py's authenticity, you can detect MITM against crypto library downloads.

Added installation configuration for HWRNG Pi that TxM connects to over SSH. Ensured support for *buntu 16.04 OS and fixed issues with Lubuntu/Xubuntu in previous versions. Dropped NH support for Fedora and OpenSUSE and added it to Raspbian Jessie. Ensured NH configuration works with the latest release of Tails (2.2.1).

Changed installer naming style, instead of tfcOTPinstaller.py, it's just setup.py (run it as sudo).

TFC 0.5.5 | Update log





SHA-3 is immune against length extension attacks, thus the nested HMAC construction is not needed. However, according to paper* by Mostafa Taha and Patrick Schaumont, the optimal length of Keccak MAC-key is as close to (2 * rate - 1) as possible but not over the value. In the case of Keccak-512, the optimal key size is

(2 * 576 - 1) = 1151 bits ~ 143 bytes (1144 bits).


old construction:

entropy = (VN(VN(HWRNG samples))) ⊕ /dev/(u)random
Keccak256(common kb-entropy input + entropy)
    > independent 256-bit key (Keccak-CTR)
    > independent 256-bit key (XSalsa20)
    > independent 256-bit key (Twofish-CTR)
    > independent 256-bit key (AES(GCM))

As Von Neumann whitening requires HWRNG sampling to be a Bernoulli process, it was requested a single layer of compression with a hash function would be added between them. For feces and jollies, added two, plus a third VN whitening pass. New construction:

entropy = 
(VN(Keccak512'(VN(Keccak512'(VN(HWRNG samples)))))) ⊕ /dev/(u)random

Keccak512(kb1 + entropy) > independent 512-bit key (Keccak-CTR)
Keccak512(kb2 + entropy) > independent 512-bit key (XSalsa20)
Keccak512(kb3 + entropy) > independent 512-bit key (Twofish-CTR)
Keccak512(kb4 + entropy) > independent 512-bit key (AES(GCM))

Keccak512(kb5 + entropy) > independent 512-bit key (HMAC-SHA2-512)
Keccak512(kb6 + entropy) > independent 512-bit key (SHA3-512-MAC #1)
Keccak512(kb7 + entropy) > independent 512-bit key (SHA3-512-MAC #2)
Keccak512(kb8 + entropy) > independent 512-bit key (SHA3-512-MAC #3)

where kb1..8 is a separate input by the user from the keyboard.

The Keccak512' takes 1024 bits in and outputs 512 bits. This ensures
the entropy of input is larger than output unless HWRNG produces very 
bad entropy (4 bits / byte or less)).





Common updates


TFC 0.5.4 Hotfix



TFC 0.5.4 update log

















TFC 0.4.13 update log

TFC 0.4.11 update log

TFC 0.4.10 update log

TFC 0.4.7 update log

TFC 0.4.6 update log

TFC 0.4.5 update log

TFC 0.4.4 Hotfix

TFC 0.4.4 update log

TFC 0.4.3 update log

TFC 0.4.2 update log

*In retrospect this home-brew implementation was horrible but then again, I had only started with crypto and didn't know what a MAC was.