1.19.10
This commit is contained in:
parent
da105fe2bb
commit
eac6b54c65
|
@ -16,8 +16,7 @@ before_install:
|
|||
|
||||
install:
|
||||
- pip install pytest pytest-cov pyyaml coveralls
|
||||
- pip install -r requirements.txt --require-hashes
|
||||
- pip install -r requirements-relay.txt --require-hashes
|
||||
- pip install -r requirements-dev.txt
|
||||
|
||||
script:
|
||||
- py.test --cov=src tests/
|
||||
|
|
|
@ -83,9 +83,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
- The Argon2 library, Copyright © 2015, Hynek Schlawack
|
||||
(https://github.com/hynek/argon2_cffi)
|
||||
|
||||
- The asn1crypto library, Copyright © 2015-2018, Will Bond <will@wbond.net>
|
||||
(https://github.com/wbond/asn1crypto)
|
||||
|
||||
- The src.common.encoding Base58 implementation, Copyright © 2015, David Keijser
|
||||
(https://github.com/keis/base58)
|
||||
|
||||
|
@ -531,6 +528,11 @@ Public License instead of this License.
|
|||
- The src.relay.onion Tor class, Copyright © 2014-2019, Micah Lee <micah@micahflee.com>
|
||||
(https://github.com/micahflee/onionshare)
|
||||
|
||||
- gnome-terminal, Copyright © Guilherme de S. Pastore <gpastore@gnome.org>,
|
||||
Havoc Pennington <hp@redhat.com>,
|
||||
Mariano Suárez-Alvarez <mariano@gnome.org>
|
||||
(https://gitlab.gnome.org/GNOME/gnome-terminal)
|
||||
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
Preamble
|
||||
|
|
18
README.md
18
README.md
|
@ -1,4 +1,4 @@
|
|||
<img align="right" src="https://cs.helsinki.fi/u/oottela/tfclogo.png" style="position: relative; top: 0; left: 0;">
|
||||
<img align="right" src="https://cs.helsinki.fi/u/oottela/tfc_logo.png" style="position: relative; top: 0; left: 0;">
|
||||
|
||||
### Tinfoil Chat
|
||||
|
||||
|
@ -67,8 +67,8 @@ and schedule of communication, even if the Networked Computer is compromised.
|
|||
|
||||
### How it works
|
||||
|
||||
![](https://www.cs.helsinki.fi/u/oottela/wiki/readme/how_it_works.png)
|
||||
[System overview](https://www.cs.helsinki.fi/u/oottela/wiki/readme/how_it_works.png)
|
||||
![](https://www.cs.helsinki.fi/u/oottela/wiki/readme/how_it_works2.png)
|
||||
[System overview](https://www.cs.helsinki.fi/u/oottela/wiki/readme/how_it_works2.png)
|
||||
|
||||
TFC uses three computers per endpoint: Source Computer, Networked Computer, and
|
||||
Destination Computer.
|
||||
|
@ -114,8 +114,8 @@ the user.
|
|||
3. The Networked Computer is assumed to be compromised. All sensitive data that
|
||||
passes through it is encrypted and signed with no exceptions.
|
||||
|
||||
![](https://www.cs.helsinki.fi/u/oottela/wiki/readme/attacks.png)
|
||||
[Exfiltration security](https://www.cs.helsinki.fi/u/oottela/wiki/readme/attacks.png)
|
||||
![](https://www.cs.helsinki.fi/u/oottela/wiki/readme/attacks2.png)
|
||||
[Exfiltration security](https://www.cs.helsinki.fi/u/oottela/wiki/readme/attacks2.png)
|
||||
|
||||
#### Data diode
|
||||
Optical repeater inside the
|
||||
|
@ -131,12 +131,14 @@ fundamental laws of physics.
|
|||
|
||||
#### Source/Destination Computer
|
||||
- Debian 10
|
||||
- *buntu 19.04 (or newer)
|
||||
- PureOS 9.0
|
||||
- *buntu 19.10
|
||||
|
||||
#### Networked Computer
|
||||
- Tails (Debian Buster or newer)
|
||||
- Tails 4.0
|
||||
- Debian 10
|
||||
- *buntu 19.04 (or newer)
|
||||
- PureOS 9.0
|
||||
- *buntu 19.10
|
||||
|
||||
|
||||
### More information
|
||||
|
|
6
dd.py
6
dd.py
|
@ -29,9 +29,9 @@ from typing import Any, Dict, Tuple
|
|||
|
||||
from src.common.misc import get_terminal_height, get_terminal_width, ignored, monitor_processes
|
||||
from src.common.output import clear_screen
|
||||
from src.common.statics import DATA_FLOW, DD_ANIMATION_LENGTH, DD_OFFSET_FROM_CENTER, DST_DD_LISTEN_SOCKET
|
||||
from src.common.statics import DST_LISTEN_SOCKET, EXIT_QUEUE, IDLE, LOCALHOST, NC, NCDCLR, NCDCRL, RP_LISTEN_SOCKET
|
||||
from src.common.statics import SCNCLR, SCNCRL, SRC_DD_LISTEN_SOCKET
|
||||
from src.common.statics import (DATA_FLOW, DD_ANIMATION_LENGTH, DD_OFFSET_FROM_CENTER, DST_DD_LISTEN_SOCKET,
|
||||
DST_LISTEN_SOCKET, EXIT_QUEUE, IDLE, LOCALHOST, NC, NCDCLR, NCDCRL, RP_LISTEN_SOCKET,
|
||||
SCNCLR, SCNCRL, SRC_DD_LISTEN_SOCKET)
|
||||
|
||||
|
||||
def draw_frame(argv: str, # Arguments for the simulator position/orientation
|
||||
|
|
350
install.sh
350
install.sh
|
@ -16,6 +16,30 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with TFC. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# PIP dependency file names
|
||||
ARGON2=argon2_cffi-19.1.0-cp34-abi3-manylinux1_x86_64.whl
|
||||
CERTIFI=certifi-2019.9.11-py2.py3-none-any.whl
|
||||
CFFI=cffi-1.13.1-cp37-cp37m-manylinux1_x86_64.whl
|
||||
CHARDET=chardet-3.0.4-py2.py3-none-any.whl
|
||||
CLICK=Click-7.0-py2.py3-none-any.whl
|
||||
CRYPTOGRAPHY=cryptography-2.8-cp34-abi3-manylinux1_x86_64.whl
|
||||
FLASK=Flask-1.1.1-py2.py3-none-any.whl
|
||||
IDNA=idna-2.8-py2.py3-none-any.whl
|
||||
ITSDANGEROUS=itsdangerous-1.1.0-py2.py3-none-any.whl
|
||||
JINJA2=Jinja2-2.10.3-py2.py3-none-any.whl
|
||||
MARKUPSAFE=MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl
|
||||
PYCPARSER=pycparser-2.19.tar.gz
|
||||
PYNACL=PyNaCl-1.3.0-cp34-abi3-manylinux1_x86_64.whl
|
||||
PYSERIAL=pyserial-3.4-py2.py3-none-any.whl
|
||||
PYSOCKS=PySocks-1.7.1-py3-none-any.whl
|
||||
REQUESTS=requests-2.22.0-py2.py3-none-any.whl
|
||||
SETUPTOOLS=setuptools-41.4.0-py2.py3-none-any.whl
|
||||
SIX=six-1.12.0-py2.py3-none-any.whl
|
||||
STEM=stem-1.7.1.tar.gz
|
||||
URLLIB3=urllib3-1.25.6-py2.py3-none-any.whl
|
||||
VIRTUALENV=virtualenv-16.7.7-py2.py3-none-any.whl
|
||||
WERKZEUG=Werkzeug-0.16.0-py2.py3-none-any.whl
|
||||
|
||||
|
||||
function compare_digest {
|
||||
# Compare the SHA512 digest of TFC file against the digest pinned in
|
||||
|
@ -30,111 +54,89 @@ function compare_digest {
|
|||
|
||||
|
||||
function verify_tcb_requirements_files {
|
||||
# To minimize the time TCB installer configuration stays online, only
|
||||
# the requirements files are authenticated between downloads.
|
||||
compare_digest c3f27d766f2795bf0c87ddb0cec43f1f9919c2cf20db5eff62e818d67436f1520b6001bf9e7649c5508e62b221c02039b6cb29f7393ba1dbacf9a442cb3bb8b2 '' requirements.txt
|
||||
compare_digest 2c9e865be4231d346504bef99159d987803944b4ed7a1f0dbb7e674d4043e83c45771da34b7c4772f25101b81f41f2bafc75bfd07e58d37ddf7d3dc1aa32da24 '' requirements-venv.txt
|
||||
# To minimize the time TCB installer configuration stays online, only
|
||||
# the requirements files are authenticated between downloads.
|
||||
compare_digest 99912fe2f7240a9b163292ff83c28b6ab41ee1c10bf96cc57f2c066537d3f153b46280e2c769b0f273c6bc36c74badb42d3c66c6fb3d16862dc96ff27319788d '' requirements.txt
|
||||
compare_digest 97558ed189976ccd54e3a25bcf639f1944aa43f4a4f42ff5ef2cf22349a7b649272e91746041b4e04b2f33adf1fab8818c339b1cc58f9353af3e5ac76cb1ec0b '' requirements-venv.txt
|
||||
}
|
||||
|
||||
|
||||
function verify_files {
|
||||
# Verify the authenticity of the rest of the TFC files.
|
||||
compare_digest e4f81f752001dbd04d46314ea6d8867393c3ad5ed85c2d3e336a8018913446f5855525e0ca03671ab8d26d8af1fe16416c8f5a163cad795867284a726adfeb31 '' dd.py
|
||||
compare_digest bcb8a7ce1eb2d2f064b560ca5a8e467f84e3a0c3d643771e7782c792e89494600436e52c12f0a8471bf4a1da116f82ed732b8e06783534227a31f576f7adbd6c '' dd.py
|
||||
compare_digest d361e5e8201481c6346ee6a886592c51265112be550d5224f1a7a6e116255c2f1ab8788df579d9b8372ed7bfd19bac4b6e70e00b472642966ab5b319b99a2686 '' LICENSE
|
||||
compare_digest 651fccf97f1a1a3541078a19191b834448cb0c3256c6c2822989b572d67bc4b16932edea226ecf0cbd792fc6a11f4db841367d3ecd93efa67b27eaee0cc04cb7 '' LICENSE-3RD-PARTY
|
||||
compare_digest 84a4e5b287ba4f600fc170913f5bdcd3db67c6d75a57804331a04336a9931c7ce9c58257ad874d3f197c097869438bb1d2932f06f5762c44f264617681eab287 '' relay.py
|
||||
compare_digest 2865708ab24c3ceeaf0a6ec382fb7c331fdee52af55a111c1afb862a336dd757d597f91b94267da009eb74bbc77d01bf78824474fa6f0aa820cd8c62ddb72138 '' requirements-dev.txt
|
||||
compare_digest 1ab71b773a0451807eda87a1442dd79529de62f617e8087a21c0f3ad77e4fe22218e05a94d19d1660b2967a9908fb722f750a64a75e650b178c818c8536f64db '' requirements-relay.txt
|
||||
compare_digest 6d93d5513f66389778262031cbba95e1e38138edaec66ced278db2c2897573247d1de749cf85362ec715355c5dfa5c276c8a07a394fd5cf9b45c7a7ae6249a66 '' tfc.png
|
||||
compare_digest a7b8090855295adfc22528b2f89bed88617b5e990ffe58e3a42142a9a4bea6b1b67c757c9b7d1eafeec22eddee9f9891b44afffa52d31ce5d050f08a1734874d '' tfc.py
|
||||
compare_digest 7e519d20fef24e25e88ec4a9c03abadf513b084e05038f17c62ca7899c2f9174a953caa0bfbd3b61e455e243513cdab737c22a34d73ebab07b65d3ce99100f0a '' LICENSE-3RD-PARTY
|
||||
compare_digest 99815d0cfbca7d83409b7317947fe940fe93fd94b50e6099a566563ee6999e33830fd883ff61e5367a040d5fda3f2a43165ef0dc6155e14a573e07dc27eba70d '' relay.py
|
||||
compare_digest 28d06826a45ca4d64c2b4d06859ee7a0c7152198fe49b85681f7ce6b9c02b1a103fd7f3514b05b24e95e2ec5f48ce02529a2b4f2ea806b333e8141b1650d1257 '' requirements-dev.txt
|
||||
compare_digest 8a57366899139b9906f0a75272c702575a6cd5c6ca2dd09f0dbd1be9efd5341178f9d3d64fec113af7d1fdccbb5cbdf384133aa3afa3672292e37405f60cf0a8 '' requirements-relay.txt
|
||||
compare_digest 8ecd5957f3bfbe237549e8772720cba5b5899b51a475063edcbc416ad5f77f614da2c9069aeb31bca6d2bb74ce6f2877d29df178ec3ecf6d5dd05daaff51c6b2 '' requirements-relay-tails.txt
|
||||
compare_digest 4a44501e21d463ff8569a1665b75c2e4d8de741d445dc3e442479cbb7282646045129233bd7313df4b9c2e64ec86b7615a8196ae2b3350de933731926d39bbda '' requirements-setuptools.txt
|
||||
compare_digest 79f8272a2ab122a48c60630c965cd9d000dcafabf5ee9d69b1c33c58ec321feb17e4654dbbbf783cc8868ccdfe2777d60c6c3fc9ef16f8264d9fcf43724e83c2 '' tfc.png
|
||||
compare_digest e4dadae63adcd72108fcfa04401f42a1bae956008303d09f22e849b207ebca699306f2bd4034ee96a5531028719f5e41689205ec8ef12cd1726a86376d3aec3e '' tfc.py
|
||||
compare_digest 7ae1c2a393d96761843bea90edd569244bfb4e0f9943e68a4549ee46d93180d26d4101c2471c1a37785ccdfaef45eedecf15057c0a9cc6c056460c5f9a69d37b '' tfc.yml
|
||||
compare_digest c6a61b3050624874cabc28cc51e947aa1ba629b0fd62564466b902cc433c08be6ae64d53bb2f33158e198c60ef2eb7c38b0bee1a64ef9659d101dee07557ddc7 '' uninstall.sh
|
||||
|
||||
compare_digest d4f503df2186db02641f54a545739d90974b6d9d920f76ad7e93fe1a38a68a85c167da6c19f7574d11fbb69e57d563845d174d420c55691bc2cd75a1a72806dc launchers/ terminator-config-local-test
|
||||
compare_digest fed9f056541afe1822ec41bce315fc6ce94652318088ecc905ddc657a15525d740ca83ff335e5090b9e3844027a706990da72a754ffa3dcd255b21e263597c78 launchers/ TFC-Local-test.desktop
|
||||
compare_digest 011a64aa7790253b008f0adfe940108dc94c08adec9b3a42efd1ac0d6405a82d828b3d416736ad64235b6b90ef2f6712fc10a70d8e97870670e5b84cf25fe54e launchers/ TFC-RP.desktop
|
||||
compare_digest 12c86594b28f5dae48e4d216781034917ab80ca46f30cb3d33a0dec615d415830fdc41228f933c8fef651eb51643390fe5521e70f9efc642ea5c8b2e34442e00 launchers/ TFC-RP-Tails.desktop
|
||||
compare_digest 270153a45e5426db326ba2144ca454bb57ddf8f53731c1948ee8fa33dedcb98e32e19506b0fa923e5a7f6fca9a5b67338216c19417e52289726721efe12abd98 launchers/ TFC-RxP.desktop
|
||||
compare_digest 691d0cc4f03bd9c2874711ba97e347e705e4eadf53c5b14562e7e1a419ae892a9ce156b6e5a26945bde72c0b3e56ec6ca2d24fb3bf23848fb44808b7681e4141 launchers/ TFC-TxP.desktop
|
||||
compare_digest a5611269e2f69a452840ae13d888bd80d6f8e5e78fdab0cb666440491d8431e6c326dc57a52df7d9e68ecd139376606c9f6c945207f2427bb21c114fe26c0af7 launchers/ TFC-Local-test.desktop
|
||||
compare_digest 9263737fca4773672515e0f4708e147b634bd09c8d068966806bb77d3b38dcf60b1f933846f9a649e795760ff141a31dc2b58fad38ef2afbaedb33d2f479a29b launchers/ TFC-RP.desktop
|
||||
compare_digest 9263737fca4773672515e0f4708e147b634bd09c8d068966806bb77d3b38dcf60b1f933846f9a649e795760ff141a31dc2b58fad38ef2afbaedb33d2f479a29b launchers/ TFC-RP-Tails.desktop
|
||||
compare_digest 113d1f8f6bc03009ef1ccfe1aed8a90bdecb54e66bd91ed815bbd83cb695419a25c614de8287475d3beab832cfcaf6d549c06832f2ea098d29ff049d7cd91da7 launchers/ TFC-RxP.desktop
|
||||
compare_digest 1f4d4e216039b63f2579eef17dc18df5e2f1e65f09e619b62adb8dceb128de6ffe5784ea0ff1dc846af21e1ce641bc612df51e37e205fee210f94dd87b86f467 launchers/ TFC-TxP.desktop
|
||||
|
||||
compare_digest cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e src/ __init__.py
|
||||
compare_digest cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e src/common/ __init__.py
|
||||
compare_digest 92c7f788c41984cf75ad879e281f73a1a334dd759b858be11f91481a0e754e2db1527c9d3ab369aad7b8cf139492d5809c0c0542e46ff27637ee5b4b655b726f src/common/ crypto.py
|
||||
compare_digest 0b830ad6705f90dc8e554c8b06f31054d9222d3b35db92470eaf3f9af935aae3107b61142ea68129943e4227a45dfe26a87f23e9dd95a7794ae65c397bd35641 src/common/ db_contacts.py
|
||||
compare_digest bad54588b3e713caf94aba3216090410f77f92064aecfea004a33487a21f9fffcf5d05782b91104c27ec499e38196881a76d6002ec1149816d8c53560182fba9 src/common/ db_groups.py
|
||||
compare_digest 46e503af15fb7a1ea8387fa8f22674d0926eda0d879d43f99076ca967295d5e3727a411aa11c4bb4d832783c5daa9f4b7ef4491b90112c1be405910d269abaf4 src/common/ db_keys.py
|
||||
compare_digest 68b2c7761623ff9e4ce596e170c8e1ce2b6129bbbbb9fc9e48a120974d30db6e471bb5d514f5dc8d7edc8e0abcb0cd0c2b4e5da739f4d98c6fa8c8e07af0d8ef src/common/ db_logs.py
|
||||
compare_digest 1a3783e6ea5643bc799b9745d0a8abb82f9e814ca01da3414df86faaa007aaa21609a88e07c8947e43567a787f114db57143f10585053e92f96807858adec96a src/common/ db_masterkey.py
|
||||
compare_digest 5befbe864e2b09125be2b04cdfee8d13e7616715fc20a0fa06da270e34b555602b2df825fd429059056b2beb1497c50dafdc682d59a43a483837445861647e9d src/common/ db_onion.py
|
||||
compare_digest 328375464a5064829621fb8154c94cf15a1e7564122d99f03c9e578dcd6d6f264a671412b738b4971a6e505062673df40cc89f7b67c7ef2a761236c3f1247e93 src/common/ db_settings.py
|
||||
compare_digest e3f760b03eb38d3ec97b04123f8cf36825d616e126bfb9fb575927586cbb1911dce448c08cb8cfb26be0635c2e6abaaad8ea94f0513672c288c78892010ab146 src/common/ encoding.py
|
||||
compare_digest f7ff99c8a5f84b892b959fe391cdfe416d103a5a4d25fcd46842f768583a5b241036ee9c4b6713a938e8234f5af82a770ffe28975292d162b6d5bea69a120ad8 src/common/ exceptions.py
|
||||
compare_digest 67cbb9e983aeb49d8a0cc8666aa5c1dad4b868e4fc968c72c7fc4290b886fae9a58ef84f5942d8a08b19621cc2fe01b787ac10bff8b63393e6addaa178da4ea7 src/common/ gateway.py
|
||||
compare_digest 618cbaccb4f50bc6e551cd13570693d4d53cfdfdc00b7ff42ff9fd14f0dadf7a15c9bc32c8e53801aca42348826568a2aa9117bdbf79bbcc4217a07c94c9efd3 src/common/ input.py
|
||||
compare_digest 14f9a5f06443b50251272939b92eaf287fecfb4e3c9882254f13757259d9d791ac1e66c46fd0475d1e3161ff27f16ad7b4de04ac564500cae60d27a631d42e36 src/common/ misc.py
|
||||
compare_digest b4efca327d057f9b3304d9f0415d23e95ba88230576a1a8d8be85c3e25364e7420389af36df4b4dacf62b5df11cb62f1ca7d0b6c03aee6b591282d51bbc119e3 src/common/ output.py
|
||||
compare_digest 580c6453d8649d82973817de1fcfd83c73b0f6bb3e2597c5ffb6c0d3929d10f693c0a3d98dad4f528f1a1b62b220bcb99fa075ccd28e8b53d6cc966697523e25 src/common/ crypto.py
|
||||
compare_digest 053c431755b4d6a5a869d2f06213bd23a8fb8743644b127d3622ff0223687880e8b1d510fbc976f2112318e48caa0044a1959c9af21272001e3f107fd9f785ba src/common/ db_contacts.py
|
||||
compare_digest 8e4e8ab2dff2400f3fe72d980c95c1780d26e11f0482c555bb47cfa7513d091b06fd901666f415e0b3db291bd7f161d7182ac530a6d118a61f0b72bb665eead1 src/common/ db_groups.py
|
||||
compare_digest 712c5422193994a65eff74f0a328107232069784c6c687f700a6823435fe65afb3e31cbd1102dbe07bc1d7e5e1572a4db6e467d477ebd3e4aab6ff3685723ccf src/common/ db_keys.py
|
||||
compare_digest a2714c8aab538a5fc273f4eb58ea0d039effa4ce64559ab34dbe9eb92c762aff98a242443e2dffe5e55005b8778a5581f5c2ad3c07a0b6eee58e599e44a951b3 src/common/ db_logs.py
|
||||
compare_digest c6bbb2b75f14447ba20bf1ac214b044d5a79b11a5346a1691a823c5e5ac4db05dd24a8de52856021d8d7f7f8582c1871839945323245521f320714cc72994bdf src/common/ db_masterkey.py
|
||||
compare_digest 253521b1ed39a73a0fd6108cbcf88bbd1ffadde28be1467c1e7871094392d0a55947032ef5fc19d85727117bacf8516ac1f08f70240be20b9fc2d009a68989ee src/common/ db_onion.py
|
||||
compare_digest 2e8ff65270e0165e510f5d330fa2cbfdc6ecf8ed953220bbe18d19a8afa6fa2bf852e56ba875c1c361fdb72016d0610e8c9fa1df302fa47f916eaaecde11e423 src/common/ db_settings.py
|
||||
compare_digest 7a673e6feb7a5b2e3417d2c0eee82a59b3730a5d241938a84fd866dfc838c3cd63d7ef96772d43f62df740a2ba1001456746dd6c86e950484eac3ebabed498ce src/common/ encoding.py
|
||||
compare_digest 00ad45d8fba1a605817a9f5d64cdfd6aad9c618db66befe682728a2291384c67bf5e80a5257211717a86e4a51c7e8c74f8f7ccccc3b4ac3c6f0f4c4e1b3cc98f src/common/ exceptions.py
|
||||
compare_digest 5de68b10dc6b6ff98d7f73a2dc89d6b64c529fb4949b4a51d1b2fb4491006266bf7200b717102987347add666f12d7cfa9afc43a540304290f9a41fe48545078 src/common/ gateway.py
|
||||
compare_digest 604893a2814219b2ed4e69b45d9ac2f8c2b5fc066bd085e86b76ef9df9984e6113f79fcaf3b9eb1197a9c9fc92cf524269e595d03b5009c46e8889d813475408 src/common/ input.py
|
||||
compare_digest 1cac1bed0779f480de26054867cd732deaba5e7a46728ed8c203948ed92b96e0dbf2d5d1a696cbca445d5db2b79cde318aac28189d8806fe1ea530392a47f406 src/common/ misc.py
|
||||
compare_digest e167f8458d1a1fc549f02a42ab9c1dc78ac2540b6a8660c77a06fe32de461eb52361a99582921b00eb50115c9fc70858dff1557c6e7ac69439768481a70b3fa7 src/common/ output.py
|
||||
compare_digest c4d97b497b341f0e7865a4e27a2a2ffd3b3c5a7bfbf72f4676f6b65d6ba66a2adb8fed563f88fa25cef555f0042290ef0ae4cbeed1697a2e19a3b8cff0b9ef1b src/common/ path.py
|
||||
compare_digest 9e9db25e73e0abb312b540d7601be7dfecae8d0aef6497feb2570f9ddf788fa0e261e276ed5a40e75fee0e0c6e86ccf3b05c110846fde804c302d4aa80a930e5 src/common/ reed_solomon.py
|
||||
compare_digest 374a9846d2a1f4b462d9fd4b6fd468ecdaf3a1d1ac25916214e949604e83eff66a969a7fe6af79f23f4bf2477f7a390374714602714c42448073392e56065a41 src/common/ statics.py
|
||||
compare_digest 4365ed3b6951525cb1ec8dc1177d7fd74d5dfa5eab1ca8934775391a8736eed4df039684f19ccc2d8022f20c8cf93a57a736b259e8c7235da5060c5f62057c98 src/common/ reed_solomon.py
|
||||
compare_digest 1f26c39a8e5ec39a859e90dac2e38c08d86af02e0fc714aa0569b618dbf44de5befde601b0fec23e62e3e3b3f7281727abea37a02818c9994ac47f119a621386 src/common/ statics.py
|
||||
compare_digest 339b402790cb3002841a1212d4dc24b07236b65baf68e3a8caf8d61bfd48af12887564be449ae49cfe5d7b88107a73e01a05053e3789398fbd560ef89f14afd4 src/common/ word_list.py
|
||||
|
||||
compare_digest cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e src/receiver/ __init__.py
|
||||
compare_digest 12cf1127afc6761e61f6dadf6b6f382f4ff00291bcb00a6e9b21c98d1263596041b31892fbfc4be6348bb622aa19b50f38303d51a651d63c2024e8e466cbef07 src/receiver/ commands.py
|
||||
compare_digest 760edaa44ff6175612b02f95b02b291ae369733a18cc5f87d525b46bcebc35c8a2d169a47962417eab434cf26ea6d5bfcd8894153fae668bb4a8cf2ceb8871f0 src/receiver/ commands_g.py
|
||||
compare_digest ee192c3529c51031110a49cf919a7b9e516d0eb0cc826da85587323480065cf53d4115f78f8008c305d074628b9de65d9b262ef790c178078cf74d8b3466ae3f src/receiver/ files.py
|
||||
compare_digest b3b0a61f3aba200e06bb2c6e05a067931be37ccfbc0768167a9d8651f7a8f6ea47871dcb5d63cf238f040d6c482fc45967a5a2ccbadb1308ec76b287b88fa3a6 src/receiver/ key_exchanges.py
|
||||
compare_digest 65307a0ea2c9ae69859cc8ef62a5d7e45c27bdf5a4ec44db704df143ce3630fdc077fafc7fd4cfc0cd922f350f49f0aa0a880192c40c614b6d3117804ea683ae src/receiver/ messages.py
|
||||
compare_digest b33a7696b31265027bb137aacd80845d4fefbd112144f3e01cfe4e083e5b6575fcdab387ae9d2435dd1c82bf0cf31b55f934cd12256a7ac78b4b13cc3d593987 src/receiver/ output_loop.py
|
||||
compare_digest 7a81a142d21ebf8779fa0fec9aeef922d21bd6d57643061722d91047ecafeea7f0bfdd3a2919251a83a02cecbef909756fdb62881d5d2a844c3daba0e7069bf5 src/receiver/ packet.py
|
||||
compare_digest 01b0ce92b4ab8f37eed55356a65f4d25ffff590955b5ca124dbb541bdec6968e8235242bf47ad5c2bfe57a30d54cf494d9f3836e3d53a73748f203ff68381c14 src/receiver/ receiver_loop.py
|
||||
compare_digest 17ab4e845274ac6901902537cbea19841de56b5d6e92175599d22b74a3451ecafa3681420e2fcc7f53f6b2ebb7994e5903aabb63a5403fbf7fb892509bb5d11c src/receiver/ windows.py
|
||||
compare_digest eaf664c520b7a2b4374e258b153940625312262fc54f9435018beff3152073d6dadcf78bedbe081a92cf173b31f2e86582c98a6065be1bba143b6b41b2d4b3b8 src/receiver/ commands.py
|
||||
compare_digest 051a1eac8e1e177bdf1c94972ed511d56d9ccfb3dc97c6418355b4415b2d1dff42c4ef5420de05d90e1697375f4db119e04932c05d6a93a89e03e7ca4c7c7346 src/receiver/ commands_g.py
|
||||
compare_digest 4a48adedcb839176e6a5f18b430a97c96558a7474d0840d22d31241a2918f1ffaed53211c5c949b545e231cda5280cf42fb5c20e22bdaef03f5fb2c298e22e07 src/receiver/ files.py
|
||||
compare_digest 6eae2793bdd72b9581cbbebc012a70b11744c2585fda1d1e253ff4d67cdc1d316d1f3ad7e391f5197aa1447ce21b0ecbc3e34517a8932ecd9eec7ff5d7313b5b src/receiver/ key_exchanges.py
|
||||
compare_digest d7d28808635a0425d231770b24c1a0f332ad40dffccfcf4f88801299b121e58363b728b0a70b0bc4d1640b850f7cb069ba88f3be966eb84f9e05f0112d3e76fd src/receiver/ messages.py
|
||||
compare_digest d651d87311ec09b8aa0a3964718ea1ea22c5bc1cc078ca87a367b420fc19716438045b31618d34264f483eb009ed1aff33704d135d0d22374df31d5438f9e00c src/receiver/ output_loop.py
|
||||
compare_digest a1edb6fe5b04117174ce45739e0737b6e8eedeed134bc95201d837bf6785bb98c86f8404eee18eb398755c8df113d7f8daa9dae43465846b887d514619759ed2 src/receiver/ packet.py
|
||||
compare_digest 20c6754ddb6261c7a3b479e6ab7bf78eb0ef8783e2141373d7aba857f413091b78dcc9c32667dd8f8d5c41927102da7e35c4c4fcb0aa7376dc42b08c0c01d6e2 src/receiver/ receiver_loop.py
|
||||
compare_digest 7d3f18351dc97bf4c1c5ab5894619d1f34ec07874758751340d53a12da2d7079944e6187d7436990f09a82d39c89d64a0bb747678c85ead8126eebbac9fcddfd src/receiver/ windows.py
|
||||
|
||||
compare_digest cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e src/relay/ __init__.py
|
||||
compare_digest a324e4b065c2bb8c66c1e0e7fbc11de85f4408e4a3c3afd81bbd6829fae4f6cf6a3bba284864476356a57947670e7dffcd6f30faf03e38b38dbed84305def042 src/relay/ client.py
|
||||
compare_digest 6f0f916f7c879c0383e8dc39593385de8f1ab2f7ba99f59b7fcbee947f8fcfae0935a98dcd8de1db3a600a0d8469a369ea38915fe2932c00b6756fb4944c938d src/relay/ commands.py
|
||||
compare_digest 261c9a0e7cb552c900f5234136c6251896bb03277c9ceaf175791012433a282373d6af1eec6be916aa955e04d5ffde2fc4bf3d153077baf00731249132b90658 src/relay/ onion.py
|
||||
compare_digest a18aa0ca4ffff7be99721c094ae44a95ed407e5e4cb25016ce14bf9fca7fef950d0b0460fd91689f2003aeb7f8385cb4f88a5f2e3f7f4ba7b88634412853d888 src/relay/ server.py
|
||||
compare_digest e03b2896049d6c4f98cc9b52ae43a32078ffe93f6a18848fb96cf4051752872ad20463754fad61244e914b2082839d8297f0d3357e20c8dd22881e885bfbd32a src/relay/ tcb.py
|
||||
compare_digest 5d34be330731b8b722c3580f12abd2515984ba0589ea95c0960ae099a13b9d66118a5af5cdf137bcf376bde88b0edf055888d2a5fc267081ea118fffc05a2b08 src/relay/ client.py
|
||||
compare_digest c32b5b78e28567d5ef0c6f41f1a3c69f6d31b1cb3b9d58faf6516fa27fc62e12b2f359f7b60176b5fe20a2d94725f5fd76a879d4b795513d1588f8ecf9bae5b0 src/relay/ commands.py
|
||||
compare_digest c72a57dda6054b9c020f694740751159df4602f11f7759ff76e48a8b7f07ec829b39d6c366613f3a69e36d3dca0823491f3232506f3a03ecc9ded3e2a4f0230a src/relay/ onion.py
|
||||
compare_digest fe108f1f642bdfd01d813fd0a183e2f6039c1e64a5ee57f6159fdc67d7574a0ba0ee23608a2a8499071f0844b7d2db6b6a14740046d5d664e09856c35680a0dc src/relay/ server.py
|
||||
compare_digest 9459e6cbe17fefac356e5ce183d923efff66f6d304111f2c0dbacdfb22a92df77bb11134faf8c15400bc59174ecbec1ea0b436065a9d49d3af70b46b24a77764 src/relay/ tcb.py
|
||||
|
||||
compare_digest cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e src/transmitter/ __init__.py
|
||||
compare_digest bb471267d37c73b37f3695bd3f28929612fe2074f7303b77f40cba0be84e975e3498402cbba2b402e45e35e81512ed2f67629cf0584536f36903b326d35cf557 src/transmitter/ commands.py
|
||||
compare_digest b861e16a6fbe2a93bf9f2dcf3f7f42d3f3ebf240aeaa2d752169a6807e2371eaaaf6230e0a71e6fe1e41e7ce5eb0515e8ec95bed5a7a7a263f42d715d23c399c src/transmitter/ commands_g.py
|
||||
compare_digest d5ad53e3ba7ae32e0c6255f5fca3a60c267b028d7dfb2f2e28768c2f8824b0ad25afc7edc3a5d6d69bb76986ff8d81549d50d71d8b46ca6a0a9dc37324ae027a src/transmitter/ contact.py
|
||||
compare_digest dffc059fc25cbfb17beb9f83fc2d52ce043e9b923580ccf655933cf66fefcf6e18bcb923d7cb42a7d547126f938ff867a7638ffd13c14953b4a2d700f8f0d5c4 src/transmitter/ files.py
|
||||
compare_digest 061d7f3c4f737afafacc65bea744ac9bad16c6b064ab6281a3ab2079b18d984afa00b93e3861209425f78807e335925d824e018e80523b5380ac5244b8f5b8c2 src/transmitter/ input_loop.py
|
||||
compare_digest 6465a819b1a449145fdb676819446761d8faa96921623433b12b9386b781d7964368c0f2cd5fe48aad5beda3cdc0f6496e95ae89650addd91619c5984979584c src/transmitter/ key_exchanges.py
|
||||
compare_digest 1249ba23d30031ec8c1b42e999e52bc0903a0ad00c317d27d51c8d650b3343c9475319db6e0f2f16dfae39a942e01eeaae8eb862d541957b1f51690b1eb0f202 src/transmitter/ packet.py
|
||||
compare_digest 93f874a8d97312ab4be10e803ba5b0432a40cf2c05541775c7117aa68c18e78e52e814d38639b33e87dc33df1773e04dc2c789e61e08c12d9c15512dd9e5d4d3 src/transmitter/ sender_loop.py
|
||||
compare_digest bcad5d4b9932f1b35b2c74dc083233af584012cacdd1d2cb04b28115b4e224118ce220252754ae532df0adcc6c343b1913f711cf3bd94da9c4cd3eaf23e4b196 src/transmitter/ traffic_masking.py
|
||||
compare_digest ccbda8415c23b23cc10cda57fb6b32df71e6510f3cb94c7f932b40adcf5f0abdd9842c48a992d56c95755e3024aebd7ecb05f69eb18f3c41656d94cfeabb38fa src/transmitter/ user_input.py
|
||||
compare_digest 4c5b9c877474257d078723d32533ba614d72bad7b108588f49fae6c26dcb2c49994b256b5457caee0e5ab4628f728b25a54551ce778b585cbb8b1f8c947dc7e6 src/transmitter/ windows.py
|
||||
compare_digest 2d002fe7aab987512534bdb18433d4a626b404aafc0360253056995559b0d2097938604325bc882a36ba456af0fbcf9620909945ebddf668bc44748a31a225c0 src/transmitter/ commands.py
|
||||
compare_digest 74291c8b952588caf7c3c6ac3e99679eaf97ba9113bfd560da4c461e16cd36c28d78a4e8750090d18606a9c1610a1a781d37fc6fe52baf47a955e0b5ec801b97 src/transmitter/ commands_g.py
|
||||
compare_digest 3a2940afcf8752f33c8f5a06293046a83d245630dde1a6877eb3c724cb03ee7b84147b4a57a62135a32862b40db1dc6c6823bedc52404146aeb6e9ef1f79692f src/transmitter/ contact.py
|
||||
compare_digest 2e78e578e62771adf7ae9f2a576d72b69a64e6b28649361244bd7a75959f2022845d73d68c3d6a4586841bb10cce906edf1c5d863fbf99b6d081dfc030f98a3d src/transmitter/ files.py
|
||||
compare_digest 7cb9fc9d095f40ce2de6b49c9bd58b9dcab6b835fe7749dce8642c3c87b0eee10c4e53ff986c09ae26fb7b8aad7fe87c5fd56a734f2e013f69195213b9d5e9ec src/transmitter/ input_loop.py
|
||||
compare_digest e723db5bc403cec60b7df3e34d80caa7868bed4e8f0d08a6504d060fdd6c188f4afa41ecd8feb3a6520a384ccff7f8c64efeaeddb084f825d40e1854fb528f9f src/transmitter/ key_exchanges.py
|
||||
compare_digest 41798dfe91868b37c130a373accac93c4200dc77bd8b6c40a38835ecf4187b955ccfaa53f842ccddf78ce5607b3e361a30a4bb53bd7cb5ab6d2fb4785454dead src/transmitter/ packet.py
|
||||
compare_digest 3f1e7a5cb58ba8fcf0ccc66195d589ac0e34153296e2c395ca099304a99bb61c25248078dc453b3cc47e08a0b207a688b0a426d095df4a7bd235d1a95bb3c8d6 src/transmitter/ sender_loop.py
|
||||
compare_digest c5a6c85e57d4456353f89fc4b2d30fc60775511720a32287720b3b301e0d6e7539677b47c4ff8c6b6f223b93da7dfbb38d1830f43e6f25c598efd54799262956 src/transmitter/ traffic_masking.py
|
||||
compare_digest 678ae2b63667d93b1d4467d029ab04778614ddf6c09dff4bb61d262373353cd7fe6b8b535292fdf28e1be36c8b57534dee9eb745ee94c72b051798ac4e1cbccd src/transmitter/ user_input.py
|
||||
compare_digest 00e247854f067194f80c86c9a3b9fbe1975e600844a1f33af79e36618680e0c9ddebaa25ef6df1a48e324e241f2b113f719fc29a2b43626eeeba4b92bdbb8528 src/transmitter/ windows.py
|
||||
}
|
||||
|
||||
|
||||
# PIP dependency file names
|
||||
ARGON2=argon2_cffi-19.1.0-cp34-abi3-manylinux1_x86_64.whl
|
||||
ASN1CRYPTO=asn1crypto-0.24.0-py2.py3-none-any.whl
|
||||
CERTIFI=certifi-2019.6.16-py2.py3-none-any.whl
|
||||
CFFI=cffi-1.12.3-cp37-cp37m-manylinux1_x86_64.whl
|
||||
CHARDET=chardet-3.0.4-py2.py3-none-any.whl
|
||||
CLICK=Click-7.0-py2.py3-none-any.whl
|
||||
CRYPTOGRAPHY=cryptography-2.7-cp34-abi3-manylinux1_x86_64.whl
|
||||
FLASK=Flask-1.1.1-py2.py3-none-any.whl
|
||||
IDNA=idna-2.8-py2.py3-none-any.whl
|
||||
ITSDANGEROUS=itsdangerous-1.1.0-py2.py3-none-any.whl
|
||||
JINJA2=Jinja2-2.10.1-py2.py3-none-any.whl
|
||||
MARKUPSAFE=MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl
|
||||
PYCPARSER=pycparser-2.19.tar.gz
|
||||
PYNACL=PyNaCl-1.3.0-cp34-abi3-manylinux1_x86_64.whl
|
||||
PYSERIAL=pyserial-3.4-py2.py3-none-any.whl
|
||||
PYSOCKS=PySocks-1.7.0-py3-none-any.whl
|
||||
REQUESTS=requests-2.22.0-py2.py3-none-any.whl
|
||||
SETUPTOOLS=setuptools-41.2.0-py2.py3-none-any.whl
|
||||
SIX=six-1.12.0-py2.py3-none-any.whl
|
||||
STEM=stem-1.7.1.tar.gz
|
||||
URLLIB3=urllib3-1.25.3-py2.py3-none-any.whl
|
||||
VIRTUALENV=virtualenv-16.7.3-py2.py3-none-any.whl
|
||||
WERKZEUG=Werkzeug-0.15.5-py2.py3-none-any.whl
|
||||
|
||||
|
||||
function process_tcb_dependencies {
|
||||
# Manage TCB dependencies in batch. The command that uses the files
|
||||
# is passed to the function as a parameter.
|
||||
|
@ -145,7 +147,6 @@ function process_tcb_dependencies {
|
|||
sudo $1 /opt/tfc/${SETUPTOOLS}
|
||||
sudo $1 /opt/tfc/${PYNACL}
|
||||
sudo $1 /opt/tfc/${PYSERIAL}
|
||||
sudo $1 /opt/tfc/${ASN1CRYPTO}
|
||||
sudo $1 /opt/tfc/${CRYPTOGRAPHY}
|
||||
}
|
||||
|
||||
|
@ -154,37 +155,113 @@ function process_tails_dependencies {
|
|||
# Manage Tails dependencies in batch. The command that uses the
|
||||
# files is passed to the function as a parameter.
|
||||
|
||||
# Pyserial
|
||||
t_sudo $1 /opt/tfc/${PYSERIAL}
|
||||
|
||||
# Stem
|
||||
t_sudo $1 /opt/tfc/${STEM}
|
||||
|
||||
# PySocks
|
||||
t_sudo $1 /opt/tfc/${PYSOCKS}
|
||||
t_sudo -E $1 /opt/tfc/${PYSERIAL}
|
||||
# t_sudo -E $1 /opt/tfc/${STEM}
|
||||
t_sudo -E $1 /opt/tfc/${PYSOCKS}
|
||||
|
||||
# Requests
|
||||
t_sudo $1 /opt/tfc/${URLLIB3}
|
||||
t_sudo $1 /opt/tfc/${IDNA}
|
||||
t_sudo $1 /opt/tfc/${CHARDET}
|
||||
t_sudo $1 /opt/tfc/${CERTIFI}
|
||||
t_sudo $1 /opt/tfc/${REQUESTS}
|
||||
t_sudo -E $1 /opt/tfc/${URLLIB3}
|
||||
t_sudo -E $1 /opt/tfc/${IDNA}
|
||||
t_sudo -E $1 /opt/tfc/${CHARDET}
|
||||
t_sudo -E $1 /opt/tfc/${CERTIFI}
|
||||
t_sudo -E $1 /opt/tfc/${REQUESTS}
|
||||
|
||||
# Flask
|
||||
t_sudo $1 /opt/tfc/${WERKZEUG}
|
||||
t_sudo $1 /opt/tfc/${MARKUPSAFE}
|
||||
t_sudo $1 /opt/tfc/${JINJA2}
|
||||
t_sudo $1 /opt/tfc/${ITSDANGEROUS}
|
||||
t_sudo $1 /opt/tfc/${CLICK}
|
||||
t_sudo $1 /opt/tfc/${FLASK}
|
||||
t_sudo -E $1 /opt/tfc/${WERKZEUG}
|
||||
t_sudo -E $1 /opt/tfc/${MARKUPSAFE}
|
||||
t_sudo -E $1 /opt/tfc/${JINJA2}
|
||||
t_sudo -E $1 /opt/tfc/${ITSDANGEROUS}
|
||||
t_sudo -E $1 /opt/tfc/${CLICK}
|
||||
t_sudo -E $1 /opt/tfc/${FLASK}
|
||||
|
||||
# Cryptography
|
||||
t_sudo $1 /opt/tfc/${SETUPTOOLS}
|
||||
t_sudo $1 /opt/tfc/${SIX}
|
||||
t_sudo $1 /opt/tfc/${ASN1CRYPTO}
|
||||
t_sudo $1 /opt/tfc/${PYCPARSER}
|
||||
t_sudo $1 /opt/tfc/${CFFI}
|
||||
t_sudo $1 /opt/tfc/${CRYPTOGRAPHY}
|
||||
t_sudo -E $1 /opt/tfc/${SIX}
|
||||
t_sudo -E $1 /opt/tfc/${PYCPARSER}
|
||||
t_sudo -E $1 /opt/tfc/${CFFI}
|
||||
t_sudo -E $1 /opt/tfc/${CRYPTOGRAPHY}
|
||||
|
||||
# PyNaCl
|
||||
t_sudo -E $1 /opt/tfc/${PYNACL}
|
||||
}
|
||||
|
||||
|
||||
function move_tails_dependencies {
|
||||
# Move Tails dependencies in batch.
|
||||
t_sudo mv $HOME/${VIRTUALENV} /opt/tfc/
|
||||
t_sudo mv $HOME/${PYSERIAL} /opt/tfc/
|
||||
# t_sudo mv $HOME/${STEM} /opt/tfc/
|
||||
t_sudo mv $HOME/${PYSOCKS} /opt/tfc/
|
||||
|
||||
# Requests
|
||||
t_sudo mv $HOME/${URLLIB3} /opt/tfc/
|
||||
t_sudo mv $HOME/${IDNA} /opt/tfc/
|
||||
t_sudo mv $HOME/${CHARDET} /opt/tfc/
|
||||
t_sudo mv $HOME/${CERTIFI} /opt/tfc/
|
||||
t_sudo mv $HOME/${REQUESTS} /opt/tfc/
|
||||
|
||||
# Flask
|
||||
t_sudo mv $HOME/${WERKZEUG} /opt/tfc/
|
||||
t_sudo mv $HOME/${MARKUPSAFE} /opt/tfc/
|
||||
t_sudo mv $HOME/${JINJA2} /opt/tfc/
|
||||
t_sudo mv $HOME/${ITSDANGEROUS} /opt/tfc/
|
||||
t_sudo mv $HOME/${CLICK} /opt/tfc/
|
||||
t_sudo mv $HOME/${FLASK} /opt/tfc/
|
||||
|
||||
# Cryptography
|
||||
t_sudo mv $HOME/${SIX} /opt/tfc/
|
||||
t_sudo mv $HOME/${PYCPARSER} /opt/tfc/
|
||||
t_sudo mv $HOME/${CFFI} /opt/tfc/
|
||||
t_sudo mv $HOME/${CRYPTOGRAPHY} /opt/tfc/
|
||||
|
||||
# PyNaCl
|
||||
t_sudo mv $HOME/${PYNACL} /opt/tfc/
|
||||
}
|
||||
|
||||
|
||||
function verify_tails_dependencies {
|
||||
# Tails doesn't allow downloading over PIP to /opt/tfc, so we
|
||||
# first download to $HOME, move the files to /opt/tfc, and then
|
||||
# perform additional hash verification
|
||||
compare_digest e80eb04615d1dcd2546bd5ceef5408bbb577fa0dd725bc69f20dd7840518af575f0b41e629e8164fdaea398628813720a6f70a42e7748336601391605b79f542 '' ${VIRTUALENV}
|
||||
compare_digest 8333ac2843fd136d5d0d63b527b37866f7d18afc3bb33c4938b63af077492aeb118eb32a89ac78547f14d59a2adb1e5d00728728275de62317da48dadf6cdff9 '' ${PYSERIAL}
|
||||
# compare_digest a275f59bba650cb5bb151cf53fb1dd820334f9abbeae1a25e64502adc854c7f54c51bc3d6c1656b595d142fc0695ffad53aab3c57bc285421c1f4f10c9c3db4c '' ${STEM}
|
||||
compare_digest 313b954102231d038d52ab58f41e3642579be29f827135b8dd92c06acb362effcb0a7fd5f35de9273372b92d9fe29f38381ae44f8b41aa90d2564d6dd07ecd12 '' ${PYSOCKS}
|
||||
|
||||
# Requests
|
||||
compare_digest 719cfa3841d0fe7c7f0a1901b8029df6685825da7f510ba61f095df64f115fae8bfa4118fa7536231ed8187cdf3385cb2d52e53c1b35b8f4aa42f7117cc4d447 '' ${URLLIB3}
|
||||
compare_digest fb07dbec1de86efbad82a4f73d98123c59b083c1f1277445204bef75de99ca200377ad2f1db8924ae79b31b3dd984891c87d0a6344ec4d07a0ddbbbc655821a3 '' ${IDNA}
|
||||
compare_digest bfae58c8ea19c87cc9c9bf3d0b6146bfdb3630346bd954fe8e9f7da1f09da1fc0d6943ff04802798a665ea3b610ee2d65658ce84fe5a89f9e93625ea396a17f4 '' ${CHARDET}
|
||||
compare_digest 06e8e1546d375e528a1486e1dee4fda3e585a03ef23ede85d1dad006e0eda837ebade1edde62fdc987a7f310bda69159e94ec36b79a066e0e13bbe8bf7019cfc '' ${CERTIFI}
|
||||
compare_digest 9186ce4e39bb64f5931a205ffc9afac61657bc42078bc4754ed12a2b66a12b7a620583440849fc2e161d1061ac0750ddef4670f54916931ace1e9abd2a9fb09c '' ${REQUESTS}
|
||||
|
||||
# Flask
|
||||
compare_digest 3905022d0c398856b30d2ed6bae046c1532e87f56a0a40060030c18124c6c9c98976d9429e2ab03676c4ce75be4ea915ffc2719e04e4b4912a96e498dcd9eb89 '' ${WERKZEUG}
|
||||
compare_digest 69e9b9c9ac4fdf3cfa1a3de23d14964b843989128f8cc6ea58617fc5d6ef937bcc3eae9cb32b5164b5f54b06f96bdff9bc249529f20671cc26adc9e6ce8f6bec '' ${MARKUPSAFE}
|
||||
compare_digest 658d069944c81f9d8b2e90577a9d2c844b4c6a26764efefd7a86f26c05276baf6c7255f381e20e5178782be1786b7400cab12dec15653e7262b36194228bf649 '' ${JINJA2}
|
||||
compare_digest 891c294867f705eb9c66274bd04ac5d93140d6e9beea6cbf9a44e7f9c13c0e2efa3554bdf56620712759a5cd579e112a782d25f3f91ba9419d60b2b4d2bc5b7c '' ${ITSDANGEROUS}
|
||||
compare_digest 6b30987349df7c45c5f41cff9076ed45b178b444fca1ab1965f4ae33d1631522ce0a2868392c736666e83672b8b20e9503ae9ce5016dce3fa8f77bc8a3674130 '' ${CLICK}
|
||||
compare_digest bd49cb364307569480196289fa61fbb5493e46199620333f67617367278e1f56b20fc0d40fd540bef15642a8065e488c24e97f50535e8ec143875095157d8069 '' ${FLASK}
|
||||
|
||||
# Cryptography
|
||||
compare_digest 326574c7542110d2cd8071136a36a6cffc7637ba948b55e0abb7f30f3821843073223301ecbec1d48b8361b0d7ccb338725eeb0424696efedc3f6bd2a23331d3 '' ${SIX}
|
||||
compare_digest 7f830e1c9066ee2d297a55e2bf6db4bf6447b6d9da0145d11a88c3bb98505755fb7986eafa6e06ae0b7680838f5e5d6a6d188245ca5ad45c2a727587bac93ab5 '' ${PYCPARSER}
|
||||
compare_digest fdefd3f63f56adff50723d6a88dc6db816d3d8a31b563599d2a3633ba796f6f70d5a9430510852b3d62b97357f8764f17eeab74b13df16c7cc34e1671a82373b '' ${CFFI}
|
||||
compare_digest 184003c89fee74892de25c3e5ec366faea7a5f1fcca3c82b0d5e5f9f797286671a820ca54da5266d6f879ab342c97e25bce9db366c5fb1178690cd5978d4d622 '' ${CRYPTOGRAPHY} # manylinux1
|
||||
# compare_digest d8ddabe127ae8d7330d219e284de68b37fa450a27b4cf05334e9115388295b00148d9861c23b1a2e5ea9df0c33a2d27f3e4b25ce9abd3c334f1979920b19c902 '' ${CRYPTOGRAPHY} # manylinux2010
|
||||
|
||||
|
||||
# PyNaCl
|
||||
compare_digest c4017c38b026a5c531b15839b8d61d1fae9907ba1960c2f97f4cd67fe0827729346d5186a6d6927ba84f64b4cbfdece12b287aa7750a039f4160831be871cea3 '' ${PYNACL}
|
||||
}
|
||||
|
||||
|
||||
function install_tails_setuptools {
|
||||
# Download setuptools package for Tails and then authenticate and install it.
|
||||
torsocks python3.7 -m pip download --no-cache-dir -r /opt/tfc/requirements-setuptools.txt --require-hashes -d $HOME/
|
||||
t_sudo mv $HOME/${SETUPTOOLS} /opt/tfc/
|
||||
compare_digest a27b38d596931dfef81d705d05689b7748ce0e02d21af4a37204fc74b0913fa7241b8135535eb7749f09af361cad90c475af98493fef11c4ad974780ee01243d '' ${SETUPTOOLS}
|
||||
t_sudo python3.7 -m pip install /opt/tfc/${SETUPTOOLS}
|
||||
t_sudo -E rm /opt/tfc/${SETUPTOOLS}
|
||||
}
|
||||
|
||||
|
||||
|
@ -203,6 +280,8 @@ function remove_common_files {
|
|||
$1 rm /opt/tfc/requirements.txt
|
||||
$1 rm /opt/tfc/requirements-dev.txt
|
||||
$1 rm /opt/tfc/requirements-relay.txt
|
||||
$1 rm /opt/tfc/requirements-relay-tails.txt
|
||||
$1 rm /opt/tfc/requirements-setuptools.txt
|
||||
$1 rm /opt/tfc/requirements-venv.txt
|
||||
$1 rm -f /opt/install.sh
|
||||
$1 rm -f /opt/install.sh.asc
|
||||
|
@ -222,7 +301,7 @@ function steps_before_network_kill {
|
|||
check_rm_existing_installation
|
||||
|
||||
sudo torsocks apt update
|
||||
sudo torsocks apt install git libssl-dev python3-pip python3-tk net-tools -y
|
||||
sudo torsocks apt install git gnome-terminal libssl-dev python3-pip python3-tk net-tools -y
|
||||
sudo torsocks git clone --depth 1 https://github.com/maqp/tfc.git /opt/tfc
|
||||
|
||||
verify_tcb_requirements_files
|
||||
|
@ -261,6 +340,7 @@ function install_tcb {
|
|||
sudo rm -r /opt/tfc/src/relay/
|
||||
sudo rm /opt/tfc/dd.py
|
||||
sudo rm /opt/tfc/relay.py
|
||||
sudo rm /opt/tfc/tfc.yml
|
||||
sudo rm /opt/tfc/${VIRTUALENV}
|
||||
|
||||
add_serial_permissions
|
||||
|
@ -294,6 +374,7 @@ function install_local_test {
|
|||
# Remove unnecessary files
|
||||
remove_common_files "sudo"
|
||||
process_tcb_dependencies "rm"
|
||||
sudo rm /opt/tfc/tfc.yml
|
||||
sudo rm /opt/tfc/${VIRTUALENV}
|
||||
|
||||
install_complete "Installation of TFC for local testing is now complete."
|
||||
|
@ -319,11 +400,10 @@ function install_developer {
|
|||
torsocks git clone https://github.com/maqp/tfc.git $HOME/tfc
|
||||
|
||||
torsocks python3.7 -m pip install -r $HOME/tfc/requirements-venv.txt --require-hashes
|
||||
|
||||
python3.7 -m virtualenv $HOME/tfc/venv_tfc --system-site-packages
|
||||
|
||||
. $HOME/tfc/venv_tfc/bin/activate
|
||||
torsocks python3.7 -m pip install -r $HOME/tfc/requirements.txt --require-hashes
|
||||
torsocks python3.7 -m pip install -r $HOME/tfc/requirements-relay.txt --require-hashes
|
||||
torsocks python3.7 -m pip install -r $HOME/tfc/requirements-dev.txt
|
||||
deactivate
|
||||
|
||||
|
@ -368,6 +448,7 @@ function install_relay_ubuntu {
|
|||
sudo rm -r /opt/tfc/src/transmitter/
|
||||
sudo rm /opt/tfc/dd.py
|
||||
sudo rm /opt/tfc/tfc.py
|
||||
sudo rm /opt/tfc/tfc.yml
|
||||
sudo rm /opt/tfc/${VIRTUALENV}
|
||||
|
||||
add_serial_permissions
|
||||
|
@ -380,28 +461,45 @@ function install_relay_tails {
|
|||
# Install TFC Relay configuration on Networked Computer running
|
||||
# Tails live distro (https://tails.boum.org/).
|
||||
check_tails_tor_version
|
||||
|
||||
read_sudo_pwd
|
||||
|
||||
# Apt dependencies
|
||||
t_sudo apt update
|
||||
t_sudo apt install git libssl-dev python3-pip -y || true # Ignore error in case packets can not be persistently installed
|
||||
|
||||
git clone --depth 1 https://github.com/maqp/tfc.git $HOME/tfc
|
||||
torsocks git clone --depth 1 https://github.com/maqp/tfc.git $HOME/tfc
|
||||
t_sudo mv $HOME/tfc/ /opt/tfc/
|
||||
t_sudo chown -R root /opt/tfc/
|
||||
|
||||
verify_tcb_requirements_files
|
||||
verify_files
|
||||
|
||||
create_user_data_dir
|
||||
|
||||
t_sudo python3.7 -m pip download --no-cache-dir -r /opt/tfc/requirements-relay.txt --require-hashes -d /opt/tfc/
|
||||
install_tails_setuptools
|
||||
|
||||
torsocks python3.7 -m pip download --no-cache-dir -r /opt/tfc/requirements-venv.txt --require-hashes -d $HOME/
|
||||
torsocks python3.7 -m pip download --no-cache-dir -r /opt/tfc/requirements-relay-tails.txt --require-hashes -d $HOME/
|
||||
|
||||
move_tails_dependencies
|
||||
verify_tails_dependencies
|
||||
|
||||
t_sudo python3.7 -m pip install /opt/tfc/${VIRTUALENV}
|
||||
t_sudo python3.7 -m virtualenv /opt/tfc/venv_relay --system-site-packages
|
||||
|
||||
. /opt/tfc/venv_relay/bin/activate
|
||||
process_tails_dependencies "python3.7 -m pip install"
|
||||
deactivate
|
||||
|
||||
# Complete setup
|
||||
t_sudo mv /opt/tfc/tfc.png /usr/share/pixmaps/
|
||||
t_sudo mv /opt/tfc/launchers/TFC-RP-Tails.desktop /usr/share/applications/
|
||||
t_sudo mv /opt/tfc/tfc.yml /etc/onion-grater.d/
|
||||
|
||||
remove_common_files "t_sudo"
|
||||
process_tails_dependencies "rm"
|
||||
|
||||
t_sudo rm /opt/tfc/${VIRTUALENV}
|
||||
t_sudo rm -r /opt/tfc/src/receiver/
|
||||
t_sudo rm -r /opt/tfc/src/transmitter/
|
||||
t_sudo rm /opt/tfc/dd.py
|
||||
|
@ -419,7 +517,7 @@ function t_sudo {
|
|||
|
||||
function install_relay {
|
||||
# Determine the Networked Computer OS for Relay Program installation.
|
||||
if [[ "$(lsb_release -a 2>/dev/null | grep Tails)" ]]; then
|
||||
if [[ "$(cat /etc/os-release 2>/dev/null | grep Tails)" ]]; then
|
||||
install_relay_tails
|
||||
else
|
||||
install_relay_ubuntu
|
||||
|
@ -428,16 +526,10 @@ function install_relay {
|
|||
|
||||
|
||||
function install_virtualenv {
|
||||
# Determine if OS is debian and install virtualenv as sudo so that
|
||||
# the user (who should be on sudoers list) can see virtualenv on
|
||||
# when the installer sets up virtual environment to /opt/tfc/.
|
||||
distro=$(lsb_release -d | awk -F"\t" '{print $2}')
|
||||
|
||||
if [[ "$distro" =~ ^Debian* ]]; then
|
||||
sudo torsocks python3.7 -m pip install -r /opt/tfc/requirements-venv.txt --require-hashes
|
||||
else
|
||||
torsocks python3.7 -m pip install -r /opt/tfc/requirements-venv.txt --require-hashes
|
||||
fi
|
||||
# Some distros want virtualenv installed as sudo and other do
|
||||
# not. Install both to improve the chances of compatibility.
|
||||
sudo torsocks python3.7 -m pip install -r /opt/tfc/requirements-venv.txt --require-hashes
|
||||
torsocks python3.7 -m pip install -r /opt/tfc/requirements-venv.txt --require-hashes
|
||||
}
|
||||
|
||||
|
||||
|
@ -624,6 +716,12 @@ function root_check {
|
|||
|
||||
function sudoer_check {
|
||||
# Check that the user who launched the installer is on the sudoers list.
|
||||
|
||||
# Tails allows sudo without the user `amnesia` being on sudoers list.
|
||||
if ! [[ "$(lsb_release -a 2>/dev/null | grep Tails)" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
sudoers=$(getent group sudo |cut -d: -f4 | tr "," "\n")
|
||||
user_is_sudoer=false
|
||||
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
iQIzBAABCAAdFiEE6o84umdLJC6ZRIRcmBNw6XJaD7oFAlxJLVgACgkQmBNw6XJa
|
||||
D7rz2g//S9fHYr2YHtkW35pywZz2OqrzEjeQUgLkSrDrV6DSgKiuAnVJdqGUzu47
|
||||
xvlGXV7w7dGAppvSU7J0kd/b2LXSdB8cPyBC+yoxPt+Uym6+KA79JzggHQ9j2uFR
|
||||
2st7+Gjdl/UjpP2KL4dyDyBGAuE8rDUhTMsVc7BbYIx2d3PTD6W6N6wbq97Wdjxu
|
||||
aqq589jd12NIxsJbPMcNhoc9Yw38WlI4vEZaQClVjkyZA6iA4fV/cevLYeR377Cu
|
||||
61NLByJ/rAfTmcHgbruNuQPUtoinV07eoPmhmpvT8UFpuH05/9+UVikl/1BbhpcX
|
||||
P279oawjcjF4ZUCQDNtOHRAn+PcVBQ8GeZeN4USqlWm4m14tEdkk5hy5DaQjfmU7
|
||||
jco5SrAcWiK1wuIUdc2Z4y2heuZCGP1a1POYfBDjiS6IvXo8tMrkl/sgFlpA72lx
|
||||
rbJQBRffsWkropvzrdkWo0ShkMYcmUhbUiUstUmML2L7OrI4o/wEc6ZE29MELqnC
|
||||
nRrcIq7PRchJ/AHg99MWHZA74H106cWBGI82SiJsiShuQDK2S8SvvhVQ0nqPVu8B
|
||||
o9GnUbuFGooT9HKjZGUVGlSE6VLArb9zzUPTe2BBgV/oRc7+DHH2P4O1VV29jbCt
|
||||
sAn4wi3Eoah7igulEvvnF6kjdZ3vlkRJv/lDHc4Tz7w1CZY28yI=
|
||||
=yumw
|
||||
iQIzBAABCAAdFiEE6o84umdLJC6ZRIRcmBNw6XJaD7oFAlxJMGkACgkQmBNw6XJa
|
||||
D7pbig//f0vdjRENVNXjVzrtr93ZWiKalltzz6tQJwRMS4Nkc6MlfVI+yEx4ahMP
|
||||
cFpmRDrUov/g1F5SJYQhiaQVIsM61CLPVHnAilJmGI2g9hx8zmd6pM+ax+JeMmi5
|
||||
RSwW1ZrYfNUExLa50XtoCRf/UGFsRoG1u1Ri0xmEWJMMiYfMggFpzynhiitRBeEK
|
||||
vnHdIRf0b1ZsMmoW6PGPXM6NT4swT75xbI+/MfRg2YotbczEgJFz9WLdPTZBOo/9
|
||||
3SvqfMnc+XfbY+GYpgJpD397cY6BZ/qzTge7ffn8YfaM9X0p+M27teyQjHmmpm3t
|
||||
PVDSyR8f0RIV+8LtizQ5dbJ1tpG1S1UP6eMvupBuiL9LPXtpgLMByIDnfZg1czMS
|
||||
llaOVbCSbFEb2yioAh+r4G/R1geKZOjmPAt9Sddlf/QakH3Jqj7+SUE/kvSHBzoV
|
||||
Fcu3sgmrbCp2fWuhyU868471DkGY4MlNYDCLdYxXT7UpRoEryGo+mkp6G4ZDeorT
|
||||
DK4mx3JKSw+OWVyKOXqhECUWBT/7w9CRC8GfVraan/a4dHaTkoPPwtBGqfP3T2Z/
|
||||
NiVvtPCryU2qk762sYXh1cLLRWo6BKIU6VHWQ0CDJqPWhktMXs/zWxH+MDGFoiLw
|
||||
bKEbOMYZkYEy+2BpjYh19W0nQAk52t/smOM+ehhNiH8jhUiJwUM=
|
||||
=thVJ
|
||||
-----END PGP SIGNATURE-----
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[Desktop Entry]
|
||||
Version=1.19.08
|
||||
Version=1.19.10
|
||||
Name=TFC-Dev-LR
|
||||
Comment=Developer configuration
|
||||
Exec=terminator -m -u -g $HOME/tfc/launchers/terminator-config-dev -p tfc -l tfc-lr
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[Desktop Entry]
|
||||
Version=1.19.08
|
||||
Version=1.19.10
|
||||
Name=TFC-Local-Test-LR
|
||||
Comment=Local testing configuration
|
||||
Exec=terminator -m -u -g /opt/tfc/terminator-config-local-test -p tfc -l tfc-lr
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[Desktop Entry]
|
||||
Version=1.19.08
|
||||
Version=1.19.10
|
||||
Name=TFC-Relay
|
||||
Exec=gnome-terminal -x bash -c "cd /opt/tfc && python3.7 'relay.py' || bash"
|
||||
Exec=gnome-terminal -x bash -c "cd /opt/tfc && source venv_relay/bin/activate && python3.7 'relay.py' && deactivate || bash"
|
||||
Icon=tfc.png
|
||||
Terminal=false
|
||||
Type=Application
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[Desktop Entry]
|
||||
Version=1.19.08
|
||||
Version=1.19.10
|
||||
Name=TFC-Relay
|
||||
Exec=gnome-terminal -x bash -c "cd /opt/tfc && source venv_relay/bin/activate && python3.7 'relay.py' && deactivate || bash"
|
||||
Icon=tfc.png
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[Desktop Entry]
|
||||
Version=1.19.08
|
||||
Version=1.19.10
|
||||
Name=TFC-Receiver
|
||||
Exec=gnome-terminal --maximize -x bash -c "cd /opt/tfc && source venv_tcb/bin/activate && python3.7 'tfc.py' -r && deactivate || bash"
|
||||
Icon=tfc.png
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[Desktop Entry]
|
||||
Version=1.19.08
|
||||
Version=1.19.10
|
||||
Name=TFC-Transmitter
|
||||
Exec=gnome-terminal --maximize -x bash -c "cd /opt/tfc && source venv_tcb/bin/activate && python3.7 'tfc.py' && deactivate || bash"
|
||||
Icon=tfc.png
|
||||
|
|
8
relay.py
8
relay.py
|
@ -31,10 +31,10 @@ from cryptography.hazmat.primitives.serialization import Encoding, PublicForma
|
|||
from src.common.gateway import Gateway, gateway_loop
|
||||
from src.common.misc import ensure_dir, monitor_processes, process_arguments
|
||||
from src.common.output import print_title
|
||||
from src.common.statics import C_REQ_MGMT_QUEUE, C_REQ_STATE_QUEUE, CONTACT_MGMT_QUEUE, CONTACT_REQ_QUEUE, DIR_TFC
|
||||
from src.common.statics import DST_COMMAND_QUEUE, DST_MESSAGE_QUEUE, EXIT_QUEUE, F_TO_FLASK_QUEUE, GATEWAY_QUEUE
|
||||
from src.common.statics import GROUP_MGMT_QUEUE, GROUP_MSG_QUEUE, M_TO_FLASK_QUEUE, NC, ONION_CLOSE_QUEUE
|
||||
from src.common.statics import ONION_KEY_QUEUE, SRC_TO_RELAY_QUEUE, TOR_DATA_QUEUE, URL_TOKEN_QUEUE
|
||||
from src.common.statics import (CONTACT_MGMT_QUEUE, CONTACT_REQ_QUEUE, C_REQ_MGMT_QUEUE, C_REQ_STATE_QUEUE, DIR_TFC,
|
||||
DST_COMMAND_QUEUE, DST_MESSAGE_QUEUE, EXIT_QUEUE, F_TO_FLASK_QUEUE, GATEWAY_QUEUE,
|
||||
GROUP_MGMT_QUEUE, GROUP_MSG_QUEUE, M_TO_FLASK_QUEUE, NC, ONION_CLOSE_QUEUE,
|
||||
ONION_KEY_QUEUE, SRC_TO_RELAY_QUEUE, TOR_DATA_QUEUE, URL_TOKEN_QUEUE)
|
||||
|
||||
from src.relay.client import c_req_manager, client_scheduler, g_msg_manager
|
||||
from src.relay.commands import relay_command
|
||||
|
|
|
@ -1,7 +1,46 @@
|
|||
# Static type checking tool
|
||||
mypy
|
||||
mypy>=0.740
|
||||
|
||||
# Unit test tools
|
||||
pytest
|
||||
pytest-cov
|
||||
pytest-xdist
|
||||
pytest>=5.2.1
|
||||
pytest-cov>=2.8.1
|
||||
pytest-xdist>=1.30.0
|
||||
|
||||
# TFC dependencies (note: not authenticated with hashes)
|
||||
|
||||
# pyserial
|
||||
pyserial>=3.4
|
||||
|
||||
# argon2_cffi
|
||||
argon2_cffi>=19.1.0
|
||||
cffi>=1.13.1
|
||||
pycparser>=2.19
|
||||
six>=1.12.0
|
||||
|
||||
# pyca/pynacl
|
||||
PyNaCl>=1.3.0
|
||||
setuptools>=41.4.0
|
||||
|
||||
# pyca/cryptography
|
||||
cryptography>=2.8
|
||||
|
||||
# Stem
|
||||
stem>=1.7.1
|
||||
|
||||
# PySocks
|
||||
pysocks>=1.7.1
|
||||
|
||||
# Requests
|
||||
requests>=2.22.0
|
||||
certifi>=2019.9.11
|
||||
chardet>=3.0.4
|
||||
idna>=2.8
|
||||
urllib3>=1.25.6
|
||||
|
||||
# Flask
|
||||
flask>=1.1.1
|
||||
click>=7.0
|
||||
itsdangerous>=1.1.0
|
||||
jinja2>=2.10.3
|
||||
markupsafe>=1.1.1
|
||||
werkzeug>=0.16.0
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
# Sub-dependencies are listed below dependencies
|
||||
|
||||
# Pyserial (Connects the Source/Destination Computer to the Networked Computer)
|
||||
pyserial==3.4 --hash=sha512:8333ac2843fd136d5d0d63b527b37866f7d18afc3bb33c4938b63af077492aeb118eb32a89ac78547f14d59a2adb1e5d00728728275de62317da48dadf6cdff9
|
||||
|
||||
# Stem (Connects to Tor and manages Onion Services)
|
||||
# stem==1.7.1 --hash=sha512:a275f59bba650cb5bb151cf53fb1dd820334f9abbeae1a25e64502adc854c7f54c51bc3d6c1656b595d142fc0695ffad53aab3c57bc285421c1f4f10c9c3db4c
|
||||
|
||||
# PySocks (Routes requests library through SOCKS5 proxy making Onion Service connections possible)
|
||||
pysocks==1.7.1 --hash=sha512:313b954102231d038d52ab58f41e3642579be29f827135b8dd92c06acb362effcb0a7fd5f35de9273372b92d9fe29f38381ae44f8b41aa90d2564d6dd07ecd12
|
||||
|
||||
# Requests (Connects to the contact's Tor Onion Service)
|
||||
requests==2.22.0 --hash=sha512:9186ce4e39bb64f5931a205ffc9afac61657bc42078bc4754ed12a2b66a12b7a620583440849fc2e161d1061ac0750ddef4670f54916931ace1e9abd2a9fb09c
|
||||
certifi==2019.9.11 --hash=sha512:06e8e1546d375e528a1486e1dee4fda3e585a03ef23ede85d1dad006e0eda837ebade1edde62fdc987a7f310bda69159e94ec36b79a066e0e13bbe8bf7019cfc
|
||||
chardet==3.0.4 --hash=sha512:bfae58c8ea19c87cc9c9bf3d0b6146bfdb3630346bd954fe8e9f7da1f09da1fc0d6943ff04802798a665ea3b610ee2d65658ce84fe5a89f9e93625ea396a17f4
|
||||
idna==2.8 --hash=sha512:fb07dbec1de86efbad82a4f73d98123c59b083c1f1277445204bef75de99ca200377ad2f1db8924ae79b31b3dd984891c87d0a6344ec4d07a0ddbbbc655821a3
|
||||
urllib3==1.25.6 --hash=sha512:719cfa3841d0fe7c7f0a1901b8029df6685825da7f510ba61f095df64f115fae8bfa4118fa7536231ed8187cdf3385cb2d52e53c1b35b8f4aa42f7117cc4d447
|
||||
|
||||
# Flask (Onion Service web server that serves TFC public keys and ciphertexts to contacts)
|
||||
flask==1.1.1 --hash=sha512:bd49cb364307569480196289fa61fbb5493e46199620333f67617367278e1f56b20fc0d40fd540bef15642a8065e488c24e97f50535e8ec143875095157d8069
|
||||
click==7.0 --hash=sha512:6b30987349df7c45c5f41cff9076ed45b178b444fca1ab1965f4ae33d1631522ce0a2868392c736666e83672b8b20e9503ae9ce5016dce3fa8f77bc8a3674130
|
||||
itsdangerous==1.1.0 --hash=sha512:891c294867f705eb9c66274bd04ac5d93140d6e9beea6cbf9a44e7f9c13c0e2efa3554bdf56620712759a5cd579e112a782d25f3f91ba9419d60b2b4d2bc5b7c
|
||||
jinja2==2.10.3 --hash=sha512:658d069944c81f9d8b2e90577a9d2c844b4c6a26764efefd7a86f26c05276baf6c7255f381e20e5178782be1786b7400cab12dec15653e7262b36194228bf649
|
||||
markupsafe==1.1.1 --hash=sha512:69e9b9c9ac4fdf3cfa1a3de23d14964b843989128f8cc6ea58617fc5d6ef937bcc3eae9cb32b5164b5f54b06f96bdff9bc249529f20671cc26adc9e6ce8f6bec
|
||||
werkzeug==0.16.0 --hash=sha512:3905022d0c398856b30d2ed6bae046c1532e87f56a0a40060030c18124c6c9c98976d9429e2ab03676c4ce75be4ea915ffc2719e04e4b4912a96e498dcd9eb89
|
||||
|
||||
# Cryptography (Handles URL token derivation)
|
||||
cryptography==2.8 --hash=sha512:184003c89fee74892de25c3e5ec366faea7a5f1fcca3c82b0d5e5f9f797286671a820ca54da5266d6f879ab342c97e25bce9db366c5fb1178690cd5978d4d622
|
||||
cffi==1.13.1 --hash=sha512:fdefd3f63f56adff50723d6a88dc6db816d3d8a31b563599d2a3633ba796f6f70d5a9430510852b3d62b97357f8764f17eeab74b13df16c7cc34e1671a82373b
|
||||
pycparser==2.19 --hash=sha512:7f830e1c9066ee2d297a55e2bf6db4bf6447b6d9da0145d11a88c3bb98505755fb7986eafa6e06ae0b7680838f5e5d6a6d188245ca5ad45c2a727587bac93ab5
|
||||
six==1.12.0 --hash=sha512:326574c7542110d2cd8071136a36a6cffc7637ba948b55e0abb7f30f3821843073223301ecbec1d48b8361b0d7ccb338725eeb0424696efedc3f6bd2a23331d3
|
||||
|
||||
# PyNaCl (Derives TFC account from Onion Service private key)
|
||||
PyNaCl==1.3.0 --hash=sha512:c4017c38b026a5c531b15839b8d61d1fae9907ba1960c2f97f4cd67fe0827729346d5186a6d6927ba84f64b4cbfdece12b287aa7750a039f4160831be871cea3
|
||||
# Duplicate sub-dependencies
|
||||
# cffi==1.13.1 --hash=sha512:fdefd3f63f56adff50723d6a88dc6db816d3d8a31b563599d2a3633ba796f6f70d5a9430510852b3d62b97357f8764f17eeab74b13df16c7cc34e1671a82373b
|
||||
# pycparser==2.19 --hash=sha512:7f830e1c9066ee2d297a55e2bf6db4bf6447b6d9da0145d11a88c3bb98505755fb7986eafa6e06ae0b7680838f5e5d6a6d188245ca5ad45c2a727587bac93ab5
|
||||
# six==1.12.0 --hash=sha512:326574c7542110d2cd8071136a36a6cffc7637ba948b55e0abb7f30f3821843073223301ecbec1d48b8361b0d7ccb338725eeb0424696efedc3f6bd2a23331d3
|
|
@ -7,34 +7,33 @@ pyserial==3.4 --hash=sha512:8333ac2843fd136d5d0d63b527b37866f7d18afc3bb33c
|
|||
stem==1.7.1 --hash=sha512:a275f59bba650cb5bb151cf53fb1dd820334f9abbeae1a25e64502adc854c7f54c51bc3d6c1656b595d142fc0695ffad53aab3c57bc285421c1f4f10c9c3db4c
|
||||
|
||||
# PySocks (Routes requests library through SOCKS5 proxy making Onion Service connections possible)
|
||||
pysocks==1.7.0 --hash=sha512:5bbffb2714a04fb53417058703d8112c5e5dca768df627e64618e8ab8a36a8bdbc27f5d6852f39cff6b8fb4c9a5d13909f86eeb5fe9741ba42bdc985685e5d51
|
||||
pysocks==1.7.1 --hash=sha512:313b954102231d038d52ab58f41e3642579be29f827135b8dd92c06acb362effcb0a7fd5f35de9273372b92d9fe29f38381ae44f8b41aa90d2564d6dd07ecd12
|
||||
|
||||
# Requests (Connects to the contact's Tor Onion Service)
|
||||
requests==2.22.0 --hash=sha512:9186ce4e39bb64f5931a205ffc9afac61657bc42078bc4754ed12a2b66a12b7a620583440849fc2e161d1061ac0750ddef4670f54916931ace1e9abd2a9fb09c
|
||||
certifi==2019.6.16 --hash=sha512:d81fe3a75ea611466d5ece7788f47c7946a4226bf4622c2accfd28c1e37b817e748609710c176c51ef2621cbc7ee200dd8d8106e738f1ef7cb96d7f2f82539cc
|
||||
certifi==2019.9.11 --hash=sha512:06e8e1546d375e528a1486e1dee4fda3e585a03ef23ede85d1dad006e0eda837ebade1edde62fdc987a7f310bda69159e94ec36b79a066e0e13bbe8bf7019cfc
|
||||
chardet==3.0.4 --hash=sha512:bfae58c8ea19c87cc9c9bf3d0b6146bfdb3630346bd954fe8e9f7da1f09da1fc0d6943ff04802798a665ea3b610ee2d65658ce84fe5a89f9e93625ea396a17f4
|
||||
idna==2.8 --hash=sha512:fb07dbec1de86efbad82a4f73d98123c59b083c1f1277445204bef75de99ca200377ad2f1db8924ae79b31b3dd984891c87d0a6344ec4d07a0ddbbbc655821a3
|
||||
urllib3==1.25.3 --hash=sha512:46d144af3633080b9ec8a642ab855b401b8224edb839c237639998b004f19b8cb191155c57e633954cf70b100d6d8b21105cd280acd1ea975aef1dec9a4a5860
|
||||
urllib3==1.25.6 --hash=sha512:719cfa3841d0fe7c7f0a1901b8029df6685825da7f510ba61f095df64f115fae8bfa4118fa7536231ed8187cdf3385cb2d52e53c1b35b8f4aa42f7117cc4d447
|
||||
|
||||
# Flask (Onion Service web server that serves TFC public keys and ciphertexts to contacts)
|
||||
flask==1.1.1 --hash=sha512:bd49cb364307569480196289fa61fbb5493e46199620333f67617367278e1f56b20fc0d40fd540bef15642a8065e488c24e97f50535e8ec143875095157d8069
|
||||
click==7.0 --hash=sha512:6b30987349df7c45c5f41cff9076ed45b178b444fca1ab1965f4ae33d1631522ce0a2868392c736666e83672b8b20e9503ae9ce5016dce3fa8f77bc8a3674130
|
||||
itsdangerous==1.1.0 --hash=sha512:891c294867f705eb9c66274bd04ac5d93140d6e9beea6cbf9a44e7f9c13c0e2efa3554bdf56620712759a5cd579e112a782d25f3f91ba9419d60b2b4d2bc5b7c
|
||||
jinja2==2.10.1 --hash=sha512:04860c7ff7086f051368787289f75198eec3357c7da7565dc5045353122650a887e063b1a5297578ddefcc77bfdfe3d9a23c868cb3e7f18a0b5f1c475e29339e
|
||||
jinja2==2.10.3 --hash=sha512:658d069944c81f9d8b2e90577a9d2c844b4c6a26764efefd7a86f26c05276baf6c7255f381e20e5178782be1786b7400cab12dec15653e7262b36194228bf649
|
||||
markupsafe==1.1.1 --hash=sha512:69e9b9c9ac4fdf3cfa1a3de23d14964b843989128f8cc6ea58617fc5d6ef937bcc3eae9cb32b5164b5f54b06f96bdff9bc249529f20671cc26adc9e6ce8f6bec
|
||||
werkzeug==0.15.5 --hash=sha512:19728875a846f895b7e20f1e8762455147253b295c29e4fb981f734a7ec6a491ae4a5427b0fcac54013c9fcca3d9a53d2639c00a0913c8d9ce69d8e8e24cab42
|
||||
werkzeug==0.16.0 --hash=sha512:3905022d0c398856b30d2ed6bae046c1532e87f56a0a40060030c18124c6c9c98976d9429e2ab03676c4ce75be4ea915ffc2719e04e4b4912a96e498dcd9eb89
|
||||
|
||||
# Cryptography (Handles URL token derivation)
|
||||
cryptography==2.7 --hash=sha512:1285c3f5181da41bace4f9fd5ce5fc4bfba71143b39a4f3d8bab642db65bec9556b1965b1c2990236fed9d6b156bf81e6c0642d1531eadf7b92379c25cc4aeac
|
||||
asn1crypto==0.24.0 --hash=sha512:8d9bc344981079ac6c00e71e161c34b6f403e575bbfe1ad06e30a3bcb33e0db317bdcb7aed2d18d510cb1b3ee340a649f7f77a00d271fcf3cc388e6655b67533
|
||||
cffi==1.12.3 --hash=sha512:69a2d725395a1a3585556cb44b62c49bd7f88f41ff194b60d4b9b591c4878a907c0770ef4052b588eaa9d420a53cbeb6b13237fff4054bf26ba5deaa84e25afa
|
||||
cryptography==2.8 --hash=sha512:184003c89fee74892de25c3e5ec366faea7a5f1fcca3c82b0d5e5f9f797286671a820ca54da5266d6f879ab342c97e25bce9db366c5fb1178690cd5978d4d622
|
||||
cffi==1.13.1 --hash=sha512:fdefd3f63f56adff50723d6a88dc6db816d3d8a31b563599d2a3633ba796f6f70d5a9430510852b3d62b97357f8764f17eeab74b13df16c7cc34e1671a82373b
|
||||
pycparser==2.19 --hash=sha512:7f830e1c9066ee2d297a55e2bf6db4bf6447b6d9da0145d11a88c3bb98505755fb7986eafa6e06ae0b7680838f5e5d6a6d188245ca5ad45c2a727587bac93ab5
|
||||
six==1.12.0 --hash=sha512:326574c7542110d2cd8071136a36a6cffc7637ba948b55e0abb7f30f3821843073223301ecbec1d48b8361b0d7ccb338725eeb0424696efedc3f6bd2a23331d3
|
||||
|
||||
# PyNaCl (Derives TFC account from Onion Service private key)
|
||||
PyNaCl==1.3.0 --hash=sha512:c4017c38b026a5c531b15839b8d61d1fae9907ba1960c2f97f4cd67fe0827729346d5186a6d6927ba84f64b4cbfdece12b287aa7750a039f4160831be871cea3
|
||||
setuptools==41.2.0 --hash=sha512:125341f0c22e11d2bd24c453b22e8fd7fd71605ee7a44eb61228686326eaca2e8f35b7ad4d0eacde4865f4d8cb8acb5cb5e3ff2856e756632b71af2f0dbdbee9
|
||||
setuptools==41.4.0 --hash=sha512:a27b38d596931dfef81d705d05689b7748ce0e02d21af4a37204fc74b0913fa7241b8135535eb7749f09af361cad90c475af98493fef11c4ad974780ee01243d
|
||||
# Duplicate sub-dependencies
|
||||
# cffi==1.12.3 --hash=sha512:69a2d725395a1a3585556cb44b62c49bd7f88f41ff194b60d4b9b591c4878a907c0770ef4052b588eaa9d420a53cbeb6b13237fff4054bf26ba5deaa84e25afa
|
||||
# cffi==1.13.1 --hash=sha512:fdefd3f63f56adff50723d6a88dc6db816d3d8a31b563599d2a3633ba796f6f70d5a9430510852b3d62b97357f8764f17eeab74b13df16c7cc34e1671a82373b
|
||||
# pycparser==2.19 --hash=sha512:7f830e1c9066ee2d297a55e2bf6db4bf6447b6d9da0145d11a88c3bb98505755fb7986eafa6e06ae0b7680838f5e5d6a6d188245ca5ad45c2a727587bac93ab5
|
||||
# six==1.12.0 --hash=sha512:326574c7542110d2cd8071136a36a6cffc7637ba948b55e0abb7f30f3821843073223301ecbec1d48b8361b0d7ccb338725eeb0424696efedc3f6bd2a23331d3
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
# Setuptools (Allows installation of pycparser which is a sub-dependency of the cryptography and PyNaCl packages)
|
||||
setuptools==41.4.0 --hash=sha512:a27b38d596931dfef81d705d05689b7748ce0e02d21af4a37204fc74b0913fa7241b8135535eb7749f09af361cad90c475af98493fef11c4ad974780ee01243d # Tails4: 40.8.0 OnionShare2: -
|
|
@ -1,2 +1,2 @@
|
|||
# Virtual environment (Used to create an isolated Python environment for TFC dependencies)
|
||||
virtualenv==16.7.3 --hash=sha512:760587ac587609607526d20d62c5ef2d768d4bc2dc1f7d5ce338d3525ec49cdb60782311dfd4b814defc486292e181a802f561508980f4eb332366355c5e8cb1
|
||||
virtualenv==16.7.7 --hash=sha512:e80eb04615d1dcd2546bd5ceef5408bbb577fa0dd725bc69f20dd7840518af575f0b41e629e8164fdaea398628813720a6f70a42e7748336601391605b79f542
|
||||
|
|
|
@ -5,24 +5,23 @@ pyserial==3.4 --hash=sha512:8333ac2843fd136d5d0d63b527b37866f7d18afc3bb33c
|
|||
|
||||
# Argon2 (Derives keys that protect persistent user data)
|
||||
argon2_cffi==19.1.0 --hash=sha512:77b17303a5d22fc35ac4771be5c710627c80ed7d6bf6705f70015197dbbc2b699ad6af0604b4517d1afd2f6d153058150a5d2933d38e4b4ca741e4ac560ddf72
|
||||
cffi==1.12.3 --hash=sha512:69a2d725395a1a3585556cb44b62c49bd7f88f41ff194b60d4b9b591c4878a907c0770ef4052b588eaa9d420a53cbeb6b13237fff4054bf26ba5deaa84e25afa
|
||||
cffi==1.13.1 --hash=sha512:fdefd3f63f56adff50723d6a88dc6db816d3d8a31b563599d2a3633ba796f6f70d5a9430510852b3d62b97357f8764f17eeab74b13df16c7cc34e1671a82373b
|
||||
pycparser==2.19 --hash=sha512:7f830e1c9066ee2d297a55e2bf6db4bf6447b6d9da0145d11a88c3bb98505755fb7986eafa6e06ae0b7680838f5e5d6a6d188245ca5ad45c2a727587bac93ab5
|
||||
six==1.12.0 --hash=sha512:326574c7542110d2cd8071136a36a6cffc7637ba948b55e0abb7f30f3821843073223301ecbec1d48b8361b0d7ccb338725eeb0424696efedc3f6bd2a23331d3
|
||||
|
||||
# PyNaCl (Handles TCB-side XChaCha20-Poly1305 symmetric encryption)
|
||||
PyNaCl==1.3.0 --hash=sha512:c4017c38b026a5c531b15839b8d61d1fae9907ba1960c2f97f4cd67fe0827729346d5186a6d6927ba84f64b4cbfdece12b287aa7750a039f4160831be871cea3
|
||||
setuptools==41.2.0 --hash=sha512:125341f0c22e11d2bd24c453b22e8fd7fd71605ee7a44eb61228686326eaca2e8f35b7ad4d0eacde4865f4d8cb8acb5cb5e3ff2856e756632b71af2f0dbdbee9
|
||||
setuptools==41.4.0 --hash=sha512:a27b38d596931dfef81d705d05689b7748ce0e02d21af4a37204fc74b0913fa7241b8135535eb7749f09af361cad90c475af98493fef11c4ad974780ee01243d
|
||||
|
||||
# Duplicate sub-dependencies
|
||||
# cffi==1.12.3 --hash=sha512:69a2d725395a1a3585556cb44b62c49bd7f88f41ff194b60d4b9b591c4878a907c0770ef4052b588eaa9d420a53cbeb6b13237fff4054bf26ba5deaa84e25afa
|
||||
# cffi==1.13.1 --hash=sha512:fdefd3f63f56adff50723d6a88dc6db816d3d8a31b563599d2a3633ba796f6f70d5a9430510852b3d62b97357f8764f17eeab74b13df16c7cc34e1671a82373b
|
||||
# pycparser==2.19 --hash=sha512:7f830e1c9066ee2d297a55e2bf6db4bf6447b6d9da0145d11a88c3bb98505755fb7986eafa6e06ae0b7680838f5e5d6a6d188245ca5ad45c2a727587bac93ab5
|
||||
# six==1.12.0 --hash=sha512:326574c7542110d2cd8071136a36a6cffc7637ba948b55e0abb7f30f3821843073223301ecbec1d48b8361b0d7ccb338725eeb0424696efedc3f6bd2a23331d3
|
||||
|
||||
# Cryptography (Handles TCB-side X448 key exchange)
|
||||
cryptography==2.7 --hash=sha512:1285c3f5181da41bace4f9fd5ce5fc4bfba71143b39a4f3d8bab642db65bec9556b1965b1c2990236fed9d6b156bf81e6c0642d1531eadf7b92379c25cc4aeac
|
||||
asn1crypto==0.24.0 --hash=sha512:8d9bc344981079ac6c00e71e161c34b6f403e575bbfe1ad06e30a3bcb33e0db317bdcb7aed2d18d510cb1b3ee340a649f7f77a00d271fcf3cc388e6655b67533
|
||||
cryptography==2.8 --hash=sha512:184003c89fee74892de25c3e5ec366faea7a5f1fcca3c82b0d5e5f9f797286671a820ca54da5266d6f879ab342c97e25bce9db366c5fb1178690cd5978d4d622
|
||||
|
||||
# Duplicate sub-dependencies
|
||||
# cffi==1.12.3 --hash=sha512:69a2d725395a1a3585556cb44b62c49bd7f88f41ff194b60d4b9b591c4878a907c0770ef4052b588eaa9d420a53cbeb6b13237fff4054bf26ba5deaa84e25afa
|
||||
# cffi==1.13.1 --hash=sha512:fdefd3f63f56adff50723d6a88dc6db816d3d8a31b563599d2a3633ba796f6f70d5a9430510852b3d62b97357f8764f17eeab74b13df16c7cc34e1671a82373b
|
||||
# pycparser==2.19 --hash=sha512:7f830e1c9066ee2d297a55e2bf6db4bf6447b6d9da0145d11a88c3bb98505755fb7986eafa6e06ae0b7680838f5e5d6a6d188245ca5ad45c2a727587bac93ab5
|
||||
# six==1.12.0 --hash=sha512:326574c7542110d2cd8071136a36a6cffc7637ba948b55e0abb7f30f3821843073223301ecbec1d48b8361b0d7ccb338725eeb0424696efedc3f6bd2a23331d3
|
||||
|
|
|
@ -48,9 +48,9 @@ from cryptography.hazmat.primitives.serialization import Encoding, PublicForma
|
|||
|
||||
from src.common.exceptions import CriticalError
|
||||
from src.common.misc import separate_header
|
||||
from src.common.statics import ARGON2_SALT_LENGTH, BITS_PER_BYTE, BLAKE2_DIGEST_LENGTH, BLAKE2_DIGEST_LENGTH_MAX
|
||||
from src.common.statics import BLAKE2_DIGEST_LENGTH_MIN, PADDING_LENGTH, SYMMETRIC_KEY_LENGTH, TFC_PUBLIC_KEY_LENGTH
|
||||
from src.common.statics import XCHACHA20_NONCE_LENGTH
|
||||
from src.common.statics import (ARGON2_SALT_LENGTH, BITS_PER_BYTE, BLAKE2_DIGEST_LENGTH, BLAKE2_DIGEST_LENGTH_MAX,
|
||||
BLAKE2_DIGEST_LENGTH_MIN, PADDING_LENGTH, SYMMETRIC_KEY_LENGTH,
|
||||
TFC_PUBLIC_KEY_LENGTH, X448_SHARED_SECRET_LENGTH, XCHACHA20_NONCE_LENGTH)
|
||||
|
||||
|
||||
def blake2b(message: bytes, # Message to hash
|
||||
|
@ -118,14 +118,20 @@ def blake2b(message: bytes, # Message to hash
|
|||
https://github.com/python/cpython/blob/3.7/Lib/hashlib.py
|
||||
"""
|
||||
try:
|
||||
digest = hashlib.blake2b(message, digest_size=digest_size, key=key, salt=salt, person=person).digest() # type: bytes
|
||||
|
||||
if len(digest) != digest_size:
|
||||
raise CriticalError(f"BLAKE2b digest had invalid length ({len(digest)} bytes).")
|
||||
|
||||
digest = hashlib.blake2b(message,
|
||||
digest_size=digest_size,
|
||||
key=key,
|
||||
salt=salt,
|
||||
person=person).digest() # type: bytes
|
||||
except ValueError as e:
|
||||
raise CriticalError(str(e))
|
||||
|
||||
if not isinstance(digest, bytes):
|
||||
raise CriticalError(f"BLAKE2b returned an invalid type ({type(digest)}) digest.")
|
||||
|
||||
if len(digest) != digest_size:
|
||||
raise CriticalError(f"BLAKE2b digest had invalid length ({len(digest)} bytes).")
|
||||
|
||||
return digest
|
||||
|
||||
|
||||
|
@ -220,6 +226,12 @@ def argon2_kdf(password: str, # Password to derive the key from
|
|||
except argon2.exceptions.Argon2Error as e:
|
||||
raise CriticalError(str(e))
|
||||
|
||||
if not isinstance(key, bytes):
|
||||
raise CriticalError(f"Argon2 returned an invalid type ({type(key)}) key.")
|
||||
|
||||
if len(key) != SYMMETRIC_KEY_LENGTH:
|
||||
raise CriticalError(f"Derived an invalid length key from password ({len(key)} bytes).")
|
||||
|
||||
return key
|
||||
|
||||
|
||||
|
@ -357,8 +369,11 @@ class X448(object):
|
|||
public_key = private_key.public_key().public_bytes(encoding=Encoding.Raw,
|
||||
format=PublicFormat.Raw) # type: bytes
|
||||
|
||||
if not isinstance(public_key, bytes):
|
||||
raise CriticalError(f"Generated an invalid type ({type(public_key)}) public key.")
|
||||
|
||||
if len(public_key) != TFC_PUBLIC_KEY_LENGTH:
|
||||
raise CriticalError(f"Generated invalid size public key from private key ({len(public_key)} bytes).")
|
||||
raise CriticalError(f"Generated an invalid size public key from private key ({len(public_key)} bytes).")
|
||||
|
||||
return public_key
|
||||
|
||||
|
@ -392,6 +407,12 @@ class X448(object):
|
|||
except ValueError as e:
|
||||
raise CriticalError(str(e))
|
||||
|
||||
if not isinstance(shared_secret, bytes): # pragma: no cover
|
||||
raise CriticalError(f"Derived an invalid type ({type(shared_secret)}) shared secret.")
|
||||
|
||||
if len(shared_secret) != X448_SHARED_SECRET_LENGTH: # pragma: no cover
|
||||
raise CriticalError(f"Generated an invalid size shared secret ({len(shared_secret)} bytes).")
|
||||
|
||||
return blake2b(shared_secret, digest_size=SYMMETRIC_KEY_LENGTH)
|
||||
|
||||
|
||||
|
@ -548,6 +569,9 @@ def byte_padding(bytestring: bytes # Bytestring to be padded
|
|||
padded = padder.update(bytestring) # type: bytes
|
||||
padded += padder.finalize()
|
||||
|
||||
if not isinstance(padded, bytes):
|
||||
raise CriticalError(f"Padded message had invalid type ({type(padded)}).")
|
||||
|
||||
if len(padded) % PADDING_LENGTH != 0:
|
||||
raise CriticalError(f"Padded message had an invalid length ({len(padded)}).")
|
||||
|
||||
|
@ -623,7 +647,7 @@ def csprng(key_length: int = SYMMETRIC_KEY_LENGTH # Length of the key
|
|||
┃add_device ┃┃add_hwgenerator┃┃ add_input ┃┃ add_disk ┃┃add_interrupt┃
|
||||
┃_randomness┃┃ _randomness ┃┃_randomness┃┃_randomness┃┃ _randomness ┃
|
||||
┗━━━━━━━━━━━┛┗━━━━━━━━━━━━━━━┛┗━━━━━━━━━━━┛┗━━━━━━━━━━━┛┗━━━━━━━━━━━━━┛
|
||||
|
||||
|
||||
[1] https://www.bsi.bund.de/SharedDocs/Downloads/EN/BSI/Publications/Studies/LinuxRNG/LinuxRNG_EN.pdf?__blob=publicationFile&v=16
|
||||
|
||||
|
||||
|
@ -642,15 +666,15 @@ def csprng(key_length: int = SYMMETRIC_KEY_LENGTH # Length of the key
|
|||
o add_hwgenerator_randomness: HWRNGs supported by the Linux
|
||||
kernel, if available. The output of the HWRNG device is used
|
||||
to seed the ChaCha20 DRNG if needed, and then to seed the
|
||||
input_pool directly when the entropy estimator's value falls
|
||||
input_pool directly when the entropy estimator's value falls
|
||||
below the set threshold. (CPU HWRNG is not processed by the
|
||||
add_hwgenerator_randomness service function).[1; pp.52-54]
|
||||
|
||||
o add_input_randomness: Key presses, mouse movements, mouse
|
||||
button presses etc. Repeated event values (e.g. key presses or
|
||||
button presses etc. Repeated event values (e.g. key presses or
|
||||
same direction mouse movements) are ignored by the service
|
||||
function.[1; p.44]
|
||||
The event data consists of four LSBs of the event type,
|
||||
The event data consists of four LSBs of the event type,
|
||||
four MSBs of the event code, the event code itself, and the
|
||||
event value, all XORed together.[1; p.45]
|
||||
The resulting event data is fed into the input_pool via
|
||||
|
@ -673,7 +697,7 @@ def csprng(key_length: int = SYMMETRIC_KEY_LENGTH # Length of the key
|
|||
|
||||
o add_interrupt_randomness: Interrupts (i.e. signals from SW/HW
|
||||
to processor that an event needs immediate attention) occur
|
||||
hundreds of thousands of times per second under average load.
|
||||
hundreds of thousands of times per second under average load.
|
||||
The interrupt timestamps and event data are mixed into
|
||||
128-bit, per-CPU pool called fast_pool. When an interrupt
|
||||
occurs
|
||||
|
@ -684,14 +708,14 @@ def csprng(key_length: int = SYMMETRIC_KEY_LENGTH # Length of the key
|
|||
high-resolution timestamp are XORed with the second word
|
||||
of the fast_pool.
|
||||
* The 32 MSBs and LSBs of the 64-bit CPU instruction pointer
|
||||
value are XORed with the third and fourth word of the
|
||||
value are XORed with the third and fourth word of the
|
||||
fast_pool. If no pointer is available, the XORed value is
|
||||
instead the return address of the add_interrupt_randomness
|
||||
function.
|
||||
The raw entropy mixed into the fast_pool is then distributed
|
||||
The raw entropy mixed into the fast_pool is then distributed
|
||||
more evenly with a function called fast_mix.
|
||||
The content of the fast_pool is mixed into the input_pool
|
||||
once it has data about at least 64 interrupt events, and
|
||||
The content of the fast_pool is mixed into the input_pool
|
||||
once it has data about at least 64 interrupt events, and
|
||||
(unless the ChaCha20 DRNG is being seeded) at least one second
|
||||
has passed since the fast_pool was last mixed in. The counter
|
||||
keeping track of the interrupt events is then zeroed.
|
||||
|
@ -721,16 +745,16 @@ def csprng(key_length: int = SYMMETRIC_KEY_LENGTH # Length of the key
|
|||
|
||||
- AMD: A set of 16 ring oscillator chains feeds 512 bits
|
||||
of raw entropy to AES256-CBC-MAC based conditioner
|
||||
again available via RDSEED instruction. The
|
||||
again available via RDSEED instruction. The
|
||||
conditioner is used to produce 128-bit seeds --
|
||||
a process that is repeated thrice to create a
|
||||
384-bit seed for the AES256-CTR based DRBG
|
||||
available via the RDRAND instruction. The DRBG is
|
||||
a process that is repeated thrice to create a
|
||||
384-bit seed for the AES256-CTR based DRBG
|
||||
available via the RDRAND instruction. The DRBG is
|
||||
reseeded at least every 2048 queries of 32-bits
|
||||
(8kB).[4; pp.2-3]
|
||||
|
||||
While the RDSEED/RDRAND instructions are used extensively,
|
||||
because the CPU HWRNG is not an auditable source, it is
|
||||
because the CPU HWRNG is not an auditable source, it is
|
||||
assumed to provide only a very small amount of entropy.
|
||||
[1; p.83]
|
||||
|
||||
|
@ -765,7 +789,7 @@ def csprng(key_length: int = SYMMETRIC_KEY_LENGTH # Length of the key
|
|||
|
||||
Initialization of the input_pool
|
||||
--------------------------------
|
||||
The input_pool is initialized during boot time of the kernel by
|
||||
The input_pool is initialized during boot time of the kernel by
|
||||
mixing following data into the entropy pool:
|
||||
1. The current time with nanosecond precision (64-bit CPUs).
|
||||
2. Entropy obtained from CPU HWRNG via RDRAND instruction, if
|
||||
|
@ -776,7 +800,7 @@ def csprng(key_length: int = SYMMETRIC_KEY_LENGTH # Length of the key
|
|||
Initial seeding and seeding levels of the input_pool
|
||||
----------------------------------------------------
|
||||
After a hardware event has occurred, the entropy of the event value
|
||||
is estimated, and both values are mixed into the input_pool using a
|
||||
is estimated, and both values are mixed into the input_pool using a
|
||||
function based on a linear feedback shift register (LFSR), one byte
|
||||
at a time.[1; p.23]
|
||||
|
||||
|
@ -805,7 +829,7 @@ def csprng(key_length: int = SYMMETRIC_KEY_LENGTH # Length of the key
|
|||
The "SHA-1" digest is mixed back into the input_pool using the
|
||||
LFSR-based state transition function to provide backtracking
|
||||
resistance.[1; p.18]
|
||||
If more than 80-bits of entropy is requested, the
|
||||
If more than 80-bits of entropy is requested, the
|
||||
hash-fold-yield-mix-back operation is repeated until the requested
|
||||
number of bytes are generated. (Reseeding the ChaCha20 DRNG requires
|
||||
four consecutive requests.)[1; p.18]
|
||||
|
@ -839,7 +863,7 @@ def csprng(key_length: int = SYMMETRIC_KEY_LENGTH # Length of the key
|
|||
- 4-byte counter (the counter is actually 64-bits[2])
|
||||
- 12-byte nonce
|
||||
|
||||
In addition, the DRNG state contains a 4-byte timestamp called
|
||||
In addition, the DRNG state contains a 4-byte timestamp called
|
||||
init_time, that keeps track of when the DRNG was last seeded.
|
||||
[1; pp.32-33]
|
||||
|
||||
|
@ -866,9 +890,9 @@ def csprng(key_length: int = SYMMETRIC_KEY_LENGTH # Length of the key
|
|||
input_pool the next time it is called.[1; p.35]
|
||||
|
||||
**Initially seeded state**
|
||||
During initialization time of the kernel, the kernel injects four
|
||||
sets of data from fast_pool into the DRNG (instead of the
|
||||
input_pool). Each set contains event data and timestamps of 64
|
||||
During initialization time of the kernel, the kernel injects four
|
||||
sets of data from fast_pool into the DRNG (instead of the
|
||||
input_pool). Each set contains event data and timestamps of 64
|
||||
interrupt events from add_interrupt_randomness.[1; p.35]
|
||||
In addition, all content from the add_device_randomness source
|
||||
is mixed into the DRNG key state using an LFSR with a period of 255.
|
||||
|
@ -920,7 +944,7 @@ def csprng(key_length: int = SYMMETRIC_KEY_LENGTH # Length of the key
|
|||
The ChaCha20 DRNG is reseeded automatically every 300 seconds
|
||||
irrespective of the amount of data produced by the DRNG[1; p.32].
|
||||
The DRNG is reseeded by obtaining 128..256 bits of entropy
|
||||
from the input_pool. In the order of preference, the entropy from
|
||||
from the input_pool. In the order of preference, the entropy from
|
||||
the input_pool is XORed with the output of
|
||||
1. 32-byte value obtained via RDSEED CPU instruction, or
|
||||
2. 32-byte value obtained via RDRAND CPU instruction, or
|
||||
|
@ -939,7 +963,7 @@ def csprng(key_length: int = SYMMETRIC_KEY_LENGTH # Length of the key
|
|||
GETRANDOM and Python
|
||||
====================
|
||||
|
||||
Since Python 3.6.0, `os.urandom` has been a wrapper for the best
|
||||
Since Python 3.6.0, `os.urandom` has been a wrapper for the best
|
||||
available CSPRNG. The 3.17 and earlier versions of the Linux kernel
|
||||
do not support the GETRANDOM call, and Python 3.7's `os.urandom`
|
||||
will in those cases fall back to non-blocking `/dev/urandom` that is
|
||||
|
@ -952,7 +976,7 @@ def csprng(key_length: int = SYMMETRIC_KEY_LENGTH # Length of the key
|
|||
the kernel version required by TFC is bumped to 4.8, and to make
|
||||
sure the ChaCha20 DRNG is always seeded from input_pool before its
|
||||
considered fully seeded, the final minimum requirement is 4.17).
|
||||
The flag 0 means GETRANDOM will block if the DRNG is not fully
|
||||
The flag 0 means GETRANDOM will block if the DRNG is not fully
|
||||
seeded.[1]
|
||||
|
||||
Quoting PEP 524 [2]:
|
||||
|
@ -1000,6 +1024,9 @@ def csprng(key_length: int = SYMMETRIC_KEY_LENGTH # Length of the key
|
|||
|
||||
entropy = os.getrandom(key_length, flags=0) # type: bytes
|
||||
|
||||
if not isinstance(entropy, bytes):
|
||||
raise CriticalError(f"GETRANDOM returned invalid type data ({type(entropy)}).")
|
||||
|
||||
if len(entropy) != key_length:
|
||||
raise CriticalError(f"GETRANDOM returned invalid amount of entropy ({len(entropy)} bytes).")
|
||||
|
||||
|
|
|
@ -30,7 +30,10 @@ from src.common.encoding import bytes_to_bool, onion_address_to_pub_key, bytes
|
|||
from src.common.exceptions import CriticalError
|
||||
from src.common.misc import ensure_dir, get_terminal_width, separate_headers, split_byte_string
|
||||
from src.common.output import clear_screen
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (CONTACT_LENGTH, CONTACT_LIST_INDENT, DIR_USER_DATA, DUMMY_CONTACT, DUMMY_NICK, ECDHE,
|
||||
ENCODED_BOOLEAN_LENGTH, FINGERPRINT_LENGTH, KEX_STATUS_HAS_RX_PSK, KEX_STATUS_LENGTH,
|
||||
KEX_STATUS_NONE, KEX_STATUS_NO_RX_PSK, KEX_STATUS_PENDING, KEX_STATUS_UNVERIFIED,
|
||||
KEX_STATUS_VERIFIED, LOCAL_ID, ONION_SERVICE_PUBLIC_KEY_LENGTH, PSK)
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from src.common.db_masterkey import MasterKey
|
||||
|
|
|
@ -32,7 +32,10 @@ from src.common.encoding import bytes_to_bool, bytes_to_int, bytes_to_str
|
|||
from src.common.exceptions import CriticalError
|
||||
from src.common.misc import ensure_dir, get_terminal_width, round_up, separate_header, separate_headers
|
||||
from src.common.misc import split_byte_string
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (CONTACT_LIST_INDENT, DIR_USER_DATA, DUMMY_GROUP, DUMMY_MEMBER,
|
||||
ENCODED_BOOLEAN_LENGTH, ENCODED_INTEGER_LENGTH, GROUP_DB_HEADER_LENGTH,
|
||||
GROUP_ID_LENGTH, GROUP_STATIC_LENGTH, ONION_SERVICE_PUBLIC_KEY_LENGTH,
|
||||
PADDED_UTF32_STR_LENGTH)
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from src.common.db_contacts import ContactList
|
||||
|
|
|
@ -29,7 +29,10 @@ from src.common.encoding import int_to_bytes, onion_address_to_pub_key
|
|||
from src.common.encoding import bytes_to_int
|
||||
from src.common.exceptions import CriticalError
|
||||
from src.common.misc import ensure_dir, separate_headers, split_byte_string
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (DIR_USER_DATA, DUMMY_CONTACT, HARAC_LENGTH, INITIAL_HARAC, KDB_ADD_ENTRY_HEADER,
|
||||
KDB_CHANGE_MASTER_KEY_HEADER, KDB_REMOVE_ENTRY_HEADER, KDB_UPDATE_SIZE_HEADER,
|
||||
KEYSET_LENGTH, LOCAL_PUBKEY, ONION_SERVICE_PUBLIC_KEY_LENGTH, RX,
|
||||
SYMMETRIC_KEY_LENGTH, TX)
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from src.common.db_masterkey import MasterKey
|
||||
|
|
|
@ -34,7 +34,12 @@ from src.common.encoding import b58encode, bytes_to_bool, bytes_to_timestamp,
|
|||
from src.common.exceptions import CriticalError, FunctionReturn
|
||||
from src.common.misc import ensure_dir, get_terminal_width, ignored, separate_header, separate_headers
|
||||
from src.common.output import clear_screen
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (ASSEMBLY_PACKET_HEADER_LENGTH, DIR_USER_DATA, GROUP_ID_LENGTH, GROUP_MESSAGE_HEADER,
|
||||
GROUP_MSG_ID_LENGTH, LOGFILE_MASKING_QUEUE, LOG_ENTRY_LENGTH, LOG_PACKET_QUEUE,
|
||||
LOG_SETTING_QUEUE, MESSAGE, MESSAGE_HEADER_LENGTH, ONION_SERVICE_PUBLIC_KEY_LENGTH,
|
||||
ORIGIN_HEADER_LENGTH, ORIGIN_USER_HEADER, PLACEHOLDER_DATA, PRIVATE_MESSAGE_HEADER,
|
||||
P_N_HEADER, RX, TIMESTAMP_LENGTH, TRAFFIC_MASKING_QUEUE, TX, UNIT_TEST_QUEUE,
|
||||
WHISPER_FIELD_LENGTH, WIN_TYPE_CONTACT, WIN_TYPE_GROUP)
|
||||
|
||||
from src.receiver.packet import PacketList
|
||||
from src.receiver.windows import RxWindow
|
||||
|
@ -52,7 +57,7 @@ MsgTuple = Tuple[datetime, str, bytes, bytes, bool, bool]
|
|||
|
||||
def log_writer_loop(queues: Dict[bytes, 'Queue[Any]'], # Dictionary of queues
|
||||
settings: 'Settings', # Settings object
|
||||
unit_test: bool = False # When True, exits the loop when UNIT_TEST_QUEUE is no longer empty.
|
||||
unit_test: bool = False # True, exits loop when UNIT_TEST_QUEUE is no longer empty.
|
||||
) -> None:
|
||||
"""Write assembly packets to log database.
|
||||
|
||||
|
@ -299,7 +304,7 @@ def change_log_db_key(previous_key: bytes,
|
|||
temp_name = f'{file_name}_temp'
|
||||
|
||||
if not os.path.isfile(file_name):
|
||||
raise FunctionReturn("Error: Could not find log database.")
|
||||
raise FunctionReturn("No log database available.")
|
||||
|
||||
if os.path.isfile(temp_name):
|
||||
os.remove(temp_name)
|
||||
|
|
|
@ -19,11 +19,13 @@ You should have received a copy of the GNU General Public License
|
|||
along with TFC. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import math
|
||||
import multiprocessing
|
||||
import os.path
|
||||
import random
|
||||
import time
|
||||
|
||||
from typing import Tuple
|
||||
from typing import List, Tuple
|
||||
|
||||
from src.common.crypto import argon2_kdf, blake2b, csprng
|
||||
from src.common.encoding import bytes_to_int, int_to_bytes
|
||||
|
@ -31,7 +33,11 @@ from src.common.exceptions import CriticalError, graceful_exit
|
|||
from src.common.input import pwd_prompt
|
||||
from src.common.misc import ensure_dir, separate_headers
|
||||
from src.common.output import clear_screen, m_print, phase, print_on_previous_line
|
||||
from src.common.statics import *
|
||||
from src.common.word_list import eff_wordlist
|
||||
from src.common.statics import (ARGON2_MIN_MEMORY_COST, ARGON2_MIN_PARALLELISM, ARGON2_MIN_TIME_COST,
|
||||
ARGON2_SALT_LENGTH, BLAKE2_DIGEST_LENGTH, DIR_USER_DATA, DONE,
|
||||
ENCODED_INTEGER_LENGTH, GENERATE, MASTERKEY_DB_SIZE, MAX_KEY_DERIVATION_TIME,
|
||||
MIN_KEY_DERIVATION_TIME, PASSWORD_MIN_BIT_STRENGTH, RESET)
|
||||
|
||||
|
||||
class MasterKey(object):
|
||||
|
@ -69,13 +75,30 @@ class MasterKey(object):
|
|||
return master_key, kd_time
|
||||
|
||||
@staticmethod
|
||||
def get_free_memory() -> int:
|
||||
"""Return the amount of free memory in the system."""
|
||||
fields = os.popen("cat /proc/meminfo").read().splitlines()
|
||||
field = [f for f in fields if f.startswith('MemFree')][0]
|
||||
mem_free = int(field.split()[1])
|
||||
def get_available_memory() -> int:
|
||||
"""Return the amount of available memory in the system."""
|
||||
fields = os.popen("cat /proc/meminfo").read().splitlines()
|
||||
field = [f for f in fields if f.startswith('MemAvailable')][0]
|
||||
mem_avail = int(field.split()[1])
|
||||
|
||||
return mem_free
|
||||
return mem_avail
|
||||
|
||||
@staticmethod
|
||||
def generate_master_password() -> Tuple[int, str]:
|
||||
"""Generate a strong password using the EFF wordlist."""
|
||||
word_space = len(eff_wordlist)
|
||||
sys_rand = random.SystemRandom()
|
||||
|
||||
pwd_bit_strength = 0.0
|
||||
password_words = [] # type: List[str]
|
||||
|
||||
while pwd_bit_strength < PASSWORD_MIN_BIT_STRENGTH:
|
||||
password_words.append(sys_rand.choice(eff_wordlist))
|
||||
pwd_bit_strength = math.log2(word_space ** len(password_words))
|
||||
|
||||
password = ' '.join(password_words)
|
||||
|
||||
return int(pwd_bit_strength), password
|
||||
|
||||
def new_master_key(self) -> bytes:
|
||||
"""Create a new master key from password and salt.
|
||||
|
@ -143,7 +166,7 @@ class MasterKey(object):
|
|||
time_cost = ARGON2_MIN_TIME_COST
|
||||
|
||||
# Determine the amount of memory used from the amount of free RAM in the system.
|
||||
memory_cost = self.get_free_memory()
|
||||
memory_cost = self.get_available_memory()
|
||||
if self.local_test:
|
||||
memory_cost //= 2
|
||||
|
||||
|
@ -180,8 +203,8 @@ class MasterKey(object):
|
|||
middle = (lower_bound + upper_bound) // 2
|
||||
master_key, kd_time = self.timed_key_derivation(password, salt, time_cost, middle, parallelism)
|
||||
|
||||
# End of search might happen e.g. if external CPU load causes delay in key derivation, which causes
|
||||
# the search to continue into wrong branch. In such situation the search is restarted. The binary search
|
||||
# The search might fail e.g. if external CPU load causes delay in key derivation, which causes the
|
||||
# search to continue into wrong branch. In such a situation the search is restarted. The binary search
|
||||
# is problematic with tight key derivation time target ranges, so if the search keeps restarting,
|
||||
# increasing MAX_KEY_DERIVATION_TIME (and thus expanding the range) will help finding suitable
|
||||
# memory_cost value faster. Increasing MAX_KEY_DERIVATION_TIME slightly affects security (positively)
|
||||
|
@ -251,7 +274,19 @@ class MasterKey(object):
|
|||
def new_password(cls, purpose: str = "master password") -> str:
|
||||
"""Prompt the user to enter and confirm a new password."""
|
||||
password_1 = pwd_prompt(f"Enter a new {purpose}: ")
|
||||
password_2 = pwd_prompt(f"Confirm the {purpose}: ", repeat=True)
|
||||
|
||||
if password_1 == GENERATE:
|
||||
pwd_bit_strength, password_1 = MasterKey.generate_master_password()
|
||||
|
||||
m_print([f"Generated a {pwd_bit_strength}-bit password:",
|
||||
'', password_1, '',
|
||||
"Write down this password and dispose of the copy once you remember it.",
|
||||
"Press <Enter> to continue."], manual_proceed=True, box=True, head=1, tail=1)
|
||||
os.system(RESET)
|
||||
|
||||
password_2 = password_1
|
||||
else:
|
||||
password_2 = pwd_prompt(f"Confirm the {purpose}: ", repeat=True)
|
||||
|
||||
if password_1 == password_2:
|
||||
return password_1
|
||||
|
|
|
@ -29,7 +29,7 @@ from src.common.encoding import pub_key_to_onion_address, pub_key_to_short_add
|
|||
from src.common.exceptions import CriticalError
|
||||
from src.common.misc import ensure_dir
|
||||
from src.common.output import phase
|
||||
from src.common.statics import *
|
||||
from src.common.statics import CONFIRM_CODE_LENGTH, DIR_USER_DATA, DONE, ONION_SERVICE_PRIVATE_KEY_LENGTH, TX
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from src.common.db_masterkey import MasterKey
|
||||
|
|
|
@ -32,7 +32,9 @@ from src.common.exceptions import CriticalError, FunctionReturn
|
|||
from src.common.input import yes
|
||||
from src.common.misc import ensure_dir, get_terminal_width, round_up
|
||||
from src.common.output import clear_screen, m_print
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (DIR_USER_DATA, ENCODED_BOOLEAN_LENGTH, ENCODED_FLOAT_LENGTH, ENCODED_INTEGER_LENGTH,
|
||||
MAX_INT, SETTINGS_INDENT, TRAFFIC_MASKING_MIN_RANDOM_DELAY,
|
||||
TRAFFIC_MASKING_MIN_STATIC_DELAY, TX)
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from src.common.db_contacts import ContactList
|
||||
|
@ -183,7 +185,7 @@ class Settings(object):
|
|||
raise CriticalError("Invalid attribute type in settings.")
|
||||
|
||||
except (KeyError, ValueError):
|
||||
raise FunctionReturn(f"Error: Invalid value '{value_str}'.", head_clear=True)
|
||||
raise FunctionReturn(f"Error: Invalid setting value '{value_str}'.", head_clear=True)
|
||||
|
||||
self.validate_key_value_pair(key, value, contact_list, group_list)
|
||||
|
||||
|
|
|
@ -26,7 +26,9 @@ import struct
|
|||
from datetime import datetime
|
||||
from typing import List, Union
|
||||
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (B58_ALPHABET, B58_CHECKSUM_LENGTH, MAINNET_HEADER, ONION_ADDRESS_CHECKSUM_ID,
|
||||
ONION_ADDRESS_CHECKSUM_LENGTH, ONION_SERVICE_VERSION, ONION_SERVICE_VERSION_LENGTH,
|
||||
PADDING_LENGTH, TESTNET_HEADER, TRUNC_ADDRESS_LENGTH)
|
||||
|
||||
|
||||
def sha256d(message: bytes) -> bytes:
|
||||
|
@ -43,11 +45,7 @@ def b58encode(byte_string: bytes, public_key: bool = False) -> str:
|
|||
(WIF) for mainnet and testnet addresses.
|
||||
https://en.bitcoin.it/wiki/Wallet_import_format
|
||||
"""
|
||||
|
||||
mainnet_header = b'\x80'
|
||||
testnet_header = b'\xef'
|
||||
net_id = testnet_header if public_key else mainnet_header
|
||||
|
||||
net_id = TESTNET_HEADER if public_key else MAINNET_HEADER
|
||||
byte_string = net_id + byte_string
|
||||
byte_string += sha256d(byte_string)[:B58_CHECKSUM_LENGTH]
|
||||
|
||||
|
@ -70,11 +68,7 @@ def b58encode(byte_string: bytes, public_key: bool = False) -> str:
|
|||
|
||||
def b58decode(string: str, public_key: bool = False) -> bytes:
|
||||
"""Decode a Base58-encoded string and verify the checksum."""
|
||||
|
||||
mainnet_header = b'\x80'
|
||||
testnet_header = b'\xef'
|
||||
net_id = testnet_header if public_key else mainnet_header
|
||||
|
||||
net_id = TESTNET_HEADER if public_key else MAINNET_HEADER
|
||||
orig_len = len(string)
|
||||
string = string.lstrip(B58_ALPHABET[0])
|
||||
new_len = len(string)
|
||||
|
@ -129,10 +123,10 @@ def b10encode(fingerprint: bytes) -> str:
|
|||
(fingerprints are usually read aloud over off band call).
|
||||
|
||||
Base10 has 41% efficiency but natural languages have evolved in a
|
||||
way that makes a clear distinction between the way different numbers
|
||||
are pronounced: reading them is faster and less error-prone.
|
||||
Compliments to Signal/WA developers for discovering this.
|
||||
https://signal.org/blog/safety-number-updates/
|
||||
way that makes a clear distinction between the way different
|
||||
numbers are pronounced: reading them is faster and less
|
||||
error-prone. Compliments to Signal/WA developers for
|
||||
discovering this: https://signal.org/blog/safety-number-updates/
|
||||
"""
|
||||
return str(int(fingerprint.hex(), base=16))
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ from datetime import datetime
|
|||
from typing import Optional
|
||||
|
||||
from src.common.output import clear_screen, m_print
|
||||
from src.common.statics import *
|
||||
from src.common.statics import TFC
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from src.receiver.windows import RxWindow
|
||||
|
|
|
@ -31,7 +31,7 @@ import time
|
|||
import typing
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, Optional, Tuple, Union
|
||||
from typing import Dict, Optional, Tuple, Union
|
||||
|
||||
from serial.serialutil import SerialException
|
||||
|
||||
|
@ -41,13 +41,16 @@ from src.common.misc import calculate_race_condition_delay, ensure_dir,
|
|||
from src.common.misc import separate_trailer
|
||||
from src.common.output import m_print, phase, print_on_previous_line
|
||||
from src.common.reed_solomon import ReedSolomonError, RSCodec
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (BAUDS_PER_BYTE, DIR_USER_DATA, DONE, DST_DD_LISTEN_SOCKET, DST_LISTEN_SOCKET,
|
||||
GATEWAY_QUEUE, LOCALHOST, LOCAL_TESTING_PACKET_DELAY, MAX_INT, NC,
|
||||
PACKET_CHECKSUM_LENGTH, RECEIVER, RELAY, RP_LISTEN_SOCKET, RX,
|
||||
SERIAL_RX_MIN_TIMEOUT, SETTINGS_INDENT, SRC_DD_LISTEN_SOCKET, TRANSMITTER, TX)
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from multiprocessing import Queue
|
||||
|
||||
|
||||
def gateway_loop(queues: Dict[bytes, 'Queue[Any]'],
|
||||
def gateway_loop(queues: Dict[bytes, 'Queue[Tuple[datetime, bytes]]'],
|
||||
gateway: 'Gateway',
|
||||
unit_test: bool = False
|
||||
) -> None:
|
||||
|
@ -131,9 +134,9 @@ class Gateway(object):
|
|||
the time it takes to send one byte with given baud rate.
|
||||
"""
|
||||
try:
|
||||
serial_interface = self.search_serial_interface()
|
||||
baudrate = self.settings.session_serial_baudrate
|
||||
self.tx_serial = self.rx_serial = serial.Serial(serial_interface, baudrate, timeout=0)
|
||||
self.tx_serial = self.rx_serial = serial.Serial(self.search_serial_interface(),
|
||||
self.settings.session_serial_baudrate,
|
||||
timeout=0)
|
||||
except SerialException:
|
||||
raise CriticalError("SerialException. Ensure $USER is in the dialout group by restarting this computer.")
|
||||
|
||||
|
@ -509,7 +512,7 @@ class GatewaySettings(object):
|
|||
raise CriticalError("Invalid attribute type in settings.")
|
||||
|
||||
except (KeyError, ValueError):
|
||||
raise FunctionReturn(f"Error: Invalid value '{value_str}'.", delay=1, tail_clear=True)
|
||||
raise FunctionReturn(f"Error: Invalid setting value '{value_str}'.", delay=1, tail_clear=True)
|
||||
|
||||
self.validate_key_value_pair(key, value)
|
||||
|
||||
|
|
|
@ -28,7 +28,8 @@ from src.common.encoding import b58decode
|
|||
from src.common.exceptions import CriticalError
|
||||
from src.common.misc import get_terminal_width, terminal_width_check
|
||||
from src.common.output import clear_screen, m_print, print_on_previous_line, print_spacing
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (B58_LOCAL_KEY, B58_LOCAL_KEY_GUIDE, B58_PUBLIC_KEY, B58_PUBLIC_KEY_GUIDE,
|
||||
CURSOR_UP_ONE_LINE, ECDHE, NC_BYPASS_START, NC_BYPASS_STOP)
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from src.common.db_settings import Settings
|
||||
|
|
|
@ -34,12 +34,17 @@ import zlib
|
|||
|
||||
from contextlib import contextmanager
|
||||
from typing import Any, Callable, Dict, Iterator, List, Optional, Tuple, Type, Union
|
||||
from multiprocessing import Process, Queue
|
||||
from multiprocessing import Process
|
||||
|
||||
from src.common.reed_solomon import RSCodec
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (BAUDS_PER_BYTE, COMMAND_LENGTH, CURSOR_UP_ONE_LINE, DIR_RECV_FILES, DIR_USER_DATA,
|
||||
DUMMY_CONTACT, DUMMY_GROUP, DUMMY_MEMBER, ECDHE, EVENT, EXIT, EXIT_QUEUE, LOCAL_ID,
|
||||
LOCAL_PUBKEY, ME, ONION_ADDRESS_CHECKSUM_ID, ONION_ADDRESS_CHECKSUM_LENGTH,
|
||||
ONION_ADDRESS_LENGTH, ONION_SERVICE_PUBLIC_KEY_LENGTH, PACKET_LENGTH,
|
||||
PADDING_LENGTH, POWEROFF, PSK, RX, TAILS, TX, WIPE)
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from multiprocessing import Queue
|
||||
from src.common.db_contacts import ContactList
|
||||
from src.common.db_groups import GroupList
|
||||
from src.common.db_settings import Settings
|
||||
|
@ -164,7 +169,7 @@ def ignored(*exceptions: Type[BaseException]) -> Iterator[Any]:
|
|||
|
||||
def monitor_processes(process_list: List[Process],
|
||||
software_operation: str,
|
||||
queues: Dict[bytes, 'Queue[Any]'],
|
||||
queues: Dict[bytes, 'Queue[bytes]'],
|
||||
error_exit_code: int = 1
|
||||
) -> None:
|
||||
"""Monitor the status of `process_list` and EXIT_QUEUE.
|
||||
|
@ -195,7 +200,9 @@ def monitor_processes(process_list: List[Process],
|
|||
sys.exit(0)
|
||||
|
||||
if command == WIPE:
|
||||
if TAILS not in subprocess.check_output('lsb_release -a', shell=True):
|
||||
with open('/etc/os-release') as f:
|
||||
data = f.read()
|
||||
if TAILS not in data:
|
||||
if software_operation == RX:
|
||||
subprocess.Popen("find {} -type f -exec shred -n 3 -z -u {{}} \\;"
|
||||
.format(DIR_RECV_FILES), shell=True).wait()
|
||||
|
|
|
@ -29,7 +29,10 @@ from typing import List, Optional, Union
|
|||
|
||||
from src.common.encoding import b10encode, b58encode, pub_key_to_onion_address
|
||||
from src.common.misc import get_terminal_width, split_string
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (ADDED_MEMBERS, ALREADY_MEMBER, B58_LOCAL_KEY_GUIDE, B58_PUBLIC_KEY_GUIDE, BOLD_ON,
|
||||
CLEAR_ENTIRE_LINE, CLEAR_ENTIRE_SCREEN, CURSOR_LEFT_UP_CORNER, CURSOR_UP_ONE_LINE,
|
||||
DONE, NC, NEW_GROUP, NORMAL_TEXT, NOT_IN_GROUP, RECEIVER, RELAY, REMOVED_MEMBERS, RX,
|
||||
TFC, TRANSMITTER, TX, UNKNOWN_ACCOUNTS, VERSION)
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from src.common.db_contacts import ContactList
|
||||
|
@ -205,9 +208,9 @@ def print_key(message: str, # Instructive messag
|
|||
if settings.local_testing_mode:
|
||||
m_print([message, b58key], box=True)
|
||||
else:
|
||||
guide, chunk_len = (B58_PUBLIC_KEY_GUIDE, 7) if public_key else (B58_LOCAL_KEY_GUIDE, 3)
|
||||
guide, chunk_length = (B58_PUBLIC_KEY_GUIDE, 7) if public_key else (B58_LOCAL_KEY_GUIDE, 3)
|
||||
|
||||
key = ' '.join(split_string(b58key, item_len=chunk_len))
|
||||
key = ' '.join(split_string(b58key, item_len=chunk_length))
|
||||
m_print([message, guide, key], box=True)
|
||||
|
||||
|
||||
|
|
|
@ -197,11 +197,11 @@ class ReedSolomonError(Exception):
|
|||
|
||||
|
||||
"""
|
||||
For efficiency, gf_exp[] has size 2*GF_SIZE, so that a simple
|
||||
multiplication of two numbers can be resolved without calling % 255.
|
||||
For more info on how to generate this extended exponentiation table,
|
||||
see paper:
|
||||
"Fast software implementation of finite field operations",
|
||||
For efficiency, gf_exp[] has size 2*GF_SIZE, so that a simple
|
||||
multiplication of two numbers can be resolved without calling % 255.
|
||||
For more info on how to generate this extended exponentiation table,
|
||||
see paper:
|
||||
"Fast software implementation of finite field operations",
|
||||
Cheng Huang and Lihao Xu
|
||||
Washington University in St. Louis, Tech. Rep (2003).
|
||||
"""
|
||||
|
@ -1568,12 +1568,12 @@ class RSCodec(object):
|
|||
"""
|
||||
|
||||
def __init__(self,
|
||||
nsym: int = 10,
|
||||
nsize: int = 255,
|
||||
fcr: int = 0,
|
||||
prim: int = 0x11d,
|
||||
generator: int = 2,
|
||||
c_exp: int = 8,
|
||||
nsym: int = 10,
|
||||
nsize: int = 255,
|
||||
fcr: int = 0,
|
||||
prim: int = 0x11d,
|
||||
generator: int = 2,
|
||||
c_exp: int = 8,
|
||||
single_gen: bool = True
|
||||
) -> None:
|
||||
"""\
|
||||
|
|
|
@ -21,7 +21,7 @@ along with TFC. If not, see <https://www.gnu.org/licenses/>.
|
|||
|
||||
"""Program details"""
|
||||
TFC = 'TFC'
|
||||
VERSION = '1.19.08'
|
||||
VERSION = '1.19.10'
|
||||
TRANSMITTER = 'Transmitter'
|
||||
RECEIVER = 'Receiver'
|
||||
RELAY = 'Relay'
|
||||
|
@ -41,7 +41,7 @@ DUMMY_GROUP = 'dummy_group'
|
|||
TX = 'tx'
|
||||
RX = 'rx'
|
||||
NC = 'nc'
|
||||
TAILS = b'Tails'
|
||||
TAILS = 'TAILS_PRODUCT_NAME="Tails"'
|
||||
|
||||
|
||||
"""Window identifiers"""
|
||||
|
@ -71,8 +71,10 @@ NOT_IN_GROUP = 'not_in_group'
|
|||
UNKNOWN_ACCOUNTS = 'unknown_accounts'
|
||||
|
||||
|
||||
"""Base58 alphabet"""
|
||||
B58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
|
||||
"""Base58 encoding"""
|
||||
B58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
|
||||
MAINNET_HEADER = b'\x80'
|
||||
TESTNET_HEADER = b'\xef'
|
||||
|
||||
|
||||
"""Base58 key types"""
|
||||
|
@ -81,7 +83,7 @@ B58_LOCAL_KEY = 'b58_local_key'
|
|||
|
||||
|
||||
"""Base58 key input guides"""
|
||||
B58_PUBLIC_KEY_GUIDE = ' A B C D E F H H I J K L '
|
||||
B58_PUBLIC_KEY_GUIDE = ' A B C D E F G H I J K L '
|
||||
B58_LOCAL_KEY_GUIDE = ' A B C D E F G H I J K L M N O P Q '
|
||||
|
||||
|
||||
|
@ -99,7 +101,9 @@ NOTIFY = 'notify'
|
|||
"""Command identifiers"""
|
||||
CLEAR = 'clear'
|
||||
RESET = 'reset'
|
||||
POWEROFF = 'poweroff'
|
||||
POWEROFF = 'systemctl poweroff'
|
||||
GENERATE = 'generate'
|
||||
|
||||
|
||||
"""Contact setting management"""
|
||||
CONTACT_SETTING_HEADER_LENGTH = 2
|
||||
|
@ -130,7 +134,7 @@ NCDCRL = 'ncdcrl'
|
|||
|
||||
"""VT100 codes
|
||||
|
||||
VT100 codes are used to control printing to the terminal. These make
|
||||
VT100 codes are used to control printing to the terminal. These make
|
||||
building functions like textbox drawers possible.
|
||||
"""
|
||||
CURSOR_UP_ONE_LINE = '\x1b[1A'
|
||||
|
@ -144,7 +148,7 @@ NORMAL_TEXT = '\033[0m'
|
|||
|
||||
"""Separators
|
||||
|
||||
Separator byte is a non-printable byte used to separate fields in
|
||||
Separator byte is a non-printable byte used to separate fields in
|
||||
serialized data structures.
|
||||
"""
|
||||
US_BYTE = b'\x1f'
|
||||
|
@ -157,44 +161,44 @@ serial or over the network. They tell the receiving device what type of
|
|||
datagram is in question.
|
||||
|
||||
Datagrams with local key header contain the encrypted local key, used to
|
||||
encrypt commands and data transferred between local Source and
|
||||
Destination computers. Packets with the header are only accepted by the
|
||||
Relay Program when they originate from the user's Source Computer. Even
|
||||
if the Networked Computer is compromised and the local key datagram is
|
||||
injected to the Destination Computer, the injected key could not be
|
||||
encrypt commands and data transferred between local Source and
|
||||
Destination computers. Packets with the header are only accepted by the
|
||||
Relay Program when they originate from the user's Source Computer. Even
|
||||
if the Networked Computer is compromised and the local key datagram is
|
||||
injected to the Destination Computer, the injected key could not be
|
||||
accepted by the user as they don't know the decryption key for it. The
|
||||
worst case scenario is a DoS attack where the Receiver Program receives
|
||||
new local keys continuously. Such an attack would, however, reveal the
|
||||
user they are under a sophisticated attack, and that their Networked
|
||||
worst case scenario is a DoS attack where the Receiver Program receives
|
||||
new local keys continuously. Such an attack would, however, reveal the
|
||||
user they are under a sophisticated attack, and that their Networked
|
||||
Computer has been compromised.
|
||||
|
||||
Datagrams with Public key header contain TCB-level public keys that
|
||||
Datagrams with Public key header contain TCB-level public keys that
|
||||
originate from the sender's Source Computer, and are displayed by the
|
||||
recipient's Networked Computer, from where they are manually typed to
|
||||
recipient's Destination Computer.
|
||||
|
||||
Message and command type datagrams tell the Receiver Program whether to
|
||||
parse the trailing fields that determine which XChaCha20-Poly1305
|
||||
decryption keys it should load. Contacts can of course try to alter
|
||||
their datagrams to contain a COMMAND_DATAGRAM_HEADER header, but Relay
|
||||
Program will by design drop them. Even if a compromised Networked
|
||||
Computer injects such a datagram to Destination Computer, the Receiver
|
||||
Program will drop the datagram when the MAC verification of the
|
||||
Message and command type datagrams tell the Receiver Program whether to
|
||||
parse the trailing fields that determine which XChaCha20-Poly1305
|
||||
decryption keys it should load. Contacts can of course try to alter
|
||||
their datagrams to contain a COMMAND_DATAGRAM_HEADER header, but Relay
|
||||
Program will by design drop them. Even if a compromised Networked
|
||||
Computer injects such a datagram to Destination Computer, the Receiver
|
||||
Program will drop the datagram when the MAC verification of the
|
||||
encrypted hash ratchet counter value fails.
|
||||
|
||||
File type datagram contains an encrypted file that the Receiver Program
|
||||
caches until its decryption key arrives from the sender inside a
|
||||
File type datagram contains an encrypted file that the Receiver Program
|
||||
caches until its decryption key arrives from the sender inside a
|
||||
special, automated key delivery message.
|
||||
|
||||
Unencrypted type datagrams contain commands intended for the Relay
|
||||
Program. These commands are in some cases preceded by an encrypted
|
||||
version of the command, that the Relay Program forwards to Receiver
|
||||
Program on Destination Computer. The unencrypted Relay commands are
|
||||
disabled during traffic masking to hide the quantity and schedule of
|
||||
communication even from the Networked Computer (in case it's compromised
|
||||
and monitoring the user). The fact these commands are unencrypted, do
|
||||
not cause security issues because if an adversary can compromise the
|
||||
Networked Computer to the point it can issue commands to the Relay
|
||||
Unencrypted type datagrams contain commands intended for the Relay
|
||||
Program. These commands are in some cases preceded by an encrypted
|
||||
version of the command, that the Relay Program forwards to Receiver
|
||||
Program on Destination Computer. The unencrypted Relay commands are
|
||||
disabled during traffic masking to hide the quantity and schedule of
|
||||
communication even from the Networked Computer (in case it's compromised
|
||||
and monitoring the user). The fact these commands are unencrypted, do
|
||||
not cause security issues because if an adversary can compromise the
|
||||
Networked Computer to the point it can issue commands to the Relay
|
||||
Program, they could DoS the Relay Program, and thus TFC, anyway.
|
||||
"""
|
||||
DATAGRAM_TIMESTAMP_LENGTH = 8
|
||||
|
@ -209,9 +213,9 @@ UNENCRYPTED_DATAGRAM_HEADER = b'U'
|
|||
|
||||
"""Group management headers
|
||||
|
||||
Group management datagrams are are automatic messages that the
|
||||
Transmitter Program recommends the user to send when they make changes
|
||||
to the member list of a group, or when they add or remove groups. These
|
||||
Group management datagrams are are automatic messages that the
|
||||
Transmitter Program recommends the user to send when they make changes
|
||||
to the member list of a group, or when they add or remove groups. These
|
||||
messages are displayed by the Relay Program.
|
||||
"""
|
||||
GROUP_ID_LENGTH = 4
|
||||
|
@ -227,10 +231,10 @@ GROUP_MSG_EXIT_GROUP_HEADER = b'X'
|
|||
|
||||
"""Assembly packet headers
|
||||
|
||||
These one-byte assembly packet headers are not part of the padded
|
||||
These one-byte assembly packet headers are not part of the padded
|
||||
message parsed from assembly packets. They are however the very first
|
||||
plaintext byte, prepended to every padded assembly packet that is
|
||||
delivered to the recipient/local Destination Computer. The header
|
||||
plaintext byte, prepended to every padded assembly packet that is
|
||||
delivered to the recipient/local Destination Computer. The header
|
||||
delivers the information about if and when to assemble the packet,
|
||||
as well as when to drop any previously collected assembly packets.
|
||||
"""
|
||||
|
@ -260,12 +264,12 @@ C_N_HEADER = b'5' # Noise command packet
|
|||
|
||||
"""Unencrypted command headers
|
||||
|
||||
These two-byte headers are only used to control the Relay Program on
|
||||
Networked Computer. These commands will not be used during traffic
|
||||
These two-byte headers are only used to control the Relay Program on
|
||||
Networked Computer. These commands will not be used during traffic
|
||||
masking, as they would reveal when TFC is being used. These commands do
|
||||
not require encryption, because if an attacker can compromise the
|
||||
Networked Computer to the point it could inject commands to Relay
|
||||
Program, it could most likely also access any decryption keys used by
|
||||
not require encryption, because if an attacker can compromise the
|
||||
Networked Computer to the point it could inject commands to Relay
|
||||
Program, it could most likely also access any decryption keys used by
|
||||
the Relay Program.
|
||||
"""
|
||||
UNENCRYPTED_COMMAND_HEADER_LENGTH = 2
|
||||
|
@ -285,10 +289,10 @@ UNENCRYPTED_MANAGE_CONTACT_REQ = b'UM'
|
|||
|
||||
"""Encrypted command headers
|
||||
|
||||
These two-byte headers determine the type of command for Receiver
|
||||
Program on local Destination Computer. The header is evaluated after the
|
||||
Receiver Program has received all assembly packets and assembled the
|
||||
command. These headers tell the Receiver Program to which function the
|
||||
These two-byte headers determine the type of command for Receiver
|
||||
Program on local Destination Computer. The header is evaluated after the
|
||||
Receiver Program has received all assembly packets and assembled the
|
||||
command. These headers tell the Receiver Program to which function the
|
||||
provided parameters (if any) must be redirected.
|
||||
"""
|
||||
ENCRYPTED_COMMAND_HEADER_LENGTH = 2
|
||||
|
@ -322,17 +326,17 @@ WIPE_USR_DATA = b'WD'
|
|||
|
||||
"""Origin headers
|
||||
|
||||
This one-byte header tells the Relay and Receiver Programs whether the
|
||||
This one-byte header tells the Relay and Receiver Programs whether the
|
||||
account included in the packet is the source or the destination of the
|
||||
transmission. The user origin header is used when the Relay Program
|
||||
forwards the message packets from user's Source Computer to user's
|
||||
Destination Computer. The contact origin header is used when the program
|
||||
forwards packets that are loaded from servers of contacts to the user's
|
||||
Destination Computer.
|
||||
transmission. The user origin header is used when the Relay Program
|
||||
forwards the message packets from user's Source Computer to user's
|
||||
Destination Computer. The contact origin header is used when the program
|
||||
forwards packets that are loaded from servers of contacts to the user's
|
||||
Destination Computer.
|
||||
|
||||
On Destination Computer, the Receiver Program uses the origin header to
|
||||
determine which unidirectional keys it should load to decrypt the
|
||||
datagram payload.
|
||||
determine which unidirectional keys it should load to decrypt the
|
||||
datagram payload.
|
||||
"""
|
||||
ORIGIN_HEADER_LENGTH = 1
|
||||
ORIGIN_USER_HEADER = b'o'
|
||||
|
@ -341,25 +345,25 @@ ORIGIN_CONTACT_HEADER = b'i'
|
|||
|
||||
"""Message headers
|
||||
|
||||
This one-byte header will be prepended to each plaintext message before
|
||||
padding and splitting the message. It will be evaluated once the Relay
|
||||
This one-byte header will be prepended to each plaintext message before
|
||||
padding and splitting the message. It will be evaluated once the Relay
|
||||
Program has received all assembly packets and assembled the message.
|
||||
|
||||
The private and group message headers allow the Receiver Program to
|
||||
determine whether the message should be displayed in a private or in a
|
||||
group window. This does not allow re-direction of messages to
|
||||
unauthorized group windows, because TFC's manually managed group
|
||||
configuration is also a whitelist for accounts that are authorized to
|
||||
The private and group message headers allow the Receiver Program to
|
||||
determine whether the message should be displayed in a private or in a
|
||||
group window. This does not allow re-direction of messages to
|
||||
unauthorized group windows, because TFC's manually managed group
|
||||
configuration is also a whitelist for accounts that are authorized to
|
||||
display messages under the group's window.
|
||||
|
||||
Messages with the whisper message header have "sender-based control".
|
||||
Unless the contact maliciously alters their Receiver Program's behavior,
|
||||
Messages with the whisper message header have "sender-based control".
|
||||
Unless the contact maliciously alters their Receiver Program's behavior,
|
||||
whispered messages are not logged regardless of in-program controlled
|
||||
settings.
|
||||
|
||||
Messages with file key header contain the hash of the file ciphertext
|
||||
that was sent to the user earlier. It also contains the symmetric
|
||||
decryption key for that file.
|
||||
Messages with file key header contain the hash of the file ciphertext
|
||||
that was sent to the user earlier. It also contains the symmetric
|
||||
decryption key for that file.
|
||||
"""
|
||||
MESSAGE_HEADER_LENGTH = 1
|
||||
WHISPER_FIELD_LENGTH = 1
|
||||
|
@ -370,14 +374,14 @@ FILE_KEY_HEADER = b'k'
|
|||
|
||||
"""Delays
|
||||
|
||||
Traffic masking packet queue check delay ensures that the lookup time
|
||||
Traffic masking packet queue check delay ensures that the lookup time
|
||||
for the packet queue is obfuscated.
|
||||
|
||||
The local testing packet delay is an arbitrary delay that simulates the
|
||||
The local testing packet delay is an arbitrary delay that simulates the
|
||||
slight delay caused by data transmission over a serial interface.
|
||||
|
||||
The Relay client delays are values that determine the delays between
|
||||
checking the online status of the contact (and the state of their
|
||||
checking the online status of the contact (and the state of their
|
||||
ephemeral URL token public key).
|
||||
"""
|
||||
TRAFFIC_MASKING_QUEUE_CHECK_DELAY = 0.1
|
||||
|
@ -514,21 +518,25 @@ BITS_PER_BYTE = 8
|
|||
MAX_INT = 2 ** 64 - 1
|
||||
B58_CHECKSUM_LENGTH = 4
|
||||
TRUNC_ADDRESS_LENGTH = 5
|
||||
TOR_CONTROL_PORT = 9051
|
||||
TOR_SOCKS_PORT = 9050
|
||||
|
||||
# Key derivation
|
||||
ARGON2_MIN_TIME_COST = 1
|
||||
ARGON2_MIN_MEMORY_COST = 8
|
||||
ARGON2_MIN_PARALLELISM = 1
|
||||
ARGON2_SALT_LENGTH = 32
|
||||
ARGON2_PSK_TIME_COST = 25
|
||||
ARGON2_PSK_MEMORY_COST = 512 * 1024 # kibibytes
|
||||
ARGON2_PSK_PARALLELISM = 2
|
||||
MIN_KEY_DERIVATION_TIME = 3.0 # seconds
|
||||
MAX_KEY_DERIVATION_TIME = 4.0 # seconds
|
||||
ARGON2_MIN_TIME_COST = 1
|
||||
ARGON2_MIN_MEMORY_COST = 8
|
||||
ARGON2_MIN_PARALLELISM = 1
|
||||
ARGON2_SALT_LENGTH = 32
|
||||
ARGON2_PSK_TIME_COST = 25
|
||||
ARGON2_PSK_MEMORY_COST = 512 * 1024 # kibibytes
|
||||
ARGON2_PSK_PARALLELISM = 2
|
||||
MIN_KEY_DERIVATION_TIME = 3.0 # seconds
|
||||
MAX_KEY_DERIVATION_TIME = 4.0 # seconds
|
||||
PASSWORD_MIN_BIT_STRENGTH = 128
|
||||
|
||||
# Cryptographic field sizes
|
||||
TFC_PRIVATE_KEY_LENGTH = 56
|
||||
TFC_PUBLIC_KEY_LENGTH = 56
|
||||
X448_SHARED_SECRET_LENGTH = 56
|
||||
FINGERPRINT_LENGTH = 32
|
||||
ONION_SERVICE_PRIVATE_KEY_LENGTH = 32
|
||||
ONION_SERVICE_PUBLIC_KEY_LENGTH = 32
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -29,7 +29,14 @@ from src.common.encoding import bytes_to_int, pub_key_to_short_address
|
|||
from src.common.exceptions import FunctionReturn
|
||||
from src.common.misc import ensure_dir, separate_header
|
||||
from src.common.output import clear_screen, m_print, phase, print_on_previous_line
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (CH_FILE_RECV, CH_LOGGING, CH_MASTER_KEY, CH_NICKNAME, CH_NOTIFY, CH_SETTING,
|
||||
CLEAR_SCREEN, COMMAND, CONTACT_REM, CONTACT_SETTING_HEADER_LENGTH, DIR_USER_DATA,
|
||||
DISABLE, DONE, ENABLE, ENCODED_INTEGER_LENGTH, ENCRYPTED_COMMAND_HEADER_LENGTH, EXIT,
|
||||
EXIT_PROGRAM, GROUP_ADD, GROUP_CREATE, GROUP_DELETE, GROUP_REMOVE, GROUP_RENAME,
|
||||
KEY_EX_ECDHE, KEY_EX_PSK_RX, KEY_EX_PSK_TX, LOCAL_KEY_RDY, LOCAL_PUBKEY, LOG_DISPLAY,
|
||||
LOG_EXPORT, LOG_REMOVE, ONION_SERVICE_PUBLIC_KEY_LENGTH, ORIGIN_USER_HEADER, RESET,
|
||||
RESET_SCREEN, US_BYTE, WIN_ACTIVITY, WIN_SELECT, WIN_TYPE_CONTACT, WIN_TYPE_GROUP,
|
||||
WIN_UID_FILE, WIN_UID_LOCAL, WIPE, WIPE_USR_DATA)
|
||||
|
||||
from src.receiver.commands_g import group_add, group_create, group_delete, group_remove, group_rename
|
||||
from src.receiver.key_exchanges import key_ex_ecdhe, key_ex_psk_rx, key_ex_psk_tx, local_key_rdy
|
||||
|
@ -58,7 +65,7 @@ def process_command(ts: 'datetime',
|
|||
settings: 'Settings',
|
||||
master_key: 'MasterKey',
|
||||
gateway: 'Gateway',
|
||||
exit_queue: 'Queue[Any]'
|
||||
exit_queue: 'Queue[bytes]'
|
||||
) -> None:
|
||||
"""Decrypt command assembly packet and process command."""
|
||||
assembly_packet = decrypt_assembly_packet(assembly_ct, LOCAL_PUBKEY, ORIGIN_USER_HEADER,
|
||||
|
@ -73,7 +80,7 @@ def process_command(ts: 'datetime',
|
|||
header, cmd = separate_header(cmd_packet.assemble_command_packet(), ENCRYPTED_COMMAND_HEADER_LENGTH)
|
||||
no = None
|
||||
|
||||
# Keyword Function to run ( Parameters )
|
||||
# Keyword Function to run ( Parameters )
|
||||
# --------------------------------------------------------------------------------------------------------------
|
||||
d = {LOCAL_KEY_RDY: (local_key_rdy, ts, window_list, contact_list ),
|
||||
WIN_ACTIVITY: (win_activity, window_list ),
|
||||
|
@ -136,7 +143,7 @@ def reset_screen(win_uid: bytes, window_list: 'WindowList') -> None:
|
|||
os.system(RESET)
|
||||
|
||||
|
||||
def exit_tfc(exit_queue: 'Queue[Any]') -> None:
|
||||
def exit_tfc(exit_queue: 'Queue[str]') -> None:
|
||||
"""Exit TFC."""
|
||||
exit_queue.put(EXIT)
|
||||
|
||||
|
|
|
@ -25,7 +25,9 @@ from src.common.encoding import b58encode
|
|||
from src.common.exceptions import FunctionReturn
|
||||
from src.common.misc import separate_header, split_byte_string, validate_group_name
|
||||
from src.common.output import group_management_print, m_print
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (ADDED_MEMBERS, ALREADY_MEMBER, GROUP_ID_LENGTH, NEW_GROUP, NOT_IN_GROUP,
|
||||
ONION_SERVICE_PUBLIC_KEY_LENGTH, REMOVED_MEMBERS, UNKNOWN_ACCOUNTS, US_BYTE,
|
||||
WIN_UID_LOCAL)
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from datetime import datetime
|
||||
|
|
|
@ -32,7 +32,8 @@ from src.common.encoding import bytes_to_str
|
|||
from src.common.exceptions import FunctionReturn
|
||||
from src.common.misc import decompress, ensure_dir, separate_headers, separate_trailer
|
||||
from src.common.output import phase, print_on_previous_line
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (DIR_RECV_FILES, DONE, ONION_SERVICE_PUBLIC_KEY_LENGTH, ORIGIN_HEADER_LENGTH,
|
||||
PADDED_UTF32_STR_LENGTH, SYMMETRIC_KEY_LENGTH, US_BYTE)
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from datetime import datetime
|
||||
|
@ -171,10 +172,9 @@ def process_file(ts: 'datetime', # Timestamp of received_packet
|
|||
if not file_name.isprintable() or not file_name or '/' in file_name:
|
||||
raise FunctionReturn(f"Error: Name of file from {nick} was invalid.")
|
||||
|
||||
f_data = file_dc[PADDED_UTF32_STR_LENGTH:]
|
||||
|
||||
file_data = file_dc[PADDED_UTF32_STR_LENGTH:]
|
||||
file_dir = f'{DIR_RECV_FILES}{nick}/'
|
||||
final_name = store_unique(f_data, file_dir, file_name)
|
||||
final_name = store_unique(file_data, file_dir, file_name)
|
||||
message = f"Stored file from {nick} as '{final_name}'."
|
||||
|
||||
if settings.traffic_masking and window_list.active_win is not None:
|
||||
|
|
|
@ -27,7 +27,7 @@ import subprocess
|
|||
import tkinter
|
||||
import typing
|
||||
|
||||
from typing import Any, List, Tuple
|
||||
from typing import List, Tuple
|
||||
|
||||
import nacl.exceptions
|
||||
|
||||
|
@ -39,7 +39,11 @@ from src.common.input import get_b58_key
|
|||
from src.common.misc import separate_header, separate_headers
|
||||
from src.common.output import m_print, phase, print_on_previous_line
|
||||
from src.common.path import ask_path_gui
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (ARGON2_PSK_MEMORY_COST, ARGON2_PSK_PARALLELISM, ARGON2_PSK_TIME_COST,
|
||||
ARGON2_SALT_LENGTH, B58_LOCAL_KEY, CONFIRM_CODE_LENGTH, DONE, FINGERPRINT_LENGTH,
|
||||
KEX_STATUS_HAS_RX_PSK, KEX_STATUS_LOCAL_KEY, KEX_STATUS_NONE, KEX_STATUS_NO_RX_PSK,
|
||||
LOCAL_NICK, LOCAL_PUBKEY, ONION_SERVICE_PUBLIC_KEY_LENGTH, PSK_FILE_SIZE, RESET,
|
||||
SYMMETRIC_KEY_LENGTH, WIN_TYPE_CONTACT, WIN_TYPE_GROUP)
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from datetime import datetime
|
||||
|
@ -60,7 +64,7 @@ def process_local_key(ts: 'datetime',
|
|||
settings: 'Settings',
|
||||
kdk_hashes: List[bytes],
|
||||
packet_hashes: List[bytes],
|
||||
l_queue: 'Queue[Any]'
|
||||
l_queue: 'Queue[Tuple[datetime, bytes]]'
|
||||
) -> None:
|
||||
"""Decrypt local key packet and add local contact/keyset."""
|
||||
bootstrap = not key_list.has_local_keyset()
|
||||
|
@ -259,11 +263,12 @@ def key_ex_psk_tx(packet: bytes,
|
|||
tx_hk=tx_hk,
|
||||
rx_hk=bytes(SYMMETRIC_KEY_LENGTH))
|
||||
|
||||
c_code = blake2b(onion_pub_key, digest_size=CONFIRM_CODE_LENGTH)
|
||||
message = f"Added Tx-side PSK for {nick} ({pub_key_to_short_address(onion_pub_key)})."
|
||||
local_win = window_list.get_local_window()
|
||||
local_win.add_new(ts, message)
|
||||
|
||||
m_print(message, bold=True, tail_clear=True, delay=1)
|
||||
m_print([message, f"Confirmation code (to Transmitter): {c_code.hex()}"], box=True)
|
||||
|
||||
|
||||
def key_ex_psk_rx(packet: bytes,
|
||||
|
|
|
@ -28,7 +28,11 @@ from src.common.db_logs import write_log_entry
|
|||
from src.common.encoding import bytes_to_bool
|
||||
from src.common.exceptions import FunctionReturn
|
||||
from src.common.misc import separate_header, separate_headers
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (ASSEMBLY_PACKET_HEADER_LENGTH, BLAKE2_DIGEST_LENGTH, FILE, FILE_KEY_HEADER,
|
||||
GROUP_ID_LENGTH, GROUP_MESSAGE_HEADER, GROUP_MSG_ID_LENGTH, LOCAL_PUBKEY, MESSAGE,
|
||||
MESSAGE_HEADER_LENGTH, ONION_SERVICE_PUBLIC_KEY_LENGTH, ORIGIN_CONTACT_HEADER,
|
||||
ORIGIN_HEADER_LENGTH, ORIGIN_USER_HEADER, PLACEHOLDER_DATA, PRIVATE_MESSAGE_HEADER,
|
||||
SYMMETRIC_KEY_LENGTH, WHISPER_FIELD_LENGTH)
|
||||
|
||||
from src.receiver.packet import decrypt_assembly_packet
|
||||
|
||||
|
@ -57,8 +61,8 @@ def process_message(ts: 'datetime',
|
|||
"""Process received private / group message."""
|
||||
local_window = window_list.get_local_window()
|
||||
|
||||
onion_pub_key, origin, assembly_packet_ct \
|
||||
= separate_headers(assembly_packet_ct, [ONION_SERVICE_PUBLIC_KEY_LENGTH, ORIGIN_HEADER_LENGTH])
|
||||
onion_pub_key, origin, assembly_packet_ct = separate_headers(assembly_packet_ct, [ONION_SERVICE_PUBLIC_KEY_LENGTH,
|
||||
ORIGIN_HEADER_LENGTH])
|
||||
|
||||
if onion_pub_key == LOCAL_PUBKEY:
|
||||
raise FunctionReturn("Warning! Received packet masqueraded as a command.", window=local_window)
|
||||
|
@ -123,6 +127,7 @@ def process_message(ts: 'datetime',
|
|||
else:
|
||||
raise FunctionReturn("Error: Message from contact had an invalid header.")
|
||||
|
||||
# Logging
|
||||
if whisper:
|
||||
raise FunctionReturn("Whisper message complete.", output=False)
|
||||
|
||||
|
|
|
@ -28,7 +28,9 @@ from typing import Any, Dict, List, Tuple
|
|||
|
||||
from src.common.exceptions import FunctionReturn
|
||||
from src.common.output import clear_screen
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (COMMAND_DATAGRAM_HEADER, EXIT_QUEUE, FILE_DATAGRAM_HEADER, LOCAL_KEY_DATAGRAM_HEADER,
|
||||
MESSAGE_DATAGRAM_HEADER, ONION_SERVICE_PUBLIC_KEY_LENGTH, UNIT_TEST_QUEUE,
|
||||
WIN_UID_FILE)
|
||||
|
||||
from src.receiver.commands import process_command
|
||||
from src.receiver.files import new_file, process_file
|
||||
|
@ -59,16 +61,16 @@ def output_loop(queues: Dict[bytes, 'Queue[Any]'],
|
|||
unit_test: bool = False
|
||||
) -> None:
|
||||
"""Process packets in message queues according to their priority."""
|
||||
l_queue = queues[LOCAL_KEY_DATAGRAM_HEADER]
|
||||
m_queue = queues[MESSAGE_DATAGRAM_HEADER]
|
||||
f_queue = queues[FILE_DATAGRAM_HEADER]
|
||||
c_queue = queues[COMMAND_DATAGRAM_HEADER]
|
||||
e_queue = queues[EXIT_QUEUE]
|
||||
local_key_queue = queues[LOCAL_KEY_DATAGRAM_HEADER]
|
||||
message_queue = queues[MESSAGE_DATAGRAM_HEADER]
|
||||
file_queue = queues[FILE_DATAGRAM_HEADER]
|
||||
command_queue = queues[COMMAND_DATAGRAM_HEADER]
|
||||
exit_queue = queues[EXIT_QUEUE]
|
||||
|
||||
sys.stdin = os.fdopen(stdin_fd)
|
||||
packet_buf = dict() # type: Dict[bytes, List[Tuple[datetime, bytes]]]
|
||||
file_buf = dict() # type: Dict[bytes, Tuple[datetime, bytes]]
|
||||
file_keys = dict() # type: Dict[bytes, bytes]
|
||||
sys.stdin = os.fdopen(stdin_fd)
|
||||
packet_buffer = dict() # type: Dict[bytes, List[Tuple[datetime, bytes]]]
|
||||
file_buffer = dict() # type: Dict[bytes, Tuple[datetime, bytes]]
|
||||
file_keys = dict() # type: Dict[bytes, bytes]
|
||||
|
||||
kdk_hashes = [] # type: List[bytes]
|
||||
packet_hashes = [] # type: List[bytes]
|
||||
|
@ -79,10 +81,10 @@ def output_loop(queues: Dict[bytes, 'Queue[Any]'],
|
|||
clear_screen()
|
||||
while True:
|
||||
try:
|
||||
if l_queue.qsize() != 0:
|
||||
ts, packet = l_queue.get()
|
||||
if local_key_queue.qsize() != 0:
|
||||
ts, packet = local_key_queue.get()
|
||||
process_local_key(ts, packet, window_list, contact_list, key_list,
|
||||
settings, kdk_hashes, packet_hashes, l_queue)
|
||||
settings, kdk_hashes, packet_hashes, local_key_queue)
|
||||
continue
|
||||
|
||||
if not contact_list.has_local_contact():
|
||||
|
@ -90,10 +92,10 @@ def output_loop(queues: Dict[bytes, 'Queue[Any]'],
|
|||
continue
|
||||
|
||||
# Commands
|
||||
if c_queue.qsize() != 0:
|
||||
ts, packet = c_queue.get()
|
||||
if command_queue.qsize() != 0:
|
||||
ts, packet = command_queue.get()
|
||||
process_command(ts, packet, window_list, packet_list, contact_list, key_list,
|
||||
group_list, settings, master_key, gateway, e_queue)
|
||||
group_list, settings, master_key, gateway, exit_queue)
|
||||
continue
|
||||
|
||||
# File window refresh
|
||||
|
@ -101,48 +103,48 @@ def output_loop(queues: Dict[bytes, 'Queue[Any]'],
|
|||
window_list.active_win.redraw_file_win()
|
||||
|
||||
# Cached message packets
|
||||
for onion_pub_key in packet_buf:
|
||||
for onion_pub_key in packet_buffer:
|
||||
if (contact_list.has_pub_key(onion_pub_key)
|
||||
and key_list.has_rx_mk(onion_pub_key)
|
||||
and packet_buf[onion_pub_key]):
|
||||
ts, packet = packet_buf[onion_pub_key].pop(0)
|
||||
and packet_buffer[onion_pub_key]):
|
||||
ts, packet = packet_buffer[onion_pub_key].pop(0)
|
||||
process_message(ts, packet, window_list, packet_list, contact_list, key_list,
|
||||
group_list, settings, master_key, file_keys)
|
||||
continue
|
||||
|
||||
# New messages
|
||||
if m_queue.qsize() != 0:
|
||||
ts, packet = m_queue.get()
|
||||
if message_queue.qsize() != 0:
|
||||
ts, packet = message_queue.get()
|
||||
onion_pub_key = packet[:ONION_SERVICE_PUBLIC_KEY_LENGTH]
|
||||
|
||||
if contact_list.has_pub_key(onion_pub_key) and key_list.has_rx_mk(onion_pub_key):
|
||||
process_message(ts, packet, window_list, packet_list, contact_list, key_list,
|
||||
group_list, settings, master_key, file_keys)
|
||||
else:
|
||||
packet_buf.setdefault(onion_pub_key, []).append((ts, packet))
|
||||
packet_buffer.setdefault(onion_pub_key, []).append((ts, packet))
|
||||
continue
|
||||
|
||||
# Cached files
|
||||
if file_buf:
|
||||
for k in file_buf:
|
||||
if file_buffer:
|
||||
for k in file_buffer:
|
||||
key_to_remove = b''
|
||||
try:
|
||||
if k in file_keys:
|
||||
key_to_remove = k
|
||||
ts_, file_ct = file_buf[k]
|
||||
ts_, file_ct = file_buffer[k]
|
||||
dec_key = file_keys[k]
|
||||
onion_pub_key = k[:ONION_SERVICE_PUBLIC_KEY_LENGTH]
|
||||
process_file(ts_, onion_pub_key, file_ct, dec_key, contact_list, window_list, settings)
|
||||
finally:
|
||||
if key_to_remove:
|
||||
file_buf.pop(k)
|
||||
file_buffer.pop(k)
|
||||
file_keys.pop(k)
|
||||
break
|
||||
|
||||
# New files
|
||||
if f_queue.qsize() != 0:
|
||||
ts, packet = f_queue.get()
|
||||
new_file(ts, packet, file_keys, file_buf, contact_list, window_list, settings)
|
||||
if file_queue.qsize() != 0:
|
||||
ts, packet = file_queue.get()
|
||||
new_file(ts, packet, file_keys, file_buffer, contact_list, window_list, settings)
|
||||
|
||||
time.sleep(0.01)
|
||||
|
||||
|
|
|
@ -34,7 +34,12 @@ from src.common.exceptions import FunctionReturn
|
|||
from src.common.input import yes
|
||||
from src.common.misc import decompress, readable_size, separate_header, separate_headers, separate_trailer
|
||||
from src.common.output import m_print
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (ASSEMBLY_PACKET_HEADER_LENGTH, BLAKE2_DIGEST_LENGTH, COMMAND, C_A_HEADER, C_C_HEADER,
|
||||
C_E_HEADER, C_L_HEADER, C_N_HEADER, C_S_HEADER, ENCODED_INTEGER_LENGTH, FILE,
|
||||
F_A_HEADER, F_C_HEADER, F_E_HEADER, F_L_HEADER, F_S_HEADER, HARAC_CT_LENGTH,
|
||||
HARAC_WARN_THRESHOLD, LOCAL_PUBKEY, MAX_MESSAGE_SIZE, MESSAGE, M_A_HEADER,
|
||||
M_C_HEADER, M_E_HEADER, M_L_HEADER, M_S_HEADER, ORIGIN_CONTACT_HEADER,
|
||||
ORIGIN_USER_HEADER, P_N_HEADER, RX, SYMMETRIC_KEY_LENGTH, TX, US_BYTE)
|
||||
|
||||
from src.receiver.files import process_assembled_file
|
||||
|
||||
|
@ -100,16 +105,16 @@ def decrypt_assembly_packet(packet: bytes, # Assembly packet cip
|
|||
try:
|
||||
harac_bytes = auth_and_decrypt(ct_harac, header_key)
|
||||
except nacl.exceptions.CryptoError:
|
||||
raise FunctionReturn(
|
||||
f"Warning! Received {p_type} {direction} {nick} had an invalid hash ratchet MAC.", window=local_window)
|
||||
raise FunctionReturn(f"Warning! Received {p_type} {direction} {nick} had an invalid hash ratchet MAC.",
|
||||
window=local_window)
|
||||
|
||||
# Catch up with hash ratchet offset
|
||||
purp_harac = bytes_to_int(harac_bytes)
|
||||
stored_harac = getattr(keyset, f'{key_dir}_harac')
|
||||
offset = purp_harac - stored_harac
|
||||
if offset < 0:
|
||||
raise FunctionReturn(
|
||||
f"Warning! Received {p_type} {direction} {nick} had an expired hash ratchet counter.", window=local_window)
|
||||
raise FunctionReturn(f"Warning! Received {p_type} {direction} {nick} had an expired hash ratchet counter.",
|
||||
window=local_window)
|
||||
|
||||
process_offset(offset, origin, direction, nick, local_window)
|
||||
for harac in range(stored_harac, stored_harac + offset):
|
||||
|
@ -119,7 +124,8 @@ def decrypt_assembly_packet(packet: bytes, # Assembly packet cip
|
|||
try:
|
||||
assembly_packet = auth_and_decrypt(ct_assemby_packet, message_key)
|
||||
except nacl.exceptions.CryptoError:
|
||||
raise FunctionReturn(f"Warning! Received {p_type} {direction} {nick} had an invalid MAC.", window=local_window)
|
||||
raise FunctionReturn(f"Warning! Received {p_type} {direction} {nick} had an invalid MAC.",
|
||||
window=local_window)
|
||||
|
||||
# Update message key and harac
|
||||
new_key = blake2b(message_key + int_to_bytes(stored_harac + offset), digest_size=SYMMETRIC_KEY_LENGTH)
|
||||
|
|
|
@ -30,7 +30,9 @@ from src.common.encoding import bytes_to_int
|
|||
from src.common.exceptions import FunctionReturn
|
||||
from src.common.misc import ignored, separate_headers
|
||||
from src.common.output import m_print
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (COMMAND_DATAGRAM_HEADER, DATAGRAM_HEADER_LENGTH, DATAGRAM_TIMESTAMP_LENGTH,
|
||||
FILE_DATAGRAM_HEADER, GATEWAY_QUEUE, LOCAL_KEY_DATAGRAM_HEADER,
|
||||
MESSAGE_DATAGRAM_HEADER)
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from multiprocessing import Queue
|
||||
|
|
|
@ -27,11 +27,14 @@ import typing
|
|||
from datetime import datetime
|
||||
from typing import Any, Dict, Iterable, Iterator, List, Optional, Tuple
|
||||
|
||||
from src.common.encoding import pub_key_to_short_address
|
||||
from src.common.encoding import b58encode, pub_key_to_onion_address, pub_key_to_short_address
|
||||
from src.common.exceptions import FunctionReturn
|
||||
from src.common.misc import get_terminal_width
|
||||
from src.common.output import clear_screen, m_print, print_on_previous_line
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (BOLD_ON, EVENT, FILE, FILE_TRANSFER_INDENT, GROUP_ID_LENGTH, GROUP_MSG_ID_LENGTH, ME,
|
||||
NORMAL_TEXT, ONION_SERVICE_PUBLIC_KEY_LENGTH, ORIGIN_CONTACT_HEADER,
|
||||
ORIGIN_USER_HEADER, WIN_TYPE_COMMAND, WIN_TYPE_CONTACT, WIN_TYPE_FILE,
|
||||
WIN_TYPE_GROUP, WIN_UID_FILE, WIN_UID_LOCAL)
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from src.common.db_contacts import Contact, ContactList
|
||||
|
@ -96,7 +99,14 @@ class RxWindow(Iterable[MsgTuple]):
|
|||
self.window_contacts = self.group.members
|
||||
|
||||
else:
|
||||
raise FunctionReturn(f"Invalid window '{uid}'.")
|
||||
if len(uid) == ONION_SERVICE_PUBLIC_KEY_LENGTH:
|
||||
hr_uid = pub_key_to_onion_address(uid)
|
||||
elif len(uid) == GROUP_ID_LENGTH:
|
||||
hr_uid = b58encode(uid)
|
||||
else:
|
||||
hr_uid = "<unable to encode>"
|
||||
|
||||
raise FunctionReturn(f"Invalid window '{hr_uid}'.")
|
||||
|
||||
def __iter__(self) -> Iterator[MsgTuple]:
|
||||
"""Iterate over window's message log."""
|
||||
|
@ -238,7 +248,6 @@ class RxWindow(Iterable[MsgTuple]):
|
|||
event_msg: bool = False # When True, uses "-!-" as message handle
|
||||
) -> None:
|
||||
"""Add message tuple to message log and optionally print it."""
|
||||
|
||||
self.update_handle_dict(onion_pub_key)
|
||||
|
||||
msg_tuple = (timestamp, message, onion_pub_key, origin, whisper, event_msg)
|
||||
|
|
|
@ -36,7 +36,15 @@ from src.common.encoding import b58encode, int_to_bytes, onion_address_to_pub_ke
|
|||
from src.common.encoding import pub_key_to_short_address
|
||||
from src.common.misc import ignored, separate_header, split_byte_string, validate_onion_addr
|
||||
from src.common.output import m_print, print_key, rp_print
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (CLIENT_OFFLINE_THRESHOLD, CONTACT_MGMT_QUEUE, CONTACT_REQ_QUEUE, C_REQ_MGMT_QUEUE,
|
||||
C_REQ_STATE_QUEUE, DATAGRAM_HEADER_LENGTH, DST_MESSAGE_QUEUE, FILE_DATAGRAM_HEADER,
|
||||
GROUP_ID_LENGTH, GROUP_MGMT_QUEUE, GROUP_MSG_EXIT_GROUP_HEADER,
|
||||
GROUP_MSG_INVITE_HEADER, GROUP_MSG_JOIN_HEADER, GROUP_MSG_MEMBER_ADD_HEADER,
|
||||
GROUP_MSG_MEMBER_REM_HEADER, GROUP_MSG_QUEUE, MESSAGE_DATAGRAM_HEADER,
|
||||
ONION_SERVICE_PUBLIC_KEY_LENGTH, ORIGIN_CONTACT_HEADER, PUBLIC_KEY_DATAGRAM_HEADER,
|
||||
RELAY_CLIENT_MAX_DELAY, RELAY_CLIENT_MIN_DELAY, RP_ADD_CONTACT_HEADER,
|
||||
RP_REMOVE_CONTACT_HEADER, TFC_PUBLIC_KEY_LENGTH, TOR_DATA_QUEUE, UNIT_TEST_QUEUE,
|
||||
URL_TOKEN_LENGTH, URL_TOKEN_QUEUE)
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from src.common.gateway import Gateway
|
||||
|
@ -205,7 +213,7 @@ def get_data_loop(onion_addr: str,
|
|||
except requests.exceptions.RequestException:
|
||||
return None
|
||||
|
||||
for line in r.iter_lines(): # Iterates over newline-separated datagrams
|
||||
for line in r.iter_lines(): # Iterate over newline-separated datagrams
|
||||
|
||||
if not line:
|
||||
continue
|
||||
|
@ -219,14 +227,16 @@ def get_data_loop(onion_addr: str,
|
|||
ts = datetime.now()
|
||||
ts_bytes = int_to_bytes(int(ts.strftime('%Y%m%d%H%M%S%f')[:-4]))
|
||||
|
||||
# Packet type specific handling
|
||||
|
||||
if header == PUBLIC_KEY_DATAGRAM_HEADER:
|
||||
if len(payload_bytes) == TFC_PUBLIC_KEY_LENGTH:
|
||||
msg = f"Received public key from {short_addr} at {ts.strftime('%b %d - %H:%M:%S.%f')[:-4]}:"
|
||||
print_key(msg, payload_bytes, gateway.settings, public_key=True)
|
||||
|
||||
elif header == MESSAGE_DATAGRAM_HEADER:
|
||||
queues[DST_MESSAGE_QUEUE].put(header + ts_bytes + onion_pub_key
|
||||
+ ORIGIN_CONTACT_HEADER + payload_bytes)
|
||||
queues[DST_MESSAGE_QUEUE].put(header + ts_bytes
|
||||
+ onion_pub_key + ORIGIN_CONTACT_HEADER + payload_bytes)
|
||||
rp_print(f"Message from contact {short_addr}", ts)
|
||||
|
||||
elif header in [GROUP_MSG_INVITE_HEADER, GROUP_MSG_JOIN_HEADER,
|
||||
|
@ -282,13 +292,13 @@ def g_msg_manager(queues: 'QueueDict',
|
|||
pub_keys = split_byte_string(data, ONION_SERVICE_PUBLIC_KEY_LENGTH)
|
||||
pub_key_length = ONION_SERVICE_PUBLIC_KEY_LENGTH
|
||||
|
||||
members = [k for k in pub_keys if len(k) == pub_key_length ]
|
||||
known = [f" * {pub_key_to_onion_address(m)}" for m in members if m in existing_contacts]
|
||||
unknown = [f" * {pub_key_to_onion_address(m)}" for m in members if m not in existing_contacts]
|
||||
members = [k for k in pub_keys if len(k) == pub_key_length ]
|
||||
known = [f" * {pub_key_to_onion_address(m)}" for m in members if m in existing_contacts]
|
||||
unknown = [f" * {pub_key_to_onion_address(m)}" for m in members if m not in existing_contacts]
|
||||
|
||||
line_list = []
|
||||
if known:
|
||||
line_list.extend(["Known contacts"] + known)
|
||||
line_list.extend(["Known contacts"] + known)
|
||||
if unknown:
|
||||
line_list.extend(["Unknown contacts"] + unknown)
|
||||
|
||||
|
@ -353,7 +363,8 @@ def c_req_manager(queues: 'QueueDict',
|
|||
|
||||
if show_requests:
|
||||
ts_fmt = datetime.now().strftime('%b %d - %H:%M:%S.%f')[:-4]
|
||||
m_print([f"{ts_fmt} - New contact request from an unknown TFC account:", purp_onion_address], box=True)
|
||||
m_print([f"{ts_fmt} - New contact request from an unknown TFC account:", purp_onion_address],
|
||||
box=True)
|
||||
contact_requests.append(onion_pub_key)
|
||||
|
||||
if unit_test and queues[UNIT_TEST_QUEUE].qsize() != 0:
|
||||
|
|
|
@ -31,7 +31,16 @@ from src.common.encoding import bytes_to_bool, bytes_to_int
|
|||
from src.common.exceptions import FunctionReturn
|
||||
from src.common.misc import ignored, separate_header, separate_headers, split_byte_string
|
||||
from src.common.output import clear_screen, m_print
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (CONFIRM_CODE_LENGTH, CONTACT_MGMT_QUEUE, C_REQ_MGMT_QUEUE, C_REQ_STATE_QUEUE,
|
||||
ENCODED_BOOLEAN_LENGTH, ENCODED_INTEGER_LENGTH, EXIT, GROUP_MGMT_QUEUE,
|
||||
LOCAL_TESTING_PACKET_DELAY, MAX_INT, ONION_CLOSE_QUEUE, ONION_KEY_QUEUE,
|
||||
ONION_SERVICE_PRIVATE_KEY_LENGTH, ONION_SERVICE_PUBLIC_KEY_LENGTH, RESET,
|
||||
RP_ADD_CONTACT_HEADER, RP_REMOVE_CONTACT_HEADER, SRC_TO_RELAY_QUEUE,
|
||||
UNENCRYPTED_ADD_EXISTING_CONTACT, UNENCRYPTED_ADD_NEW_CONTACT, UNENCRYPTED_BAUDRATE,
|
||||
UNENCRYPTED_COMMAND_HEADER_LENGTH, UNENCRYPTED_EC_RATIO, UNENCRYPTED_EXIT_COMMAND,
|
||||
UNENCRYPTED_MANAGE_CONTACT_REQ, UNENCRYPTED_ONION_SERVICE_DATA,
|
||||
UNENCRYPTED_REM_CONTACT, UNENCRYPTED_SCREEN_CLEAR, UNENCRYPTED_SCREEN_RESET,
|
||||
UNENCRYPTED_WIPE_COMMAND, WIPE)
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from multiprocessing import Queue
|
||||
|
|
|
@ -27,22 +27,28 @@ import shlex
|
|||
import socket
|
||||
import tempfile
|
||||
import time
|
||||
import typing
|
||||
|
||||
from multiprocessing import Queue
|
||||
from typing import Any, Dict
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
import nacl.signing
|
||||
|
||||
import stem.control
|
||||
import stem.process
|
||||
|
||||
from stem.control import Controller
|
||||
|
||||
from src.common.encoding import pub_key_to_onion_address
|
||||
from src.common.exceptions import CriticalError
|
||||
from src.common.output import m_print, rp_print
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (EXIT, EXIT_QUEUE, ONION_CLOSE_QUEUE, ONION_KEY_QUEUE,
|
||||
ONION_SERVICE_PRIVATE_KEY_LENGTH, TOR_CONTROL_PORT, TOR_DATA_QUEUE, TOR_SOCKS_PORT)
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from multiprocessing import Queue
|
||||
|
||||
|
||||
def get_available_port(min_port: int, max_port: int) -> str:
|
||||
def get_available_port(min_port: int, max_port: int) -> int:
|
||||
"""Find a random available port within the given range."""
|
||||
with socket.socket() as temp_sock:
|
||||
while True:
|
||||
|
@ -51,7 +57,11 @@ def get_available_port(min_port: int, max_port: int) -> str:
|
|||
break
|
||||
except OSError:
|
||||
pass
|
||||
_, port = temp_sock.getsockname() # type: Any, str
|
||||
_, port = temp_sock.getsockname() # type: Any, int
|
||||
|
||||
if Tor.platform_is_tails():
|
||||
return TOR_SOCKS_PORT
|
||||
|
||||
return port
|
||||
|
||||
|
||||
|
@ -59,11 +69,27 @@ class Tor(object):
|
|||
"""Tor class manages the starting and stopping of Tor client."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.tor_process = None # type: Any
|
||||
self.controller = None # type: Any
|
||||
self.tor_process = None # type: Optional[Any]
|
||||
self.controller = None # type: Optional[Controller]
|
||||
|
||||
@staticmethod
|
||||
def platform_is_tails() -> bool:
|
||||
"""Return True if Relay Program is running on Tails."""
|
||||
with open('/etc/os-release') as f:
|
||||
data = f.read()
|
||||
return 'TAILS_PRODUCT_NAME="Tails"' in data
|
||||
|
||||
def connect(self, port: int) -> None:
|
||||
"""Launch Tor as a subprocess.
|
||||
|
||||
If TFC is running on top of Tails, do not launch a separate
|
||||
instance of Tor.
|
||||
"""
|
||||
if self.platform_is_tails():
|
||||
self.controller = Controller.from_port(port=TOR_CONTROL_PORT)
|
||||
self.controller.authenticate()
|
||||
return None
|
||||
|
||||
def connect(self, port: str) -> None:
|
||||
"""Launch Tor as a subprocess."""
|
||||
tor_data_directory = tempfile.TemporaryDirectory()
|
||||
tor_control_socket = os.path.join(tor_data_directory.name, 'control_socket')
|
||||
|
||||
|
@ -113,7 +139,7 @@ class Tor(object):
|
|||
|
||||
def stop(self) -> None:
|
||||
"""Stop the Tor subprocess."""
|
||||
if self.tor_process:
|
||||
if self.tor_process is not None:
|
||||
self.tor_process.terminate()
|
||||
time.sleep(0.1)
|
||||
if not self.tor_process.poll():
|
||||
|
@ -174,6 +200,9 @@ def onion_service(queues: Dict[bytes, 'Queue[Any]']) -> None:
|
|||
except (EOFError, KeyboardInterrupt):
|
||||
return
|
||||
|
||||
if tor.controller is None:
|
||||
raise CriticalError("No Tor controller")
|
||||
|
||||
try:
|
||||
rp_print("Setup 75% - Launching Onion Service...", bold=True)
|
||||
key_data = stem_compatible_ed25519_key_from_private_key(private_key)
|
||||
|
@ -206,9 +235,11 @@ def onion_service(queues: Dict[bytes, 'Queue[Any]']) -> None:
|
|||
|
||||
if queues[ONION_CLOSE_QUEUE].qsize() > 0:
|
||||
command = queues[ONION_CLOSE_QUEUE].get()
|
||||
tor.controller.remove_hidden_service(response.service_id)
|
||||
tor.stop()
|
||||
if not tor.platform_is_tails() and command == EXIT:
|
||||
tor.controller.remove_hidden_service(response.service_id)
|
||||
tor.stop()
|
||||
queues[EXIT_QUEUE].put(command)
|
||||
time.sleep(5)
|
||||
break
|
||||
|
||||
except (EOFError, KeyboardInterrupt):
|
||||
|
|
|
@ -31,7 +31,7 @@ from typing import Any, Dict, List, Optional
|
|||
|
||||
from flask import Flask, send_file
|
||||
|
||||
from src.common.statics import *
|
||||
from src.common.statics import CONTACT_REQ_QUEUE, F_TO_FLASK_QUEUE, M_TO_FLASK_QUEUE, URL_TOKEN_QUEUE
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
QueueDict = Dict[bytes, Queue[Any]]
|
||||
|
|
|
@ -22,14 +22,20 @@ along with TFC. If not, see <https://www.gnu.org/licenses/>.
|
|||
import time
|
||||
import typing
|
||||
|
||||
from typing import Any, Dict, Union
|
||||
from typing import Any, Dict, Tuple, Union
|
||||
|
||||
from src.common.encoding import bytes_to_int, pub_key_to_short_address
|
||||
from src.common.encoding import int_to_bytes, b85encode
|
||||
from src.common.exceptions import FunctionReturn
|
||||
from src.common.misc import ignored, separate_header, split_byte_string
|
||||
from src.common.output import rp_print
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (COMMAND_DATAGRAM_HEADER, DATAGRAM_HEADER_LENGTH, DST_COMMAND_QUEUE,
|
||||
DST_MESSAGE_QUEUE, ENCODED_INTEGER_LENGTH, FILE_DATAGRAM_HEADER, F_TO_FLASK_QUEUE,
|
||||
GATEWAY_QUEUE, GROUP_ID_LENGTH, GROUP_MSG_EXIT_GROUP_HEADER, GROUP_MSG_INVITE_HEADER,
|
||||
GROUP_MSG_JOIN_HEADER, GROUP_MSG_MEMBER_ADD_HEADER, GROUP_MSG_MEMBER_REM_HEADER,
|
||||
LOCAL_KEY_DATAGRAM_HEADER, MESSAGE_DATAGRAM_HEADER, M_TO_FLASK_QUEUE,
|
||||
ONION_SERVICE_PUBLIC_KEY_LENGTH, ORIGIN_USER_HEADER, PUBLIC_KEY_DATAGRAM_HEADER,
|
||||
SRC_TO_RELAY_QUEUE, UNENCRYPTED_DATAGRAM_HEADER, UNIT_TEST_QUEUE)
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from datetime import datetime
|
||||
|
@ -40,7 +46,7 @@ if typing.TYPE_CHECKING:
|
|||
|
||||
def queue_to_flask(packet: Union[bytes, str],
|
||||
onion_pub_key: bytes,
|
||||
flask_queue: 'Queue[Any]',
|
||||
flask_queue: 'Queue[Tuple[Union[bytes, str], bytes]]',
|
||||
ts: 'datetime',
|
||||
header: bytes
|
||||
) -> None:
|
||||
|
@ -122,7 +128,7 @@ def src_incoming(queues: 'QueueDict',
|
|||
def process_group_management_message(ts: 'datetime',
|
||||
packet: bytes,
|
||||
header: bytes,
|
||||
messages_to_flask: 'Queue[Any]') -> None:
|
||||
messages_to_flask: 'Queue[Tuple[Union[bytes, str], bytes]]') -> None:
|
||||
"""Parse and display group management message."""
|
||||
header_str = header.decode()
|
||||
group_id, packet = separate_header(packet, GROUP_ID_LENGTH)
|
||||
|
|
|
@ -35,7 +35,16 @@ from src.common.exceptions import FunctionReturn
|
|||
from src.common.input import yes
|
||||
from src.common.misc import ensure_dir, get_terminal_width, validate_onion_addr
|
||||
from src.common.output import clear_screen, m_print, phase, print_on_previous_line
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (CH_MASTER_KEY, CH_SETTING, CLEAR, CLEAR_SCREEN, COMMAND_PACKET_QUEUE, DIR_USER_DATA,
|
||||
DONE, EXIT_PROGRAM, GROUP_ID_ENC_LENGTH, KDB_CHANGE_MASTER_KEY_HEADER,
|
||||
KDB_UPDATE_SIZE_HEADER, KEX_STATUS_UNVERIFIED, KEX_STATUS_VERIFIED,
|
||||
KEY_MANAGEMENT_QUEUE, LOCAL_TESTING_PACKET_DELAY, LOGFILE_MASKING_QUEUE, LOG_DISPLAY,
|
||||
LOG_EXPORT, LOG_REMOVE, MESSAGE, ONION_ADDRESS_LENGTH, RELAY_PACKET_QUEUE, RESET,
|
||||
RESET_SCREEN, RX, SENDER_MODE_QUEUE, TRAFFIC_MASKING_QUEUE, TX, UNENCRYPTED_BAUDRATE,
|
||||
UNENCRYPTED_DATAGRAM_HEADER, UNENCRYPTED_EC_RATIO, UNENCRYPTED_EXIT_COMMAND,
|
||||
UNENCRYPTED_MANAGE_CONTACT_REQ, UNENCRYPTED_SCREEN_CLEAR, UNENCRYPTED_SCREEN_RESET,
|
||||
UNENCRYPTED_WIPE_COMMAND, US_BYTE, VERSION, WIN_ACTIVITY, WIN_SELECT, WIN_TYPE_GROUP,
|
||||
WIN_UID_FILE, WIN_UID_LOCAL, WIPE_USR_DATA)
|
||||
|
||||
from src.transmitter.commands_g import process_group_command
|
||||
from src.transmitter.contact import add_new_contact, change_nick, contact_setting, remove_contact
|
||||
|
|
|
@ -22,7 +22,7 @@ along with TFC. If not, see <https://www.gnu.org/licenses/>.
|
|||
import os
|
||||
import typing
|
||||
|
||||
from typing import Any, Callable, Dict, List, Optional
|
||||
from typing import Callable, Dict, List, Optional
|
||||
|
||||
from src.common.db_logs import remove_logs
|
||||
from src.common.encoding import b58decode, int_to_bytes
|
||||
|
@ -30,7 +30,11 @@ from src.common.exceptions import FunctionReturn
|
|||
from src.common.input import yes
|
||||
from src.common.misc import ignored, validate_group_name
|
||||
from src.common.output import group_management_print, m_print
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (ADDED_MEMBERS, ALREADY_MEMBER, GROUP_ADD, GROUP_CREATE, GROUP_DELETE,
|
||||
GROUP_ID_LENGTH, GROUP_MSG_EXIT_GROUP_HEADER, GROUP_MSG_INVITE_HEADER,
|
||||
GROUP_MSG_JOIN_HEADER, GROUP_MSG_MEMBER_ADD_HEADER, GROUP_MSG_MEMBER_REM_HEADER,
|
||||
GROUP_REMOVE, GROUP_RENAME, LOG_REMOVE, NEW_GROUP, NOT_IN_GROUP, RELAY_PACKET_QUEUE,
|
||||
REMOVED_MEMBERS, UNKNOWN_ACCOUNTS, US_BYTE, WIN_TYPE_CONTACT)
|
||||
|
||||
from src.transmitter.packet import queue_command, queue_to_nc
|
||||
from src.transmitter.user_input import UserInput
|
||||
|
@ -42,7 +46,7 @@ if typing.TYPE_CHECKING:
|
|||
from src.common.db_masterkey import MasterKey
|
||||
from src.common.db_settings import Settings
|
||||
from src.transmitter.windows import TxWindow
|
||||
QueueDict = Dict[bytes, Queue[Any]]
|
||||
QueueDict = Dict[bytes, Queue[bytes]]
|
||||
FuncDict = (Dict[str, Callable[[str,
|
||||
List[bytes],
|
||||
ContactList,
|
||||
|
@ -105,7 +109,7 @@ def process_group_command(user_input: 'UserInput',
|
|||
func_d = dict(create=group_create,
|
||||
join =group_create,
|
||||
add =group_add_member,
|
||||
rm =group_rm_member) # type: FuncDict
|
||||
rm =group_rm_member) # type: FuncDict
|
||||
|
||||
func = func_d[command_type]
|
||||
|
||||
|
@ -123,7 +127,7 @@ def group_create(group_name: str,
|
|||
group_id: Optional[bytes] = None
|
||||
) -> None:
|
||||
"""Create a new group.
|
||||
|
||||
|
||||
Validate the group name and determine what members can be added.
|
||||
"""
|
||||
error_msg = validate_group_name(group_name, contact_list, group_list)
|
||||
|
|
|
@ -29,7 +29,11 @@ from src.common.exceptions import FunctionReturn
|
|||
from src.common.input import box_input, yes
|
||||
from src.common.misc import ignored, validate_key_exchange, validate_nick, validate_onion_addr
|
||||
from src.common.output import m_print
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (ALL, CH_FILE_RECV, CH_LOGGING, CH_NICKNAME, CH_NOTIFY, CONTACT_REM, DISABLE, ECDHE,
|
||||
ENABLE, KDB_REMOVE_ENTRY_HEADER, KEY_MANAGEMENT_QUEUE, LOGGING, LOG_SETTING_QUEUE,
|
||||
NOTIFY, ONION_ADDRESS_LENGTH, PSK, RELAY_PACKET_QUEUE, STORE, TRUNC_ADDRESS_LENGTH,
|
||||
UNENCRYPTED_ADD_NEW_CONTACT, UNENCRYPTED_DATAGRAM_HEADER, UNENCRYPTED_REM_CONTACT,
|
||||
WIN_TYPE_CONTACT, WIN_TYPE_GROUP)
|
||||
|
||||
from src.transmitter.commands_g import group_rename
|
||||
from src.transmitter.key_exchanges import create_pre_shared_key, start_key_exchange
|
||||
|
@ -176,7 +180,7 @@ def remove_contact(user_input: 'UserInput',
|
|||
|
||||
# If the last member of the group is removed, deselect
|
||||
# the group. Deselection is not done in
|
||||
# update_group_win_members because it would prevent
|
||||
# `TxWindow.update_window()` because it would prevent
|
||||
# selecting the empty group for group related commands
|
||||
# such as notifications.
|
||||
if not window.window_contacts:
|
||||
|
|
|
@ -30,7 +30,8 @@ from src.common.crypto import byte_padding, csprng, encrypt_and_sign
|
|||
from src.common.encoding import int_to_bytes
|
||||
from src.common.exceptions import FunctionReturn
|
||||
from src.common.misc import readable_size, split_byte_string
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (COMPRESSION_LEVEL, FILE_ETA_FIELD_LENGTH, FILE_PACKET_CTR_LENGTH,
|
||||
FILE_SIZE_FIELD_LENGTH, PADDING_LENGTH, TRAFFIC_MASKING_QUEUE_CHECK_DELAY, US_BYTE)
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from src.common.db_settings import Settings
|
||||
|
|
|
@ -24,11 +24,11 @@ import readline
|
|||
import sys
|
||||
import typing
|
||||
|
||||
from typing import Any, Dict, NoReturn
|
||||
from typing import Dict, NoReturn
|
||||
|
||||
from src.common.exceptions import FunctionReturn
|
||||
from src.common.misc import get_tab_completer, ignored
|
||||
from src.common.statics import *
|
||||
from src.common.statics import COMMAND, FILE, MESSAGE
|
||||
|
||||
from src.transmitter.commands import process_command
|
||||
from src.transmitter.contact import add_new_contact
|
||||
|
@ -47,7 +47,7 @@ if typing.TYPE_CHECKING:
|
|||
from src.common.gateway import Gateway
|
||||
|
||||
|
||||
def input_loop(queues: Dict[bytes, 'Queue[Any]'],
|
||||
def input_loop(queues: Dict[bytes, 'Queue[bytes]'],
|
||||
settings: 'Settings',
|
||||
gateway: 'Gateway',
|
||||
contact_list: 'ContactList',
|
||||
|
|
|
@ -32,7 +32,16 @@ from src.common.exceptions import FunctionReturn
|
|||
from src.common.input import ask_confirmation_code, get_b58_key, nc_bypass_msg, yes
|
||||
from src.common.output import m_print, phase, print_fingerprint, print_key, print_on_previous_line
|
||||
from src.common.path import ask_path_gui
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (ARGON2_PSK_MEMORY_COST, ARGON2_PSK_PARALLELISM, ARGON2_PSK_TIME_COST,
|
||||
B58_PUBLIC_KEY, CONFIRM_CODE_LENGTH, DONE, ECDHE, FINGERPRINT, FINGERPRINT_LENGTH,
|
||||
HEADER_KEY, KDB_ADD_ENTRY_HEADER, KEX_STATUS_HAS_RX_PSK, KEX_STATUS_LOCAL_KEY,
|
||||
KEX_STATUS_NO_RX_PSK, KEX_STATUS_PENDING, KEX_STATUS_UNVERIFIED,
|
||||
KEX_STATUS_VERIFIED, KEY_EX_ECDHE, KEY_EX_PSK_RX, KEY_EX_PSK_TX,
|
||||
KEY_MANAGEMENT_QUEUE, LOCAL_KEY_DATAGRAM_HEADER, LOCAL_KEY_RDY, LOCAL_NICK,
|
||||
LOCAL_PUBKEY, MESSAGE_KEY, NC_BYPASS_START, NC_BYPASS_STOP,
|
||||
PUBLIC_KEY_DATAGRAM_HEADER, RELAY_PACKET_QUEUE, RESET, SYMMETRIC_KEY_LENGTH,
|
||||
TFC_PUBLIC_KEY_LENGTH, UNENCRYPTED_DATAGRAM_HEADER, UNENCRYPTED_ONION_SERVICE_DATA,
|
||||
WIN_TYPE_GROUP)
|
||||
|
||||
from src.transmitter.packet import queue_command, queue_to_nc
|
||||
|
||||
|
@ -484,6 +493,7 @@ def create_pre_shared_key(onion_pub_key: bytes, # Public key of contac
|
|||
m_print("Error: Did not have permission to write to the directory.", delay=0.5)
|
||||
continue
|
||||
|
||||
c_code = blake2b(onion_pub_key, digest_size=CONFIRM_CODE_LENGTH)
|
||||
command = (KEY_EX_PSK_TX
|
||||
+ onion_pub_key
|
||||
+ tx_mk + csprng()
|
||||
|
@ -492,6 +502,21 @@ def create_pre_shared_key(onion_pub_key: bytes, # Public key of contac
|
|||
|
||||
queue_command(command, settings, queues)
|
||||
|
||||
while True:
|
||||
purp_code = ask_confirmation_code('Receiver')
|
||||
if purp_code == c_code.hex():
|
||||
break
|
||||
|
||||
elif purp_code == '':
|
||||
phase("Resending contact data", head=2)
|
||||
queue_command(command, settings, queues)
|
||||
phase(DONE)
|
||||
print_on_previous_line(reps=5)
|
||||
|
||||
else:
|
||||
m_print("Incorrect confirmation code.", head=1)
|
||||
print_on_previous_line(reps=4, delay=2)
|
||||
|
||||
contact_list.add_contact(onion_pub_key, nick,
|
||||
bytes(FINGERPRINT_LENGTH), bytes(FINGERPRINT_LENGTH),
|
||||
KEX_STATUS_NO_RX_PSK,
|
||||
|
|
|
@ -24,7 +24,7 @@ import os
|
|||
import typing
|
||||
import zlib
|
||||
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
||||
|
||||
from src.common.crypto import blake2b, byte_padding, csprng, encrypt_and_sign
|
||||
from src.common.encoding import bool_to_bytes, int_to_bytes, str_to_bytes
|
||||
|
@ -33,7 +33,14 @@ from src.common.input import yes
|
|||
from src.common.misc import split_byte_string
|
||||
from src.common.output import m_print, phase, print_on_previous_line
|
||||
from src.common.path import ask_path_gui
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (ASSEMBLY_PACKET_LENGTH, COMMAND, COMMAND_DATAGRAM_HEADER, COMMAND_PACKET_QUEUE,
|
||||
COMPRESSION_LEVEL, C_A_HEADER, C_E_HEADER, C_L_HEADER, C_S_HEADER, DONE, FILE,
|
||||
FILE_DATAGRAM_HEADER, FILE_KEY_HEADER, FILE_PACKET_CTR_LENGTH, F_A_HEADER,
|
||||
F_C_HEADER, F_E_HEADER, F_L_HEADER, F_S_HEADER, GROUP_MESSAGE_HEADER,
|
||||
GROUP_MSG_ID_LENGTH, LOCAL_PUBKEY, MESSAGE, MESSAGE_DATAGRAM_HEADER,
|
||||
MESSAGE_PACKET_QUEUE, M_A_HEADER, M_C_HEADER, M_E_HEADER, M_L_HEADER, M_S_HEADER,
|
||||
PADDING_LENGTH, PRIVATE_MESSAGE_HEADER, RELAY_PACKET_QUEUE, TM_COMMAND_PACKET_QUEUE,
|
||||
TM_FILE_PACKET_QUEUE, TM_MESSAGE_PACKET_QUEUE, WIN_TYPE_GROUP)
|
||||
|
||||
from src.transmitter.files import File
|
||||
from src.transmitter.user_input import UserInput
|
||||
|
@ -41,14 +48,16 @@ from src.transmitter.user_input import UserInput
|
|||
if typing.TYPE_CHECKING:
|
||||
from multiprocessing import Queue
|
||||
from src.common.db_keys import KeyList
|
||||
from src.common.db_masterkey import MasterKey
|
||||
from src.common.db_settings import Settings
|
||||
from src.common.gateway import Gateway
|
||||
from src.transmitter.windows import TxWindow, MockWindow
|
||||
QueueDict = Dict[bytes, Queue[Any]]
|
||||
QueueDict = Dict[bytes, Queue[Any]]
|
||||
log_queue_data = Tuple[Optional[bytes], bytes, Optional[bool], Optional[bool], MasterKey]
|
||||
|
||||
|
||||
def queue_to_nc(packet: bytes,
|
||||
nc_queue: 'Queue[Any]',
|
||||
nc_queue: 'Queue[bytes]',
|
||||
) -> None:
|
||||
"""Queue unencrypted command/exported file to Networked Computer.
|
||||
|
||||
|
@ -360,7 +369,7 @@ def queue_assembly_packets(assembly_packet_list: List[bytes],
|
|||
settings: 'Settings',
|
||||
queues: 'QueueDict',
|
||||
window: Optional[Union['TxWindow', 'MockWindow']] = None,
|
||||
log_as_ph: bool = False
|
||||
log_as_ph: bool = False
|
||||
) -> None:
|
||||
"""Queue assembly packets for sender_loop().
|
||||
|
||||
|
@ -388,13 +397,13 @@ def queue_assembly_packets(assembly_packet_list: List[bytes],
|
|||
queue.put(assembly_packet)
|
||||
|
||||
|
||||
def send_packet(key_list: 'KeyList', # Key list object
|
||||
gateway: 'Gateway', # Gateway object
|
||||
log_queue: 'Queue[Any]', # Multiprocessing queue for logged messages
|
||||
assembly_packet: bytes, # Padded plaintext assembly packet
|
||||
onion_pub_key: Optional[bytes] = None, # Recipient v3 Onion Service address
|
||||
log_messages: Optional[bool] = None, # When True, log the message assembly packet
|
||||
log_as_ph: Optional[bool] = None # When True, log assembly packet as placeholder data
|
||||
def send_packet(key_list: 'KeyList', # Key list object
|
||||
gateway: 'Gateway', # Gateway object
|
||||
log_queue: 'Queue[log_queue_data]', # Multiprocessing queue for logged messages
|
||||
assembly_packet: bytes, # Padded plaintext assembly packet
|
||||
onion_pub_key: Optional[bytes] = None, # Recipient v3 Onion Service address
|
||||
log_messages: Optional[bool] = None, # When True, log the message assembly packet
|
||||
log_as_ph: Optional[bool] = None # When True, log assembly packet as placeholder data
|
||||
) -> None:
|
||||
"""Encrypt and send assembly packet.
|
||||
|
||||
|
|
|
@ -25,7 +25,12 @@ import typing
|
|||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
from src.common.misc import ignored
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (COMMAND_PACKET_QUEUE, DATAGRAM_HEADER_LENGTH, EXIT, EXIT_QUEUE, KEY_MANAGEMENT_QUEUE,
|
||||
LOG_PACKET_QUEUE, MESSAGE_PACKET_QUEUE, RELAY_PACKET_QUEUE, SENDER_MODE_QUEUE,
|
||||
TM_COMMAND_PACKET_QUEUE, TM_FILE_PACKET_QUEUE, TM_MESSAGE_PACKET_QUEUE,
|
||||
TM_NOISE_COMMAND_QUEUE, TM_NOISE_PACKET_QUEUE, TRAFFIC_MASKING,
|
||||
TRAFFIC_MASKING_QUEUE_CHECK_DELAY, UNENCRYPTED_EXIT_COMMAND, UNENCRYPTED_WIPE_COMMAND,
|
||||
WINDOW_SELECT_QUEUE, WIPE)
|
||||
|
||||
from src.transmitter.packet import send_packet
|
||||
from src.transmitter.traffic_masking import HideRunTime
|
||||
|
@ -35,7 +40,6 @@ if typing.TYPE_CHECKING:
|
|||
from src.common.db_keys import KeyList
|
||||
from src.common.db_settings import Settings
|
||||
from src.common.gateway import Gateway
|
||||
from src.common.db_settings import Settings
|
||||
QueueDict = Dict[bytes, Queue[Any]]
|
||||
Message_buffer = Dict[bytes, List[Tuple[bytes, bytes, bool, bool, bytes]]]
|
||||
|
||||
|
|
|
@ -27,7 +27,8 @@ import typing
|
|||
from typing import Any, Dict, Optional, Tuple, Union
|
||||
|
||||
from src.common.misc import ignored
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (C_N_HEADER, NOISE_PACKET_BUFFER, PADDING_LENGTH, P_N_HEADER, STATIC,
|
||||
TM_NOISE_COMMAND_QUEUE, TM_NOISE_PACKET_QUEUE, TRAFFIC_MASKING)
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from multiprocessing import Queue
|
||||
|
|
|
@ -22,7 +22,7 @@ along with TFC. If not, see <https://www.gnu.org/licenses/>.
|
|||
import typing
|
||||
|
||||
from src.common.output import print_on_previous_line
|
||||
from src.common.statics import *
|
||||
from src.common.statics import COMMAND, FILE, MESSAGE, WIN_TYPE_GROUP
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from src.common.db_settings import Settings
|
||||
|
|
|
@ -27,7 +27,7 @@ from src.common.db_contacts import Contact
|
|||
from src.common.exceptions import FunctionReturn
|
||||
from src.common.input import yes
|
||||
from src.common.output import clear_screen
|
||||
from src.common.statics import *
|
||||
from src.common.statics import KEX_STATUS_PENDING, WINDOW_SELECT_QUEUE, WIN_SELECT, WIN_TYPE_CONTACT, WIN_TYPE_GROUP
|
||||
|
||||
from src.transmitter.contact import add_new_contact
|
||||
from src.transmitter.key_exchanges import export_onion_service_data, start_key_exchange
|
||||
|
|
|
@ -40,11 +40,11 @@ from cryptography.hazmat.primitives.serialization import Encoding, NoEncryptio
|
|||
|
||||
from src.common.crypto import argon2_kdf, auth_and_decrypt, blake2b, byte_padding, check_kernel_version, csprng
|
||||
from src.common.crypto import encrypt_and_sign, rm_padding_bytes, X448
|
||||
from src.common.statics import ARGON2_MIN_MEMORY_COST, ARGON2_MIN_PARALLELISM, ARGON2_MIN_TIME_COST, ARGON2_SALT_LENGTH
|
||||
from src.common.statics import BLAKE2_DIGEST_LENGTH, BLAKE2_DIGEST_LENGTH_MAX, BLAKE2_DIGEST_LENGTH_MIN
|
||||
from src.common.statics import BLAKE2_KEY_LENGTH_MAX, BLAKE2_PERSON_LENGTH_MAX, BLAKE2_SALT_LENGTH_MAX, PADDING_LENGTH
|
||||
from src.common.statics import SYMMETRIC_KEY_LENGTH, TFC_PRIVATE_KEY_LENGTH, TFC_PUBLIC_KEY_LENGTH
|
||||
from src.common.statics import XCHACHA20_NONCE_LENGTH
|
||||
from src.common.statics import (ARGON2_MIN_MEMORY_COST, ARGON2_MIN_PARALLELISM, ARGON2_MIN_TIME_COST,
|
||||
ARGON2_SALT_LENGTH, BLAKE2_DIGEST_LENGTH, BLAKE2_DIGEST_LENGTH_MAX,
|
||||
BLAKE2_DIGEST_LENGTH_MIN, BLAKE2_KEY_LENGTH_MAX, BLAKE2_PERSON_LENGTH_MAX,
|
||||
BLAKE2_SALT_LENGTH_MAX, PADDING_LENGTH, SYMMETRIC_KEY_LENGTH, TFC_PRIVATE_KEY_LENGTH,
|
||||
TFC_PUBLIC_KEY_LENGTH, XCHACHA20_NONCE_LENGTH)
|
||||
|
||||
from tests.utils import cd_unit_test, cleanup
|
||||
|
||||
|
@ -68,6 +68,7 @@ class TestBLAKE2b(unittest.TestCase):
|
|||
"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
|
||||
kat_file_url = 'https://raw.githubusercontent.com/BLAKE2/BLAKE2/master/testvectors/blake2b-kat.txt'
|
||||
|
@ -109,6 +110,7 @@ class TestBLAKE2b(unittest.TestCase):
|
|||
self.assertEqual(len(set(digests)), 256)
|
||||
|
||||
def tearDown(self) -> None:
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
|
||||
def test_blake2b_using_the_official_known_answer_tests(self):
|
||||
|
@ -144,8 +146,15 @@ class TestBLAKE2bWrapper(unittest.TestCase):
|
|||
with self.assertRaises(SystemExit):
|
||||
blake2b(b'test_string', digest_size=invalid_digest_size)
|
||||
|
||||
@mock.patch('hashlib.blake2b', return_value=MagicMock(digest=(MagicMock(side_effect=[(BLAKE2_DIGEST_LENGTH-1)*b'a',
|
||||
(BLAKE2_DIGEST_LENGTH+1)*b'a']))))
|
||||
@mock.patch('hashlib.blake2b', return_value=MagicMock(digest=(MagicMock(side_effect=[BLAKE2_DIGEST_LENGTH*'a']))))
|
||||
def test_invalid_blake2b_digest_type_raises_critical_error(self, mock_blake2b):
|
||||
with self.assertRaises(SystemExit):
|
||||
blake2b(b'test_string')
|
||||
mock_blake2b.assert_called()
|
||||
|
||||
@mock.patch('hashlib.blake2b', return_value=MagicMock(digest=(
|
||||
MagicMock(side_effect=[(BLAKE2_DIGEST_LENGTH-1)*b'a',
|
||||
(BLAKE2_DIGEST_LENGTH+1)*b'a']))))
|
||||
def test_invalid_size_blake2b_digest_raises_critical_error(self, mock_blake2b):
|
||||
with self.assertRaises(SystemExit):
|
||||
blake2b(b'test_string')
|
||||
|
@ -175,6 +184,7 @@ class TestArgon2KDF(unittest.TestCase):
|
|||
"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.number_of_tests = 256
|
||||
|
||||
|
@ -197,6 +207,7 @@ class TestArgon2KDF(unittest.TestCase):
|
|||
subprocess.Popen('make test', shell=True).wait()
|
||||
|
||||
def tearDown(self) -> None:
|
||||
"""Post-test actions."""
|
||||
os.chdir('..')
|
||||
cleanup(self.unit_test_dir)
|
||||
|
||||
|
@ -260,6 +271,7 @@ class TestArgon2KDF(unittest.TestCase):
|
|||
class TestArgon2Wrapper(unittest.TestCase):
|
||||
|
||||
def setUp(self) -> None:
|
||||
"""Pre-test actions."""
|
||||
self.salt = os.urandom(ARGON2_SALT_LENGTH)
|
||||
|
||||
def test_invalid_length_salt_raises_critical_error(self):
|
||||
|
@ -267,7 +279,21 @@ class TestArgon2Wrapper(unittest.TestCase):
|
|||
ARGON2_SALT_LENGTH+1, 1000]]
|
||||
for invalid_salt in invalid_salts:
|
||||
with self.assertRaises(SystemExit):
|
||||
argon2_kdf('password', invalid_salt, ARGON2_MIN_TIME_COST, ARGON2_MIN_MEMORY_COST, ARGON2_MIN_PARALLELISM)
|
||||
argon2_kdf('password', invalid_salt,
|
||||
ARGON2_MIN_TIME_COST, ARGON2_MIN_MEMORY_COST, ARGON2_MIN_PARALLELISM)
|
||||
|
||||
@mock.patch("argon2.low_level.hash_secret_raw", MagicMock(side_effect=[SYMMETRIC_KEY_LENGTH*'a']))
|
||||
def test_invalid_type_key_from_argon2_raises_critical_error(self):
|
||||
with self.assertRaises(SystemExit):
|
||||
argon2_kdf('password', self.salt, ARGON2_MIN_TIME_COST, ARGON2_MIN_MEMORY_COST, ARGON2_MIN_PARALLELISM)
|
||||
|
||||
@mock.patch("argon2.low_level.hash_secret_raw", MagicMock(side_effect=[(SYMMETRIC_KEY_LENGTH-1)*b'a',
|
||||
(SYMMETRIC_KEY_LENGTH+1)*b'a']))
|
||||
def test_invalid_size_key_from_argon2_raises_critical_error(self):
|
||||
with self.assertRaises(SystemExit):
|
||||
argon2_kdf('password', self.salt, ARGON2_MIN_TIME_COST, ARGON2_MIN_MEMORY_COST, ARGON2_MIN_PARALLELISM)
|
||||
with self.assertRaises(SystemExit):
|
||||
argon2_kdf('password', self.salt, ARGON2_MIN_TIME_COST, ARGON2_MIN_MEMORY_COST, ARGON2_MIN_PARALLELISM)
|
||||
|
||||
def test_too_small_time_cost_raises_critical_error(self):
|
||||
with self.assertRaises(SystemExit):
|
||||
|
@ -345,6 +371,13 @@ class TestX448(unittest.TestCase):
|
|||
self.assertIsInstance(public_key, bytes)
|
||||
self.assertEqual(len(public_key), TFC_PUBLIC_KEY_LENGTH)
|
||||
|
||||
def test_deriving_invalid_type_public_key_raises_critical_error(self):
|
||||
private_key = MagicMock(public_key=MagicMock(return_value=MagicMock(
|
||||
public_bytes=MagicMock(side_effect=[TFC_PUBLIC_KEY_LENGTH * 'a']))))
|
||||
|
||||
with self.assertRaises(SystemExit):
|
||||
X448.derive_public_key(private_key)
|
||||
|
||||
def test_deriving_invalid_size_public_key_raises_critical_error(self):
|
||||
"""
|
||||
The public key is already validated by the pyca/cryptography
|
||||
|
@ -513,6 +546,7 @@ class TestXChaCha20Poly1305(unittest.TestCase):
|
|||
nonce_ct_tag_libsodium = libsodium_nonce + libsodium_ct_tag
|
||||
|
||||
def setUp(self) -> None:
|
||||
"""Pre-test actions."""
|
||||
self.assertEqual(self.ietf_plaintext, self.libsodium_plaintext)
|
||||
self.assertEqual(self.ietf_ad, self.libsodium_ad)
|
||||
self.assertEqual(self.ietf_key, self.libsodium_key)
|
||||
|
@ -589,6 +623,16 @@ class TestBytePadding(unittest.TestCase):
|
|||
self.assertEqual(padded_bytestring_lengths, {1*PADDING_LENGTH, 2*PADDING_LENGTH,
|
||||
3*PADDING_LENGTH, 4*PADDING_LENGTH})
|
||||
|
||||
@mock.patch('cryptography.hazmat.primitives.padding.PKCS7',
|
||||
return_value=MagicMock(
|
||||
padder=MagicMock(return_value=MagicMock(
|
||||
update=MagicMock(return_value=''),
|
||||
finalize=MagicMock(return_value=(PADDING_LENGTH*'a'))))))
|
||||
def test_invalid_padding_type_raises_critical_error(self, mock_padder):
|
||||
with self.assertRaises(SystemExit):
|
||||
byte_padding(b'test_string')
|
||||
mock_padder.assert_called()
|
||||
|
||||
@mock.patch('cryptography.hazmat.primitives.padding.PKCS7',
|
||||
return_value=MagicMock(
|
||||
padder=MagicMock(return_value=MagicMock(
|
||||
|
@ -663,6 +707,11 @@ class TestCSPRNG(unittest.TestCase):
|
|||
key = csprng(key_size)
|
||||
self.assertEqual(len(key), key_size)
|
||||
|
||||
@mock.patch('os.getrandom', return_value=SYMMETRIC_KEY_LENGTH*'a')
|
||||
def test_invalid_entropy_type_from_getrandom_raises_critical_error(self, _):
|
||||
with self.assertRaises(SystemExit):
|
||||
csprng()
|
||||
|
||||
def test_subceeding_hash_function_min_digest_size_raises_critical_error(self):
|
||||
with self.assertRaises(SystemExit):
|
||||
csprng(BLAKE2_DIGEST_LENGTH_MIN-1)
|
||||
|
|
|
@ -25,7 +25,10 @@ import unittest
|
|||
from src.common.crypto import encrypt_and_sign
|
||||
from src.common.db_contacts import Contact, ContactList
|
||||
from src.common.misc import ensure_dir
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (CLEAR_ENTIRE_SCREEN, CONTACT_LENGTH, CURSOR_LEFT_UP_CORNER, DIR_USER_DATA, ECDHE,
|
||||
FINGERPRINT_LENGTH, KEX_STATUS_HAS_RX_PSK, KEX_STATUS_LOCAL_KEY, KEX_STATUS_NONE,
|
||||
KEX_STATUS_NO_RX_PSK, KEX_STATUS_PENDING, KEX_STATUS_UNVERIFIED,
|
||||
KEX_STATUS_VERIFIED, LOCAL_ID, POLY1305_TAG_LENGTH, PSK, XCHACHA20_NONCE_LENGTH)
|
||||
|
||||
from tests.mock_classes import create_contact, MasterKey, Settings
|
||||
from tests.utils import cd_unit_test, cleanup, nick_to_onion_address, nick_to_pub_key, tamper_file, TFCTestCase
|
||||
|
@ -34,6 +37,7 @@ from tests.utils import cd_unit_test, cleanup, nick_to_onion_address, nic
|
|||
class TestContact(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.contact = Contact(nick_to_pub_key('Bob'),
|
||||
'Bob',
|
||||
FINGERPRINT_LENGTH * b'\x01',
|
||||
|
@ -62,6 +66,7 @@ class TestContact(unittest.TestCase):
|
|||
class TestContactList(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.master_key = MasterKey()
|
||||
self.settings = Settings()
|
||||
|
@ -73,6 +78,7 @@ class TestContactList(TFCTestCase):
|
|||
self.real_contact_list.remove(LOCAL_ID)
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
|
||||
def test_contact_list_iterates_over_contact_objects(self):
|
||||
|
@ -101,7 +107,8 @@ class TestContactList(TFCTestCase):
|
|||
def test_invalid_content_raises_critical_error(self):
|
||||
# Setup
|
||||
invalid_data = b'a'
|
||||
pt_bytes = b''.join([c.serialize_c() for c in self.contact_list.contacts + self.contact_list._dummy_contacts()])
|
||||
pt_bytes = b''.join([c.serialize_c() for c in self.contact_list.contacts
|
||||
+ self.contact_list._dummy_contacts()])
|
||||
ct_bytes = encrypt_and_sign(pt_bytes + invalid_data, self.master_key.master_key)
|
||||
|
||||
ensure_dir(DIR_USER_DATA)
|
||||
|
|
|
@ -27,7 +27,8 @@ from src.common.db_contacts import Contact, ContactList
|
|||
from src.common.db_groups import Group, GroupList
|
||||
from src.common.encoding import b58encode
|
||||
from src.common.misc import ensure_dir
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (DIR_USER_DATA, GROUP_DB_HEADER_LENGTH, GROUP_ID_LENGTH, GROUP_STATIC_LENGTH,
|
||||
ONION_SERVICE_PUBLIC_KEY_LENGTH, POLY1305_TAG_LENGTH, XCHACHA20_NONCE_LENGTH)
|
||||
|
||||
from tests.mock_classes import create_contact, group_name_to_group_id, MasterKey, nick_to_pub_key, Settings
|
||||
from tests.utils import cd_unit_test, cleanup, tamper_file, TFCTestCase
|
||||
|
@ -36,6 +37,7 @@ from tests.utils import cd_unit_test, cleanup, tamper_file, TFCTestCase
|
|||
class TestGroup(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.nicks = ['Alice', 'Bob', 'Charlie']
|
||||
members = list(map(create_contact, self.nicks))
|
||||
|
@ -50,6 +52,7 @@ class TestGroup(unittest.TestCase):
|
|||
ensure_dir(DIR_USER_DATA)
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
|
||||
def test_group_iterates_over_contact_objects(self):
|
||||
|
@ -117,6 +120,7 @@ class TestGroup(unittest.TestCase):
|
|||
class TestGroupList(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.master_key = MasterKey()
|
||||
self.settings = Settings()
|
||||
|
@ -146,6 +150,7 @@ class TestGroupList(TFCTestCase):
|
|||
+ self.settings.max_number_of_group_members * ONION_SERVICE_PUBLIC_KEY_LENGTH)
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
|
||||
def test_group_list_iterates_over_group_objects(self):
|
||||
|
|
|
@ -26,7 +26,9 @@ from src.common.crypto import blake2b, encrypt_and_sign
|
|||
from src.common.db_keys import KeyList, KeySet
|
||||
from src.common.encoding import int_to_bytes
|
||||
from src.common.misc import ensure_dir
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (DIR_USER_DATA, INITIAL_HARAC, KDB_ADD_ENTRY_HEADER, KDB_CHANGE_MASTER_KEY_HEADER,
|
||||
KDB_REMOVE_ENTRY_HEADER, KDB_UPDATE_SIZE_HEADER, KEYSET_LENGTH, LOCAL_ID, LOCAL_PUBKEY,
|
||||
POLY1305_TAG_LENGTH, RX, SYMMETRIC_KEY_LENGTH, TX, XCHACHA20_NONCE_LENGTH)
|
||||
|
||||
from tests.mock_classes import create_keyset, MasterKey, nick_to_pub_key, Settings
|
||||
from tests.utils import cd_unit_test, cleanup, tamper_file
|
||||
|
@ -35,6 +37,7 @@ from tests.utils import cd_unit_test, cleanup, tamper_file
|
|||
class TestKeySet(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.keyset = KeySet(onion_pub_key=nick_to_pub_key('Alice'),
|
||||
tx_mk=bytes(SYMMETRIC_KEY_LENGTH),
|
||||
rx_mk=bytes(SYMMETRIC_KEY_LENGTH),
|
||||
|
@ -86,6 +89,7 @@ class TestKeySet(unittest.TestCase):
|
|||
class TestKeyList(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.master_key = MasterKey()
|
||||
self.settings = Settings()
|
||||
|
@ -95,6 +99,7 @@ class TestKeyList(unittest.TestCase):
|
|||
self.keylist.keysets = [create_keyset(n, store_f=self.keylist.store_keys) for n in self.full_contact_list]
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
|
||||
def test_storing_and_loading_of_keysets(self):
|
||||
|
|
|
@ -30,7 +30,12 @@ from unittest import mock
|
|||
from src.common.db_contacts import ContactList
|
||||
from src.common.db_logs import access_logs, change_log_db_key, log_writer_loop, remove_logs, write_log_entry
|
||||
from src.common.encoding import bytes_to_timestamp
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (CLEAR_ENTIRE_SCREEN, CURSOR_LEFT_UP_CORNER, C_S_HEADER, DIR_USER_DATA, EXIT,
|
||||
F_S_HEADER, GROUP_ID_LENGTH, LOGFILE_MASKING_QUEUE, LOG_ENTRY_LENGTH,
|
||||
LOG_PACKET_QUEUE, LOG_SETTING_QUEUE, MESSAGE, M_A_HEADER, M_C_HEADER, M_S_HEADER,
|
||||
ORIGIN_CONTACT_HEADER, PADDING_LENGTH, P_N_HEADER, RX, SYMMETRIC_KEY_LENGTH,
|
||||
TIMESTAMP_LENGTH, TRAFFIC_MASKING_QUEUE, UNIT_TEST_QUEUE, WIN_TYPE_CONTACT,
|
||||
WIN_TYPE_GROUP)
|
||||
|
||||
from tests.mock_classes import create_contact, GroupList, MasterKey, RxWindow, Settings
|
||||
from tests.utils import assembly_packet_creator, cd_unit_test, cleanup, group_name_to_group_id, nick_to_pub_key
|
||||
|
@ -40,12 +45,15 @@ TIMESTAMP_BYTES = bytes.fromhex('08ceae02')
|
|||
STATIC_TIMESTAMP = bytes_to_timestamp(TIMESTAMP_BYTES).strftime('%H:%M:%S.%f')[:-TIMESTAMP_LENGTH]
|
||||
SLEEP_DELAY = 0.02
|
||||
|
||||
|
||||
class TestLogWriterLoop(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
|
||||
def test_function_logs_normal_data(self):
|
||||
|
@ -56,10 +64,10 @@ class TestLogWriterLoop(unittest.TestCase):
|
|||
|
||||
def queue_delayer():
|
||||
"""Place messages to queue one at a time."""
|
||||
for p in [(nick_to_pub_key('Alice'), M_S_HEADER + bytes(PADDING_LENGTH), False, False, master_key),
|
||||
(None, C_S_HEADER + bytes(PADDING_LENGTH), True, False, master_key),
|
||||
(nick_to_pub_key('Alice'), P_N_HEADER + bytes(PADDING_LENGTH), True, True, master_key),
|
||||
(nick_to_pub_key('Alice'), F_S_HEADER + bytes(PADDING_LENGTH), True, True, master_key),
|
||||
for p in [(nick_to_pub_key('Alice'), M_S_HEADER + bytes(PADDING_LENGTH), False, False, master_key),
|
||||
(None, C_S_HEADER + bytes(PADDING_LENGTH), True, False, master_key),
|
||||
(nick_to_pub_key('Alice'), P_N_HEADER + bytes(PADDING_LENGTH), True, True, master_key),
|
||||
(nick_to_pub_key('Alice'), F_S_HEADER + bytes(PADDING_LENGTH), True, True, master_key),
|
||||
(nick_to_pub_key('Alice'), M_S_HEADER + bytes(PADDING_LENGTH), True, False, master_key)]:
|
||||
queues[LOG_PACKET_QUEUE].put(p)
|
||||
time.sleep(SLEEP_DELAY)
|
||||
|
@ -90,9 +98,9 @@ class TestLogWriterLoop(unittest.TestCase):
|
|||
|
||||
def queue_delayer():
|
||||
"""Place messages to queue one at a time."""
|
||||
for p in [(nick_to_pub_key('Alice'), M_S_HEADER + bytes(PADDING_LENGTH), False, False, master_key),
|
||||
(None, C_S_HEADER + bytes(PADDING_LENGTH), True, False, master_key),
|
||||
(nick_to_pub_key('Alice'), F_S_HEADER + bytes(PADDING_LENGTH), True, True, master_key),
|
||||
for p in [(nick_to_pub_key('Alice'), M_S_HEADER + bytes(PADDING_LENGTH), False, False, master_key),
|
||||
(None, C_S_HEADER + bytes(PADDING_LENGTH), True, False, master_key),
|
||||
(nick_to_pub_key('Alice'), F_S_HEADER + bytes(PADDING_LENGTH), True, True, master_key),
|
||||
(nick_to_pub_key('Alice'), M_S_HEADER + bytes(PADDING_LENGTH), True, False, master_key)]:
|
||||
queues[LOG_PACKET_QUEUE].put(p)
|
||||
time.sleep(SLEEP_DELAY)
|
||||
|
@ -194,12 +202,14 @@ class TestLogWriterLoop(unittest.TestCase):
|
|||
class TestWriteLogEntry(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.master_key = MasterKey()
|
||||
self.settings = Settings()
|
||||
self.log_file = f'{DIR_USER_DATA}{self.settings.software_operation}_logs'
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
|
||||
def test_oversize_packet_raises_critical_error(self):
|
||||
|
@ -220,6 +230,7 @@ class TestWriteLogEntry(unittest.TestCase):
|
|||
class TestAccessHistoryAndPrintLogs(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.master_key = MasterKey()
|
||||
self.settings = Settings()
|
||||
|
@ -250,6 +261,7 @@ class TestAccessHistoryAndPrintLogs(TFCTestCase):
|
|||
"s neque a facilisis. Mauris id tortor placerat, aliquam dolor ac, venenatis arcu.")
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
|
||||
def test_missing_log_file_raises_fr(self):
|
||||
|
@ -509,6 +521,7 @@ Log file of message(s) to/from group test_group
|
|||
class TestReEncrypt(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.old_key = MasterKey()
|
||||
self.new_key = MasterKey(master_key=os.urandom(SYMMETRIC_KEY_LENGTH))
|
||||
|
@ -517,10 +530,11 @@ class TestReEncrypt(TFCTestCase):
|
|||
self.time = STATIC_TIMESTAMP
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
|
||||
def test_missing_log_database_raises_fr(self):
|
||||
self.assert_fr(f"Error: Could not find log database.",
|
||||
self.assert_fr(f"No log database available.",
|
||||
change_log_db_key, self.old_key.master_key, self.new_key.master_key, self.settings)
|
||||
|
||||
@mock.patch('struct.pack', return_value=TIMESTAMP_BYTES)
|
||||
|
@ -569,6 +583,7 @@ Log file of message(s) sent to contact Alice
|
|||
class TestRemoveLog(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.master_key = MasterKey()
|
||||
self.settings = Settings()
|
||||
|
@ -591,6 +606,7 @@ class TestRemoveLog(TFCTestCase):
|
|||
"s neque a facilisis. Mauris id tortor placerat, aliquam dolor ac, venenatis arcu.")
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
|
||||
def test_missing_log_file_raises_fr(self):
|
||||
|
|
|
@ -28,12 +28,14 @@ from unittest.mock import MagicMock
|
|||
|
||||
from src.common.db_masterkey import MasterKey
|
||||
from src.common.misc import ensure_dir
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (DIR_USER_DATA, MASTERKEY_DB_SIZE, PASSWORD_MIN_BIT_STRENGTH,
|
||||
SYMMETRIC_KEY_LENGTH, TX)
|
||||
|
||||
from tests.utils import cd_unit_test, cleanup
|
||||
|
||||
KL = SYMMETRIC_KEY_LENGTH
|
||||
|
||||
|
||||
class TestMasterKey(unittest.TestCase):
|
||||
input_list = ['password', 'different_password', # Invalid new password pair
|
||||
'password', 'password', # Valid new password pair
|
||||
|
@ -41,13 +43,22 @@ class TestMasterKey(unittest.TestCase):
|
|||
'password'] # Valid login password
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.operation = TX
|
||||
self.file_name = f"{DIR_USER_DATA}{self.operation}_login_data"
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
|
||||
def test_password_generation(self):
|
||||
bit_strength, password = MasterKey.generate_master_password()
|
||||
self.assertIsInstance(bit_strength, int)
|
||||
self.assertIsInstance(password, str)
|
||||
self.assertGreaterEqual(bit_strength, PASSWORD_MIN_BIT_STRENGTH)
|
||||
self.assertEqual(len(password.split(' ')), 10)
|
||||
|
||||
@mock.patch('time.sleep', return_value=None)
|
||||
def test_invalid_data_in_db_raises_critical_error(self, _):
|
||||
for delta in [-1, 1]:
|
||||
|
@ -60,6 +71,8 @@ class TestMasterKey(unittest.TestCase):
|
|||
|
||||
@mock.patch('src.common.db_masterkey.MIN_KEY_DERIVATION_TIME', 0.01)
|
||||
@mock.patch('src.common.db_masterkey.MAX_KEY_DERIVATION_TIME', 0.1)
|
||||
@mock.patch('os.popen', return_value=MagicMock(
|
||||
read=MagicMock(return_value=MagicMock(splitlines=MagicMock(return_value=["MemAvailable 10240"])))))
|
||||
@mock.patch('os.path.isfile', side_effect=[KeyboardInterrupt, False, True])
|
||||
@mock.patch('getpass.getpass', side_effect=input_list)
|
||||
@mock.patch('time.sleep', return_value=None)
|
||||
|
@ -75,6 +88,18 @@ class TestMasterKey(unittest.TestCase):
|
|||
self.assertIsInstance(master_key2.master_key, bytes)
|
||||
self.assertEqual(master_key.master_key, master_key2.master_key)
|
||||
|
||||
@mock.patch('src.common.db_masterkey.MIN_KEY_DERIVATION_TIME', 0.01)
|
||||
@mock.patch('src.common.db_masterkey.MAX_KEY_DERIVATION_TIME', 0.1)
|
||||
@mock.patch('os.popen', return_value=MagicMock(
|
||||
read=MagicMock(return_value=MagicMock(splitlines=MagicMock(return_value=["MemAvailable 10240"])))))
|
||||
@mock.patch('getpass.getpass', side_effect=['generate'])
|
||||
@mock.patch('builtins.input', side_effect=[''])
|
||||
@mock.patch('os.system', return_value=None)
|
||||
@mock.patch('time.sleep', return_value=None)
|
||||
def test_password_generation(self, *_):
|
||||
master_key = MasterKey(self.operation, local_test=True)
|
||||
self.assertIsInstance(master_key.master_key, bytes)
|
||||
|
||||
@mock.patch('src.common.db_masterkey.MasterKey.timed_key_derivation',
|
||||
MagicMock(side_effect= [(KL*b'a', 0.01)]
|
||||
+ 100 * [(KL*b'b', 5.0)]
|
||||
|
@ -83,7 +108,7 @@ class TestMasterKey(unittest.TestCase):
|
|||
@mock.patch('os.path.isfile', side_effect=[False, True])
|
||||
@mock.patch('getpass.getpass', side_effect=input_list)
|
||||
@mock.patch('time.sleep', return_value=None)
|
||||
def test_kd_binary_serach(self, *_):
|
||||
def test_kd_binary_search(self, *_):
|
||||
MasterKey(self.operation, local_test=True)
|
||||
|
||||
|
||||
|
|
|
@ -27,7 +27,8 @@ from unittest import mock
|
|||
from src.common.crypto import encrypt_and_sign
|
||||
from src.common.db_onion import OnionService
|
||||
from src.common.misc import ensure_dir, validate_onion_addr
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (DIR_USER_DATA, ONION_SERVICE_PRIVATE_KEY_LENGTH,
|
||||
POLY1305_TAG_LENGTH, TX, XCHACHA20_NONCE_LENGTH)
|
||||
|
||||
from tests.mock_classes import MasterKey
|
||||
from tests.utils import cd_unit_test, cleanup, tamper_file
|
||||
|
@ -36,11 +37,13 @@ from tests.utils import cd_unit_test, cleanup, tamper_file
|
|||
class TestOnionService(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.master_key = MasterKey()
|
||||
self.file_name = f"{DIR_USER_DATA}{TX}_onion_db"
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
|
||||
@mock.patch('time.sleep', return_value=None)
|
||||
|
@ -67,7 +70,7 @@ class TestOnionService(unittest.TestCase):
|
|||
@mock.patch('time.sleep', return_value=None)
|
||||
def test_loading_invalid_onion_key_raises_critical_error(self, _):
|
||||
# Setup
|
||||
ct_bytes = encrypt_and_sign((ONION_SERVICE_PRIVATE_KEY_LENGTH +1) * b'a', self.master_key.master_key)
|
||||
ct_bytes = encrypt_and_sign((ONION_SERVICE_PRIVATE_KEY_LENGTH + 1) * b'a', self.master_key.master_key)
|
||||
|
||||
ensure_dir(DIR_USER_DATA)
|
||||
with open(f'{DIR_USER_DATA}{TX}_onion_db', 'wb+') as f:
|
||||
|
|
|
@ -25,7 +25,7 @@ import unittest
|
|||
from unittest import mock
|
||||
|
||||
from src.common.db_settings import Settings
|
||||
from src.common.statics import *
|
||||
from src.common.statics import CLEAR_ENTIRE_SCREEN, CURSOR_LEFT_UP_CORNER, DIR_USER_DATA, RX, SETTING_LENGTH, TX
|
||||
|
||||
from tests.mock_classes import ContactList, create_group, GroupList, MasterKey
|
||||
from tests.utils import cd_unit_test, cleanup, tamper_file, TFCTestCase
|
||||
|
@ -34,6 +34,7 @@ from tests.utils import cd_unit_test, cleanup, tamper_file, TFCTestCase
|
|||
class TestSettings(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.file_name = f"{DIR_USER_DATA}{TX}_settings"
|
||||
self.master_key = MasterKey()
|
||||
|
@ -44,6 +45,7 @@ class TestSettings(TFCTestCase):
|
|||
self.args = self.contact_list, self.group_list
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
|
||||
def test_invalid_type_raises_critical_error_on_store(self):
|
||||
|
@ -99,15 +101,15 @@ class TestSettings(TFCTestCase):
|
|||
self.assertIsNone(self.settings.change_setting('traffic_masking', 'True', *self.args))
|
||||
|
||||
def test_change_settings(self):
|
||||
self.assert_fr("Error: Invalid value 'Falsee'.",
|
||||
self.assert_fr("Error: Invalid setting value 'Falsee'.",
|
||||
self.settings.change_setting, 'disable_gui_dialog', 'Falsee', *self.args)
|
||||
self.assert_fr("Error: Invalid value '1.1'.",
|
||||
self.assert_fr("Error: Invalid setting value '1.1'.",
|
||||
self.settings.change_setting, 'max_number_of_group_members', '1.1', *self.args)
|
||||
self.assert_fr("Error: Invalid value '18446744073709551616'.",
|
||||
self.assert_fr("Error: Invalid setting value '18446744073709551616'.",
|
||||
self.settings.change_setting, 'max_number_of_contacts', str(2 ** 64), *self.args)
|
||||
self.assert_fr("Error: Invalid value '-1.1'.",
|
||||
self.assert_fr("Error: Invalid setting value '-1.1'.",
|
||||
self.settings.change_setting, 'tm_static_delay', '-1.1', *self.args)
|
||||
self.assert_fr("Error: Invalid value 'True'.",
|
||||
self.assert_fr("Error: Invalid setting value 'True'.",
|
||||
self.settings.change_setting, 'tm_static_delay', 'True', *self.args)
|
||||
|
||||
self.assertIsNone(self.settings.change_setting('traffic_masking', 'True', *self.args))
|
||||
|
|
|
@ -29,12 +29,15 @@ from src.common.encoding import b58encode, bool_to_bytes, double_to_bytes, str_t
|
|||
from src.common.encoding import b58decode, bytes_to_bool, bytes_to_double, bytes_to_str, bytes_to_int
|
||||
from src.common.encoding import onion_address_to_pub_key, unicode_padding, pub_key_to_short_address, b85encode
|
||||
from src.common.encoding import pub_key_to_onion_address, rm_padding_str, bytes_to_timestamp, b10encode
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (ENCODED_BOOLEAN_LENGTH, ENCODED_FLOAT_LENGTH, ENCODED_INTEGER_LENGTH,
|
||||
FINGERPRINT_LENGTH, ONION_SERVICE_PUBLIC_KEY_LENGTH, PADDED_UTF32_STR_LENGTH,
|
||||
PADDING_LENGTH, SYMMETRIC_KEY_LENGTH, TFC_PUBLIC_KEY_LENGTH, TRUNC_ADDRESS_LENGTH)
|
||||
|
||||
|
||||
class TestBase58EncodeAndDecode(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.key = SYMMETRIC_KEY_LENGTH * b'\x01'
|
||||
|
||||
def test_encoding_and_decoding_of_random_local_keys(self):
|
||||
|
@ -74,7 +77,7 @@ class TestBase58EncodeAndDecode(unittest.TestCase):
|
|||
byte_key = bytes.fromhex("0C28FCA386C7A227600B2FE50B7CAE11"
|
||||
"EC86D3BF1FBE471BE89827E19D72AA1D")
|
||||
|
||||
b58_key = "5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ"
|
||||
b58_key = "5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ"
|
||||
|
||||
self.assertEqual(b58encode(byte_key), b58_key)
|
||||
self.assertEqual(b58decode(b58_key), byte_key)
|
||||
|
|
|
@ -33,7 +33,7 @@ from src.common.crypto import blake2b
|
|||
from src.common.gateway import gateway_loop, Gateway, GatewaySettings
|
||||
from src.common.misc import ensure_dir
|
||||
from src.common.reed_solomon import RSCodec
|
||||
from src.common.statics import *
|
||||
from src.common.statics import DIR_USER_DATA, GATEWAY_QUEUE, NC, PACKET_CHECKSUM_LENGTH, RX, TX
|
||||
|
||||
from tests.mock_classes import Settings
|
||||
from tests.utils import cd_unit_test, cleanup, gen_queue_dict, tear_queues, TFCTestCase
|
||||
|
@ -42,10 +42,12 @@ from tests.utils import cd_unit_test, cleanup, gen_queue_dict, tear_queue
|
|||
class TestGatewayLoop(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.queues = gen_queue_dict()
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
tear_queues(self.queues)
|
||||
|
||||
|
@ -63,10 +65,12 @@ class TestGatewayLoop(unittest.TestCase):
|
|||
class TestGatewaySerial(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.settings = Settings(session_usb_serial_adapter=True)
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
|
||||
@mock.patch('time.sleep', return_value=None)
|
||||
|
@ -239,6 +243,7 @@ class TestGatewaySerial(TFCTestCase):
|
|||
class TestGatewaySettings(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.default_serialized = """\
|
||||
{
|
||||
|
@ -249,6 +254,7 @@ class TestGatewaySettings(TFCTestCase):
|
|||
}"""
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
|
||||
@mock.patch('os.listdir', side_effect=[['ttyUSB0'], ['ttyS0'], ['ttyUSB0'], ['ttyS0']])
|
||||
|
@ -462,13 +468,13 @@ class TestGatewaySettings(TFCTestCase):
|
|||
@mock.patch('time.sleep', return_value=None)
|
||||
def test_change_setting(self, _):
|
||||
settings = GatewaySettings(operation=TX, local_test=True, dd_sockets=True)
|
||||
self.assert_fr("Error: Invalid value 'Falsee'.",
|
||||
self.assert_fr("Error: Invalid setting value 'Falsee'.",
|
||||
settings.change_setting, 'serial_baudrate', 'Falsee')
|
||||
self.assert_fr("Error: Invalid value '1.1'.",
|
||||
self.assert_fr("Error: Invalid setting value '1.1'.",
|
||||
settings.change_setting, 'serial_baudrate', '1.1', )
|
||||
self.assert_fr("Error: Invalid value '18446744073709551616'.",
|
||||
self.assert_fr("Error: Invalid setting value '18446744073709551616'.",
|
||||
settings.change_setting, 'serial_baudrate', str(2 ** 64))
|
||||
self.assert_fr("Error: Invalid value 'Falsee'.",
|
||||
self.assert_fr("Error: Invalid setting value 'Falsee'.",
|
||||
settings.change_setting, 'use_serial_usb_adapter', 'Falsee')
|
||||
|
||||
self.assertIsNone(settings.change_setting('serial_baudrate', '9600'))
|
||||
|
|
|
@ -24,7 +24,8 @@ import unittest
|
|||
from unittest import mock
|
||||
|
||||
from src.common.input import ask_confirmation_code, box_input, get_b58_key, nc_bypass_msg, pwd_prompt, yes
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (B58_LOCAL_KEY, B58_PUBLIC_KEY, NC_BYPASS_START, NC_BYPASS_STOP, SYMMETRIC_KEY_LENGTH,
|
||||
TFC_PUBLIC_KEY_LENGTH)
|
||||
|
||||
from tests.mock_classes import Settings
|
||||
from tests.utils import nick_to_short_address, VALID_ECDHE_PUB_KEY, VALID_LOCAL_KEY_KDK
|
||||
|
@ -53,6 +54,7 @@ class TestBoxInput(unittest.TestCase):
|
|||
class TestGetB58Key(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.settings = Settings()
|
||||
|
||||
@mock.patch('time.sleep', return_value=None)
|
||||
|
|
|
@ -35,7 +35,8 @@ from src.common.misc import get_tab_completer, get_terminal_height, get_termi
|
|||
from src.common.misc import process_arguments, readable_size, round_up, separate_header, separate_headers
|
||||
from src.common.misc import separate_trailer, split_string, split_byte_string, terminal_width_check
|
||||
from src.common.misc import validate_group_name, validate_key_exchange, validate_onion_addr, validate_nick
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (DIR_RECV_FILES, DIR_USER_DATA, DUMMY_GROUP, ECDHE, EXIT, EXIT_QUEUE, LOCAL_ID,
|
||||
PADDING_LENGTH, RX, TAILS, WIPE)
|
||||
|
||||
from tests.mock_classes import ContactList, Gateway, GroupList, Settings
|
||||
from tests.utils import cd_unit_test, cleanup, gen_queue_dict, ignored, nick_to_onion_address
|
||||
|
@ -45,6 +46,7 @@ from tests.utils import nick_to_pub_key, tear_queues, TFCTestCase
|
|||
class TestCalculateRaceConditionDelay(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.settings = Settings()
|
||||
|
||||
def test_race_condition_delay_calculation(self):
|
||||
|
@ -54,6 +56,7 @@ class TestCalculateRaceConditionDelay(unittest.TestCase):
|
|||
class TestDecompress(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.settings = Settings()
|
||||
self.settings.max_decompress_size = 1000
|
||||
|
||||
|
@ -78,6 +81,7 @@ class TestDecompress(TFCTestCase):
|
|||
class TestEnsureDir(unittest.TestCase):
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
with ignored(OSError):
|
||||
os.rmdir('test_dir/')
|
||||
|
||||
|
@ -90,6 +94,7 @@ class TestEnsureDir(unittest.TestCase):
|
|||
class TestTabCompleteList(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.contact_list = ContactList(nicks=['Alice', 'Bob'])
|
||||
self.group_list = GroupList(groups=['test_group'])
|
||||
self.settings = Settings(key_list=['key1', 'key2'])
|
||||
|
@ -145,10 +150,12 @@ class TestIgnored(unittest.TestCase):
|
|||
class TestMonitorProcesses(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.settings = Settings()
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
|
||||
@staticmethod
|
||||
|
@ -218,13 +225,13 @@ class TestMonitorProcesses(TFCTestCase):
|
|||
monitor_processes(process_list, RX, queues)
|
||||
self.assertFalse(os.path.isdir(DIR_USER_DATA))
|
||||
self.assertFalse(os.path.isdir(DIR_RECV_FILES))
|
||||
mock_os_system.assert_called_with('poweroff')
|
||||
mock_os_system.assert_called_with('systemctl poweroff')
|
||||
|
||||
tear_queues(queues)
|
||||
|
||||
@mock.patch('time.sleep', return_value=None)
|
||||
@mock.patch('os.system', return_value=None)
|
||||
@mock.patch('subprocess.check_output', lambda *popenargs, timeout=None, **kwargs: TAILS)
|
||||
@mock.patch('builtins.open', mock.mock_open(read_data=TAILS))
|
||||
def test_wipe_tails(self, mock_os_system, *_):
|
||||
queues = gen_queue_dict()
|
||||
process_list = [Process(target=self.mock_process)]
|
||||
|
@ -244,7 +251,7 @@ class TestMonitorProcesses(TFCTestCase):
|
|||
with self.assertRaises(SystemExit):
|
||||
monitor_processes(process_list, RX, queues)
|
||||
|
||||
mock_os_system.assert_called_with('poweroff')
|
||||
mock_os_system.assert_called_with('systemctl poweroff')
|
||||
|
||||
# Test that user data wasn't removed
|
||||
self.assertTrue(os.path.isdir(DIR_USER_DATA))
|
||||
|
@ -254,6 +261,8 @@ class TestMonitorProcesses(TFCTestCase):
|
|||
class TestProcessArguments(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
|
||||
class MockParser(object):
|
||||
"""MockParse object."""
|
||||
def __init__(self, *_, **__):
|
||||
|
@ -280,6 +289,7 @@ class TestProcessArguments(unittest.TestCase):
|
|||
argparse.ArgumentParser = MockParser
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
argparse.ArgumentParser = self.o_argparse
|
||||
|
||||
def test_process_arguments(self):
|
||||
|
@ -429,6 +439,7 @@ class TestValidateOnionAddr(unittest.TestCase):
|
|||
class TestValidateGroupName(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.contact_list = ContactList(nicks=['Alice'])
|
||||
self.group_list = GroupList(groups=['test_group'])
|
||||
|
||||
|
@ -467,6 +478,7 @@ class TestValidateKeyExchange(unittest.TestCase):
|
|||
class TestValidateNick(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.contact_list = ContactList(nicks=['Alice', 'Bob'])
|
||||
self.group_list = GroupList(groups=['test_group'])
|
||||
|
||||
|
|
|
@ -26,7 +26,10 @@ from unittest import mock
|
|||
|
||||
from src.common.output import clear_screen, group_management_print, m_print, phase, print_fingerprint, print_key
|
||||
from src.common.output import print_title, print_on_previous_line, print_spacing, rp_print
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (ADDED_MEMBERS, ALREADY_MEMBER, BOLD_ON, CLEAR_ENTIRE_LINE, CLEAR_ENTIRE_SCREEN,
|
||||
CURSOR_LEFT_UP_CORNER, CURSOR_UP_ONE_LINE, DONE, FINGERPRINT_LENGTH, NEW_GROUP,
|
||||
NORMAL_TEXT, NOT_IN_GROUP, REMOVED_MEMBERS, RX, SYMMETRIC_KEY_LENGTH, TX,
|
||||
UNKNOWN_ACCOUNTS, VERSION)
|
||||
|
||||
from tests.mock_classes import ContactList, nick_to_pub_key, Settings
|
||||
from tests.utils import TFCTestCase
|
||||
|
@ -41,6 +44,7 @@ class TestClearScreen(TFCTestCase):
|
|||
class TestGroupManagementPrint(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.contact_list = ContactList(nicks=['Alice'])
|
||||
self.lines = [nick_to_pub_key('Alice'), nick_to_pub_key('Bob')]
|
||||
self.group_name = 'test_group'
|
||||
|
@ -238,6 +242,7 @@ class TestPrintFingerprint(TFCTestCase):
|
|||
class TestPrintKey(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.settings = Settings()
|
||||
|
||||
def test_print_kdk(self):
|
||||
|
@ -294,6 +299,7 @@ class TestPrintSpacing(TFCTestCase):
|
|||
class TestRPPrint(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.ts = datetime.now()
|
||||
self.timestamp = self.ts.strftime("%b %d - %H:%M:%S.%f")[:-4]
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ class TestAskPathGui(TFCTestCase):
|
|||
path = '/home/user/'
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.settings = Settings()
|
||||
|
||||
@mock.patch('os.path.isfile', return_value=True)
|
||||
|
@ -78,6 +79,7 @@ class TestAskPathGui(TFCTestCase):
|
|||
class TestCompleter(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.cwd = os.getcwd()
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
|
||||
|
@ -93,6 +95,7 @@ class TestCompleter(unittest.TestCase):
|
|||
os.chdir('..')
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
os.chdir(self.cwd)
|
||||
|
||||
|
@ -116,10 +119,12 @@ class TestCompleter(unittest.TestCase):
|
|||
class TestPath(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
with ignored(FileExistsError):
|
||||
os.mkdir('test_dir/')
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
with ignored(OSError):
|
||||
os.remove('testfile')
|
||||
with ignored(OSError):
|
||||
|
|
|
@ -16,7 +16,10 @@ import unittest
|
|||
|
||||
from random import sample
|
||||
|
||||
from src.common.reed_solomon import *
|
||||
from src.common.reed_solomon import (RSCodec, ReedSolomonError, find_prime_polys, gf_add, gf_div, gf_mul, gf_mult_nolut,
|
||||
gf_mult_nolut_slow, gf_neg, gf_poly_mul, gf_poly_mul_simple, gf_poly_neg, gf_sub,
|
||||
init_tables, itertools, rs_check, rs_correct_msg, rs_correct_msg_nofsynd,
|
||||
rs_encode_msg, rs_generator_poly, rs_generator_poly_all, rs_simple_encode_msg)
|
||||
|
||||
|
||||
class TestReedSolomon(unittest.TestCase):
|
||||
|
@ -75,8 +78,8 @@ class TestReedSolomon(unittest.TestCase):
|
|||
kk = 18
|
||||
tt = nn - kk
|
||||
rs = RSCodec(tt, fcr=120, prim=0x187)
|
||||
hexencmsg = '00faa123555555c000000354064432' \
|
||||
'c02800fe97c434e1ff5365cf8fafe4'
|
||||
hexencmsg = ('00faa123555555c000000354064432'
|
||||
'c02800fe97c434e1ff5365cf8fafe4')
|
||||
strf = str
|
||||
encmsg = bytearray.fromhex(strf(hexencmsg))
|
||||
decmsg = encmsg[:kk]
|
||||
|
@ -108,8 +111,8 @@ class TestReedSolomon(unittest.TestCase):
|
|||
kk = 34
|
||||
tt = nn - kk
|
||||
rs = RSCodec(tt, fcr=120, prim=0x187)
|
||||
hexencmsg = '08faa123555555c000000354064432c0280e1b4d090cfc04' \
|
||||
'887400000003500000000e1985ff9c6b33066ca9f43d12e8'
|
||||
hexencmsg = ('08faa123555555c000000354064432c0280e1b4d090cfc04'
|
||||
'887400000003500000000e1985ff9c6b33066ca9f43d12e8')
|
||||
strf = str
|
||||
encmsg = bytearray.fromhex(strf(hexencmsg))
|
||||
decmsg = encmsg[:kk]
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
#!/usr/bin/env python3.7
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
TFC - Onion-routed, endpoint secure messaging system
|
||||
Copyright (C) 2013-2019 Markus Ottela
|
||||
|
||||
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.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with TFC. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
|
||||
from src.common.word_list import eff_wordlist
|
||||
|
||||
|
||||
class TestWordList(unittest.TestCase):
|
||||
|
||||
def test_each_word_is_unique(self):
|
||||
self.assertEqual(len(eff_wordlist),
|
||||
len(set(eff_wordlist)))
|
||||
|
||||
def test_word_list_length(self):
|
||||
self.assertEqual(len(eff_wordlist),
|
||||
7776)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
|
@ -41,7 +41,9 @@ from src.common.db_settings import Settings as OrigSettings
|
|||
from src.common.encoding import pub_key_to_onion_address, pub_key_to_short_address
|
||||
from src.common.misc import calculate_race_condition_delay
|
||||
from src.common.reed_solomon import RSCodec
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (DIR_USER_DATA, FINGERPRINT_LENGTH, INITIAL_HARAC, KEX_STATUS_VERIFIED, LOCAL_ID,
|
||||
LOCAL_NICK, LOCAL_PUBKEY, ONION_SERVICE_PRIVATE_KEY_LENGTH, SYMMETRIC_KEY_LENGTH,
|
||||
TX, WIN_TYPE_GROUP, WIN_UID_LOCAL)
|
||||
|
||||
from src.transmitter.windows import TxWindow as OrigTxWindow
|
||||
|
||||
|
@ -209,11 +211,13 @@ class MasterKey(OrigMasterKey):
|
|||
setattr(self, key, value)
|
||||
|
||||
def load_master_key(self) -> bytes:
|
||||
"""Create mock master key bytes."""
|
||||
if getpass.getpass() == 'test_password':
|
||||
return self.master_key
|
||||
else:
|
||||
return SYMMETRIC_KEY_LENGTH * b'f'
|
||||
|
||||
|
||||
class OnionService(OrigOnionService):
|
||||
"""Mock the object for unit testing."""
|
||||
|
||||
|
|
|
@ -30,7 +30,10 @@ from unittest.mock import MagicMock
|
|||
|
||||
from src.common.db_logs import write_log_entry
|
||||
from src.common.encoding import int_to_bytes
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (CH_FILE_RECV, CH_LOGGING, CH_NOTIFY, CLEAR_ENTIRE_LINE, COMMAND, CURSOR_UP_ONE_LINE,
|
||||
C_L_HEADER, DISABLE, ENABLE, F_S_HEADER, LOCAL_ID, LOCAL_PUBKEY, LOG_REMOVE, MESSAGE,
|
||||
ORIGIN_CONTACT_HEADER, PADDING_LENGTH, RESET, RX, SYMMETRIC_KEY_LENGTH, US_BYTE,
|
||||
WIN_TYPE_CONTACT, WIN_TYPE_GROUP, WIN_UID_FILE, WIPE)
|
||||
|
||||
from src.receiver.packet import PacketList
|
||||
from src.receiver.commands import ch_contact_s, ch_master_key, ch_nick, ch_setting, contact_rem, exit_tfc, log_command
|
||||
|
@ -38,13 +41,14 @@ from src.receiver.commands import process_command, remove_log, reset_screen, win
|
|||
|
||||
from tests.mock_classes import ContactList, Gateway, group_name_to_group_id, GroupList, KeyList, MasterKey
|
||||
from tests.mock_classes import nick_to_pub_key, RxWindow, Settings, WindowList
|
||||
from tests.utils import assembly_packet_creator, cd_unit_test, cleanup, ignored, nick_to_short_address, tear_queue
|
||||
from tests.utils import TFCTestCase
|
||||
from tests.utils import assembly_packet_creator, cd_unit_test, cleanup, ignored, nick_to_short_address
|
||||
from tests.utils import tear_queue, TFCTestCase
|
||||
|
||||
|
||||
class TestProcessCommand(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.ts = datetime.now()
|
||||
self.settings = Settings()
|
||||
|
@ -62,6 +66,7 @@ class TestProcessCommand(TFCTestCase):
|
|||
self.settings, self.master_key, self.gateway, self.exit_queue)
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
tear_queue(self.exit_queue)
|
||||
|
||||
|
@ -81,6 +86,7 @@ class TestProcessCommand(TFCTestCase):
|
|||
class TestWinActivity(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.window_list = WindowList()
|
||||
self.window_list.windows = [RxWindow(name='Alice', unread_messages=4),
|
||||
RxWindow(name='Bob', unread_messages=15)]
|
||||
|
@ -99,6 +105,7 @@ class TestWinActivity(TFCTestCase):
|
|||
class TestWinSelect(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.window_list = WindowList()
|
||||
self.window_list.windows = [RxWindow(uid=nick_to_pub_key("Alice"), name='Alice'),
|
||||
RxWindow(uid=nick_to_pub_key("Bob"), name='Bob')]
|
||||
|
@ -117,6 +124,7 @@ class TestWinSelect(unittest.TestCase):
|
|||
class TestResetScreen(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.cmd_data = nick_to_pub_key("Alice")
|
||||
self.window_list = WindowList()
|
||||
self.window_list.windows = [RxWindow(uid=nick_to_pub_key("Alice"), name='Alice'),
|
||||
|
@ -141,9 +149,11 @@ class TestResetScreen(unittest.TestCase):
|
|||
class TestExitTFC(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.exit_queue = Queue()
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
tear_queue(self.exit_queue)
|
||||
|
||||
def test_function(self):
|
||||
|
@ -154,6 +164,7 @@ class TestExitTFC(unittest.TestCase):
|
|||
class TestLogCommand(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.cmd_data = int_to_bytes(1) + nick_to_pub_key("Bob")
|
||||
self.ts = datetime.now()
|
||||
|
@ -173,6 +184,7 @@ class TestLogCommand(TFCTestCase):
|
|||
self.time = datetime.fromtimestamp(time_float).strftime("%H:%M:%S.%f")[:-4]
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
with ignored(OSError):
|
||||
os.remove('Receiver - Plaintext log (None)')
|
||||
|
@ -204,6 +216,7 @@ Log file of 1 most recent message(s) to/from contact Bob
|
|||
class TestRemoveLog(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.win_name = nick_to_pub_key("Alice")
|
||||
self.contact_list = ContactList()
|
||||
|
@ -212,6 +225,7 @@ class TestRemoveLog(TFCTestCase):
|
|||
self.master_key = MasterKey()
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
|
||||
def test_remove_log_file(self):
|
||||
|
@ -222,6 +236,7 @@ class TestRemoveLog(TFCTestCase):
|
|||
class TestChMasterKey(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.ts = datetime.now()
|
||||
self.master_key = MasterKey()
|
||||
|
@ -234,11 +249,13 @@ class TestChMasterKey(TFCTestCase):
|
|||
self.key_list, self.settings, self.master_key)
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
|
||||
@mock.patch('src.common.db_masterkey.MIN_KEY_DERIVATION_TIME', 0.1)
|
||||
@mock.patch('src.common.db_masterkey.MIN_KEY_DERIVATION_TIME', 1.0)
|
||||
@mock.patch('os.popen', return_value=MagicMock(read=MagicMock(return_value=MagicMock(splitlines=MagicMock(return_value=["MemFree 10240"])))))
|
||||
@mock.patch('os.popen', return_value=MagicMock(
|
||||
read=MagicMock(return_value=MagicMock(splitlines=MagicMock(return_value=["MemAvailable 10240"])))))
|
||||
@mock.patch('multiprocessing.cpu_count', return_value=1)
|
||||
@mock.patch('getpass.getpass', return_value='a')
|
||||
@mock.patch('time.sleep', return_value=None)
|
||||
|
@ -261,6 +278,7 @@ class TestChMasterKey(TFCTestCase):
|
|||
class TestChNick(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.ts = datetime.now()
|
||||
self.contact_list = ContactList(nicks=['Alice'])
|
||||
self.window_list = WindowList(contact_list=self.contact_list)
|
||||
|
@ -290,6 +308,7 @@ class TestChNick(TFCTestCase):
|
|||
class TestChSetting(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.ts = datetime.now()
|
||||
self.window_list = WindowList()
|
||||
self.contact_list = ContactList()
|
||||
|
@ -339,6 +358,7 @@ class TestChSetting(TFCTestCase):
|
|||
class TestChContactSetting(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.ts = datetime.fromtimestamp(1502750000)
|
||||
self.contact_list = ContactList(nicks=['Alice', 'Bob'])
|
||||
self.group_list = GroupList(groups=['test_group', 'test_group2'])
|
||||
|
@ -425,6 +445,7 @@ class TestChContactSetting(TFCTestCase):
|
|||
class TestContactRemove(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.ts = datetime.now()
|
||||
self.window_list = WindowList()
|
||||
|
@ -434,6 +455,7 @@ class TestContactRemove(TFCTestCase):
|
|||
self.args = self.cmd_data, self.ts, self.window_list
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
|
||||
def test_no_contact_raises_fr(self):
|
||||
|
@ -466,9 +488,11 @@ class TestContactRemove(TFCTestCase):
|
|||
class TestWipe(unittest.TestCase):
|
||||
|
||||
def setUp(self) -> None:
|
||||
"""Pre-test actions."""
|
||||
self.exit_queue = Queue()
|
||||
|
||||
def tearDown(self) -> None:
|
||||
"""Post-test actions."""
|
||||
tear_queue(self.exit_queue)
|
||||
|
||||
@mock.patch('os.system', return_value=None)
|
||||
|
|
|
@ -22,7 +22,8 @@ along with TFC. If not, see <https://www.gnu.org/licenses/>.
|
|||
import datetime
|
||||
import unittest
|
||||
|
||||
from src.common.statics import *
|
||||
from src.common.statics import US_BYTE
|
||||
|
||||
from src.receiver.commands_g import group_add, group_create, group_delete, group_remove, group_rename
|
||||
|
||||
from tests.mock_classes import Contact, ContactList, GroupList, RxWindow, Settings, WindowList
|
||||
|
@ -32,6 +33,7 @@ from tests.utils import group_name_to_group_id, nick_to_pub_key, TFCTestC
|
|||
class TestGroupCreate(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.ts = datetime.datetime.now()
|
||||
self.settings = Settings()
|
||||
self.window_list = WindowList()
|
||||
|
@ -78,6 +80,7 @@ class TestGroupCreate(TFCTestCase):
|
|||
class TestGroupAdd(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.ts = datetime.datetime.now()
|
||||
self.settings = Settings()
|
||||
self.window_list = WindowList()
|
||||
|
@ -125,6 +128,7 @@ class TestGroupAdd(TFCTestCase):
|
|||
class TestGroupRemove(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.ts = datetime.datetime.now()
|
||||
self.window_list = WindowList()
|
||||
self.contact_list = ContactList(nicks=[f"contact_{n}" for n in range(21)])
|
||||
|
@ -152,6 +156,7 @@ class TestGroupRemove(TFCTestCase):
|
|||
class TestGroupDelete(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.ts = datetime.datetime.now()
|
||||
self.window_list = WindowList()
|
||||
self.group_list = GroupList(groups=['test_group'])
|
||||
|
@ -179,6 +184,7 @@ class TestGroupDelete(TFCTestCase):
|
|||
class TestGroupRename(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.ts = datetime.datetime.now()
|
||||
self.group_list = GroupList(groups=['test_group'])
|
||||
self.window_list = WindowList()
|
||||
|
@ -186,7 +192,7 @@ class TestGroupRename(TFCTestCase):
|
|||
self.window_list.windows = [self.window]
|
||||
self.contact_list = ContactList(nicks=['alice'])
|
||||
self.args = self.ts, self.window_list, self.contact_list, self.group_list
|
||||
|
||||
|
||||
def test_missing_group_id_raises_fr(self):
|
||||
# Setup
|
||||
cmd_data = group_name_to_group_id('test_group2') + b'new_name'
|
||||
|
|
|
@ -28,7 +28,7 @@ from unittest import mock
|
|||
|
||||
from src.common.crypto import blake2b, encrypt_and_sign
|
||||
from src.common.encoding import str_to_bytes
|
||||
from src.common.statics import *
|
||||
from src.common.statics import COMPRESSION_LEVEL, DIR_RECV_FILES, ORIGIN_CONTACT_HEADER, SYMMETRIC_KEY_LENGTH, US_BYTE
|
||||
|
||||
from src.receiver.files import new_file, process_assembled_file, process_file, store_unique
|
||||
|
||||
|
@ -39,12 +39,14 @@ from tests.utils import cd_unit_test, cleanup, nick_to_pub_key, TFCTestCa
|
|||
class TestStoreUnique(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.file_data = os.urandom(100)
|
||||
self.file_dir = 'test_dir/'
|
||||
self.file_name = 'test_file'
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
|
||||
def test_each_file_is_store_with_unique_name(self):
|
||||
|
@ -56,16 +58,18 @@ class TestStoreUnique(unittest.TestCase):
|
|||
class ProcessAssembledFile(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.ts = datetime.now()
|
||||
self.onion_pub_key = nick_to_pub_key('Alice')
|
||||
self.nick = 'Alice'
|
||||
self.settings = Settings()
|
||||
self.window_list = WindowList(nick=['Alice', 'Bob'])
|
||||
self.key = os.urandom(SYMMETRIC_KEY_LENGTH)
|
||||
self.args = self.onion_pub_key, self.nick, self.settings, self.window_list
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.ts = datetime.now()
|
||||
self.onion_pub_key = nick_to_pub_key('Alice')
|
||||
self.nick = 'Alice'
|
||||
self.settings = Settings()
|
||||
self.window_list = WindowList(nick=['Alice', 'Bob'])
|
||||
self.key = os.urandom(SYMMETRIC_KEY_LENGTH)
|
||||
self.args = self.onion_pub_key, self.nick, self.settings, self.window_list
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
|
||||
def test_invalid_structure_raises_fr(self):
|
||||
|
@ -156,6 +160,7 @@ class ProcessAssembledFile(TFCTestCase):
|
|||
class TestNewFile(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.ts = datetime.now()
|
||||
self.packet = b''
|
||||
|
@ -169,6 +174,7 @@ class TestNewFile(TFCTestCase):
|
|||
self.args = self.file_keys, self.file_buf, self.contact_list, self.window_list, self.settings
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
|
||||
def test_unknown_account_raises_fr(self):
|
||||
|
@ -215,6 +221,7 @@ class TestNewFile(TFCTestCase):
|
|||
class TestProcessFile(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.ts = datetime.now()
|
||||
self.account = nick_to_pub_key('Alice')
|
||||
|
@ -226,6 +233,7 @@ class TestProcessFile(TFCTestCase):
|
|||
self.args = self.file_key, self.contact_list, self.window_list, self.settings
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
|
||||
def test_invalid_key_raises_fr(self):
|
||||
|
|
|
@ -32,7 +32,9 @@ from unittest.mock import MagicMock
|
|||
from src.common.crypto import argon2_kdf, encrypt_and_sign
|
||||
from src.common.encoding import b58encode, str_to_bytes
|
||||
from src.common.exceptions import FunctionReturn
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (ARGON2_SALT_LENGTH, BOLD_ON, CLEAR_ENTIRE_SCREEN, CONFIRM_CODE_LENGTH,
|
||||
CURSOR_LEFT_UP_CORNER, FINGERPRINT_LENGTH, LOCAL_ID, NORMAL_TEXT, PSK_FILE_SIZE,
|
||||
SYMMETRIC_KEY_LENGTH, WIN_TYPE_CONTACT, WIN_UID_LOCAL, XCHACHA20_NONCE_LENGTH)
|
||||
|
||||
from src.receiver.key_exchanges import key_ex_ecdhe, key_ex_psk_rx, key_ex_psk_tx, local_key_rdy, process_local_key
|
||||
|
||||
|
@ -47,6 +49,7 @@ class TestProcessLocalKey(TFCTestCase):
|
|||
new_kek = os.urandom(SYMMETRIC_KEY_LENGTH)
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.contact_list = ContactList(nicks=[LOCAL_ID, 'Alice'])
|
||||
self.key_list = KeyList( nicks=[LOCAL_ID, 'Alice'])
|
||||
self.window_list = WindowList( nicks=[LOCAL_ID, 'Alice'])
|
||||
|
@ -59,10 +62,11 @@ class TestProcessLocalKey(TFCTestCase):
|
|||
self.hek = os.urandom(SYMMETRIC_KEY_LENGTH)
|
||||
self.conf_code = os.urandom(CONFIRM_CODE_LENGTH)
|
||||
self.packet = encrypt_and_sign(self.key + self.hek + self.conf_code, key=self.kek)
|
||||
self.args = (self.window_list, self.contact_list, self.key_list, self.settings,
|
||||
self.args = (self.window_list, self.contact_list, self.key_list, self.settings,
|
||||
self.kdk_hashes, self.packet_hashes, self.l_queue)
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
tear_queue(self.l_queue)
|
||||
|
||||
@mock.patch('tkinter.Tk', return_value=MagicMock())
|
||||
|
@ -150,6 +154,7 @@ class TestProcessLocalKey(TFCTestCase):
|
|||
class TestLocalKeyRdy(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.ts = datetime.fromtimestamp(1502750000)
|
||||
|
||||
@mock.patch('time.sleep', return_value=None)
|
||||
|
@ -181,6 +186,7 @@ class TestLocalKeyRdy(TFCTestCase):
|
|||
class TestKeyExECDHE(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.ts = datetime.fromtimestamp(1502750000)
|
||||
self.window_list = WindowList(nicks=[LOCAL_ID])
|
||||
self.contact_list = ContactList()
|
||||
|
@ -230,6 +236,7 @@ class TestKeyExECDHE(TFCTestCase):
|
|||
class TestKeyExPSKTx(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.ts = datetime.fromtimestamp(1502750000)
|
||||
self.window_list = WindowList(nicks=[LOCAL_ID])
|
||||
self.contact_list = ContactList()
|
||||
|
@ -282,6 +289,7 @@ class TestKeyExPSKRx(TFCTestCase):
|
|||
file_name = f"{nick_to_short_address('User')}.psk - give to {nick_to_short_address('Alice')}"
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.packet = b'\x00' + nick_to_pub_key("Alice")
|
||||
self.ts = datetime.now()
|
||||
|
@ -293,6 +301,7 @@ class TestKeyExPSKRx(TFCTestCase):
|
|||
self.args = self.packet, self.ts, self.window_list, self.contact_list, self.key_list, self.settings
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
|
||||
def test_unknown_account_raises_fr(self):
|
||||
|
@ -359,14 +368,14 @@ class TestKeyExPSKRx(TFCTestCase):
|
|||
@mock.patch('getpass.getpass', return_value='test_password')
|
||||
def test_valid_psk(self, *_):
|
||||
# Setup
|
||||
keyset = self.key_list.get_keyset(nick_to_pub_key("Alice"))
|
||||
keyset.rx_mk = bytes(SYMMETRIC_KEY_LENGTH)
|
||||
keyset.rx_hk = bytes(SYMMETRIC_KEY_LENGTH)
|
||||
salt = os.urandom(ARGON2_SALT_LENGTH)
|
||||
rx_key = os.urandom(SYMMETRIC_KEY_LENGTH)
|
||||
rx_hek = os.urandom(SYMMETRIC_KEY_LENGTH)
|
||||
kek = argon2_kdf('test_password', salt, time_cost=1, memory_cost=100, parallelism=1)
|
||||
ct_tag = encrypt_and_sign(rx_key + rx_hek, key=kek)
|
||||
keyset = self.key_list.get_keyset(nick_to_pub_key("Alice"))
|
||||
keyset.rx_mk = bytes(SYMMETRIC_KEY_LENGTH)
|
||||
keyset.rx_hk = bytes(SYMMETRIC_KEY_LENGTH)
|
||||
salt = os.urandom(ARGON2_SALT_LENGTH)
|
||||
rx_key = os.urandom(SYMMETRIC_KEY_LENGTH)
|
||||
rx_hek = os.urandom(SYMMETRIC_KEY_LENGTH)
|
||||
kek = argon2_kdf('test_password', salt, time_cost=1, memory_cost=100, parallelism=1)
|
||||
ct_tag = encrypt_and_sign(rx_key + rx_hek, key=kek)
|
||||
|
||||
with open(self.file_name, 'wb+') as f:
|
||||
f.write(salt + ct_tag)
|
||||
|
@ -387,9 +396,9 @@ class TestKeyExPSKRx(TFCTestCase):
|
|||
@mock.patch('getpass.getpass', return_value='test_password')
|
||||
def test_valid_psk_overwrite_failure(self, *_):
|
||||
# Setup
|
||||
keyset = self.key_list.get_keyset(nick_to_pub_key("Alice"))
|
||||
keyset.rx_mk = bytes(SYMMETRIC_KEY_LENGTH)
|
||||
keyset.rx_hk = bytes(SYMMETRIC_KEY_LENGTH)
|
||||
keyset = self.key_list.get_keyset(nick_to_pub_key("Alice"))
|
||||
keyset.rx_mk = bytes(SYMMETRIC_KEY_LENGTH)
|
||||
keyset.rx_hk = bytes(SYMMETRIC_KEY_LENGTH)
|
||||
|
||||
salt = os.urandom(ARGON2_SALT_LENGTH)
|
||||
rx_key = os.urandom(SYMMETRIC_KEY_LENGTH)
|
||||
|
|
|
@ -28,7 +28,9 @@ from unittest import mock
|
|||
|
||||
from src.common.encoding import bool_to_bytes
|
||||
from src.common.misc import ensure_dir
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (BLAKE2_DIGEST_LENGTH, DIR_USER_DATA, FILE, FILE_KEY_HEADER, GROUP_ID_LENGTH, LOCAL_ID,
|
||||
LOCAL_PUBKEY, LOG_ENTRY_LENGTH, MESSAGE, MESSAGE_LENGTH, ORIGIN_CONTACT_HEADER,
|
||||
ORIGIN_USER_HEADER, SYMMETRIC_KEY_LENGTH)
|
||||
|
||||
from src.receiver.messages import process_message
|
||||
from src.receiver.packet import PacketList
|
||||
|
@ -42,6 +44,7 @@ from tests.utils import nick_to_pub_key, TFCTestCase
|
|||
class TestProcessMessage(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
|
||||
self.msg = ("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean condimentum consectetur purus quis"
|
||||
|
@ -64,18 +67,19 @@ class TestProcessMessage(TFCTestCase):
|
|||
self.key_list = KeyList( nicks=['Alice', 'Bob', 'Charlie', LOCAL_ID])
|
||||
self.group_list = GroupList( groups=['test_group'])
|
||||
self.packet_list = PacketList(contact_list=self.contact_list, settings=self.settings)
|
||||
self.window_list = WindowList(contact_list=self.contact_list, settings=self.settings,
|
||||
self.window_list = WindowList(contact_list=self.contact_list, settings=self.settings,
|
||||
group_list=self.group_list, packet_list=self.packet_list)
|
||||
self.group_id = group_name_to_group_id('test_group')
|
||||
self.file_keys = dict()
|
||||
|
||||
self.group_list.get_group('test_group').log_messages = True
|
||||
self.args = (self.window_list, self.packet_list, self.contact_list, self.key_list,
|
||||
self.args = (self.window_list, self.packet_list, self.contact_list, self.key_list,
|
||||
self.group_list, self.settings, self.master_key, self.file_keys)
|
||||
|
||||
ensure_dir(DIR_USER_DATA)
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
|
||||
# Invalid packets
|
||||
|
|
|
@ -31,7 +31,11 @@ from unittest.mock import MagicMock
|
|||
|
||||
from src.common.crypto import blake2b, encrypt_and_sign
|
||||
from src.common.encoding import b58encode, bool_to_bytes, int_to_bytes, str_to_bytes
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (CH_FILE_RECV, COMMAND, COMMAND_DATAGRAM_HEADER, CONFIRM_CODE_LENGTH, ENABLE, EXIT,
|
||||
FILE_DATAGRAM_HEADER, FILE_KEY_HEADER, INITIAL_HARAC, KEY_EX_ECDHE,
|
||||
LOCAL_KEY_DATAGRAM_HEADER, MESSAGE, MESSAGE_DATAGRAM_HEADER, ORIGIN_CONTACT_HEADER,
|
||||
PRIVATE_MESSAGE_HEADER, SYMMETRIC_KEY_LENGTH, UNIT_TEST_QUEUE, US_BYTE, WIN_SELECT,
|
||||
WIN_UID_FILE, WIN_UID_LOCAL)
|
||||
|
||||
from src.transmitter.packet import split_to_assembly_packets
|
||||
|
||||
|
@ -49,10 +53,12 @@ def rotate_key(key: bytes, harac: int) -> Tuple[bytes, int]:
|
|||
class TestOutputLoop(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.o_sleep = time.sleep
|
||||
time.sleep = lambda _: None
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
time.sleep = self.o_sleep
|
||||
|
||||
@mock.patch('tkinter.Tk', return_value=MagicMock())
|
||||
|
|
|
@ -28,7 +28,9 @@ from unittest import mock
|
|||
|
||||
from src.common.crypto import byte_padding, encrypt_and_sign
|
||||
from src.common.encoding import int_to_bytes
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (COMMAND, COMPRESSION_LEVEL, DIR_RECV_FILES, FILE, F_C_HEADER, LOCAL_ID, MESSAGE,
|
||||
M_A_HEADER, M_E_HEADER, ORIGIN_CONTACT_HEADER, ORIGIN_USER_HEADER, PADDING_LENGTH,
|
||||
PRIVATE_MESSAGE_HEADER, P_N_HEADER, SYMMETRIC_KEY_LENGTH, US_BYTE)
|
||||
|
||||
from src.transmitter.packet import split_to_assembly_packets
|
||||
|
||||
|
@ -42,6 +44,7 @@ from tests.utils import UNDECODABLE_UNICODE
|
|||
class TestDecryptAssemblyPacket(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.onion_pub_key = nick_to_pub_key("Alice")
|
||||
self.origin = ORIGIN_CONTACT_HEADER
|
||||
self.window_list = WindowList(nicks=['Alice', LOCAL_ID])
|
||||
|
@ -113,6 +116,7 @@ class TestDecryptAssemblyPacket(TFCTestCase):
|
|||
class TestPacket(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.short_msg = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
|
||||
self.msg = ("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean condimentum consectetur purus quis"
|
||||
" dapibus. Fusce venenatis lacus ut rhoncus faucibus. Cras sollicitudin commodo sapien, sed bibendu"
|
||||
|
@ -140,6 +144,7 @@ class TestPacket(TFCTestCase):
|
|||
self.short_f_data = (int_to_bytes(1) + int_to_bytes(2) + b'testfile.txt' + US_BYTE + encrypted)
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
|
||||
def test_invalid_assembly_packet_header_raises_fr(self):
|
||||
|
@ -412,6 +417,7 @@ class TestPacket(TFCTestCase):
|
|||
class TestPacketList(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.contact_list = ContactList(nicks=['Alice', 'Bob'])
|
||||
self.settings = Settings()
|
||||
self.onion_pub_key = nick_to_pub_key('Alice')
|
||||
|
|
|
@ -28,7 +28,9 @@ from multiprocessing import Queue
|
|||
|
||||
from src.common.encoding import int_to_bytes
|
||||
from src.common.reed_solomon import RSCodec
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (COMMAND_DATAGRAM_HEADER, FILE_DATAGRAM_HEADER, GATEWAY_QUEUE,
|
||||
LOCAL_KEY_DATAGRAM_HEADER, MESSAGE_DATAGRAM_HEADER,
|
||||
ONION_SERVICE_PUBLIC_KEY_LENGTH)
|
||||
|
||||
from src.receiver.receiver_loop import receiver_loop
|
||||
|
||||
|
@ -54,9 +56,9 @@ class TestReceiverLoop(unittest.TestCase):
|
|||
ts_bytes = int_to_bytes(int(ts.strftime('%Y%m%d%H%M%S%f')[:-4]))
|
||||
|
||||
for key in queues:
|
||||
packet = key + ts_bytes + bytes(ONION_SERVICE_PUBLIC_KEY_LENGTH)
|
||||
encoded = rs.encode(packet)
|
||||
broken_p = key + bytes.fromhex('df9005313af4136d') + bytes(ONION_SERVICE_PUBLIC_KEY_LENGTH)
|
||||
packet = key + ts_bytes + bytes(ONION_SERVICE_PUBLIC_KEY_LENGTH)
|
||||
encoded = rs.encode(packet)
|
||||
broken_p = key + bytes.fromhex('df9005313af4136d') + bytes(ONION_SERVICE_PUBLIC_KEY_LENGTH)
|
||||
broken_p += rs.encode(b'a')
|
||||
|
||||
def queue_delayer():
|
||||
|
|
|
@ -24,7 +24,11 @@ import unittest
|
|||
from datetime import datetime
|
||||
from unittest import mock
|
||||
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (BOLD_ON, CLEAR_ENTIRE_LINE, CLEAR_ENTIRE_SCREEN, CURSOR_LEFT_UP_CORNER,
|
||||
CURSOR_UP_ONE_LINE, FILE, GROUP_ID_LENGTH, LOCAL_ID, NORMAL_TEXT,
|
||||
ONION_SERVICE_PUBLIC_KEY_LENGTH, ORIGIN_CONTACT_HEADER, ORIGIN_USER_HEADER,
|
||||
WIN_TYPE_COMMAND, WIN_TYPE_CONTACT, WIN_TYPE_FILE, WIN_TYPE_GROUP, WIN_UID_FILE,
|
||||
WIN_UID_LOCAL)
|
||||
|
||||
from src.receiver.windows import RxWindow, WindowList
|
||||
|
||||
|
@ -35,6 +39,7 @@ from tests.utils import group_name_to_group_id, nick_to_pub_key, nick_to_
|
|||
class TestRxWindow(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.contact_list = ContactList(nicks=['Alice', 'Bob', 'Charlie', LOCAL_ID])
|
||||
self.group_list = GroupList(groups=['test_group', 'test_group2'])
|
||||
self.settings = Settings()
|
||||
|
@ -71,7 +76,14 @@ class TestRxWindow(TFCTestCase):
|
|||
self.assertEqual(window.name, 'test_group')
|
||||
|
||||
def test_invalid_uid_raises_fr(self):
|
||||
self.assert_fr("Invalid window 'bad_uid'.", self.create_window, 'bad_uid')
|
||||
self.assert_fr("Invalid window 'mfqwcylbmfqwcylbmfqwcylbmfqwcylbmfqwcylbmfqwcylbmfqwbfad'.",
|
||||
self.create_window, ONION_SERVICE_PUBLIC_KEY_LENGTH*b'a')
|
||||
|
||||
self.assert_fr("Invalid window '2dnAMoWNfTXAJ'.",
|
||||
self.create_window, GROUP_ID_LENGTH*b'a')
|
||||
|
||||
self.assert_fr("Invalid window '<unable to encode>'.",
|
||||
self.create_window, b'bad_uid')
|
||||
|
||||
def test_window_iterates_over_message_tuples(self):
|
||||
# Setup
|
||||
|
@ -373,6 +385,7 @@ testfile2.txt 15.0KB Charlie 7.00%
|
|||
class TestWindowList(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.settings = Settings()
|
||||
self.contact_list = ContactList(nicks=['Alice', 'Bob', 'Charlie', LOCAL_ID])
|
||||
self.group_list = GroupList(groups=['test_group', 'test_group2'])
|
||||
|
|
|
@ -31,7 +31,13 @@ import requests
|
|||
|
||||
from src.common.crypto import X448
|
||||
from src.common.db_onion import pub_key_to_onion_address, pub_key_to_short_address
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (CONTACT_MGMT_QUEUE, CONTACT_REQ_QUEUE, C_REQ_MGMT_QUEUE, C_REQ_STATE_QUEUE,
|
||||
DST_MESSAGE_QUEUE, EXIT, GROUP_ID_LENGTH, GROUP_MGMT_QUEUE,
|
||||
GROUP_MSG_EXIT_GROUP_HEADER, GROUP_MSG_INVITE_HEADER, GROUP_MSG_JOIN_HEADER,
|
||||
GROUP_MSG_MEMBER_ADD_HEADER, GROUP_MSG_MEMBER_REM_HEADER, GROUP_MSG_QUEUE,
|
||||
MESSAGE_DATAGRAM_HEADER, ONION_SERVICE_PUBLIC_KEY_LENGTH, PUBLIC_KEY_DATAGRAM_HEADER,
|
||||
RP_ADD_CONTACT_HEADER, RP_REMOVE_CONTACT_HEADER, TFC_PUBLIC_KEY_LENGTH, TOR_DATA_QUEUE,
|
||||
UNIT_TEST_QUEUE, URL_TOKEN_QUEUE)
|
||||
|
||||
from src.relay.client import c_req_manager, client, client_scheduler, g_msg_manager, get_data_loop
|
||||
|
||||
|
@ -107,11 +113,13 @@ class TestClient(unittest.TestCase):
|
|||
return TestClient.MockSession()
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.o_session = requests.session
|
||||
self.queues = gen_queue_dict()
|
||||
requests.session = TestClient.mock_session
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
requests.session = self.o_session
|
||||
tear_queues(self.queues)
|
||||
|
||||
|
@ -243,11 +251,13 @@ class TestGetDataLoop(unittest.TestCase):
|
|||
return TestGetDataLoop.Session()
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.o_session = requests.session
|
||||
self.queues = gen_queue_dict()
|
||||
requests.session = TestGetDataLoop.mock_session
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
requests.session = self.o_session
|
||||
tear_queues(self.queues)
|
||||
|
||||
|
|
|
@ -28,7 +28,11 @@ from unittest import mock
|
|||
from unittest.mock import MagicMock
|
||||
|
||||
from src.common.encoding import int_to_bytes
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (CLEAR_ENTIRE_SCREEN, CONTACT_MGMT_QUEUE, CURSOR_LEFT_UP_CORNER, C_REQ_MGMT_QUEUE,
|
||||
C_REQ_STATE_QUEUE, EXIT, GROUP_MGMT_QUEUE, LOCAL_TESTING_PACKET_DELAY,
|
||||
ONION_CLOSE_QUEUE, ONION_KEY_QUEUE, ONION_SERVICE_PRIVATE_KEY_LENGTH,
|
||||
RP_ADD_CONTACT_HEADER, RP_REMOVE_CONTACT_HEADER, SRC_TO_RELAY_QUEUE,
|
||||
UNENCRYPTED_SCREEN_CLEAR, WIPE)
|
||||
|
||||
from src.relay.commands import add_contact, add_onion_data, change_baudrate, change_ec_ratio, clear_windows, exit_tfc
|
||||
from src.relay.commands import manage_contact_req, process_command, race_condition_delay, relay_command, remove_contact
|
||||
|
@ -41,11 +45,13 @@ from tests.utils import gen_queue_dict, tear_queues, TFCTestCase
|
|||
class TestRelayCommand(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.gateway = Gateway()
|
||||
self.queues = gen_queue_dict()
|
||||
self.gateway.settings.race_condition_delay = 0.0
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
tear_queues(self.queues)
|
||||
|
||||
@mock.patch('sys.stdin', MagicMock())
|
||||
|
@ -64,10 +70,12 @@ class TestRelayCommand(unittest.TestCase):
|
|||
class TestProcessCommand(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.gateway = Gateway()
|
||||
self.queues = gen_queue_dict()
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
tear_queues(self.queues)
|
||||
|
||||
def test_invalid_key(self):
|
||||
|
@ -77,6 +85,7 @@ class TestProcessCommand(TFCTestCase):
|
|||
class TestRaceConditionDelay(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.gateway = Gateway(local_testing_mode=True,
|
||||
data_diode_sockets=True)
|
||||
|
||||
|
@ -89,6 +98,7 @@ class TestRaceConditionDelay(unittest.TestCase):
|
|||
class TestClearWindows(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.gateway = Gateway(race_condition_delay=0.0)
|
||||
|
||||
def test_clear_display(self):
|
||||
|
@ -106,10 +116,12 @@ class TestResetWindows(TFCTestCase):
|
|||
class TestExitTFC(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.gateway = Gateway(race_condition_delay=0.0)
|
||||
self.queues = gen_queue_dict()
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
tear_queues(self.queues)
|
||||
|
||||
def test_exit_tfc(self):
|
||||
|
@ -120,6 +132,7 @@ class TestExitTFC(unittest.TestCase):
|
|||
class TestChangeECRatio(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.gateway = Gateway()
|
||||
|
||||
def test_non_digit_value_raises_fr(self):
|
||||
|
@ -138,6 +151,7 @@ class TestChangeECRatio(TFCTestCase):
|
|||
class TestChangeBaudrate(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.gateway = Gateway()
|
||||
|
||||
def test_non_digit_value_raises_fr(self):
|
||||
|
@ -156,10 +170,12 @@ class TestChangeBaudrate(TFCTestCase):
|
|||
class TestWipe(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.gateway = Gateway(race_condition_delay=0.0)
|
||||
self.queues = gen_queue_dict()
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
tear_queues(self.queues)
|
||||
|
||||
@mock.patch('os.system', return_value=None)
|
||||
|
@ -171,9 +187,11 @@ class TestWipe(unittest.TestCase):
|
|||
class TestManageContactReq(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.queues = gen_queue_dict()
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
tear_queues(self.queues)
|
||||
|
||||
def test_setting_management(self):
|
||||
|
@ -186,68 +204,74 @@ class TestManageContactReq(unittest.TestCase):
|
|||
|
||||
class TestAddContact(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.queues = gen_queue_dict()
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.queues = gen_queue_dict()
|
||||
|
||||
def tearDown(self):
|
||||
tear_queues(self.queues)
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
tear_queues(self.queues)
|
||||
|
||||
def test_add_contact(self):
|
||||
command = b''.join([nick_to_pub_key('Alice'), nick_to_pub_key('Bob')])
|
||||
def test_add_contact(self):
|
||||
command = b''.join([nick_to_pub_key('Alice'), nick_to_pub_key('Bob')])
|
||||
|
||||
self.assertIsNone(add_contact(command, True, self.queues))
|
||||
self.assertEqual(self.queues[CONTACT_MGMT_QUEUE].qsize(), 1)
|
||||
for q in [GROUP_MGMT_QUEUE, C_REQ_MGMT_QUEUE]:
|
||||
command = self.queues[q].get()
|
||||
self.assertEqual(command,
|
||||
(RP_ADD_CONTACT_HEADER, b''.join([nick_to_pub_key('Alice'), nick_to_pub_key('Bob')])))
|
||||
self.assertEqual(self.queues[CONTACT_MGMT_QUEUE].get(),
|
||||
(RP_ADD_CONTACT_HEADER, b''.join(list(map(nick_to_pub_key, ['Alice', 'Bob']))), True))
|
||||
self.assertIsNone(add_contact(command, True, self.queues))
|
||||
self.assertEqual(self.queues[CONTACT_MGMT_QUEUE].qsize(), 1)
|
||||
for q in [GROUP_MGMT_QUEUE, C_REQ_MGMT_QUEUE]:
|
||||
command = self.queues[q].get()
|
||||
self.assertEqual(command,
|
||||
(RP_ADD_CONTACT_HEADER, b''.join([nick_to_pub_key('Alice'), nick_to_pub_key('Bob')])))
|
||||
self.assertEqual(self.queues[CONTACT_MGMT_QUEUE].get(),
|
||||
(RP_ADD_CONTACT_HEADER, b''.join(list(map(nick_to_pub_key, ['Alice', 'Bob']))), True))
|
||||
|
||||
|
||||
class TestRemContact(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.queues = gen_queue_dict()
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.queues = gen_queue_dict()
|
||||
|
||||
def tearDown(self):
|
||||
tear_queues(self.queues)
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
tear_queues(self.queues)
|
||||
|
||||
def test_add_contact(self):
|
||||
command = b''.join([nick_to_pub_key('Alice'), nick_to_pub_key('Bob')])
|
||||
def test_add_contact(self):
|
||||
command = b''.join([nick_to_pub_key('Alice'), nick_to_pub_key('Bob')])
|
||||
|
||||
self.assertIsNone(remove_contact(command, self.queues))
|
||||
self.assertEqual(self.queues[CONTACT_MGMT_QUEUE].qsize(), 1)
|
||||
self.assertEqual(self.queues[CONTACT_MGMT_QUEUE].get(),
|
||||
(RP_REMOVE_CONTACT_HEADER,
|
||||
b''.join([nick_to_pub_key('Alice'), nick_to_pub_key('Bob')]),
|
||||
False)
|
||||
)
|
||||
self.assertIsNone(remove_contact(command, self.queues))
|
||||
self.assertEqual(self.queues[CONTACT_MGMT_QUEUE].qsize(), 1)
|
||||
self.assertEqual(self.queues[CONTACT_MGMT_QUEUE].get(),
|
||||
(RP_REMOVE_CONTACT_HEADER,
|
||||
b''.join([nick_to_pub_key('Alice'), nick_to_pub_key('Bob')]),
|
||||
False)
|
||||
)
|
||||
|
||||
for q in [GROUP_MGMT_QUEUE, C_REQ_MGMT_QUEUE]:
|
||||
command = self.queues[q].get()
|
||||
self.assertEqual(command, (RP_REMOVE_CONTACT_HEADER,
|
||||
b''.join([nick_to_pub_key('Alice'), nick_to_pub_key('Bob')])))
|
||||
for q in [GROUP_MGMT_QUEUE, C_REQ_MGMT_QUEUE]:
|
||||
command = self.queues[q].get()
|
||||
self.assertEqual(command, (RP_REMOVE_CONTACT_HEADER,
|
||||
b''.join([nick_to_pub_key('Alice'), nick_to_pub_key('Bob')])))
|
||||
|
||||
|
||||
class TestAddOnionKey(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.queues = gen_queue_dict()
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.queues = gen_queue_dict()
|
||||
|
||||
def tearDown(self):
|
||||
tear_queues(self.queues)
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
tear_queues(self.queues)
|
||||
|
||||
def test_add_contact(self):
|
||||
command = (ONION_SERVICE_PRIVATE_KEY_LENGTH * b'a'
|
||||
+ b'b'
|
||||
+ b'\x01'
|
||||
+ int_to_bytes(1)
|
||||
+ nick_to_pub_key('Alice')
|
||||
+ nick_to_pub_key('Bob'))
|
||||
self.assertIsNone(add_onion_data(command, self.queues))
|
||||
self.assertEqual(self.queues[ONION_KEY_QUEUE].qsize(), 1)
|
||||
self.assertEqual(self.queues[ONION_KEY_QUEUE].get(), (ONION_SERVICE_PRIVATE_KEY_LENGTH * b'a', b'b'))
|
||||
def test_add_contact(self):
|
||||
command = (ONION_SERVICE_PRIVATE_KEY_LENGTH * b'a'
|
||||
+ b'b'
|
||||
+ b'\x01'
|
||||
+ int_to_bytes(1)
|
||||
+ nick_to_pub_key('Alice')
|
||||
+ nick_to_pub_key('Bob'))
|
||||
self.assertIsNone(add_onion_data(command, self.queues))
|
||||
self.assertEqual(self.queues[ONION_KEY_QUEUE].qsize(), 1)
|
||||
self.assertEqual(self.queues[ONION_KEY_QUEUE].get(), (ONION_SERVICE_PRIVATE_KEY_LENGTH * b'a', b'b'))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -30,7 +30,8 @@ from unittest.mock import MagicMock
|
|||
import stem.control
|
||||
|
||||
from src.common.misc import validate_onion_addr
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (EXIT, EXIT_QUEUE, ONION_CLOSE_QUEUE, ONION_KEY_QUEUE, ONION_SERVICE_PRIVATE_KEY_LENGTH,
|
||||
TOR_DATA_QUEUE, TOR_SOCKS_PORT)
|
||||
|
||||
from src.relay.onion import get_available_port, onion_service, stem_compatible_ed25519_key_from_private_key, Tor
|
||||
|
||||
|
@ -44,6 +45,11 @@ class TestGetAvailablePort(unittest.TestCase):
|
|||
port = get_available_port(1000, 65535)
|
||||
self.assertEqual(port, 1234)
|
||||
|
||||
@mock.patch('builtins.open', mock.mock_open(read_data='TAILS_PRODUCT_NAME="Tails"'))
|
||||
def test_port_is_tor_socket_port_when_running_on_tails(self):
|
||||
port = get_available_port(1000, 65535)
|
||||
self.assertEqual(port, TOR_SOCKS_PORT)
|
||||
|
||||
|
||||
class TestTor(unittest.TestCase):
|
||||
|
||||
|
@ -52,7 +58,7 @@ class TestTor(unittest.TestCase):
|
|||
def test_missing_binary_raises_critical_error(self, *_):
|
||||
tor = Tor()
|
||||
with self.assertRaises(SystemExit):
|
||||
tor.connect('1234')
|
||||
tor.connect(1234)
|
||||
|
||||
@mock.patch('time.sleep', return_value=None)
|
||||
@mock.patch('stem.process.launch_tor_with_config', side_effect=[MagicMock(), OSError, MagicMock()])
|
||||
|
@ -60,9 +66,9 @@ class TestTor(unittest.TestCase):
|
|||
side_effect=['NOTICE BOOTSTRAP PROGRESS=100 TAG=done SUMMARY="Done"', stem.SocketClosed])))
|
||||
def test_closed_socket_raises_critical_error(self, *_):
|
||||
tor = Tor()
|
||||
self.assertIsNone(tor.connect('1234'))
|
||||
self.assertIsNone(tor.connect(1234))
|
||||
with self.assertRaises(SystemExit):
|
||||
tor.connect('1234')
|
||||
tor.connect(1234)
|
||||
|
||||
@mock.patch('time.sleep', return_value=None)
|
||||
@mock.patch('time.monotonic', side_effect=[1, 20, 30, 40])
|
||||
|
@ -72,7 +78,7 @@ class TestTor(unittest.TestCase):
|
|||
@mock.patch('stem.process.launch_tor_with_config', return_value=MagicMock(poll=lambda: False))
|
||||
def test_timeout_restarts_tor(self, *_):
|
||||
tor = Tor()
|
||||
self.assertIsNone(tor.connect('1234'))
|
||||
self.assertIsNone(tor.connect(1234))
|
||||
tor.stop()
|
||||
|
||||
|
||||
|
@ -171,6 +177,33 @@ class TestOnionService(unittest.TestCase):
|
|||
# Teardown
|
||||
tear_queues(queues)
|
||||
|
||||
@mock.patch('stem.control.Controller.from_port', MagicMock())
|
||||
@mock.patch('builtins.open', mock.mock_open(read_data='TAILS_PRODUCT_NAME="Tails"'))
|
||||
def test_no_tor_process_is_created_when_tails_is_used(self, *_):
|
||||
tor = Tor()
|
||||
self.assertIsNone(tor.connect(1234))
|
||||
self.assertIsNone(tor.tor_process)
|
||||
|
||||
@mock.patch('time.sleep', return_value=None)
|
||||
def test_missing_tor_controller_raises_critical_error(self, *_):
|
||||
# Setup
|
||||
queues = gen_queue_dict()
|
||||
orig_tor_connect = Tor.connect
|
||||
Tor.connect = MagicMock(return_value=None)
|
||||
|
||||
controller = stem.control.Controller
|
||||
controller.create_ephemeral_hidden_service = MagicMock()
|
||||
|
||||
queues[ONION_KEY_QUEUE].put((bytes(ONION_SERVICE_PRIVATE_KEY_LENGTH), b'\x01'))
|
||||
|
||||
# Test
|
||||
with self.assertRaises(SystemExit):
|
||||
onion_service(queues)
|
||||
|
||||
# Teardown
|
||||
tear_queues(queues)
|
||||
Tor.connect = orig_tor_connect
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(exit=False)
|
||||
|
|
|
@ -22,7 +22,7 @@ along with TFC. If not, see <https://www.gnu.org/licenses/>.
|
|||
import unittest
|
||||
|
||||
from src.common.crypto import X448
|
||||
from src.common.statics import *
|
||||
from src.common.statics import CONTACT_REQ_QUEUE, F_TO_FLASK_QUEUE, M_TO_FLASK_QUEUE, URL_TOKEN_QUEUE
|
||||
|
||||
from src.relay.server import flask_server
|
||||
|
||||
|
|
|
@ -28,7 +28,13 @@ from unittest import mock
|
|||
|
||||
from src.common.encoding import int_to_bytes
|
||||
from src.common.reed_solomon import RSCodec
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (COMMAND_DATAGRAM_HEADER, DST_COMMAND_QUEUE, DST_MESSAGE_QUEUE, EXIT,
|
||||
FILE_DATAGRAM_HEADER, F_TO_FLASK_QUEUE, GATEWAY_QUEUE, GROUP_ID_LENGTH,
|
||||
GROUP_MSG_EXIT_GROUP_HEADER, GROUP_MSG_INVITE_HEADER, GROUP_MSG_JOIN_HEADER,
|
||||
GROUP_MSG_MEMBER_ADD_HEADER, GROUP_MSG_MEMBER_REM_HEADER,
|
||||
LOCAL_KEY_DATAGRAM_HEADER, MESSAGE_DATAGRAM_HEADER, M_TO_FLASK_QUEUE,
|
||||
PUBLIC_KEY_DATAGRAM_HEADER, SRC_TO_RELAY_QUEUE, TFC_PUBLIC_KEY_LENGTH,
|
||||
UNENCRYPTED_DATAGRAM_HEADER, UNIT_TEST_QUEUE)
|
||||
|
||||
from src.relay.tcb import dst_outgoing, src_incoming
|
||||
|
||||
|
@ -39,6 +45,7 @@ from tests.utils import cd_unit_test, cleanup, gen_queue_dict, tear_queue
|
|||
class TestSRCIncoming(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.settings = Settings()
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.gateway = Gateway()
|
||||
|
@ -48,6 +55,7 @@ class TestSRCIncoming(unittest.TestCase):
|
|||
self.args = self.queues, self.gateway
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
tear_queues(self.queues)
|
||||
cleanup(self.unit_test_dir)
|
||||
|
||||
|
@ -200,10 +208,11 @@ class TestDSTOutGoing(unittest.TestCase):
|
|||
|
||||
def queue_delayer():
|
||||
"""Place packets into queue after delay."""
|
||||
time.sleep(0.01)
|
||||
time.sleep(0.015)
|
||||
queues[DST_COMMAND_QUEUE].put(packet)
|
||||
time.sleep(0.015)
|
||||
queues[DST_MESSAGE_QUEUE].put(packet)
|
||||
time.sleep(0.01)
|
||||
time.sleep(0.015)
|
||||
queues[UNIT_TEST_QUEUE].put(EXIT)
|
||||
|
||||
threading.Thread(target=queue_delayer).start()
|
||||
|
|
|
@ -28,9 +28,10 @@ from multiprocessing import Queue
|
|||
from unittest import mock
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from src.common.statics import DATA_FLOW, DST_LISTEN_SOCKET, EXIT, EXIT_QUEUE, IDLE, NCDCLR, NCDCRL, RP_LISTEN_SOCKET
|
||||
from src.common.statics import SCNCLR, SCNCRL
|
||||
from dd import animate, draw_frame, main, process_arguments, rx_loop, tx_loop
|
||||
from src.common.statics import (DATA_FLOW, DST_LISTEN_SOCKET, EXIT, EXIT_QUEUE, IDLE,
|
||||
NCDCLR, NCDCRL, RP_LISTEN_SOCKET, SCNCLR, SCNCRL)
|
||||
|
||||
from dd import animate, draw_frame, main, process_arguments, rx_loop, tx_loop
|
||||
|
||||
from tests.utils import tear_queue, TFCTestCase
|
||||
|
||||
|
@ -111,9 +112,11 @@ class TestAnimate(unittest.TestCase):
|
|||
class TestRxLoop(unittest.TestCase):
|
||||
|
||||
def setUp(self) -> None:
|
||||
"""Pre-test actions."""
|
||||
self.queue = Queue()
|
||||
|
||||
def tearDown(self) -> None:
|
||||
"""Post-test actions."""
|
||||
tear_queue(self.queue)
|
||||
|
||||
@mock.patch('multiprocessing.connection.Listener', return_value=MagicMock(
|
||||
|
@ -132,9 +135,11 @@ class TestRxLoop(unittest.TestCase):
|
|||
class TestTxLoop(unittest.TestCase):
|
||||
|
||||
def setUp(self) -> None:
|
||||
"""Pre-test actions."""
|
||||
self.o_sleep = time.sleep
|
||||
|
||||
def tearDown(self) -> None:
|
||||
"""Post-test actions."""
|
||||
time.sleep = self.o_sleep
|
||||
|
||||
@mock.patch('time.sleep', lambda _: None)
|
||||
|
@ -144,6 +149,7 @@ class TestTxLoop(unittest.TestCase):
|
|||
queue = Queue()
|
||||
|
||||
def queue_delayer():
|
||||
"""Place packet to queue after timer runs out."""
|
||||
self.o_sleep(0.1)
|
||||
queue.put(b'test_packet')
|
||||
threading.Thread(target=queue_delayer).start()
|
||||
|
@ -175,9 +181,11 @@ class TestProcessArguments(unittest.TestCase):
|
|||
class TestMain(unittest.TestCase):
|
||||
|
||||
def setUp(self) -> None:
|
||||
"""Pre-test actions."""
|
||||
self.queue = Queue()
|
||||
|
||||
def tearDown(self) -> None:
|
||||
"""Post-test actions."""
|
||||
tear_queue(self.queue)
|
||||
|
||||
@mock.patch('time.sleep', lambda _: None)
|
||||
|
@ -187,6 +195,7 @@ class TestMain(unittest.TestCase):
|
|||
queues = {EXIT_QUEUE: self.queue}
|
||||
|
||||
def queue_delayer():
|
||||
"""Place packet to queue after timer runs out."""
|
||||
time.sleep(0.1)
|
||||
queues[EXIT_QUEUE].put(EXIT)
|
||||
threading.Thread(target=queue_delayer).start()
|
||||
|
|
|
@ -27,7 +27,13 @@ from unittest.mock import MagicMock
|
|||
|
||||
from src.common.db_logs import write_log_entry
|
||||
from src.common.encoding import bool_to_bytes
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (BOLD_ON, CLEAR_ENTIRE_SCREEN, COMMAND_PACKET_QUEUE, CURSOR_LEFT_UP_CORNER,
|
||||
DIR_USER_DATA, KEX_STATUS_NO_RX_PSK, KEX_STATUS_UNVERIFIED, KEX_STATUS_VERIFIED,
|
||||
KEY_MANAGEMENT_QUEUE, LOGFILE_MASKING_QUEUE, LOG_ENTRY_LENGTH, MESSAGE,
|
||||
MESSAGE_PACKET_QUEUE, M_S_HEADER, NORMAL_TEXT, PADDING_LENGTH, PRIVATE_MESSAGE_HEADER,
|
||||
RELAY_PACKET_QUEUE, RESET, SENDER_MODE_QUEUE, TM_COMMAND_PACKET_QUEUE,
|
||||
TRAFFIC_MASKING_QUEUE, TX, UNENCRYPTED_DATAGRAM_HEADER, UNENCRYPTED_WIPE_COMMAND,
|
||||
VERSION, WIN_TYPE_CONTACT, WIN_TYPE_GROUP)
|
||||
|
||||
from src.transmitter.commands import change_master_key, change_setting, clear_screens, exit_tfc, log_command
|
||||
from src.transmitter.commands import print_about, print_help, print_recipients, print_settings, process_command
|
||||
|
@ -44,6 +50,7 @@ from tests.utils import gen_queue_dict, nick_to_onion_address, nick_to_pu
|
|||
class TestProcessCommand(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.window = TxWindow()
|
||||
self.contact_list = ContactList()
|
||||
self.group_list = GroupList()
|
||||
|
@ -56,6 +63,7 @@ class TestProcessCommand(TFCTestCase):
|
|||
self.queues, self.master_key, self.onion_service, self.gateway)
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
tear_queues(self.queues)
|
||||
|
||||
def test_valid_command(self):
|
||||
|
@ -84,12 +92,14 @@ class TestPrintAbout(TFCTestCase):
|
|||
class TestClearScreens(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.window = TxWindow(uid=nick_to_pub_key('Alice'))
|
||||
self.settings = Settings()
|
||||
self.queues = gen_queue_dict()
|
||||
self.args = self.window, self.settings, self.queues
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
tear_queues(self.queues)
|
||||
|
||||
@mock.patch('os.system', return_value=None)
|
||||
|
@ -130,6 +140,7 @@ class TestClearScreens(unittest.TestCase):
|
|||
class TestRXPShowSysWin(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.window = TxWindow(name='Alice',
|
||||
uid=nick_to_pub_key('Alice'))
|
||||
self.settings = Settings()
|
||||
|
@ -137,6 +148,7 @@ class TestRXPShowSysWin(unittest.TestCase):
|
|||
self.args = self.window, self.settings, self.queues
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
tear_queues(self.queues)
|
||||
|
||||
@mock.patch('builtins.input', side_effect=['', EOFError, KeyboardInterrupt])
|
||||
|
@ -161,12 +173,14 @@ class TestRXPShowSysWin(unittest.TestCase):
|
|||
class TestExitTFC(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.settings = Settings(local_testing_mode=True)
|
||||
self.queues = gen_queue_dict()
|
||||
self.gateway = Gateway(data_diode_sockets=True)
|
||||
self.args = self.settings, self.queues, self.gateway
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
tear_queues(self.queues)
|
||||
|
||||
@mock.patch('time.sleep', return_value=None)
|
||||
|
@ -197,17 +211,19 @@ class TestLogCommand(TFCTestCase):
|
|||
|
||||
@mock.patch("getpass.getpass", return_value='test_password')
|
||||
def setUp(self, _):
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.window = TxWindow(name='Alice',
|
||||
uid=nick_to_pub_key('Alice'))
|
||||
self.window = TxWindow(name='Alice', uid=nick_to_pub_key('Alice'))
|
||||
self.contact_list = ContactList()
|
||||
self.group_list = GroupList()
|
||||
self.settings = Settings()
|
||||
self.queues = gen_queue_dict()
|
||||
self.master_key = MasterKey()
|
||||
self.args = self.window, self.contact_list, self.group_list, self.settings, self.queues, self.master_key
|
||||
self.args = (self.window, self.contact_list, self.group_list,
|
||||
self.settings, self.queues, self.master_key)
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
tear_queues(self.queues)
|
||||
|
||||
|
@ -252,7 +268,8 @@ class TestLogCommand(TFCTestCase):
|
|||
|
||||
@mock.patch('src.common.db_masterkey.MIN_KEY_DERIVATION_TIME', 0.1)
|
||||
@mock.patch('src.common.db_masterkey.MAX_KEY_DERIVATION_TIME', 1.0)
|
||||
@mock.patch('os.popen', return_value=MagicMock(read=MagicMock(return_value=MagicMock(splitlines=MagicMock(return_value=["MemFree 10240"])))))
|
||||
@mock.patch('os.popen', return_value=MagicMock(
|
||||
read=MagicMock(return_value=MagicMock(splitlines=MagicMock(return_value=["MemAvailable 10240"])))))
|
||||
@mock.patch("multiprocessing.cpu_count", return_value=1)
|
||||
@mock.patch('time.sleep', return_value=None)
|
||||
@mock.patch('builtins.input', return_value='Yes')
|
||||
|
@ -263,10 +280,10 @@ class TestLogCommand(TFCTestCase):
|
|||
self.assert_fr("Log file export aborted.",
|
||||
log_command, UserInput('export'), *self.args)
|
||||
|
||||
|
||||
@mock.patch('src.common.db_masterkey.MIN_KEY_DERIVATION_TIME', 0.1)
|
||||
@mock.patch('src.common.db_masterkey.MAX_KEY_DERIVATION_TIME', 1.0)
|
||||
@mock.patch('os.popen', return_value=MagicMock(read=MagicMock(return_value=MagicMock(splitlines=MagicMock(return_value=["MemFree 10240"])))))
|
||||
@mock.patch('os.popen', return_value=MagicMock(
|
||||
read=MagicMock(return_value=MagicMock(splitlines=MagicMock(return_value=["MemAvailable 10240"])))))
|
||||
@mock.patch("multiprocessing.cpu_count", return_value=1)
|
||||
@mock.patch("getpass.getpass", side_effect=3*['test_password'] + ['invalid_password'] + ['test_password'])
|
||||
@mock.patch('time.sleep', return_value=None)
|
||||
|
@ -293,6 +310,7 @@ class TestSendOnionServiceKey(TFCTestCase):
|
|||
confirmation_code = b'a'
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.contact_list = ContactList()
|
||||
self.settings = Settings()
|
||||
self.onion_service = OnionService()
|
||||
|
@ -334,6 +352,7 @@ class TestSendOnionServiceKey(TFCTestCase):
|
|||
class TestPrintHelp(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.settings = Settings()
|
||||
self.settings.traffic_masking = False
|
||||
|
||||
|
@ -466,6 +485,7 @@ Shift + PgUp/PgDn Scroll terminal up/down
|
|||
class TestPrintRecipients(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.contact_list = ContactList(nicks=['Alice', 'Bob'])
|
||||
self.group_list = GroupList(groups=['test_group', 'test_group_2'])
|
||||
self.args = self.contact_list, self.group_list
|
||||
|
@ -477,6 +497,7 @@ class TestPrintRecipients(TFCTestCase):
|
|||
class TestChangeMasterKey(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.contact_list = ContactList()
|
||||
self.group_list = GroupList()
|
||||
|
@ -489,6 +510,7 @@ class TestChangeMasterKey(TFCTestCase):
|
|||
self.queues, self.master_key, self.onion_service)
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
tear_queues(self.queues)
|
||||
|
||||
|
@ -508,7 +530,7 @@ class TestChangeMasterKey(TFCTestCase):
|
|||
self.assert_fr("Error: Invalid target system 't'.",
|
||||
change_master_key, UserInput("passwd t"), *self.args)
|
||||
|
||||
@mock.patch('os.popen', return_value=MagicMock(read=MagicMock(return_value='foo\nMemFree 200')))
|
||||
@mock.patch('os.popen', return_value=MagicMock(read=MagicMock(return_value='foo\nMemAvailable 200')))
|
||||
@mock.patch('getpass.getpass', return_value='a')
|
||||
@mock.patch('time.sleep', return_value=None)
|
||||
@mock.patch('src.common.db_masterkey.MIN_KEY_DERIVATION_TIME', 0.01)
|
||||
|
@ -536,6 +558,7 @@ class TestChangeMasterKey(TFCTestCase):
|
|||
class TestRemoveLog(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.contact_list = ContactList(nicks=['Alice'])
|
||||
self.group_list = GroupList(groups=['test_group'])
|
||||
|
@ -546,6 +569,7 @@ class TestRemoveLog(TFCTestCase):
|
|||
self.args = self.contact_list, self.group_list, self.settings, self.queues, self.master_key
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
tear_queues(self.queues)
|
||||
cleanup(self.unit_test_dir)
|
||||
|
||||
|
@ -641,6 +665,7 @@ class TestRemoveLog(TFCTestCase):
|
|||
class TestChangeSetting(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.window = TxWindow()
|
||||
self.contact_list = ContactList()
|
||||
self.group_list = GroupList()
|
||||
|
@ -648,10 +673,11 @@ class TestChangeSetting(TFCTestCase):
|
|||
self.queues = gen_queue_dict()
|
||||
self.master_key = MasterKey()
|
||||
self.gateway = Gateway()
|
||||
self.args = self.window, self.contact_list, self.group_list, \
|
||||
self.settings, self.queues, self.master_key, self.gateway
|
||||
self.args = (self.window, self.contact_list, self.group_list,
|
||||
self.settings, self.queues, self.master_key, self.gateway)
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
tear_queues(self.queues)
|
||||
|
||||
def test_missing_setting_raises_fr(self):
|
||||
|
@ -841,9 +867,11 @@ serial_error_correction 5 5 Number of byte
|
|||
class TestRxPDisplayUnread(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.queues = gen_queue_dict()
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
tear_queues(self.queues)
|
||||
|
||||
def test_command(self):
|
||||
|
@ -854,6 +882,7 @@ class TestRxPDisplayUnread(unittest.TestCase):
|
|||
class TestVerify(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.window = TxWindow(uid=nick_to_pub_key("Alice"),
|
||||
name='Alice',
|
||||
window_contacts=[create_contact('test_group')],
|
||||
|
@ -894,6 +923,7 @@ class TestVerify(TFCTestCase):
|
|||
class TestWhisper(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.window = TxWindow(uid=nick_to_pub_key("Alice"),
|
||||
name='Alice',
|
||||
window_contacts=[create_contact('Alice')],
|
||||
|
@ -918,6 +948,7 @@ class TestWhisper(TFCTestCase):
|
|||
class TestWhois(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.contact_list = ContactList(nicks=['Alice'])
|
||||
self.group_list = GroupList(groups=['test_group'])
|
||||
self.args = self.contact_list, self.group_list
|
||||
|
@ -960,6 +991,7 @@ class TestWhois(TFCTestCase):
|
|||
class TestWipe(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.settings = Settings()
|
||||
self.queues = gen_queue_dict()
|
||||
self.gateway = Gateway()
|
||||
|
|
|
@ -24,7 +24,8 @@ import unittest
|
|||
from unittest import mock
|
||||
|
||||
from src.common.encoding import b58encode
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (COMMAND_PACKET_QUEUE, GROUP_ID_LENGTH, RELAY_PACKET_QUEUE,
|
||||
WIN_TYPE_CONTACT, WIN_TYPE_GROUP)
|
||||
|
||||
from src.transmitter.commands_g import group_add_member, group_create, group_rm_group, group_rm_member
|
||||
from src.transmitter.commands_g import process_group_command, group_rename
|
||||
|
@ -36,6 +37,7 @@ from tests.utils import cd_unit_test, cleanup, gen_queue_dict, nick_to_pu
|
|||
class TestProcessGroupCommand(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.contact_list = ContactList(nicks=['Alice'])
|
||||
self.group_list = GroupList()
|
||||
self.settings = Settings()
|
||||
|
@ -44,12 +46,13 @@ class TestProcessGroupCommand(TFCTestCase):
|
|||
self.args = self.contact_list, self.group_list, self.settings, self.queues, self.settings
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
tear_queues(self.queues)
|
||||
|
||||
def test_raises_fr_when_traffic_masking_is_enabled(self):
|
||||
# Setup
|
||||
self.settings.traffic_masking = True
|
||||
|
||||
|
||||
# Test
|
||||
self.assert_fr("Error: Command is disabled during traffic masking.",
|
||||
process_group_command, UserInput(), *self.args)
|
||||
|
@ -80,6 +83,7 @@ class TestProcessGroupCommand(TFCTestCase):
|
|||
class TestGroupCreate(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.contact_list = ContactList(nicks=['Alice', 'Bob'])
|
||||
self.group_list = GroupList()
|
||||
self.settings = Settings()
|
||||
|
@ -89,15 +93,16 @@ class TestGroupCreate(TFCTestCase):
|
|||
self.args = self.contact_list, self.group_list, self.settings, self.queues, self.settings
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
tear_queues(self.queues)
|
||||
|
||||
def configure_groups(self, no_contacts: int) -> None:
|
||||
"""Configure group list."""
|
||||
self.contact_list = ContactList(nicks=[str(n) for n in range(no_contacts)])
|
||||
self.group_list = GroupList(groups=['test_group'])
|
||||
self.group = self.group_list.get_group('test_group')
|
||||
self.group.members = self.contact_list.contacts
|
||||
self.account_list = [nick_to_pub_key(str(n)) for n in range(no_contacts)]
|
||||
self.contact_list = ContactList(nicks=[str(n) for n in range(no_contacts)])
|
||||
self.group_list = GroupList(groups=['test_group'])
|
||||
self.group = self.group_list.get_group('test_group')
|
||||
self.group.members = self.contact_list.contacts
|
||||
self.account_list = [nick_to_pub_key(str(n)) for n in range(no_contacts)]
|
||||
|
||||
def test_invalid_group_name_raises_fr(self):
|
||||
# Setup
|
||||
|
@ -142,6 +147,7 @@ class TestGroupCreate(TFCTestCase):
|
|||
class TestGroupAddMember(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.user_input = UserInput()
|
||||
self.contact_list = ContactList(nicks=['Alice', 'Bob'])
|
||||
self.group_list = GroupList()
|
||||
|
@ -151,6 +157,7 @@ class TestGroupAddMember(TFCTestCase):
|
|||
self.args = self.contact_list, self.group_list, self.settings, self.queues, self.settings
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
tear_queues(self.queues)
|
||||
|
||||
def configure_groups(self, no_contacts: int) -> None:
|
||||
|
@ -206,6 +213,7 @@ class TestGroupAddMember(TFCTestCase):
|
|||
class TestGroupRmMember(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.user_input = UserInput()
|
||||
self.contact_list = ContactList(nicks=['Alice', 'Bob'])
|
||||
|
@ -216,6 +224,7 @@ class TestGroupRmMember(TFCTestCase):
|
|||
self.args = self.contact_list, self.group_list, self.settings, self.queues, self.settings
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
tear_queues(self.queues)
|
||||
|
||||
|
@ -239,6 +248,7 @@ class TestGroupRmMember(TFCTestCase):
|
|||
class TestGroupRmGroup(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.user_input = UserInput()
|
||||
self.contact_list = ContactList(nicks=['Alice', 'Bob'])
|
||||
|
@ -249,6 +259,7 @@ class TestGroupRmGroup(TFCTestCase):
|
|||
self.args = self.contact_list, self.group_list, self.settings, self.queues, self.settings
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
tear_queues(self.queues)
|
||||
|
||||
|
@ -280,6 +291,7 @@ class TestGroupRmGroup(TFCTestCase):
|
|||
class TestGroupRename(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.queues = gen_queue_dict()
|
||||
self.settings = Settings()
|
||||
self.contact_list = ContactList()
|
||||
|
@ -288,6 +300,7 @@ class TestGroupRename(TFCTestCase):
|
|||
self.args = self.window, self.contact_list, self.group_list, self.settings, self.queues
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
tear_queues(self.queues)
|
||||
|
||||
def test_contact_window_raises_fr(self):
|
||||
|
|
|
@ -25,7 +25,9 @@ import unittest
|
|||
from unittest import mock
|
||||
|
||||
from src.common.crypto import blake2b
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (COMMAND_PACKET_QUEUE, CONFIRM_CODE_LENGTH, FINGERPRINT_LENGTH, KDB_REMOVE_ENTRY_HEADER,
|
||||
KEY_MANAGEMENT_QUEUE, LOG_SETTING_QUEUE, RELAY_PACKET_QUEUE, TM_COMMAND_PACKET_QUEUE,
|
||||
WIN_TYPE_CONTACT, WIN_TYPE_GROUP)
|
||||
|
||||
from src.transmitter.contact import add_new_contact, change_nick, contact_setting, remove_contact
|
||||
|
||||
|
@ -38,6 +40,7 @@ from tests.utils import nick_to_onion_address, nick_to_pub_key, tear_queu
|
|||
class TestAddNewContact(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.contact_list = ContactList()
|
||||
self.group_list = GroupList()
|
||||
self.settings = Settings(disable_gui_dialog=True)
|
||||
|
@ -46,6 +49,7 @@ class TestAddNewContact(TFCTestCase):
|
|||
self.args = self.contact_list, self.group_list, self.settings, self.queues, self.onion_service
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
with ignored(OSError):
|
||||
os.remove(f'v4dkh.psk - Give to hpcra')
|
||||
tear_queues(self.queues)
|
||||
|
@ -76,9 +80,9 @@ class TestAddNewContact(TFCTestCase):
|
|||
self.assertNotEqual(contact.tx_fingerprint, bytes(FINGERPRINT_LENGTH))
|
||||
|
||||
@mock.patch('src.transmitter.key_exchanges.ARGON2_PSK_MEMORY_COST', 200)
|
||||
@mock.patch('src.transmitter.key_exchanges.MIN_KEY_DERIVATION_TIME', 0.1)
|
||||
@mock.patch('src.transmitter.key_exchanges.MIN_KEY_DERIVATION_TIME', 1.0)
|
||||
@mock.patch('builtins.input', side_effect=[nick_to_onion_address("Alice"), 'Alice_', 'psk', '.'])
|
||||
@mock.patch('src.common.statics.MIN_KEY_DERIVATION_TIME', 0.1)
|
||||
@mock.patch('src.common.statics.MAX_KEY_DERIVATION_TIME', 1.0)
|
||||
@mock.patch('builtins.input', side_effect=[nick_to_onion_address("Alice"), 'Alice_', 'psk', '.', '', 'ff', 'fc'])
|
||||
@mock.patch('getpass.getpass', return_value='test_password')
|
||||
@mock.patch('time.sleep', return_value=None)
|
||||
def test_standard_nick_psk_kex(self, *_):
|
||||
|
@ -97,6 +101,7 @@ class TestAddNewContact(TFCTestCase):
|
|||
class TestRemoveContact(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.contact_list = ContactList(nicks=['Alice'])
|
||||
self.group_list = GroupList(groups=['test_group'])
|
||||
|
@ -107,6 +112,7 @@ class TestRemoveContact(TFCTestCase):
|
|||
self.args = self.contact_list, self.group_list, self.settings, self.queues, self.master_key
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
tear_queues(self.queues)
|
||||
|
||||
|
@ -216,6 +222,7 @@ class TestRemoveContact(TFCTestCase):
|
|||
class TestChangeNick(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.contact_list = ContactList(nicks=['Alice'])
|
||||
self.group_list = GroupList()
|
||||
self.settings = Settings()
|
||||
|
@ -223,6 +230,7 @@ class TestChangeNick(TFCTestCase):
|
|||
self.args = self.contact_list, self.group_list, self.settings, self.queues
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
tear_queues(self.queues)
|
||||
|
||||
def test_missing_nick_raises_fr(self):
|
||||
|
@ -235,7 +243,8 @@ class TestChangeNick(TFCTestCase):
|
|||
contact=create_contact('Bob'))
|
||||
|
||||
# Test
|
||||
self.assert_fr("Error: Nick must be printable.", change_nick, UserInput("nick Alice\x01"), window, *self.args)
|
||||
self.assert_fr("Error: Nick must be printable.",
|
||||
change_nick, UserInput("nick Alice\x01"), window, *self.args)
|
||||
|
||||
def test_no_contact_raises_fr(self):
|
||||
# Setup
|
||||
|
@ -244,7 +253,8 @@ class TestChangeNick(TFCTestCase):
|
|||
window.contact = None
|
||||
|
||||
# Test
|
||||
self.assert_fr("Error: Window does not have contact.", change_nick, UserInput("nick Alice\x01"), window, *self.args)
|
||||
self.assert_fr("Error: Window does not have contact.",
|
||||
change_nick, UserInput("nick Alice\x01"), window, *self.args)
|
||||
|
||||
def test_successful_nick_change(self):
|
||||
# Setup
|
||||
|
@ -274,6 +284,7 @@ class TestChangeNick(TFCTestCase):
|
|||
class TestContactSetting(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.contact_list = ContactList(nicks=['Alice', 'Bob'])
|
||||
self.group_list = GroupList(groups=['test_group'])
|
||||
self.settings = Settings()
|
||||
|
@ -282,6 +293,7 @@ class TestContactSetting(TFCTestCase):
|
|||
self.args = self.contact_list, self.group_list, self.settings, self.queues
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
tear_queues(self.queues)
|
||||
|
||||
def test_invalid_command_raises_fr(self):
|
||||
|
|
|
@ -31,12 +31,14 @@ from tests.utils import cd_unit_test, cleanup, TFCTestCase
|
|||
class TestFile(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.window = TxWindow()
|
||||
self.settings = Settings()
|
||||
self.args = self.window, self.settings
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
|
||||
def test_missing_file_raises_fr(self):
|
||||
|
|
|
@ -25,7 +25,7 @@ from unittest import mock
|
|||
from unittest.mock import MagicMock
|
||||
|
||||
from src.common.crypto import blake2b
|
||||
from src.common.statics import *
|
||||
from src.common.statics import CONFIRM_CODE_LENGTH
|
||||
|
||||
from src.transmitter.input_loop import input_loop
|
||||
|
||||
|
@ -51,6 +51,7 @@ class TestInputLoop(unittest.TestCase):
|
|||
'/exit'] # Enter exit command
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.settings = Settings(disable_gui_dialog=True)
|
||||
self.gateway = Gateway()
|
||||
self.contact_list = ContactList()
|
||||
|
@ -60,6 +61,7 @@ class TestInputLoop(unittest.TestCase):
|
|||
self.queues = gen_queue_dict()
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
tear_queues(self.queues)
|
||||
|
||||
@mock.patch('builtins.input', side_effect=input_list)
|
||||
|
|
|
@ -26,7 +26,11 @@ from unittest import mock
|
|||
|
||||
from src.common.crypto import blake2b
|
||||
from src.common.encoding import b58encode
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (COMMAND_PACKET_QUEUE, CONFIRM_CODE_LENGTH, ECDHE, FINGERPRINT_LENGTH,
|
||||
KDB_ADD_ENTRY_HEADER, KEX_STATUS_HAS_RX_PSK, KEX_STATUS_NO_RX_PSK, KEX_STATUS_PENDING,
|
||||
KEX_STATUS_UNVERIFIED, KEX_STATUS_VERIFIED, KEY_MANAGEMENT_QUEUE, LOCAL_ID, LOCAL_NICK,
|
||||
LOCAL_PUBKEY, RELAY_PACKET_QUEUE, SYMMETRIC_KEY_LENGTH, TFC_PUBLIC_KEY_LENGTH,
|
||||
WIN_TYPE_CONTACT, WIN_TYPE_GROUP, XCHACHA20_NONCE_LENGTH)
|
||||
|
||||
from src.transmitter.key_exchanges import create_pre_shared_key, export_onion_service_data, new_local_key
|
||||
from src.transmitter.key_exchanges import rxp_load_psk, start_key_exchange, verify_fingerprints
|
||||
|
@ -39,6 +43,7 @@ from tests.utils import nick_to_short_address, tear_queues, TFCTestCase,
|
|||
class TestOnionService(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.contact_list = ContactList()
|
||||
self.settings = Settings()
|
||||
self.onion_service = OnionService()
|
||||
|
@ -56,12 +61,14 @@ class TestOnionService(TFCTestCase):
|
|||
class TestLocalKey(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.contact_list = ContactList()
|
||||
self.settings = Settings()
|
||||
self.queues = gen_queue_dict()
|
||||
self.args = self.contact_list, self.settings, self.queues
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
tear_queues(self.queues)
|
||||
|
||||
def test_new_local_key_when_traffic_masking_is_enabled_raises_fr(self):
|
||||
|
@ -127,12 +134,14 @@ class TestVerifyFingerprints(unittest.TestCase):
|
|||
class TestKeyExchange(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.contact_list = ContactList()
|
||||
self.settings = Settings()
|
||||
self.queues = gen_queue_dict()
|
||||
self.args = self.contact_list, self.settings, self.queues
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
tear_queues(self.queues)
|
||||
|
||||
@mock.patch('shutil.get_terminal_size', return_value=[200, 200])
|
||||
|
@ -143,7 +152,7 @@ class TestKeyExchange(TFCTestCase):
|
|||
@mock.patch('shutil.get_terminal_size', return_value=[200, 200])
|
||||
@mock.patch('builtins.input', return_value=b58encode((TFC_PUBLIC_KEY_LENGTH-1)*b'a', public_key=True))
|
||||
def test_invalid_public_key_length_raises_fr(self, *_):
|
||||
self.assert_fr("Error: Invalid public key length",
|
||||
self.assert_fr("Error: Invalid public key length",
|
||||
start_key_exchange, nick_to_pub_key("Alice"), 'Alice', *self.args)
|
||||
|
||||
@mock.patch('builtins.input', side_effect=['', # Empty message should resend key
|
||||
|
@ -235,14 +244,16 @@ class TestKeyExchange(TFCTestCase):
|
|||
class TestPSK(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.contact_list = ContactList()
|
||||
self.settings = Settings(disable_gui_dialog=True)
|
||||
self.queues = gen_queue_dict()
|
||||
self.onion_service = OnionService()
|
||||
self.args = self.contact_list, self.settings, self.onion_service, self.queues
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.contact_list = ContactList()
|
||||
self.settings = Settings(disable_gui_dialog=True)
|
||||
self.queues = gen_queue_dict()
|
||||
self.onion_service = OnionService()
|
||||
self.args = self.contact_list, self.settings, self.onion_service, self.queues
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
|
||||
with ignored(OSError):
|
||||
|
@ -250,7 +261,7 @@ class TestPSK(TFCTestCase):
|
|||
|
||||
tear_queues(self.queues)
|
||||
|
||||
@mock.patch('builtins.input', side_effect=['/root/', '.'])
|
||||
@mock.patch('builtins.input', side_effect=['/root/', '.', 'fc'])
|
||||
@mock.patch('time.sleep', return_value=None)
|
||||
@mock.patch('getpass.getpass', return_value='test_password')
|
||||
@mock.patch('src.transmitter.key_exchanges.ARGON2_PSK_MEMORY_COST', 1000)
|
||||
|
@ -292,11 +303,13 @@ class TestPSK(TFCTestCase):
|
|||
class TestReceiverLoadPSK(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.settings = Settings()
|
||||
self.queues = gen_queue_dict()
|
||||
self.args = self.settings, self.queues
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
tear_queues(self.queues)
|
||||
|
||||
def test_raises_fr_when_traffic_masking_is_enabled(self):
|
||||
|
@ -324,7 +337,7 @@ class TestReceiverLoadPSK(TFCTestCase):
|
|||
# Test
|
||||
self.assert_fr(f"Error: The current key was exchanged with {ECDHE}.",
|
||||
rxp_load_psk, window, contact_list, *self.args)
|
||||
|
||||
|
||||
@mock.patch('src.transmitter.key_exchanges.ARGON2_PSK_MEMORY_COST', 1000)
|
||||
@mock.patch('src.transmitter.key_exchanges.ARGON2_PSK_TIME_COST', 0.01)
|
||||
@mock.patch('time.sleep', return_value=None)
|
||||
|
|
|
@ -27,7 +27,12 @@ import unittest
|
|||
from multiprocessing import Queue
|
||||
from unittest import mock
|
||||
|
||||
from src.common.statics import *
|
||||
from src.common.statics import (ASSEMBLY_PACKET_LENGTH, COMMAND, COMMAND_PACKET_QUEUE, C_A_HEADER, C_E_HEADER,
|
||||
C_L_HEADER, C_S_HEADER, FILE, F_A_HEADER, F_E_HEADER, F_L_HEADER, F_S_HEADER,
|
||||
GROUP_MSG_INVITE_HEADER, LOCAL_ID, MESSAGE, MESSAGE_PACKET_QUEUE, M_A_HEADER,
|
||||
M_E_HEADER, M_L_HEADER, M_S_HEADER, RELAY_PACKET_QUEUE, SYMMETRIC_KEY_LENGTH,
|
||||
TM_COMMAND_PACKET_QUEUE, TM_FILE_PACKET_QUEUE, TM_MESSAGE_PACKET_QUEUE,
|
||||
WIN_TYPE_CONTACT, WIN_TYPE_GROUP)
|
||||
|
||||
from src.transmitter.packet import cancel_packet, queue_command, queue_file, queue_message, queue_assembly_packets
|
||||
from src.transmitter.packet import send_file, send_packet, split_to_assembly_packets
|
||||
|
@ -40,11 +45,13 @@ from tests.utils import cd_unit_test, cleanup, gen_queue_dict, tear_queue
|
|||
class TestQueueMessage(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.queues = gen_queue_dict()
|
||||
self.settings = Settings()
|
||||
self.args = self.settings, self.queues
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
tear_queues(self.queues)
|
||||
|
||||
def test_private_message_header(self):
|
||||
|
@ -84,15 +91,17 @@ class TestQueueMessage(unittest.TestCase):
|
|||
class TestSendFile(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.settings = Settings()
|
||||
self.queues = gen_queue_dict()
|
||||
self.window = TxWindow()
|
||||
self.onion_service = OnionService()
|
||||
self.contact_list = ContactList(nicks=['Alice', 'Bob', 'Charlie'])
|
||||
self.args = self.settings, self.queues, self.window
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.settings = Settings()
|
||||
self.queues = gen_queue_dict()
|
||||
self.window = TxWindow()
|
||||
self.onion_service = OnionService()
|
||||
self.contact_list = ContactList(nicks=['Alice', 'Bob', 'Charlie'])
|
||||
self.args = self.settings, self.queues, self.window
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
tear_queues(self.queues)
|
||||
|
||||
|
@ -149,10 +158,12 @@ class TestQueueFile(TFCTestCase):
|
|||
'rx_serial_settings.json', 'tx_onion_db')
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.unit_test_dir = cd_unit_test()
|
||||
self.queues = gen_queue_dict()
|
||||
self.queues = gen_queue_dict()
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
cleanup(self.unit_test_dir)
|
||||
tear_queues(self.queues)
|
||||
|
||||
|
@ -266,10 +277,12 @@ class TestQueueFile(TFCTestCase):
|
|||
class TestQueueCommand(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.settings = Settings()
|
||||
self.queues = gen_queue_dict()
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
tear_queues(self.queues)
|
||||
|
||||
def test_queue_command(self):
|
||||
|
@ -323,14 +336,16 @@ class TestSplitToAssemblyPackets(unittest.TestCase):
|
|||
class TestQueueAssemblyPackets(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.settings = Settings()
|
||||
self.queues = gen_queue_dict()
|
||||
self.window = TxWindow(uid=nick_to_pub_key("Alice"),
|
||||
log_messages=True)
|
||||
self.window.window_contacts = [create_contact('Alice')]
|
||||
self.args = self.settings, self.queues, self.window
|
||||
self.args = self.settings, self.queues, self.window
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
tear_queues(self.queues)
|
||||
|
||||
def test_queue_message_traffic_masking(self):
|
||||
|
@ -425,6 +440,7 @@ class TestSendPacket(unittest.TestCase):
|
|||
"""
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.l_queue = Queue()
|
||||
self.key_list = KeyList(nicks=['Alice'])
|
||||
self.settings = Settings()
|
||||
|
@ -432,6 +448,7 @@ class TestSendPacket(unittest.TestCase):
|
|||
self.onion_service = OnionService()
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
tear_queue(self.l_queue)
|
||||
|
||||
def test_message_length(self):
|
||||
|
@ -497,9 +514,11 @@ class TestSendPacket(unittest.TestCase):
|
|||
class TestCancelPacket(TFCTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Pre-test actions."""
|
||||
self.queues = gen_queue_dict()
|
||||
|
||||
def tearDown(self):
|
||||
"""Post-test actions."""
|
||||
tear_queues(self.queues)
|
||||
|
||||
def test_cancel_message_during_normal(self):
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue