From 5f3ac0aad81557d4a95cb98fce7109112fb9351c Mon Sep 17 00:00:00 2001 From: Markus Ottela Date: Tue, 2 Apr 2024 18:34:25 +0300 Subject: [PATCH] Pass crypto module import errors with Relay Program, use single file again. Use older format for public key serialization. --- install.sh | 26 ++--- install.sh.asc | 26 ++--- launchers/TFC-RP-Tails.desktop | 4 +- release_automation.sh | 4 +- requirements-dev.txt | 4 +- requirements-relay.txt | 10 +- requirements.txt | 4 +- src/common/crypto.py | 123 +++++++++++++++++++++-- src/common/crypto_phf.py | 104 ------------------- src/common/db_masterkey.py | 3 +- src/receiver/key_exchanges.py | 3 +- src/relay/onion.py | 5 +- src/transmitter/key_exchanges.py | 3 +- tests/common/test_crypto.py | 159 ++++++++++++++++++++++++++++- tests/common/test_crypto_phf.py | 167 ------------------------------- 15 files changed, 320 insertions(+), 325 deletions(-) delete mode 100644 src/common/crypto_phf.py delete mode 100644 tests/common/test_crypto_phf.py diff --git a/install.sh b/install.sh index f8bc021..e99040c 100644 --- a/install.sh +++ b/install.sh @@ -147,8 +147,8 @@ dependency_hashes['MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl']='913f2 dependency_hashes['MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl']='1f1306cd2b0eb0931e12900e489180d455e62646079c9fc8c0d4a53fcb592466fa71674fb0da627d617d4e7c37c65b0243248a5ac8b6fd120b6e8e903821b558' dependency_hashes['MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl']='7f4b54cf745603eb15883a76e8c978a6684f1b585e119af300e52434171d2c2411bbb0731d3d6454a0ae410de1134544163961e0fb1765bfc220afcabfb1fac4' dependency_hashes['MarkupSafe-2.1.5.tar.gz']='3ba5af43d23c266377f5d32b11e1faa7955ea8c67eb1c32886c308527f93e75e387294d0eec7794c0c20aad0c705b27f3d1f86b04202f3b63068d12d4053cc71' -dependency_hashes['pycparser-2.21-py2.py3-none-any.whl']='aae67923f45abd1d781d03e0ce848627a07e9cf4c61a89bf32f1b5f638ceda08de39a038c46ed29d2df967d76be4f2572346bad087ac32b418e6fe654fd28e43' -dependency_hashes['pycparser-2.21.tar.gz']='e61fbdde484d1cf74d4b27bdde40cf2da4b7028ca8ecd37c83d77473dab707d457321aecaf97da3b114c1d58a4eb200290b76f9c958044b57e5fed949895b5f0' +dependency_hashes['pycparser-2.22-py3-none-any.whl']='14a66293830ac4b7ffa5dcf964e6c36c25888e6f7b0fb1f50acbbc586806bfa9c691fa6159e64f060b2f00a834caa858ba62f7045291c452c163d6b42e29f62c' +dependency_hashes['pycparser-2.22.tar.gz']='c9a81c78d87162f71281a32a076b279f4f7f2e17253fe14c89c6db5f9b3554a6563ff700c385549a8b51ef8832f99f7bb4ac07f22754c7c475dd91feeb0cf87f' dependency_hashes['PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl']='853446c38ce5488e18eba166f67650bc4f50044f509987ad2ae4830d2ed85284f057c3a4304180ad265bc33fb9cd6570488a37e40bade5e202ba201ad368af84' dependency_hashes['PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl']='d3f24397a6a3a7a56652a56c8e8cfcb1ebc167b0d2dfc38c450d8fbc363b2b5c1226e58724a692b16a7e4f1022bb93e904664ff54640bba28720134058e2275f' dependency_hashes['PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl']='c3502047add3590c1a1e60908e21d62455e2b4ba2f2efaf0f5118bf915934de9604d00036b3215ed54890a51a088872909409b296141d6241159c119d751a947' @@ -170,8 +170,8 @@ dependency_hashes['typing_extensions-4.10.0-py3-none-any.whl']='f3056ce051ebf092 dependency_hashes['typing_extensions-4.10.0.tar.gz']='d3d840719ed0cf1435a959f84a65df93f55fb4bfdda926cd74a34a8bb6ab0407108ee8941f40b6cb570e2f7c440abffb0bc1d0f0414814047de6e9c3eeb24093' dependency_hashes['urllib3-2.2.1-py3-none-any.whl']='82525e89629af701c2ce44ed6766c3b4c1f7b57ce0a3418342849dd4c13802b901b0942e7199d6e268ae03a14b67bc023bdc66fd827f4f50a3c4895271245c9d' dependency_hashes['urllib3-2.2.1.tar.gz']='dfadba099db678ee1567ee95aa11a72fcea0a76df094d04dd4bd7ed5df4ea2fda6917cc122a1e2bfa4f5303916f93a7e1c881fbbf3cbb9415a25bd7eca1b14b3' -dependency_hashes['werkzeug-3.0.1-py3-none-any.whl']='56cfbfb2d084bf6ab08305f204647abbd2d66074770e1ade45515216d759e6dfeafd51d743801f2291b6befe670b5b3eaa59bdae736be8942611315f7abd105d' -dependency_hashes['werkzeug-3.0.1.tar.gz']='83bacda231cd714cf111ebcaf78b47f7e400cefbaf4a450bde99b630136c2976a2e7629a3a34140493c5f54c2ea1c034c673085dd7d1fd7ce8f1da49d8576bb8' +dependency_hashes['werkzeug-3.0.2-py3-none-any.whl']='8904c195547630ef173356c8286deb5fa9140f193ea017680b9ea67a57db4d4c7479fe9023f02f78882a5212bf8e79240d994b13322d8a7cb6e4348b6b69b760' +dependency_hashes['werkzeug-3.0.2.tar.gz']='5f9a91684653da17ef419459db11a6a4bd83b6c1f2f6b0e9f0a296bc5c7c63d2b465044a131f579557f1fb240a560793b734f3d0248ecbb7b6af62b1713484c3' # ---------------------------------------------------------------------------------------- @@ -202,7 +202,7 @@ function compare_digest { function verify_tcb_requirements_files { # To minimize the time TCB installer configuration stays online, # only the requirements.txt file is authenticated between downloads. - compare_digest 542d453ad60aabf3341b2ff06b404a269554c7c698d3e3c6020f88990c92863d0ee64f9ba344e915df735ad2603a7f943b4cf09057186bd85a344989d14c2419 '' requirements.txt + compare_digest 63e1b3a74dcc8b996502e0f489b5d682255e0608707bce1215716a7b65b8951feb30c4f3cbd82ec1cf01f773a20abb5aaec221852505eb1920609e49ea5dbbb2 '' requirements.txt } @@ -212,8 +212,8 @@ function verify_files { compare_digest 74915e048cf8b5207abf603136e7d5fcf5b8ad512cce78a2ebe3c88fc3150155893bf9824e6ed6a86414bbe4511a6bd4a42e8ec643c63353dc8eea4a44a021cd '' LICENSE compare_digest da5fad3d2dfcb2bb992df4af9ce5c7a792b489cf9f86f966610c2d5933536a8ff9f1be2ec2bf2cbc3eab46cd469ffba35a739eacd31dadb704a28f14cd75cefa '' LICENSE-3RD-PARTY compare_digest 55b954740233846aaed4360b5ec39e5fd52c3050541b221013fd62fe66e65f21c3ef3527dc97f3db1013a26768a9f2cc1400bf84bf4b26bb18f79c00e1f500f5 '' relay.py - compare_digest 1dab82c7be5704162cf7c279e3472e573b39a1000d70407c9e7731ec767b5c394b6a8c20ebfe63b9a57176800807e66d4231891d6ffa6bf82f831ecacfe3ec9b '' requirements-dev.txt - compare_digest 35de14ab7ba0fd91a15cce841c6f3276e9310b68a6fd1d0c895c93602472f104987036ebd639e3f74c31834ce7411653dd0449a144e113e24abee4409ef130f1 '' requirements-relay.txt + compare_digest 6930acb2fe76242d2037795ca3721e8fe6b49a82059fc8f53f9f9492a81a1e1e6a608437835c07dda485b7cfa8a57557032fa881246ab0cd4152a295b651b642 '' requirements-dev.txt + compare_digest f061f75177b08103f5bf835dd31f06f3db0b57374635ec51f4eb999dffc1737766e19bbb5ffed4a475596d9a26b10ed171d01e7d06889075e447672701d886f7 '' requirements-relay.txt compare_digest 0e66cb8609a0f9abc9ca0f76298593a076cd5c8b85abaa0d8b3b765d40a2599a4ea2d33fb4792a97dc5ceee3b71e1682715f71555d5502c87ea20da922d05a7f '' tfc.png compare_digest 14bbec9fce50ea69082724347d075ed81685b982743384a9690d7c95ae5c1655a35d01ec5b4d8e4ffeee91c37ef0e2c7ca3d79ea8578abb7c87c22b5d9fb9928 '' tfc.py compare_digest f9600e3e31075a53e009cec13474399f5553517f62fa2bcf9048987e47a5eb1b1c2443eadf14aea5ec7f4e67f4caa08809bc11ec8984a184a8c249bde1d2688a '' tfc.yml @@ -226,7 +226,7 @@ function verify_files { compare_digest e3b1a1ecac9fccdc4a708351fd71fd0897621446df13fde0e2ebc34d4704d945dcfdd256af1cec92d6ebe6c5497bce3db98d052f8885db8e579345769e5fe758 launchers/ tfc-qubes-transmitter compare_digest 2cb288015524281c001c2090c952a607a984568f8d62146a5e7bc4ba8cc8e4105fb411324cc8806a05ad99b501d9585bbded43b5ad8762db698ea6ef7681e1ef launchers/ TFC-RP.desktop compare_digest 6bb0b26d0f9cd800fae0a1cdd13c48bb030fa9141591d5f8dde02b55693b47213e1989095f4187f286f515314b50ee25c90364a4dad7099b6bf15cf39cbeb183 launchers/ TFC-RP-Qubes.desktop - compare_digest 06a3ca0aebba98941a4ae55dc8af84a84db84ea805f64268b9bdc273369ea1fbad322cd4c830179fff2423848c6bc8302134ea65b67afeea6caf23f5a08dbf29 launchers/ TFC-RP-Tails.desktop + compare_digest 93cce601b7f13b80848d7432262df6aabfca24291da0a465315cd36ffef05486c9e013b4b553835cdecf311c0dffa75d3935fbe2fe7d53c10a8f07d778e0ed9a launchers/ TFC-RP-Tails.desktop compare_digest 228e401ac0fe9a20125cc8a6370daf61c81da0ef24216eaca7364777a373559d170a6f27757814ff31b6856c02e37a5e004e03875d9688f76cc3f97bac5c581d launchers/ TFC-RxP.desktop compare_digest fb9fcb15ba4853cd07b61566e071dfca68a61de717ca29765e472cbbbc08215fde967e83e5744f7c29af3a45a4a4c5c6f18eab0076dab08eec3516455eb0b8b6 launchers/ TFC-RxP-Qubes.desktop compare_digest 1db227da47e7e818461b3ada5318d08e963ce55287d12d0239ad19f0746c43d8b1b3339b810e5048af48868e69dd8722976f01e981ee39240ff613cc2470ff31 launchers/ TFC-TxP.desktop @@ -237,13 +237,13 @@ function verify_files { compare_digest 90218be097758a94ed60189393756682fa84cac0c025bd9c3c1f26bf21357800398ac505eb9f4b0f565ff3cfe52c48a0463a939a60e1e197a674d88cda314583 src/ __init__.py compare_digest 90218be097758a94ed60189393756682fa84cac0c025bd9c3c1f26bf21357800398ac505eb9f4b0f565ff3cfe52c48a0463a939a60e1e197a674d88cda314583 src/common/ __init__.py - compare_digest aeb3f9b67de6ba4834910d67e1a74d1457ca1f31ca55488565ad8cb13c08bd5f9819759d17f47ed98f0300b56c98f1a7f13093433e8d316115ea4d0eaefbbebf src/common/ crypto.py + compare_digest f661099948ace9cf8cb67dbe12914611bf60d2d4354d330ed416a98323757aef79d5898129cd8027461013c0cda9db8edce3aba9f9b13d510b4de75e3a553494 src/common/ crypto.py compare_digest b18f02061b26b4bbfe2e6b7d073dc4f72b652756bcd7067509a2216e89ef41e9e3004541412e3dea00663a46b870d0423fa8040f2c1f613d8f32c649937f0b57 src/common/ database.py compare_digest e581b79859eb74096263732c84319260833bacc6d595b3592d61bf630271807c13896d2278724fd1d439c5d721d856c6b77f38d61e542bcd3c75828eb93d362f src/common/ db_contacts.py compare_digest b32d333356b53487761c6c7214f60ff526aff0f42cdce7eb7fe26f3d528d60ca97ed728e3deaa3d322e2c1d2460fb9f5bc8e8b7d5147f360362645a820f1ded1 src/common/ db_groups.py compare_digest 37bebfafaec2ca8d9e02ff2090092954f8fb07a4163d17c077c4c67958c97189babbc6cffb706440000c4ac1d78662bd52f088ab8dc6b29215077ef6a0af7a95 src/common/ db_keys.py compare_digest 9adb670eeac30cafcd95e64c5250cae5d7d3543cdb80e00ef8a71f3fc6b43547eb4b282b66e2483cd9905db99fc12ce2bdd8a2255dc8457cf8d589b0370f7770 src/common/ db_logs.py - compare_digest 87b43ed3ca6d244a311e6344507c842327162851c0e32ad5e6d12bc4f770a32d148a3cf7c1b180c06f9ce0efa6d3135ff22bb7417d72d181ffefc1b928a94c30 src/common/ db_masterkey.py + compare_digest 5b22c434083e07f17a0bc8d5a8fae47ab774e244b98575541ea39fb0472c9a43a9a6e4286fb244209965394f7dad0bf57c34f360d4f7a24924a361aea505efea src/common/ db_masterkey.py compare_digest 59cd5925a5727d796c516a038955965564d30b5fac529b069623d14fa43eb2cd344ed6b3688f42e7019483c12b587c21fcfc4875db273e6f959fb99c8c502029 src/common/ db_onion.py compare_digest a33144fe0650f30c50753178023169fdd77e9f23a94fc19e1bd3f555d216831f8752301524e63d90d2f89ee9ecb54fbdcdc52c47530466ee7a5182d4b1900f90 src/common/ db_settings.py compare_digest 9b67271611202da7bc112a460f53e96d9cd4e713407087a2a1ef1bf927a675ff8f51e65ce77168d325e599e59f4963c13d6e964464352011f27497e7783b3c5f src/common/ encoding.py @@ -261,7 +261,7 @@ function verify_files { compare_digest 6b3860213104873fbfd59917e4d52e31466696f3494ffbbd5edd4a8bccacfc72e3141b2c6779d52da7d393b501f03a40fb0d4e86270d65fd1129ad3d508ed98e src/receiver/ commands.py compare_digest b33c47f47ce37b7f3905ff99f690300cbd4b96e6b2d4b785f06361b47d9993e6a6740435e09313cfacd63650705e85eeb29c49706da369c2424205b9cdf44a45 src/receiver/ commands_g.py compare_digest 5838632327e7b3fdd5c5c17f3f2d75db7a62d75f7444b9d2bebda7b72ed3f48c82745d8aa78a74320f8f6c4e6d22b1228cab0a8c0dfb2fa1d642c8cc85ecfefd src/receiver/ files.py - compare_digest 2f2d9ecf366307c3c8bd9b18f8ad499452fae6bad9b8344ddc97e44c1ffebec9cc26fa6578206448cbf68cb33dd4546bb78121b30eb7c18eba572c0ac89eaa0d src/receiver/ key_exchanges.py + compare_digest dfc94e6762f5c20af24aff902c039733d1c9cc89344d259de62e1acd768bfb3690401f1ebfd1c26e11346ee0b443d22d0f7029373100a9c4f08078b068b47ded src/receiver/ key_exchanges.py compare_digest 92b4b00726e2722ca7e96178e5716e72a440ead63289a9acedb30784f7cdec410a8577ee71c5384fdb54c7a654ab09828f1713109ec2f167df9ae97fda9c2578 src/receiver/ messages.py compare_digest aa76be19d367c89f94884ddfcec88c76215628d2a12b815a7d9990e85d921c42885a60bd3dffd88b64b68a7a414a12e1ff2e8c5b6994df51576a1b31dd9b1b91 src/receiver/ output_loop.py compare_digest 0ec4241a996dce75009dc53783e06c500acdd55de45b11e191b1b1b0a41a896c9138299ef0ef5356370881249e7358d0a1a9e9d440883584603675cc3afa47ea src/receiver/ packet.py @@ -272,7 +272,7 @@ function verify_files { compare_digest d2336f5e6a5aa5c41c39f95b4fe6e1413e5a87ac305cb31916c58df130cff81a6952045942b63f1546c82d6d3b05c38e3d0c6ddd69428d239a9030d150bda562 src/relay/ client.py compare_digest a56aa18162a25b2fb665ec81b93745bd9aa8e2b5ef88f75a0984ef50ef2fb7d90cc2287c4e1ce54b2fcbca89f7a89e84024d58904c4d6c948fa356448466bc63 src/relay/ commands.py compare_digest a27e65526e0eb40319dd65a2739d94e2aae6c0d67eefc3885f54c8ebdd390d8331f8682aff56aea9911e94a24edecf28435011eed7713dbdca58a9e7f927bf22 src/relay/ diffs.py - compare_digest d3e2aa129e91c0048167dbce055c0fd14e4c71701bed84fed30783c2d79d94a01dcc3a1c1eb6476ef8cdaad33bed12c68c4bdfffefa3a27cb3be5f394a09ad51 src/relay/ onion.py + compare_digest a5ff25e6a99dddc87f2fa26bfb7271a108af1b418a0ebae9c90b455226e2c06016c12a01e78d05b40235e87111354f80090294c6bf5a48f74a5a94956c72bb55 src/relay/ onion.py compare_digest 49e838d7d5758cd94ff547fbebfe3a5de08bf5b2e1e7c35313d29c20ee24d258484996a11b36402de4113704efcf8fdec05db23a4ab774d5cfb7a4670f7f697b src/relay/ server.py compare_digest 0a702b1ef2be7f359b8f43e27ca917d39c36ecf47b98b57829b9d1729a9149e8543089ee332f67d0aece79fd16c12f563090d70ecc1203e355368fb80f5e508f src/relay/ tcb.py @@ -282,7 +282,7 @@ function verify_files { compare_digest e5fd8ab8978d38fe617606581783d0c9807dfcc8c95e0e31cd3a7d1ce6ad88d3f88ba49c070ec69743c11b8707a6cd1f4ea979f2dfe49621a095e910e14a82a8 src/transmitter/ contact.py compare_digest e14e407035e0b8342320f205d1b934c5f521ae2e86aaeb8fc6265572b0aed06e64fc86b0b96253efe45f4499b3ecd80c865e3424c5d2cf46d3e31f051232b8a9 src/transmitter/ files.py compare_digest ad73ffa1c20620a01cf846a73cf0f3d0dc786beaa3be00bb3996ca087ca1d8a960f962474989ccf096f98b94d73c65552e0905c8b697c9438ffbcb3fded235b9 src/transmitter/ input_loop.py - compare_digest 0bb0a0e4d51b001c12cf3135c916e34343e14f3075d210641e6d99914892af4a02a8e0652a9c81229ef6dbf746c7aca9f80331f1bc6a538bacda8dfcd25dc86e src/transmitter/ key_exchanges.py + compare_digest 0a30f4e76bce2c9cff0511ea191e55060007f302b1b985083f6c56856893fa15a2d4e5bcf069ced0cee7e9e2c81d943838e124cfd3cd670fa732b1baff149726 src/transmitter/ key_exchanges.py compare_digest de827c1be6adc2fc36c115eb1fee6670488cb9076b89d755d3b1edcc322321424e4b3c5e86d1e49ea1e26c75ea727765890f7aeffc88a7fa235f1359efa83d6b src/transmitter/ packet.py compare_digest 52e6332a949dc15be10699e5e156aea6ba1e4ac4bc10c2e8777474c627569b7e3ca79f79b1ed2e4799704ba94ea7aa653a85b891c35273e51170239d851de485 src/transmitter/ sender_loop.py compare_digest b4a0e38437caef8256fd46087b8bd85757b9c459684ba16b26fda80e3f7bb472dfe3cb0118c3831d08d5a96a40b97185144ad2e730cfb29492aab588a5db3e60 src/transmitter/ traffic_masking.py diff --git a/install.sh.asc b/install.sh.asc index d495a80..bcf9369 100644 --- a/install.sh.asc +++ b/install.sh.asc @@ -1,16 +1,16 @@ -----BEGIN PGP SIGNATURE----- -iQIzBAABCgAdFiEEbab1Q2VetFRTHbYIAsu63k/6kOYFAmYMF4EACgkQAsu63k/6 -kObnexAApL2urkYxCw+GKi7bEYmHUquGAm5vpVV/jNeXrlh8nB4IF0foJn3QcrW7 -fjqj8NO2cSKI+6CgZUoeCbJCLRJ9i1xwH+LiKmI7Nq99aSNHsl7V/MU/hYQZphYA -VwlZM2q4+t3Y1zIBSDixyUzP0l/57VugZFdK6Nk6xfv56QS1UwjbhOaUg1oHsrh5 -iEm/1aBPs0UPfpRfJjnCoFEGuaIBgJwH6dUNRAQsWOVl4HDNK7vEpZZ1FpGbYgE3 -QAOKQ7rtl7BuI96OfXxRQjFWQ7aUkpPbFDelG2yb33FGNYiYlYTCHaQ8h3X597ov -pCV0sYhDZWA8RNFn76NsJNYKl/LAmXjYNGQJYfmBWCwjuJ0Plg/0rhMPKeET4uIY -B25h6z4/bEy9zjZio/tZfwTOrzATYNS+FMpb6wT03NC8Fpk7Wy3RSUDxil/+e0LT -9p/qsS34nEJvqNAbN4qjHLNbZVKi3QRZz7cBfNcDZ6CDR9s0EYbVVfY78buY57PZ -6L5I7DifV08Q/xvynXfd41et5/RsAJZN76mWb92UjaA7ZyXU96wZahiAQY1eJlEV -nZWi8msQsN7WTrJITBN0TD7oYYvK1kFObbBLCR2m5P9u2Uhir99MfiBC1GtuQb7d -wD6KPRtP43B60AmvEM58/ROyr1kWhWyMNhGUvjJValELwQu6Kb0= -=DR1J +iQIzBAABCgAdFiEEbab1Q2VetFRTHbYIAsu63k/6kOYFAmYMJXMACgkQAsu63k/6 +kOZFwxAA2ZhofP/id4wqM02zceJlsr9xDuJo8Mx/Syn//S75tCnfmuB6NKVcgSN0 +7R/RYtyNAwLQRyraBvln334PgNX24KhGJ8j1yyMvabQ26K9NHthe8e5mbj/ozepY +yxcfuExV8B0ImZ7AMx3/Rj8GUJdtDZyHOrmY09HNmtc9C8u1ZpZdCEpmBesjF7Tz +r2xGKCbUhGgFkV+D71u4cyx1JQVp5JqfDDZ/W1ANJSlWRaLigQfRb4g5ruqzJ7S4 +gDpr9uLLGqjbZ4lMkrFyOBg3NUIqLGXVdRgJhSj+XZ5NVVSA4CNTyt4V/LfKCEon +kXxyJMH1pyz6mm07lHTv1GkWAiH5Zbd9oEgGxnaW93Vyq+UHjvXJDVmHssFBJvNP +AlTzK+/TgWpxoEEYKfxzsc94g+aHWi5Q+AFa5JFV9VlOXgtxJv1n0yHz0gEiRq3/ +iPEdx62LcE1NmxkDC5cYPnXjAb5jEsf0tDEf2BaKtsWyq/IX7LJT5YHYrEVYFhEL +FDFe+e1JM9vQJBjZ8guainpkC+GnyD0PrjV+tocyKJjB0moCyJZmFeuue2gukyS3 +394bTYUbD/XPbkZtKYG19V0f8R/TQOu2f65V1bBW1rDHwnwVO/6F2ICd+YFw9WT5 +8xnN1uCepFpNQtxNmc4tPqblCS5ES5gbRFI3ensR2t0/IoWEMeg= +=HJ0h -----END PGP SIGNATURE----- diff --git a/launchers/TFC-RP-Tails.desktop b/launchers/TFC-RP-Tails.desktop index bbc7c4c..e85426f 100755 --- a/launchers/TFC-RP-Tails.desktop +++ b/launchers/TFC-RP-Tails.desktop @@ -1,7 +1,7 @@ [Desktop Entry] Version=1.24.04 Name=TFC-Relay -Exec=gnome-terminal --geometry=105x25 -x bash -c "cd /opt/tfc && source venv_relay/bin/activate && python3.11 'relay.py' && deactivate || bash" +Exec=gnome-terminal --geometry=105x25 -x bash -c "python3 '/opt/tfc/relay.py' && deactivate || bash" Icon=tfc.png Terminal=false Type=Application @@ -10,5 +10,5 @@ Actions=Launch-Test [Desktop Action Launch-Test] Name=Launch-Test -Exec=gnome-terminal --geometry=105x25 -x bash -c "cd /opt/tfc && source venv_relay/bin/activate && python3.11 'relay.py' -t && deactivate || bash" +Exec=gnome-terminal --geometry=105x25 -x bash -c "python3 '/opt/tfc/relay.py' -t && deactivate || bash" OnlyShowIn=Unity; diff --git a/release_automation.sh b/release_automation.sh index f862ace..0f4cc86 100644 --- a/release_automation.sh +++ b/release_automation.sh @@ -201,8 +201,8 @@ function main() { # DL Actions # install_global_dependencies # update_ide_venv - #update_dependencies - #test_requirement_files_with_pinned_hashes + # update_dependencies + # test_requirement_files_with_pinned_hashes # Actions #run_mypy_type_checks diff --git a/requirements-dev.txt b/requirements-dev.txt index 3be5132..05f0223 100755 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -4,7 +4,7 @@ argon2-cffi>=23.1.0 argon2-cffi-bindings>=21.2.0 cffi>=1.16.0 -pycparser>=2.21 +pycparser>=2.22 # cryptography (pyca) (Provides X448 key exchange) cryptography>=42.0.5 @@ -16,7 +16,7 @@ click>=8.1.7 itsdangerous>=2.1.2 Jinja2>=3.1.3 MarkupSafe>=2.1.5 -Werkzeug>=3.0.1 +Werkzeug>=3.0.2 # mypy (Static type checking tool) mypy>=1.9.0 diff --git a/requirements-relay.txt b/requirements-relay.txt index 7a99ff8..d483af7 100755 --- a/requirements-relay.txt +++ b/requirements-relay.txt @@ -72,10 +72,10 @@ MarkupSafe==2.1.5 --hash=sha512:f32cc6753cfaedeae6392e1f7fc8523ccbbd --hash=sha512:1f1306cd2b0eb0931e12900e489180d455e62646079c9fc8c0d4a53fcb592466fa71674fb0da627d617d4e7c37c65b0243248a5ac8b6fd120b6e8e903821b558 \ --hash=sha512:7f4b54cf745603eb15883a76e8c978a6684f1b585e119af300e52434171d2c2411bbb0731d3d6454a0ae410de1134544163961e0fb1765bfc220afcabfb1fac4 \ --hash=sha512:3ba5af43d23c266377f5d32b11e1faa7955ea8c67eb1c32886c308527f93e75e387294d0eec7794c0c20aad0c705b27f3d1f86b04202f3b63068d12d4053cc71 -Werkzeug==3.0.1 --hash=sha512:56cfbfb2d084bf6ab08305f204647abbd2d66074770e1ade45515216d759e6dfeafd51d743801f2291b6befe670b5b3eaa59bdae736be8942611315f7abd105d \ - --hash=sha512:83bacda231cd714cf111ebcaf78b47f7e400cefbaf4a450bde99b630136c2976a2e7629a3a34140493c5f54c2ea1c034c673085dd7d1fd7ce8f1da49d8576bb8 +Werkzeug==3.0.2 --hash=sha512:8904c195547630ef173356c8286deb5fa9140f193ea017680b9ea67a57db4d4c7479fe9023f02f78882a5212bf8e79240d994b13322d8a7cb6e4348b6b69b760 \ + --hash=sha512:5f9a91684653da17ef419459db11a6a4bd83b6c1f2f6b0e9f0a296bc5c7c63d2b465044a131f579557f1fb240a560793b734f3d0248ecbb7b6af62b1713484c3 -# cryptography (pyca) (Handles URL token derivation and derives TFC account from Onion Service private key) +# cryptography (pyca) (Handles URL token derivation and and derives TFC account from Onion Service private key)) cryptography==42.0.5 --hash=sha512:fd840cb0f6c49078d2484fd2ff75a2c62c6ae58b69a01be0885a7bd088067e5f39f9e0de582e0a824525f7bbfe4d6e5831fe176f40fb01101df3f9a41e3ab14e \ --hash=sha512:615d99cedb543228cc45a49bde24883e920426cd32c964471149fcb994a74b8ca3edb00d1addd52d19c19d7689f9b978cd10f54ac6ca70368da9dc40c28625fe \ --hash=sha512:8b98785ff25b2fe0745d867e7055b54bf2ace5a21f9b42eda99c5a5fbd5bb4a6e74bedd6a3cf39c179570b351503ee0e7e937a04e1451f22a4fa0d69dac1f2cf \ @@ -130,8 +130,8 @@ cffi==1.16.0 --hash=sha512:47fc17ba58e9fc2e7829a4c028a0a067f0d2 --hash=sha512:5dc5ef04aa1b2ef2da537a932b8c11b49ee5e57c6ad214e6bddaef9a61b66a93952cc9f30b805da2c3c028fe58ea11cc25a56bb7fe2b116e7b9349dcc6075b5a \ --hash=sha512:e6d8ff3fe823c4d99dc88877e626a9428d554d671d476826bae7117a123074eaae3d42d1f16e7b94bb601ef781c22791e742319f8a9a82599184c23045412da6 \ --hash=sha512:fd2588115092202aa9289c9d4e0a0b3e264b5e9ec1dc192950f31aeb412fd9f9d4e5c96a3f9c6762987b58ccc1e229f2012ddda89211797104df672d8ed51152 -pycparser==2.21 --hash=sha512:aae67923f45abd1d781d03e0ce848627a07e9cf4c61a89bf32f1b5f638ceda08de39a038c46ed29d2df967d76be4f2572346bad087ac32b418e6fe654fd28e43 \ - --hash=sha512:e61fbdde484d1cf74d4b27bdde40cf2da4b7028ca8ecd37c83d77473dab707d457321aecaf97da3b114c1d58a4eb200290b76f9c958044b57e5fed949895b5f0 +pycparser==2.22 --hash=sha512:14a66293830ac4b7ffa5dcf964e6c36c25888e6f7b0fb1f50acbbc586806bfa9c691fa6159e64f060b2f00a834caa858ba62f7045291c452c163d6b42e29f62c \ + --hash=sha512:c9a81c78d87162f71281a32a076b279f4f7f2e17253fe14c89c6db5f9b3554a6563ff700c385549a8b51ef8832f99f7bb4ac07f22754c7c475dd91feeb0cf87f # PyNaCl (pyca) (Derives TFC account from Onion Service private key) PyNaCl==1.5.0 --hash=sha512:853446c38ce5488e18eba166f67650bc4f50044f509987ad2ae4830d2ed85284f057c3a4304180ad265bc33fb9cd6570488a37e40bade5e202ba201ad368af84 \ diff --git a/requirements.txt b/requirements.txt index cdeac73..42ef4f2 100755 --- a/requirements.txt +++ b/requirements.txt @@ -53,8 +53,8 @@ cffi==1.16.0 --hash=sha512:47fc17ba58e9fc2e7829a4c028a0a067f0d2 --hash=sha512:5dc5ef04aa1b2ef2da537a932b8c11b49ee5e57c6ad214e6bddaef9a61b66a93952cc9f30b805da2c3c028fe58ea11cc25a56bb7fe2b116e7b9349dcc6075b5a \ --hash=sha512:e6d8ff3fe823c4d99dc88877e626a9428d554d671d476826bae7117a123074eaae3d42d1f16e7b94bb601ef781c22791e742319f8a9a82599184c23045412da6 \ --hash=sha512:fd2588115092202aa9289c9d4e0a0b3e264b5e9ec1dc192950f31aeb412fd9f9d4e5c96a3f9c6762987b58ccc1e229f2012ddda89211797104df672d8ed51152 -pycparser==2.21 --hash=sha512:aae67923f45abd1d781d03e0ce848627a07e9cf4c61a89bf32f1b5f638ceda08de39a038c46ed29d2df967d76be4f2572346bad087ac32b418e6fe654fd28e43 \ - --hash=sha512:e61fbdde484d1cf74d4b27bdde40cf2da4b7028ca8ecd37c83d77473dab707d457321aecaf97da3b114c1d58a4eb200290b76f9c958044b57e5fed949895b5f0 +pycparser==2.22 --hash=sha512:14a66293830ac4b7ffa5dcf964e6c36c25888e6f7b0fb1f50acbbc586806bfa9c691fa6159e64f060b2f00a834caa858ba62f7045291c452c163d6b42e29f62c \ + --hash=sha512:c9a81c78d87162f71281a32a076b279f4f7f2e17253fe14c89c6db5f9b3554a6563ff700c385549a8b51ef8832f99f7bb4ac07f22754c7c475dd91feeb0cf87f # cryptography (pyca) (Handles TCB-side X448 key exchange) cryptography==42.0.5 --hash=sha512:fd840cb0f6c49078d2484fd2ff75a2c62c6ae58b69a01be0885a7bd088067e5f39f9e0de582e0a824525f7bbfe4d6e5831fe176f40fb01101df3f9a41e3ab14e \ diff --git a/src/common/crypto.py b/src/common/crypto.py index 95c074d..3af572c 100755 --- a/src/common/crypto.py +++ b/src/common/crypto.py @@ -36,13 +36,23 @@ import hashlib import os -import nacl.bindings -import nacl.exceptions -import nacl.secret -import nacl.utils - from typing import Tuple + +# Ignore packages not used in relay program. +try: + import argon2 +except ImportError: + pass + +try: + import nacl.bindings + import nacl.exceptions + import nacl.secret + import nacl.utils +except ImportError: + pass + from cryptography.hazmat.primitives import padding from cryptography.hazmat.primitives.asymmetric.x448 import X448PrivateKey, X448PublicKey from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat @@ -52,7 +62,7 @@ from src.common.statics import (BITS_PER_BYTE, BLAKE2_DIGEST_LENGTH, BLAKE2_DIGEST_LENGTH_MAX, BLAKE2_DIGEST_LENGTH_MIN, FINGERPRINT, FINGERPRINT_LENGTH, MESSAGE_KEY, HEADER_KEY, PADDING_LENGTH, SYMMETRIC_KEY_LENGTH, TFC_PUBLIC_KEY_LENGTH, - X448_SHARED_SECRET_LENGTH, XCHACHA20_NONCE_LENGTH) + X448_SHARED_SECRET_LENGTH, ARGON2_SALT_LENGTH, XCHACHA20_NONCE_LENGTH) def blake2b(message: bytes, # Message to hash @@ -137,7 +147,108 @@ def blake2b(message: bytes, # Message to hash return digest +def argon2_kdf(password: str, # Password to derive the key from + salt: bytes, # Salt to derive the key from + time_cost: int, # Number of iterations + memory_cost: int, # Amount of memory to use (in bytes) + parallelism: int # Number of threads to use + ) -> bytes: # The derived key + """Derive an encryption key from password and salt using Argon2id. + + Argon2 is a password hashing function designed by Alex Biryukov, + Daniel Dinu, and Dmitry Khovratovich from the University of + Luxembourg. The algorithm is the winner of the 2015 Password Hashing + Competition (PHC). + + For more details, see + https://password-hashing.net/ + https://en.wikipedia.org/wiki/Argon2 + + The reasons for using Argon2 in TFC include + + o PBKDF2 and bcrypt are not memory-hard, thus they are weak + against massively parallel computing attacks with + FPGAs/GPUs/ASICs.[1; p.2] + + o scrypt is very complex as it "combines two independent + cryptographic primitives (the SHA256 hash function, and + the Salsa20/8 core operation), and four generic operations + (HMAC, PBKDF2, Block-Mix, and ROMix)."[2; p.10] + Furthermore, scrypt is "vulnerable to trivial time-memory + trade-off (TMTO) attacks that allows compact implementations + with the same energy cost."[1; p.2] + + o Out of all the PHC finalists, only Catena and Argon2i offer + complete cache-timing resistance by using data-independent + memory access. Catena does not support parallelism[2; p.49], + thus if it later turns out TFC needs stronger protection from + cache-timing attacks, the selection of Argon2 (that always + supports parallelism) is ideal, as switching from Argon2id + to Argon2i is trivial. + + o More secure algorithms such as the Balloon hash function[3] do + not have robust implementations. + + The purpose of Argon2 is to stretch a password into a 256-bit key. + Argon2 features a slow, memory-hard hash function that consumes + computational resources of an attacker that attempts a dictionary + or a brute force attack. + + The function also takes a salt (256-bit random value in this case) + that prevents rainbow-table attacks, and forces each attack to take + place against an individual (physically compromised) TFC-endpoint, + or PSK transmission media. + + The Argon2 version used is the Argon2id, that is the current + recommendation of the draft RFC[4]. Argon2id uses data-independent + memory access for the first half of the first iteration, and + data-dependent memory access for the rest. This provides a lot of + protection against TMTO attacks which is great because most of the + expected attacks are against physically compromised data storage + devices where the encrypted data is at rest. + Argon2id also adds some security against side-channel attacks + that malicious code injected to the Destination Computer might + perform. Considering these two attacks, Argon2id is the most secure + choice. + + The correctness of the Argon2id implementation[5] is tested by TFC + unit tests. The testing is done by comparing the output of the + argon2_cffi library with the output of the Argon2 reference + command-line utility under randomized input parameters. + + [1] https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf + [2] https://password-hashing.net/submissions/specs/Catena-v5.pdf + [3] https://crypto.stanford.edu/balloon/ + [4] https://tools.ietf.org/html/draft-irtf-cfrg-argon2-12#section-7.4 + [5] https://github.com/P-H-C/phc-winner-argon2 + https://github.com/hynek/argon2_cffi + """ + if len(salt) != ARGON2_SALT_LENGTH: + raise CriticalError(f"Invalid salt length ({len(salt)} bytes).") + + try: + key = argon2.low_level.hash_secret_raw(secret=password.encode(), + salt=salt, + time_cost=time_cost, + memory_cost=memory_cost, + parallelism=parallelism, + hash_len=SYMMETRIC_KEY_LENGTH, + type=argon2.Type.ID) # type: bytes + + 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 + + class X448(object): + """\ X448 is the Diffie-Hellman function for Curve448-Goldilocks, a state-of-the-art elliptical curve published by Mike Hamburg in 2014. diff --git a/src/common/crypto_phf.py b/src/common/crypto_phf.py deleted file mode 100644 index 8e3de07..0000000 --- a/src/common/crypto_phf.py +++ /dev/null @@ -1,104 +0,0 @@ -import argon2 - -from src.common.exceptions import CriticalError -from src.common.statics import ARGON2_SALT_LENGTH, SYMMETRIC_KEY_LENGTH - - -def argon2_kdf(password: str, # Password to derive the key from - salt: bytes, # Salt to derive the key from - time_cost: int, # Number of iterations - memory_cost: int, # Amount of memory to use (in bytes) - parallelism: int # Number of threads to use - ) -> bytes: # The derived key - """Derive an encryption key from password and salt using Argon2id. - - Argon2 is a password hashing function designed by Alex Biryukov, - Daniel Dinu, and Dmitry Khovratovich from the University of - Luxembourg. The algorithm is the winner of the 2015 Password Hashing - Competition (PHC). - - For more details, see - https://password-hashing.net/ - https://en.wikipedia.org/wiki/Argon2 - - The reasons for using Argon2 in TFC include - - o PBKDF2 and bcrypt are not memory-hard, thus they are weak - against massively parallel computing attacks with - FPGAs/GPUs/ASICs.[1; p.2] - - o scrypt is very complex as it "combines two independent - cryptographic primitives (the SHA256 hash function, and - the Salsa20/8 core operation), and four generic operations - (HMAC, PBKDF2, Block-Mix, and ROMix)."[2; p.10] - Furthermore, scrypt is "vulnerable to trivial time-memory - trade-off (TMTO) attacks that allows compact implementations - with the same energy cost."[1; p.2] - - o Out of all the PHC finalists, only Catena and Argon2i offer - complete cache-timing resistance by using data-independent - memory access. Catena does not support parallelism[2; p.49], - thus if it later turns out TFC needs stronger protection from - cache-timing attacks, the selection of Argon2 (that always - supports parallelism) is ideal, as switching from Argon2id - to Argon2i is trivial. - - o More secure algorithms such as the Balloon hash function[3] do - not have robust implementations. - - The purpose of Argon2 is to stretch a password into a 256-bit key. - Argon2 features a slow, memory-hard hash function that consumes - computational resources of an attacker that attempts a dictionary - or a brute force attack. - - The function also takes a salt (256-bit random value in this case) - that prevents rainbow-table attacks, and forces each attack to take - place against an individual (physically compromised) TFC-endpoint, - or PSK transmission media. - - The Argon2 version used is the Argon2id, that is the current - recommendation of the draft RFC[4]. Argon2id uses data-independent - memory access for the first half of the first iteration, and - data-dependent memory access for the rest. This provides a lot of - protection against TMTO attacks which is great because most of the - expected attacks are against physically compromised data storage - devices where the encrypted data is at rest. - Argon2id also adds some security against side-channel attacks - that malicious code injected to the Destination Computer might - perform. Considering these two attacks, Argon2id is the most secure - choice. - - The correctness of the Argon2id implementation[5] is tested by TFC - unit tests. The testing is done by comparing the output of the - argon2_cffi library with the output of the Argon2 reference - command-line utility under randomized input parameters. - - [1] https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf - [2] https://password-hashing.net/submissions/specs/Catena-v5.pdf - [3] https://crypto.stanford.edu/balloon/ - [4] https://tools.ietf.org/html/draft-irtf-cfrg-argon2-12#section-7.4 - [5] https://github.com/P-H-C/phc-winner-argon2 - https://github.com/hynek/argon2_cffi - """ - if len(salt) != ARGON2_SALT_LENGTH: - raise CriticalError(f"Invalid salt length ({len(salt)} bytes).") - - try: - key = argon2.low_level.hash_secret_raw(secret=password.encode(), - salt=salt, - time_cost=time_cost, - memory_cost=memory_cost, - parallelism=parallelism, - hash_len=SYMMETRIC_KEY_LENGTH, - type=argon2.Type.ID) # type: bytes - - 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 diff --git a/src/common/db_masterkey.py b/src/common/db_masterkey.py index d7d0973..fc1cf24 100755 --- a/src/common/db_masterkey.py +++ b/src/common/db_masterkey.py @@ -27,8 +27,7 @@ from typing import Optional, Tuple -from src.common.crypto import blake2b, csprng -from src.common.crypto_phf import argon2_kdf +from src.common.crypto import argon2_kdf, blake2b, csprng from src.common.database import TFCUnencryptedDatabase from src.common.encoding import bytes_to_int, int_to_bytes from src.common.exceptions import CriticalError, graceful_exit, SoftError diff --git a/src/receiver/key_exchanges.py b/src/receiver/key_exchanges.py index 6283353..9b1974c 100755 --- a/src/receiver/key_exchanges.py +++ b/src/receiver/key_exchanges.py @@ -32,8 +32,7 @@ import nacl.exceptions -from src.common.crypto import auth_and_decrypt, blake2b, csprng -from src.common.crypto_phf import argon2_kdf +from src.common.crypto import argon2_kdf, auth_and_decrypt, blake2b, csprng from src.common.db_masterkey import MasterKey from src.common.encoding import b58encode, bytes_to_str, pub_key_to_short_address from src.common.exceptions import SoftError diff --git a/src/relay/onion.py b/src/relay/onion.py index fe78a6f..4c64626 100755 --- a/src/relay/onion.py +++ b/src/relay/onion.py @@ -32,6 +32,7 @@ from typing import Any, Dict, Optional, Union from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey +from cryptography.hazmat.primitives import serialization import stem.control import stem.process @@ -201,7 +202,9 @@ def onion_service(queues: Dict[bytes, 'Queue[Any]'], test_run: bool) -> None: time.sleep(0.1) private_key, c_code = queues[ONION_KEY_QUEUE].get() - public_key_user = Ed25519PrivateKey.from_private_bytes(private_key).public_key().public_bytes_raw() + public_key_user = Ed25519PrivateKey.from_private_bytes(private_key).public_key().public_bytes( + encoding=serialization.Encoding.Raw, + format=serialization.PublicFormat.Raw) onion_addr_user = pub_key_to_onion_address(public_key_user) buffer_key = hashlib.blake2b(BUFFER_KEY, key=private_key, digest_size=SYMMETRIC_KEY_LENGTH).digest() diff --git a/src/transmitter/key_exchanges.py b/src/transmitter/key_exchanges.py index 64bc3c4..ad3d104 100755 --- a/src/transmitter/key_exchanges.py +++ b/src/transmitter/key_exchanges.py @@ -25,8 +25,7 @@ from typing import Any, Dict -from src.common.crypto import blake2b, csprng, encrypt_and_sign, X448 -from src.common.crypto_phf import argon2_kdf +from src.common.crypto import argon2_kdf, blake2b, csprng, encrypt_and_sign, X448 from src.common.db_masterkey import MasterKey from src.common.encoding import bool_to_bytes, int_to_bytes, pub_key_to_short_address, str_to_bytes, b58encode from src.common.exceptions import SoftError diff --git a/tests/common/test_crypto.py b/tests/common/test_crypto.py index ee3c6dd..9bd1ec7 100755 --- a/tests/common/test_crypto.py +++ b/tests/common/test_crypto.py @@ -20,14 +20,18 @@ """ import hashlib +import multiprocessing import os +import random import subprocess import unittest +from string import ascii_letters, digits from unittest import mock from unittest.mock import MagicMock from typing import Callable +import argon2 import nacl.exceptions import nacl.public import nacl.utils @@ -35,9 +39,10 @@ from cryptography.hazmat.primitives.asymmetric.x448 import X448PrivateKey from cryptography.hazmat.primitives.serialization import Encoding, NoEncryption, PrivateFormat -from src.common.crypto import (auth_and_decrypt, blake2b, byte_padding, check_kernel_version, csprng, +from src.common.crypto import (argon2_kdf, auth_and_decrypt, blake2b, byte_padding, check_kernel_version, csprng, encrypt_and_sign, rm_padding_bytes, X448) -from src.common.statics import (BLAKE2_DIGEST_LENGTH, BLAKE2_DIGEST_LENGTH_MAX, +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) @@ -164,6 +169,156 @@ def test_invalid_size_blake2b_digest_raises_critical_error(self, mock_blake2b: M mock_blake2b.assert_called() +class TestArgon2KDF(unittest.TestCase): + """\ + Similar to normal cryptographic hash functions, a password hashing + function such as the Argon2 also generates unpredictable values + (secret keys in this case). The IETF test vectors[1] require + parameters (e.g. the "Secret" and the "Associated data" fields) that + the argon2_cffi library does not provide. The only available option + is to generate the test vectors dynamically. + To do that, this test downloads and compiles the command-line + utility[2] for the reference implementation of Argon2. Next, the + test compiles and runs the command-line utility's tests. It then + generates random (but valid) input parameters, and compares the + output of the argon2_cffi library to the output of the command-line + utility under those input parameters. + + [1] https://tools.ietf.org/html/draft-irtf-cfrg-argon2-09#section-5.3 + [2] https://github.com/P-H-C/phc-winner-argon2#command-line-utility + """ + + def setUp(self) -> None: + """Pre-test actions.""" + self.unit_test_dir = cd_unit_test() + self.number_of_tests = 256 + + file_url = 'https://github.com/P-H-C/phc-winner-argon2/archive/master.zip' + file_name = 'phc-winner-argon2-master.zip' + + # Download the Argon2 command-line utility. + subprocess.Popen(f'wget {file_url} -O {file_name}', shell=True).wait() + + # Verify the SHA256 hash of the zip-file containing the command-line utility. + with open(file_name, 'rb') as f: + file_data = f.read() + self.assertEqual('c13017dcbc3239fbcd35ef3cf8949f4c052817ad5ce8195b59110f401479ad14', + hashlib.sha256(file_data).hexdigest()) + + # Unzip, compile, and test the command-line utility. + subprocess.Popen(f'unzip {file_name}', shell=True).wait() + os.chdir('phc-winner-argon2-master/') + subprocess.Popen(f'/usr/bin/make', shell=True).wait() + subprocess.Popen('/usr/bin/make test', shell=True).wait() + + def tearDown(self) -> None: + """Post-test actions.""" + os.chdir('..') + cleanup(self.unit_test_dir) + + def test_argon2_cffi_using_the_official_command_line_utility(self) -> None: + + # Command-line utility's parameter limits. + min_password_length = 1 + max_password_length = 127 + min_salt_length = 8 + min_parallelism = 1 + max_parallelism = multiprocessing.cpu_count() + min_time_cost = 1 + min_memory_cost = 7 + min_key_length = 4 + + # Arbitrary limits set for the test. + max_salt_length = 128 + max_time_cost = 3 + max_memory_cost = 15 + max_key_length = 64 + + sys_rand = random.SystemRandom() + + for _ in range(self.number_of_tests): + + # Generate random parameters for the test. + len_password = sys_rand.randint(min_password_length, max_password_length) + len_salt = sys_rand.randint(min_salt_length, max_salt_length) + parallelism = sys_rand.randint(min_parallelism, max_parallelism) + time_cost = sys_rand.randint(min_time_cost, max_time_cost) + memory_cost = sys_rand.randint(min_memory_cost, max_memory_cost) + key_length = sys_rand.randint(min_key_length, max_key_length) + + password = ''.join([sys_rand.choice(ascii_letters + digits) for _ in range(len_password)]) + salt = ''.join([sys_rand.choice(ascii_letters + digits) for _ in range(len_salt)]) + + # Generate a key test vector using the command-line utility. + output = subprocess.check_output( + f'echo -n "{password}" | ./argon2 {salt} ' + f'-t {time_cost} ' + f'-m {memory_cost} ' + f'-p {parallelism} ' + f'-l {key_length} ' + f'-id', + shell=True).decode() # type: str + + key_test_vector = output.split('\n')[4].split('\t')[-1] + + # Generate a key using the argon2_cffi library. + purported_key = argon2.low_level.hash_secret_raw(secret=password.encode(), + salt=salt.encode(), + time_cost=time_cost, + memory_cost=2**memory_cost, + parallelism=parallelism, + hash_len=key_length, + type=argon2.Type.ID).hex() + + self.assertEqual(purported_key, key_test_vector) + + +class TestArgon2Wrapper(unittest.TestCase): + + def setUp(self) -> None: + """Pre-test actions.""" + self.salt = os.urandom(ARGON2_SALT_LENGTH) + self.password = 'password' + + def test_invalid_length_salt_raises_critical_error(self) -> None: + invalid_salts = [salt_length * b'a' for salt_length in [0, ARGON2_SALT_LENGTH-1, + ARGON2_SALT_LENGTH+1, 1000]] + for invalid_salt in invalid_salts: + with self.assertRaises(SystemExit): + argon2_kdf(self.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) -> None: + with self.assertRaises(SystemExit): + argon2_kdf(self.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) -> None: + with self.assertRaises(SystemExit): + argon2_kdf(self.password, self.salt, ARGON2_MIN_TIME_COST, ARGON2_MIN_MEMORY_COST, ARGON2_MIN_PARALLELISM) + with self.assertRaises(SystemExit): + argon2_kdf(self.password, self.salt, ARGON2_MIN_TIME_COST, ARGON2_MIN_MEMORY_COST, ARGON2_MIN_PARALLELISM) + + def test_too_small_time_cost_raises_critical_error(self) -> None: + with self.assertRaises(SystemExit): + argon2_kdf(self.password, self.salt, ARGON2_MIN_TIME_COST-1, ARGON2_MIN_MEMORY_COST, ARGON2_MIN_PARALLELISM) + + def test_too_small_memory_cost_raises_critical_error(self) -> None: + with self.assertRaises(SystemExit): + argon2_kdf(self.password, self.salt, ARGON2_MIN_TIME_COST, ARGON2_MIN_MEMORY_COST-1, ARGON2_MIN_PARALLELISM) + + def test_too_small_parallelism_raises_critical_error(self) -> None: + with self.assertRaises(SystemExit): + argon2_kdf(self.password, self.salt, ARGON2_MIN_TIME_COST, ARGON2_MIN_MEMORY_COST, ARGON2_MIN_PARALLELISM-1) + + def test_argon2_kdf_key_type_and_length(self) -> None: + key = argon2_kdf(self.password, self.salt, ARGON2_MIN_TIME_COST, ARGON2_MIN_MEMORY_COST, ARGON2_MIN_PARALLELISM) + self.assertIsInstance(key, bytes) + self.assertEqual(len(key), SYMMETRIC_KEY_LENGTH) + + class TestX448(unittest.TestCase): """\ Again, since the X448 output (shared secret) is an unpredictable diff --git a/tests/common/test_crypto_phf.py b/tests/common/test_crypto_phf.py deleted file mode 100644 index bbc898e..0000000 --- a/tests/common/test_crypto_phf.py +++ /dev/null @@ -1,167 +0,0 @@ -import hashlib -import multiprocessing -import os -import random -import subprocess -import unittest -from string import ascii_letters, digits -from unittest import mock -from unittest.mock import MagicMock - -import argon2 - -from src.common.crypto_phf import argon2_kdf -from src.common.statics import ARGON2_SALT_LENGTH, ARGON2_MIN_TIME_COST, ARGON2_MIN_MEMORY_COST, ARGON2_MIN_PARALLELISM, \ - SYMMETRIC_KEY_LENGTH - -from tests.utils import cd_unit_test, cleanup - - -class TestArgon2KDF(unittest.TestCase): - """\ - Similar to normal cryptographic hash functions, a password hashing - function such as the Argon2 also generates unpredictable values - (secret keys in this case). The IETF test vectors[1] require - parameters (e.g. the "Secret" and the "Associated data" fields) that - the argon2_cffi library does not provide. The only available option - is to generate the test vectors dynamically. - To do that, this test downloads and compiles the command-line - utility[2] for the reference implementation of Argon2. Next, the - test compiles and runs the command-line utility's tests. It then - generates random (but valid) input parameters, and compares the - output of the argon2_cffi library to the output of the command-line - utility under those input parameters. - - [1] https://tools.ietf.org/html/draft-irtf-cfrg-argon2-09#section-5.3 - [2] https://github.com/P-H-C/phc-winner-argon2#command-line-utility - """ - - def setUp(self) -> None: - """Pre-test actions.""" - self.unit_test_dir = cd_unit_test() - self.number_of_tests = 256 - - file_url = 'https://github.com/P-H-C/phc-winner-argon2/archive/master.zip' - file_name = 'phc-winner-argon2-master.zip' - - # Download the Argon2 command-line utility. - subprocess.Popen(f'wget {file_url} -O {file_name}', shell=True).wait() - - # Verify the SHA256 hash of the zip-file containing the command-line utility. - with open(file_name, 'rb') as f: - file_data = f.read() - self.assertEqual('c13017dcbc3239fbcd35ef3cf8949f4c052817ad5ce8195b59110f401479ad14', - hashlib.sha256(file_data).hexdigest()) - - # Unzip, compile, and test the command-line utility. - subprocess.Popen(f'unzip {file_name}', shell=True).wait() - os.chdir('phc-winner-argon2-master/') - subprocess.Popen(f'/usr/bin/make', shell=True).wait() - subprocess.Popen('/usr/bin/make test', shell=True).wait() - - def tearDown(self) -> None: - """Post-test actions.""" - os.chdir('..') - cleanup(self.unit_test_dir) - - def test_argon2_cffi_using_the_official_command_line_utility(self) -> None: - - # Command-line utility's parameter limits. - min_password_length = 1 - max_password_length = 127 - min_salt_length = 8 - min_parallelism = 1 - max_parallelism = multiprocessing.cpu_count() - min_time_cost = 1 - min_memory_cost = 7 - min_key_length = 4 - - # Arbitrary limits set for the test. - max_salt_length = 128 - max_time_cost = 3 - max_memory_cost = 15 - max_key_length = 64 - - sys_rand = random.SystemRandom() - - for _ in range(self.number_of_tests): - - # Generate random parameters for the test. - len_password = sys_rand.randint(min_password_length, max_password_length) - len_salt = sys_rand.randint(min_salt_length, max_salt_length) - parallelism = sys_rand.randint(min_parallelism, max_parallelism) - time_cost = sys_rand.randint(min_time_cost, max_time_cost) - memory_cost = sys_rand.randint(min_memory_cost, max_memory_cost) - key_length = sys_rand.randint(min_key_length, max_key_length) - - password = ''.join([sys_rand.choice(ascii_letters + digits) for _ in range(len_password)]) - salt = ''.join([sys_rand.choice(ascii_letters + digits) for _ in range(len_salt)]) - - # Generate a key test vector using the command-line utility. - output = subprocess.check_output( - f'echo -n "{password}" | ./argon2 {salt} ' - f'-t {time_cost} ' - f'-m {memory_cost} ' - f'-p {parallelism} ' - f'-l {key_length} ' - f'-id', - shell=True).decode() # type: str - - key_test_vector = output.split('\n')[4].split('\t')[-1] - - # Generate a key using the argon2_cffi library. - purported_key = argon2.low_level.hash_secret_raw(secret=password.encode(), - salt=salt.encode(), - time_cost=time_cost, - memory_cost=2**memory_cost, - parallelism=parallelism, - hash_len=key_length, - type=argon2.Type.ID).hex() - - self.assertEqual(purported_key, key_test_vector) - - -class TestArgon2Wrapper(unittest.TestCase): - - def setUp(self) -> None: - """Pre-test actions.""" - self.salt = os.urandom(ARGON2_SALT_LENGTH) - self.password = 'password' - - def test_invalid_length_salt_raises_critical_error(self) -> None: - invalid_salts = [salt_length * b'a' for salt_length in [0, ARGON2_SALT_LENGTH-1, - ARGON2_SALT_LENGTH+1, 1000]] - for invalid_salt in invalid_salts: - with self.assertRaises(SystemExit): - argon2_kdf(self.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) -> None: - with self.assertRaises(SystemExit): - argon2_kdf(self.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) -> None: - with self.assertRaises(SystemExit): - argon2_kdf(self.password, self.salt, ARGON2_MIN_TIME_COST, ARGON2_MIN_MEMORY_COST, ARGON2_MIN_PARALLELISM) - with self.assertRaises(SystemExit): - argon2_kdf(self.password, self.salt, ARGON2_MIN_TIME_COST, ARGON2_MIN_MEMORY_COST, ARGON2_MIN_PARALLELISM) - - def test_too_small_time_cost_raises_critical_error(self) -> None: - with self.assertRaises(SystemExit): - argon2_kdf(self.password, self.salt, ARGON2_MIN_TIME_COST-1, ARGON2_MIN_MEMORY_COST, ARGON2_MIN_PARALLELISM) - - def test_too_small_memory_cost_raises_critical_error(self) -> None: - with self.assertRaises(SystemExit): - argon2_kdf(self.password, self.salt, ARGON2_MIN_TIME_COST, ARGON2_MIN_MEMORY_COST-1, ARGON2_MIN_PARALLELISM) - - def test_too_small_parallelism_raises_critical_error(self) -> None: - with self.assertRaises(SystemExit): - argon2_kdf(self.password, self.salt, ARGON2_MIN_TIME_COST, ARGON2_MIN_MEMORY_COST, ARGON2_MIN_PARALLELISM-1) - - def test_argon2_kdf_key_type_and_length(self) -> None: - key = argon2_kdf(self.password, self.salt, ARGON2_MIN_TIME_COST, ARGON2_MIN_MEMORY_COST, ARGON2_MIN_PARALLELISM) - self.assertIsInstance(key, bytes) - self.assertEqual(len(key), SYMMETRIC_KEY_LENGTH)