0.16.10
This commit is contained in:
parent
3a83d717bd
commit
a8c4eae95f
1003
LICENSE.md
1003
LICENSE.md
File diff suppressed because it is too large
Load Diff
186
dd.py
186
dd.py
|
@ -1,27 +1,28 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# TFC-NaCl 0.16.05 || dd.py
|
||||
# TFC 0.16.10 || dd.py
|
||||
|
||||
"""
|
||||
GPL License
|
||||
Copyright (C) 2013-2016 Markus Ottela
|
||||
|
||||
This software is part of the TFC application, which is free software: You can
|
||||
redistribute it and/or modify it under the terms of the GNU General Public
|
||||
License as published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
This file is part of TFC.
|
||||
|
||||
TFC is free software: you can redistribute it and/or modify it under the terms
|
||||
of the GNU General Public License as published by the Free Software Foundation,
|
||||
either version 3 of the License, or (at your option) any later version.
|
||||
|
||||
TFC 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 General Public License for more details. For
|
||||
a copy of the GNU General Public License, see <http://www.gnu.org/licenses/>.
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
TFC. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import multiprocessing.connection
|
||||
import multiprocessing
|
||||
import os
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
|
@ -31,6 +32,8 @@ import time
|
|||
###############################################################################
|
||||
|
||||
def lr_upper():
|
||||
"""Print high signal frame (left to right)."""
|
||||
|
||||
print("""
|
||||
Data flow
|
||||
→
|
||||
|
@ -42,6 +45,8 @@ GND━━━┿━┥ ├──Rx ├──GND
|
|||
|
||||
|
||||
def lr_lower():
|
||||
"""Print low signal frame (left to right)."""
|
||||
|
||||
print("""
|
||||
Data flow
|
||||
→
|
||||
|
@ -53,6 +58,8 @@ GND━━━┿━┥ ├──Rx ├──GND
|
|||
|
||||
|
||||
def lr_idle():
|
||||
"""Print no signal frame (left to right)."""
|
||||
|
||||
print("""
|
||||
Data flow
|
||||
→
|
||||
|
@ -64,6 +71,8 @@ GND━━━┿━┥ ├──Rx ├──GND
|
|||
|
||||
|
||||
def rl_upper():
|
||||
"""Print high signal frame (right to left)."""
|
||||
|
||||
print("""
|
||||
Data flow
|
||||
←
|
||||
|
@ -75,6 +84,8 @@ GND──┤ Rx──┤ ┝━┿━━━GND
|
|||
|
||||
|
||||
def rl_lower():
|
||||
"""Print low signal frame (right to left)."""
|
||||
|
||||
print("""
|
||||
Data flow
|
||||
←
|
||||
|
@ -86,6 +97,8 @@ GND──┤ Rx──┤ ┝━┿━━━GND
|
|||
|
||||
|
||||
def rl_idle():
|
||||
"""Print no signal frame (right to left)."""
|
||||
|
||||
print("""
|
||||
Data flow
|
||||
←
|
||||
|
@ -101,6 +114,8 @@ GND──┤ Rx──┤ ┝━┿━━━GND
|
|||
###############################################################################
|
||||
|
||||
def lr():
|
||||
"""Draw animation (left to right)."""
|
||||
|
||||
for _ in range(10):
|
||||
os.system("clear")
|
||||
lr_lower()
|
||||
|
@ -112,6 +127,8 @@ def lr():
|
|||
|
||||
|
||||
def rl():
|
||||
"""Draw animation (right to left)."""
|
||||
|
||||
for _ in range(10):
|
||||
os.system("clear")
|
||||
rl_lower()
|
||||
|
@ -127,6 +144,7 @@ def rl():
|
|||
###############################################################################
|
||||
|
||||
def tx_process():
|
||||
"""Process that reads from sending computer."""
|
||||
|
||||
if tx_nh_lr or nh_rx_rl:
|
||||
lr_idle()
|
||||
|
@ -153,100 +171,106 @@ def tx_process():
|
|||
|
||||
|
||||
def rx_process():
|
||||
"""Process that sends to receiving computer."""
|
||||
|
||||
def ipc_to_queue(conn):
|
||||
"""
|
||||
Load packet from IPC.
|
||||
|
||||
:param conn: Listener object
|
||||
:return: [no return value]
|
||||
"""
|
||||
|
||||
while True:
|
||||
time.sleep(0.001)
|
||||
pkg = str(conn.recv())
|
||||
pkg = conn.recv()
|
||||
io_queue.put(pkg)
|
||||
try:
|
||||
l = multiprocessing.connection.Listener(('', input_socket))
|
||||
l = multiprocessing.connection.Listener(("localhost", input_socket))
|
||||
while True:
|
||||
ipc_to_queue(l.accept())
|
||||
except EOFError:
|
||||
exit_queue.put("exit")
|
||||
|
||||
|
||||
tx_nh_lr = False
|
||||
nh_rx_lr = False
|
||||
tx_nh_rl = False
|
||||
nh_rx_rl = False
|
||||
if __name__ == "__main__":
|
||||
|
||||
input_socket = 0
|
||||
output_socket = 0
|
||||
tx_nh_lr = False
|
||||
nh_rx_lr = False
|
||||
tx_nh_rl = False
|
||||
nh_rx_rl = False
|
||||
|
||||
# Resize terminal
|
||||
id_cmd = "xdotool getactivewindow"
|
||||
resize_cmd = "xdotool windowsize --usehints {id} 25 12"
|
||||
proc = subprocess.Popen(shlex.split(id_cmd), stdout=subprocess.PIPE)
|
||||
windowid, err = proc.communicate()
|
||||
proc = subprocess.Popen(shlex.split(resize_cmd.format(id=windowid)))
|
||||
proc.communicate()
|
||||
input_socket = 0
|
||||
output_socket = 0
|
||||
|
||||
try:
|
||||
# Simulates data diode between Tx.py on left, NH.py on right.
|
||||
if str(sys.argv[1]) == "txnhlr":
|
||||
tx_nh_lr = True
|
||||
input_socket = 5000
|
||||
output_socket = 5001
|
||||
# Resize terminal
|
||||
# sys.stdout.write("\x1b[8;{rows};{cols}t".format(rows=25, cols=12))
|
||||
|
||||
# Simulates data diode between Tx.py on right, NH.py on left.
|
||||
elif str(sys.argv[1]) == "txnhrl":
|
||||
tx_nh_rl = True
|
||||
input_socket = 5000
|
||||
output_socket = 5001
|
||||
try:
|
||||
# Simulates data diode between Tx.py on left, NH.py on right.
|
||||
if str(sys.argv[1]) == "txnhlr":
|
||||
tx_nh_lr = True
|
||||
input_socket = 5000
|
||||
output_socket = 5001
|
||||
|
||||
# Simulates data diode between Rx.py on left, NH.py on right.
|
||||
elif str(sys.argv[1]) == "nhrxlr":
|
||||
nh_rx_lr = True
|
||||
input_socket = 5002
|
||||
output_socket = 5003
|
||||
# Simulates data diode between Tx.py on right, NH.py on left.
|
||||
elif str(sys.argv[1]) == "txnhrl":
|
||||
tx_nh_rl = True
|
||||
input_socket = 5000
|
||||
output_socket = 5001
|
||||
|
||||
# Simulates data diode between Rx.py on right, NH.py on left.
|
||||
elif str(sys.argv[1]) == "nhrxrl":
|
||||
nh_rx_rl = True
|
||||
input_socket = 5002
|
||||
output_socket = 5003
|
||||
# Simulates data diode between Rx.py on left, NH.py on right.
|
||||
elif str(sys.argv[1]) == "nhrxlr":
|
||||
nh_rx_lr = True
|
||||
input_socket = 5002
|
||||
output_socket = 5003
|
||||
|
||||
else:
|
||||
# Simulates data diode between Rx.py on right, NH.py on left.
|
||||
elif str(sys.argv[1]) == "nhrxrl":
|
||||
nh_rx_rl = True
|
||||
input_socket = 5002
|
||||
output_socket = 5003
|
||||
|
||||
else:
|
||||
os.system("clear")
|
||||
print("\nUsage: python dd.py {txnh{lr,rl}, nhrx{lr,rl}\n")
|
||||
exit()
|
||||
|
||||
except IndexError:
|
||||
os.system("clear")
|
||||
print("\nUsage: python dd.py {txnh{lr,rl}, nhrx{lr,rl}\n")
|
||||
print("\nUsage: python dd.py {txnh{lr,rl}, nhrx{lr,rl}}\n")
|
||||
exit()
|
||||
|
||||
except IndexError:
|
||||
os.system("clear")
|
||||
print("\nUsage: python dd.py {txnh{lr,rl}, nhrx{lr,rl}}\n")
|
||||
exit()
|
||||
try:
|
||||
print("Waiting for socket")
|
||||
ipx_send = multiprocessing.connection.Client(("localhost",
|
||||
output_socket))
|
||||
print("Connection established.")
|
||||
time.sleep(0.3)
|
||||
os.system("clear")
|
||||
except KeyboardInterrupt:
|
||||
exit()
|
||||
|
||||
try:
|
||||
print("Waiting for socket")
|
||||
ipx_send = multiprocessing.connection.Client(("localhost", output_socket))
|
||||
print("Connection established.")
|
||||
time.sleep(0.3)
|
||||
os.system("clear")
|
||||
except KeyboardInterrupt:
|
||||
exit()
|
||||
exit_queue = multiprocessing.Queue()
|
||||
io_queue = multiprocessing.Queue()
|
||||
|
||||
exit_queue = multiprocessing.Queue()
|
||||
io_queue = multiprocessing.Queue()
|
||||
txp = multiprocessing.Process(target=tx_process)
|
||||
rxp = multiprocessing.Process(target=rx_process)
|
||||
|
||||
txp = multiprocessing.Process(target=tx_process)
|
||||
rxp = multiprocessing.Process(target=rx_process)
|
||||
txp.start()
|
||||
rxp.start()
|
||||
|
||||
txp.start()
|
||||
rxp.start()
|
||||
try:
|
||||
while True:
|
||||
if not exit_queue.empty():
|
||||
command = exit_queue.get()
|
||||
if command == "exit":
|
||||
txp.terminate()
|
||||
rxp.terminate()
|
||||
exit()
|
||||
time.sleep(0.01)
|
||||
|
||||
try:
|
||||
while True:
|
||||
if not exit_queue.empty():
|
||||
command = exit_queue.get()
|
||||
if command == "exit":
|
||||
txp.terminate()
|
||||
rxp.terminate()
|
||||
exit()
|
||||
time.sleep(0.01)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
txp.terminate()
|
||||
rxp.terminate()
|
||||
exit()
|
||||
except KeyboardInterrupt:
|
||||
txp.terminate()
|
||||
rxp.terminate()
|
||||
exit()
|
||||
|
|
108
hwrng-nacl.py
108
hwrng-nacl.py
|
@ -1,108 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# TFC-NaCl 0.16.05 || hwrng-nacl.py
|
||||
|
||||
"""
|
||||
GPL License
|
||||
|
||||
This software is part of the TFC application, which is free software: You can
|
||||
redistribute it and/or modify it under the terms of the GNU General Public
|
||||
License as published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
TFC 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 General Public License for more details. For
|
||||
a copy of the GNU General Public License, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import binascii
|
||||
import time
|
||||
|
||||
try:
|
||||
import RPi.GPIO as GPIO
|
||||
except ImportError:
|
||||
GPIO = None
|
||||
pass
|
||||
|
||||
|
||||
###############################################################################
|
||||
# CONFIGURATION #
|
||||
###############################################################################
|
||||
|
||||
sample_delay = 0.1 # Delay in seconds between samples
|
||||
|
||||
gpio_port = 4 # RPi's GPIO pin (Broadcom layout) to collect entropy from.
|
||||
|
||||
|
||||
###############################################################################
|
||||
# MAIN #
|
||||
###############################################################################
|
||||
|
||||
def main():
|
||||
"""
|
||||
Load 32 bytes of entropy from HWRNG.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
def digits_to_bytes(di):
|
||||
"""
|
||||
Convert string of binary digits to byte string.
|
||||
|
||||
:param di: Digit string.
|
||||
:return: Byte string.
|
||||
"""
|
||||
|
||||
return ''.join(chr(int(di[i:i + 8], 2)) for i in xrange(0, len(di), 8))
|
||||
|
||||
try:
|
||||
w0 = 0
|
||||
w1 = 0
|
||||
while True:
|
||||
if w1 > 1500 and w0 > 1500:
|
||||
break
|
||||
if GPIO.input(gpio_port) == 1:
|
||||
w1 += 1
|
||||
else:
|
||||
w0 += 1
|
||||
time.sleep(0.001)
|
||||
|
||||
# Perform Von Neumann whitening during sampling
|
||||
vn_digits = ''
|
||||
|
||||
while True:
|
||||
|
||||
if len(vn_digits) >= 256:
|
||||
break
|
||||
|
||||
first_bit = GPIO.input(gpio_port)
|
||||
time.sleep(sample_delay)
|
||||
|
||||
second_bit = GPIO.input(gpio_port)
|
||||
time.sleep(sample_delay)
|
||||
|
||||
if first_bit == second_bit:
|
||||
continue
|
||||
else:
|
||||
vn_digits += str(first_bit)
|
||||
|
||||
entropy = digits_to_bytes(vn_digits)
|
||||
|
||||
if len(entropy) != 32:
|
||||
print("ERROR")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
GPIO.cleanup()
|
||||
raise
|
||||
|
||||
GPIO.cleanup()
|
||||
print(binascii.hexlify(entropy))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
GPIO.setmode(GPIO.BCM)
|
||||
GPIO.setup(gpio_port, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
|
||||
|
||||
main()
|
167
readme.md
167
readme.md
|
@ -1,109 +1,134 @@
|
|||
<img align="right" src="https://cs.helsinki.fi/u/oottela/tfclogo.png" style="position: relative; top: 0; left: 0;">
|
||||
|
||||
|
||||
###Tinfoil Chat NaCl
|
||||
###Tinfoil Chat
|
||||
|
||||
Tinfoil Chat (TFC) is a high assurance encrypted messaging system that
|
||||
operates on top of existing IM clients. The
|
||||
[free and open source software](https://en.wikipedia.org/wiki/Free_and_open-source_software)
|
||||
is used together with free hardware to protect users from
|
||||
[passive eavesdropping](https://en.wikipedia.org/wiki/Upstream_collection),
|
||||
[active MITM attacks](https://en.wikipedia.org/wiki/Man-in-the-middle_attack)
|
||||
and [remote CNE](https://www.youtube.com/watch?v=3euYBPlX9LM) practised by
|
||||
organized crime and nation state attackers.
|
||||
|
||||
TFC-NaCl is a high assurance encrypted messaging system that operates on top of
|
||||
existing IM clients. The free and open source software is used in conjunction
|
||||
with free hardware to protect users from passive eavesdropping, active MITM
|
||||
attacks and remote CNE practised by organized crime and state-level adversaries.
|
||||
[XSalsa20](https://cr.yp.to/snuffle/salsafamily-20071225.pdf) encryption
|
||||
and [Poly1305-AES](https://cr.yp.to/mac/poly1305-20050329.pdf) MACs provide
|
||||
[end-to-end encrypted](https://en.wikipedia.org/wiki/End-to-end_encryption)
|
||||
communication with [deniable authentication](https://en.wikipedia.org/wiki/Deniable_encryption#Deniable_authentication): Symmetric keys are either
|
||||
pre-shared, or exchanged using [Curve25519 ECDHE](https://cr.yp.to/ecdh/curve25519-20060209.pdf), the public
|
||||
keys of which are verified via off-band channel.
|
||||
|
||||
TFC-NaCl uses XSalsa-20-Poly1305 AEAD that provides forward secrecy and
|
||||
deniability. Symmetric keys are either pre-shared, or agreed using Curve25519
|
||||
ECDHE key exchange.
|
||||
Key generation relies on Kernel CSPRNG, but also supports mixing of
|
||||
external entropy from [open circuit design HWRNG](http://holdenc.altervista.org/avalanche/),
|
||||
that is sampled by a [RPi](https://www.raspberrypi.org/) through it's
|
||||
GPIO interface either natively, or remotely (SSH over direct ethernet
|
||||
cable). TFC provides per-message forward secrecy with PBKDF2-HMAC-SHA256
|
||||
[hash ratchet](https://en.wikipedia.org/wiki/Double_Ratchet_Algorithm).
|
||||
|
||||
Key generation utilizes Kernel CSPRNG, but additionally, further entropy can be
|
||||
loaded from open circuit design HWRNG, that is sampled by a [RPi](https://www.raspberrypi.org/)
|
||||
through it's GPIO pins either natively, or via SSH. Forward secrecy is obtained
|
||||
with hash ratchet based on PBKDF2-HMAC-SHA256, where the 256-bit key is changed
|
||||
after every message.
|
||||
The software is used in hardware configuration that provides strong
|
||||
endpoint security: Encryption and decryption are separated on two
|
||||
isolated computers. The split [TCB](https://en.wikipedia.org/wiki/Trusted_computing_base)
|
||||
interacts with a third, networked computer through unidirectional [serial](https://en.wikipedia.org/wiki/RS-232)
|
||||
interfaces. Direction of data flow is enforced with free hardware design
|
||||
[data diodes](https://en.wikipedia.org/wiki/Unidirectional_network); Lack
|
||||
of bidirectional channels to isolated computers prevents insertion of
|
||||
malware to the encrypting computer and exfiltration of keys and plaintexts
|
||||
from the decrypting computer -- even with exploits against [zero-day vulnerabilities](https://en.wikipedia.org/wiki/Zero-day_(computing))
|
||||
in software and operating systems of the TCB halves.
|
||||
|
||||
The software is used in configuration that provides strong endpoint security.
|
||||
It does this by separating encryption and decryption on separate, isolated
|
||||
computers, that interact with a networked computer through unidirectional
|
||||
serial interfaces. Direction of data flow is enforced with open circuit design
|
||||
hardware data diodes; lack of bidirectional channels prevents exfiltration of
|
||||
keys and plaintexts even with exploits against zero-day vulnerabilities in
|
||||
software and operating systems of TCBs.
|
||||
|
||||
TFC defeats metadata about quantity and schedule of communication with trickle
|
||||
connection that outputs constant stream of encrypted noise data. Covert file
|
||||
transfer can take place in background during the trickle connection.
|
||||
|
||||
TFC also supports multicasting of messages to enable basic group messaging.
|
||||
TFC supports multiple IM accounts per user to hide the network structure
|
||||
of communicating parties, even during end-to-end encrypted group
|
||||
conversations.
|
||||
|
||||
TFC allows a group or two parties to defeat metadata about quantity and
|
||||
schedule of communication with trickle connection, where messages are
|
||||
inserted into a constant stream of encrypted noise traffic. Covert file
|
||||
transfer can take place in background during conversation over the
|
||||
trickle connection.
|
||||
|
||||
###How it works
|
||||
|
||||
![](https://cs.helsinki.fi/u/oottela/tfc_graph2.png)
|
||||
![](https://cs.helsinki.fi/u/oottela/tfcwiki/tfc_overview.png)
|
||||
|
||||
TFC uses three computers per endpoint. Alice enters her commands and messages to
|
||||
program Tx.py running on her Transmitter computer (TxM), a [TCB](https://en.wikipedia.org/wiki/Trusted_computing_base)
|
||||
separated from network. Tx.py encrypts and signs plaintext data and relays it
|
||||
to receiver computers (RxM) via networked computer (NH) through RS-232 interface
|
||||
and a data diode.
|
||||
TFC uses three computers per endpoint. Alice enters her messages and
|
||||
commands to program Tx.py running on her transmitter computer (TxM), a
|
||||
TCB separated from network. Tx.py encrypts and signs plaintext data and
|
||||
relays the ciphertext from TxM to her networked computer (NH) trough a
|
||||
serial (RS-232) interface and a hardware data diode.
|
||||
|
||||
Depending on packet type, the program NH.py running on Alice's NH forwards
|
||||
packets from TxM-side serial interface to Pidgin and local RxM (through another
|
||||
RS-232 interface and data diode). Local RxM authenticates and decrypts received
|
||||
data before processing it.
|
||||
Messages and commands received to NH are relayed to IM client (Pidgin or
|
||||
Finch), and to Alice's receiver computer (RxM) via another serial interface
|
||||
and data diode. The program Rx.py on Alice's RxM authenticates, decrypts
|
||||
and processes the received messages and commands.
|
||||
|
||||
Pidgin sends the packet either directly or through Tor network to IM server,
|
||||
that then forwards it directly (or again through Tor) to Bob.
|
||||
The IM client sends the packet either directly or through Tor network to
|
||||
IM server, that then forwards it directly (or again through Tor) to Bob.
|
||||
|
||||
NH.py on Bob's NH receives Alice's packet from Pidgin, and forwards it through
|
||||
RS-232 interface and data diode to Bob's RxM, where the ciphertext is
|
||||
authenticated, decrypted, and processed. When the Bob responds, he will send
|
||||
the message/file using his TxM and in the end Alice reads the message from her RxM.
|
||||
IM client on Bob's NH forwards packet to NH.py, that then forwards it to
|
||||
Bob's RxM (again through data diode enforced serial interface). Bob's
|
||||
Rx.py on his RxM then authenticates, decrypts, and processes the packet.
|
||||
|
||||
When the Bob responds, he will send the message using Tx.py on his
|
||||
TxM and in the end, Alice reads the message from Rx.py on her RxM.
|
||||
|
||||
|
||||
###Why keys can not be exfiltrated
|
||||
|
||||
1. Malware that exploits an unknown vulnerability in RxM can infiltrate to
|
||||
the system, but is unable to exfiltrate keys or plaintexts, as data diode prevents
|
||||
all outbound traffic.
|
||||
1. Malware that exploits an unknown vulnerability in RxM can infiltrate
|
||||
the system, but is unable to exfiltrate keys or plaintexts, as data
|
||||
diode prevents all outbound traffic.
|
||||
|
||||
2. Malware can not breach TxM as data diode prevents all inbound traffic. The
|
||||
only data input from RxM to TxM is the 72 char public key, manually typed by
|
||||
user.
|
||||
2. Malware can not infiltrate TxM as data diode prevents all inbound
|
||||
traffic. The only data input to TxM is the public key of contact, which
|
||||
is manually typed by the user.
|
||||
|
||||
3. The NH is assumed to be compromised, but unencrypted data never touches it.
|
||||
3. The NH is assumed to be compromised: all sensitive data that passes
|
||||
through NH is always encrypted and signed.
|
||||
|
||||
![](https://cs.helsinki.fi/u/oottela/tfc_attacks2.png)
|
||||
![](https://cs.helsinki.fi/u/oottela/tfcwiki/tfc_attacks.png)
|
||||
|
||||
Optical repeater inside the optocoupler of the data diode (below) enforces
|
||||
direction of data transmission.
|
||||
Optical repeater inside the [optocoupler](https://en.wikipedia.org/wiki/Opto-isolator)
|
||||
of the data diode (below) enforces direction of data transmission with
|
||||
the laws of physics.
|
||||
|
||||
<img src="https://cs.helsinki.fi/u/oottela/data_diode.png" align="center" width="74%" height="74%"/>
|
||||
<img src="https://cs.helsinki.fi/u/oottela/tfcwiki/pbdd.jpg" align="center" width="74%" height="74%"/>
|
||||
|
||||
###Supported Operating Systems
|
||||
|
||||
####TxM and RxM
|
||||
- *buntu 16.04
|
||||
- Linux Mint 17.3 Rosa
|
||||
- Raspbian Jessie
|
||||
- Linux Mint 18 Sarah
|
||||
- Raspbian Jessie (Only use RPi version
|
||||
[1](https://www.raspberrypi.org/products/model-b-plus/) or
|
||||
[2](https://www.raspberrypi.org/products/raspberry-pi-2-model-b/))
|
||||
|
||||
####NH
|
||||
- Tails 2.3
|
||||
- *buntu 16.04,
|
||||
- Linux Mint 17.3 Rosa
|
||||
- Tails 2.6
|
||||
- *buntu 16.04
|
||||
- Linux Mint 18 Sarah
|
||||
- Raspbian Jessie
|
||||
|
||||
###Installation
|
||||
[![Installation](http://img.youtube.com/vi/D5pDoJZj2Uw/0.jpg)](http://www.youtube.com/watch?v=D5pDoJZj2Uw)
|
||||
|
||||
|
||||
###How to use
|
||||
[![Use](http://img.youtube.com/vi/tH8qbl1USoo/0.jpg)](http://www.youtube.com/watch?v=tH8qbl1USoo)
|
||||
|
||||
|
||||
###More information
|
||||
|
||||
White paper and manual for previous versions are listed below. Version specific
|
||||
updates are listed in the updatelog. Updated white paper and documentation are
|
||||
under work.
|
||||
[Threat model](https://github.com/maqp/tfc-backup/wiki/Threat-model)<br>
|
||||
[FAQ](https://github.com/maqp/tfc-backup/wiki/FAQ)<br>
|
||||
|
||||
White paper: https://cs.helsinki.fi/u/oottela/tfc.pdf
|
||||
In depth<br>
|
||||
[Security design](https://github.com/maqp/tfc-backup/wiki/Security-design)<br>
|
||||
[Protocol](https://github.com/maqp/tfc-backup/wiki/Protocol)<br>
|
||||
|
||||
Manual: https://cs.helsinki.fi/u/oottela/tfc-manual.pdf
|
||||
Hardware<br>
|
||||
[Hardware configurations](https://github.com/maqp/tfc-backup/wiki/Hardware-configurations)<br>
|
||||
|
||||
[Data diode (perfboard)](https://github.com/maqp/tfc-backup/wiki/Data-Diode-(perfboard))<br>
|
||||
[Data diode (point to point)](https://github.com/maqp/tfc-backup/wiki/Data-diode-(point-to-point))<br>
|
||||
|
||||
[HWRNG (perfboard)](https://github.com/maqp/tfc-backup/wiki/HWRNG-(perfboard))<br>
|
||||
[HWRNG (breadboard)](https://github.com/maqp/tfc-backup/wiki/HWRNG-(breadboard))<br>
|
||||
|
||||
Software<Br>
|
||||
[Installation](https://github.com/maqp/tfc-backup/wiki/Installation)<br>
|
||||
[How to use](https://github.com/maqp/tfc-backup/wiki/How-to-use)<br>
|
||||
|
||||
[Update Log](https://github.com/maqp/tfc-backup/wiki/Update-Log)<br>
|
|
@ -1,111 +1,104 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# TFC-CEV 0.16.05 || test_nh.py
|
||||
# TFC 0.16.10 || test_nh.py
|
||||
|
||||
"""
|
||||
GPL License
|
||||
Copyright (C) 2013-2016 Markus Ottela
|
||||
|
||||
This software is part of the TFC application, which is free software: You can
|
||||
redistribute it and/or modify it under the terms of the GNU General Public
|
||||
License as published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
This file is part of TFC.
|
||||
|
||||
TFC is free software: you can redistribute it and/or modify it under the terms
|
||||
of the GNU General Public License as published by the Free Software Foundation,
|
||||
either version 3 of the License, or (at your option) any later version.
|
||||
|
||||
TFC 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 General Public License for more details. For
|
||||
a copy of the GNU General Public License, see <http://www.gnu.org/licenses/>.
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
TFC. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import NH
|
||||
from NH import *
|
||||
import binascii
|
||||
import unittest
|
||||
|
||||
# Import crypto libraries
|
||||
import hashlib
|
||||
|
||||
|
||||
###############################################################################
|
||||
# UNITTEST HELPERS #
|
||||
###############################################################################
|
||||
|
||||
def ut_sha2_256(message):
|
||||
h_function = hashlib.sha256()
|
||||
h_function.update(message)
|
||||
return binascii.hexlify(h_function.digest())
|
||||
not_str = [1, 1.0, True]
|
||||
not_int = ["string", 1.0]
|
||||
not_tup = [1.0, "string", 1, True]
|
||||
|
||||
|
||||
###############################################################################
|
||||
# HELPERS #
|
||||
# MISC #
|
||||
###############################################################################
|
||||
|
||||
NH.unittesting = True
|
||||
class TestGracefulExit(unittest.TestCase):
|
||||
|
||||
class TestGracefulExit(unittest.TestCase):
|
||||
def test_1_input_parameter(self):
|
||||
for a in not_str:
|
||||
with self.assertRaises(SystemExit):
|
||||
graceful_exit(a)
|
||||
|
||||
def test_2_function(self):
|
||||
with self.assertRaises(SystemExit):
|
||||
graceful_exit()
|
||||
|
||||
|
||||
class TestPrintBanner(unittest.TestCase):
|
||||
|
||||
def test_1_non_rpi(self):
|
||||
|
||||
# Setup
|
||||
NH.rpi_os = False
|
||||
|
||||
# Test
|
||||
self.assertIsNone(print_banner())
|
||||
|
||||
def test_2_rpi(self):
|
||||
|
||||
# Setup
|
||||
NH.rpi_os = True
|
||||
|
||||
# Test
|
||||
self.assertIsNone(print_banner())
|
||||
|
||||
|
||||
class TestInputValidation(unittest.TestCase):
|
||||
|
||||
def test_1_input_parameter(self):
|
||||
for a in not_tup:
|
||||
with self.assertRaises(TypeError):
|
||||
input_validation(a)
|
||||
|
||||
|
||||
class TestPhase(unittest.TestCase):
|
||||
|
||||
def test_1_input_parameters(self):
|
||||
for a in [1, 1.0, True]:
|
||||
for b in ["string", 1.0, True]:
|
||||
for a in not_str:
|
||||
for b in not_int:
|
||||
with self.assertRaises(SystemExit):
|
||||
phase(a, b)
|
||||
|
||||
def test_2_output_type(self):
|
||||
self.assertIsNone(phase("test", 10))
|
||||
print("Done.")
|
||||
self.assertIsNone(phase("\ntest", 10))
|
||||
print("Done.")
|
||||
self.assertIsNone(phase("\n\n\ntest", 10))
|
||||
print("Done.")
|
||||
|
||||
|
||||
class TestSHA256(unittest.TestCase):
|
||||
|
||||
def test_1_input_parameter(self):
|
||||
for a in [1, 1.0, True]:
|
||||
with self.assertRaises(SystemExit):
|
||||
sha2_256(a)
|
||||
|
||||
def test_2_SHA256_vector(self):
|
||||
"""
|
||||
Test SHA256 with official test vector:
|
||||
http://csrc.nist.gov/groups/ST/toolkit/
|
||||
documents/Examples/SHA_All.pdf // page 14
|
||||
"""
|
||||
|
||||
self.assertEqual(sha2_256("abc"), "ba7816bf8f01cfea414140de5dae2223"
|
||||
"b00361a396177a9cb410ff61f20015ad")
|
||||
|
||||
|
||||
class TestVerifyChecksum(unittest.TestCase):
|
||||
|
||||
def test_1_input_parameter(self):
|
||||
for a in [1, 1.0, True]:
|
||||
with self.assertRaises(SystemExit):
|
||||
verify_checksum(a)
|
||||
|
||||
def test_2_function(self):
|
||||
pt = "test_packet"
|
||||
tv = ut_sha2_256(pt)
|
||||
self.assertTrue(verify_checksum("%s|%s" % (pt, tv[:NH.checksum_len])))
|
||||
|
||||
|
||||
class TestGracefulExit(unittest.TestCase):
|
||||
class TestClearScreen(unittest.TestCase):
|
||||
|
||||
def test_1_function(self):
|
||||
with self.assertRaises(SystemExit):
|
||||
graceful_exit()
|
||||
|
||||
|
||||
class TestGetTTyWH(unittest.TestCase):
|
||||
|
||||
def test_output_types(self):
|
||||
w, h = get_tty_wh()
|
||||
|
||||
self.assertTrue(isinstance(w, int))
|
||||
self.assertTrue(isinstance(h, int))
|
||||
|
||||
|
||||
class TestPrintBanner(unittest.TestCase):
|
||||
|
||||
def test_output_type(self):
|
||||
self.assertIsNone(print_banner())
|
||||
self.assertIsNone(clear_screen())
|
||||
|
||||
|
||||
class TestGetSerialInterfaces(unittest.TestCase):
|
||||
|
@ -114,95 +107,150 @@ class TestGetSerialInterfaces(unittest.TestCase):
|
|||
"""
|
||||
|
||||
|
||||
###############################################################################
|
||||
# RECEIVER #
|
||||
###############################################################################
|
||||
|
||||
class TestDBusReceiver(unittest.TestCase):
|
||||
"""
|
||||
This function doesn't have any tests yet.
|
||||
"""
|
||||
|
||||
|
||||
class TestPidginToRxMQueue(unittest.TestCase):
|
||||
"""
|
||||
This function doesn't have any tests yet.
|
||||
"""
|
||||
|
||||
|
||||
class TestPidginReceiverProcess(unittest.TestCase):
|
||||
"""
|
||||
This function doesn't have any tests yet.
|
||||
"""
|
||||
|
||||
|
||||
###############################################################################
|
||||
# OTHER #
|
||||
###############################################################################
|
||||
|
||||
class TestHeaderPrinterProcess(unittest.TestCase):
|
||||
"""
|
||||
This function doesn't have any tests yet.
|
||||
"""
|
||||
|
||||
|
||||
class TestNHSideCommandProcess(unittest.TestCase):
|
||||
"""
|
||||
This function doesn't have any tests yet.
|
||||
"""
|
||||
|
||||
|
||||
class TestQueueToPidginProcess(unittest.TestCase):
|
||||
"""
|
||||
This function doesn't have any tests yet.
|
||||
"""
|
||||
|
||||
|
||||
class NHToRxMSenderProcess(unittest.TestCase):
|
||||
"""
|
||||
This function doesn't have any tests yet.
|
||||
"""
|
||||
|
||||
|
||||
###############################################################################
|
||||
# PACKETS FROM TxM #
|
||||
###############################################################################
|
||||
|
||||
class TestChooseTxMPacketQueues(unittest.TestCase):
|
||||
|
||||
def test_1_input_parameter(self):
|
||||
for a in [1, 1.0, True]:
|
||||
with self.assertRaises(SystemExit):
|
||||
choose_txm_packet_queues(a)
|
||||
|
||||
|
||||
class TestTxMPacketLoadProcess(unittest.TestCase):
|
||||
"""
|
||||
This function doesn't have any tests yet.
|
||||
"""
|
||||
|
||||
|
||||
class TestRxMPortListener(unittest.TestCase):
|
||||
"""
|
||||
This function doesn't have any tests yet.
|
||||
"""
|
||||
|
||||
|
||||
class TestTxMPortListener(unittest.TestCase):
|
||||
"""
|
||||
This function doesn't have any tests yet.
|
||||
"""
|
||||
|
||||
|
||||
class TestProcessArguments(unittest.TestCase):
|
||||
"""
|
||||
This function doesn't have any tests yet.
|
||||
"""
|
||||
|
||||
|
||||
###############################################################################
|
||||
# REED SOLOMON ENCODING #
|
||||
###############################################################################
|
||||
|
||||
class TestRSEncode(unittest.TestCase):
|
||||
|
||||
def test_1_correction(self):
|
||||
|
||||
string = 10 * "Testmessage"
|
||||
print("Original: %s" % string)
|
||||
|
||||
encoded = rs.encode(string)
|
||||
print ("After encoding: %s" % encoded)
|
||||
|
||||
error = NH.e_correction_ratio
|
||||
altered = os.urandom(error) + encoded[error:]
|
||||
print("After errors: %s" % altered)
|
||||
|
||||
corrected = rs.decode(altered)
|
||||
print("Corrected: %s" % corrected)
|
||||
|
||||
self.assertEqual(corrected, string)
|
||||
|
||||
|
||||
###############################################################################
|
||||
# LOCAL DATA PROCESSING #
|
||||
###############################################################################
|
||||
|
||||
class TestHeaderPrinterProcess(unittest.TestCase):
|
||||
"""
|
||||
This function doesn't have any tests yet.
|
||||
"""
|
||||
|
||||
|
||||
class TestNHCommandProcess(unittest.TestCase):
|
||||
"""
|
||||
This function doesn't have any tests yet.
|
||||
"""
|
||||
|
||||
|
||||
###############################################################################
|
||||
# PACKETS FROM PIDGIN #
|
||||
###############################################################################
|
||||
|
||||
class TestPidginConnection(unittest.TestCase):
|
||||
"""
|
||||
This function doesn't have any tests yet.
|
||||
"""
|
||||
|
||||
|
||||
class TestPidginToRxMQueue(unittest.TestCase):
|
||||
"""
|
||||
This function doesn't have any tests yet.
|
||||
"""
|
||||
|
||||
|
||||
class TestDBusReceiver(unittest.TestCase):
|
||||
"""
|
||||
This function doesn't have any tests yet.
|
||||
"""
|
||||
|
||||
|
||||
class TestPidginReceiverProcess(unittest.TestCase):
|
||||
"""
|
||||
This function doesn't have any tests yet.
|
||||
"""
|
||||
|
||||
|
||||
###############################################################################
|
||||
# PACKETS FROM TxM #
|
||||
###############################################################################
|
||||
|
||||
class TestTxMIPCReceiverProcess(unittest.TestCase):
|
||||
"""
|
||||
This function doesn't have any tests yet.
|
||||
"""
|
||||
|
||||
|
||||
class TestTxMSerial0ReceiverProcess(unittest.TestCase):
|
||||
"""
|
||||
This function doesn't have any tests yet.
|
||||
"""
|
||||
|
||||
|
||||
class TestTxMSerial1ReceiverProcess(unittest.TestCase):
|
||||
"""
|
||||
This function doesn't have any tests yet.
|
||||
"""
|
||||
|
||||
|
||||
class TestTxMPacketProcess(unittest.TestCase):
|
||||
"""
|
||||
This function doesn't have any tests yet.
|
||||
"""
|
||||
|
||||
|
||||
###############################################################################
|
||||
# PACKETS TO RxM #
|
||||
###############################################################################
|
||||
|
||||
class TestRxMIPCSenderProcess(unittest.TestCase):
|
||||
"""
|
||||
This function doesn't have any tests yet.
|
||||
"""
|
||||
|
||||
|
||||
class TestRxMSerialSenderProcess(unittest.TestCase):
|
||||
"""
|
||||
This function doesn't have any tests yet.
|
||||
"""
|
||||
|
||||
|
||||
###############################################################################
|
||||
# PACKETS TO PIDGIN #
|
||||
###############################################################################
|
||||
|
||||
class TestPidginSenderProcess(unittest.TestCase):
|
||||
"""
|
||||
This function doesn't have any tests yet.
|
||||
"""
|
||||
|
||||
|
||||
###############################################################################
|
||||
# MAIN #
|
||||
###############################################################################
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
NH.unit_testing = True
|
||||
|
||||
try:
|
||||
os.remove("NH.pyc")
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
unittest.main(exit=False)
|
||||
|
||||
try:
|
||||
os.remove("NH.pyc")
|
||||
except OSError:
|
||||
pass
|
||||
|
|
5898
unittests/test_rx.py
5898
unittests/test_rx.py
File diff suppressed because it is too large
Load Diff
6355
unittests/test_tx.py
6355
unittests/test_tx.py
File diff suppressed because it is too large
Load Diff
497
updatelog
497
updatelog
|
@ -1,497 +0,0 @@
|
|||
TFC NaCl 0.16.05 | Update log
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Common changes
|
||||
-------------------------------------------------------------------------------
|
||||
Removed from x import y to avoid conflicts in namespace etc.
|
||||
|
||||
Refactored code here and there.
|
||||
|
||||
Added more unittests. test_tx.py and test_rx.py now evaluate XSalsa20-Poly1305
|
||||
implementation in PyNaCl using official test vectors by djb. Unittests can now
|
||||
be run from RPi over SSH.
|
||||
|
||||
Improved LUI here and there.
|
||||
|
||||
Banner can now be skipped with keyboard interrupt
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Tx.py
|
||||
-------------------------------------------------------------------------------
|
||||
User can now send encrypted command to RxM with command '/rxkey' that opens a
|
||||
file prompt. This allows user to load the PSK for contact without turning off
|
||||
Rx.py.
|
||||
|
||||
Added boolean show_file_prompts that by default opens a storage prompt where
|
||||
user can store contact's PSK. When false, keys are stored in 'keys_to_contact'.
|
||||
|
||||
Resolved the "CTR mode needs counter parameter, not IV" issue with Paramiko's
|
||||
SSH connection with AES CTR mode.
|
||||
|
||||
Disallowed ':' and '/' chars in accounts to fix crashes.
|
||||
|
||||
Reversed PSK and ECDHE key choice answer to more intuitive order.
|
||||
|
||||
Fixed issues when sending dotfiles.
|
||||
|
||||
Changed trickle delay's time's constant delay time exceeding from critical
|
||||
error to soft warning. Raspberry Pi tends to send first packet with greater
|
||||
delay. User will now be prompted to increase trickle_c_delay if the problem
|
||||
persists.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Rx.py
|
||||
-------------------------------------------------------------------------------
|
||||
Opens a file prompt when '/rxkey' command is sent by TxM. This allows user to
|
||||
load the PSK for contact without turning off Rx.py.
|
||||
|
||||
Fixed issue where lack of rx.<xmpp>.e key on RxM crashes Rx.py when /logging
|
||||
on <xmpp> command is issued from TxM.
|
||||
|
||||
Opens file prompts based on keyfile change commands.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
setup.py
|
||||
-------------------------------------------------------------------------------
|
||||
Added preinstalled hashes of tested (not audited) crypto libraries.
|
||||
|
||||
Improved SSH configuration during installation.
|
||||
|
||||
Added NH installation configuration.
|
||||
|
||||
Reduced 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 installer as superuser.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
hwrng-nacl.py
|
||||
-------------------------------------------------------------------------------
|
||||
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
|
||||
-------------------------------------------------------------------------------
|
||||
dd.py now automatically re-sizes terminal window to proper size.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
TFC NaCl 0.16.01 | Update log
|
||||
|
||||
Local key transmission: Bootstrapping security
|
||||
----------------------------------------------
|
||||
In the initial bootstrap, Tx.py generates 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), 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 vN whitened samples have been collected, the
|
||||
256-bit entropy is compressed using SHA3-256 hash function.
|
||||
|
||||
If Tx.py is run on a computer that has 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.
|
||||
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 adversary from obtaining the signed ciphertext from NH in case it has
|
||||
been compromised at this point.
|
||||
|
||||
RxM will then ask 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 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, 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
|
||||
-------------
|
||||
|
||||
User is guided when adding first contact. Tx.py first prompts for account, e.g.
|
||||
alice@jabber.org and a nick. Empty nick uses nick 'Alice' (automatically parsed
|
||||
from account). Tx.py asks user to confirm account and nick (choosing no jumps
|
||||
to beginning of account creation). After this Tx.py prompts 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 curve 25519 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. 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 local key. User must manually type the 72 hex char
|
||||
string to TxM. Manual process ensures no automated data channel from network
|
||||
can infect TxM after setup.
|
||||
|
||||
Once key has been successfully entered, Tx.py will ask 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 has exfiltrated
|
||||
respective keys from end points 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 new public key and effectively bypass the
|
||||
MITM in network. Once the public key has been successfully typed, Tx.py will
|
||||
log the public keys for later, higher assurance verification done face to face.
|
||||
This also enables retrospective detection of MITM, if for some reason users are
|
||||
unable to verify 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 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, 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-synchronisation 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 blocks 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 with commands
|
||||
/dh <account>( <nick>)
|
||||
and
|
||||
/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 is sent
|
||||
to RxM, encrypted with local key (again, NH doesn't have to be in between if
|
||||
user so prefers). Tx.py then prompts user for his or her account name.
|
||||
|
||||
If Alice is adding bob@jabber.org as contact, a copy of her symmetric key is
|
||||
generated for Bob, and conveniently placed inside folder "PSKs", under the name
|
||||
|
||||
rx.alice@jabber.org.e - Give this file to bob@jabber.org
|
||||
|
||||
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
|
||||
|
||||
rx.bob@jabber.org.e - Give this file to alice@jabber.org
|
||||
|
||||
keyfile to folder "keys". She must then restart Rx.py. Rx.py automatically
|
||||
strips the trailing instruction from the keyfile and adds Bob as 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 command
|
||||
|
||||
/psk <account>( <nick>)
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
Other updates over TFC 0.5.5:
|
||||
|
||||
-Change of version style to 0.16.01 (0.YY.MM).
|
||||
-Fully PEP8 compliant coding style/variable naming.
|
||||
|
||||
|
||||
New local testing IPC
|
||||
---------------------
|
||||
Local testing of TFC with single computer now uses multiprocessing sockets
|
||||
instead of files. This reduces IO errors also means less files in TFC root
|
||||
directory.
|
||||
|
||||
|
||||
Data diode simulators
|
||||
---------------------
|
||||
Added dd.py program that emulates data diodes on screen. User
|
||||
needs to enable dd_sockets boolean in Tx.py and NH.py with 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 ttyAMA0 in the
|
||||
case of Raspbian) serial interface, or for a USB interface (ttyUSB#) if user
|
||||
decides to use those. The serial interface numbering is finally automatically
|
||||
detected, so if user accidentally pulls out serial interface, user doesn't have
|
||||
to manually edit the interface if OS remaps /dev/ttyUSB0 to /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.tfc and rxc.tfc as dotfiles .tx_contacts and
|
||||
.rx_contacts. The only TFC files visible in software root directory are .py
|
||||
files (and syslog.tfc once 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 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 padds, 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 even 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 internal
|
||||
state of math.random() to detect whether communication is taking 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. Default value is 500ms which should be enough to hide metadata about
|
||||
TxM device performance and make hardware fingerprinting harder. This also makes
|
||||
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 logfiles, 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 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 datadiode.
|
||||
|
||||
|
||||
Rewritten NH.py
|
||||
---------------
|
||||
Converted classes to functions to fit with procedural 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 .txt file from TxM directory. Absolute paths can
|
||||
also be defined. Non-TFC filenames in Tx.py directory are now included in
|
||||
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 to memory. It'll
|
||||
evaluate and display 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 recipient after first packet of file has been
|
||||
received. File reception must be enabled enabled.
|
||||
|
||||
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 alice@jabber.org 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 file has been received. When the
|
||||
variable is false, file storage will remain open until user enters
|
||||
/store off alice@jabber.org
|
||||
/store off
|
||||
|
||||
During trickle connection, messages and files can be cancelled with commands
|
||||
'/cm' and '/cf'. Already sent data is out of control of 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 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. 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 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
|
||||
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 user moving the mouse on NH. Python is also a
|
||||
high level language, which has it's own problems.
|
||||
|
||||
|
||||
Command line arguments
|
||||
----------------------
|
||||
Added arguments for Tx.py, Rx.py and NH.py to control many settings from
|
||||
command line. View arguments by launching program with -h/--help flag.
|
||||
|
||||
|
||||
Hard coded packet length
|
||||
------------------------
|
||||
Packet length was hard coded to 254 to ensure everyone uses same value,
|
||||
and to minimize chance for errors when sending encrypted commands.
|
||||
|
||||
Unittesting
|
||||
-----------
|
||||
Wrote unittests for Tx.py, Rx.py and NH.py to reduce the amount 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 simplified import
|
||||
of file hashes when making changes.
|
||||
|
||||
Even 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 latest release of Tails
|
||||
(2.2.1).
|
||||
|
||||
Changed installer naming style, instead of tfcOTPinstaller.py, it's just
|
||||
setup.py. Run it with 'sudo python setup.py'.
|
Loading…
Reference in New Issue