#!/usr/bin/env bash

# TFC - Onion-routed, endpoint secure messaging system
# Copyright (C) 2013-2024  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/>.

# Installer configuration

INSTALL_DIR="/opt/tfc"


# ----------------------------------------------------------------------------------------

# PIP dependency file hashes
declare -A dependency_hashes
dependency_hashes['argon2_cffi-23.1.0-py3-none-any.whl']='f0d80298b5617e8ed7ae7442f582caeeb3a5450562af18df4d7b5bd7395cfa99597f5ba31128d3105ee498e6661a50fa34602df374103ea523a2d7e832d7b7d6'
dependency_hashes['argon2_cffi-23.1.0.tar.gz']='7c7730451f5ef9bb40bb5e1bbfa6e69c9718968168f3fa9b54e1020a4f805f98fba6260039bda804241717db2338479d640c7652dc26ce1a6ade076660133383'
dependency_hashes['argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl']='c3218d723db5c8f2dbc9c737a3ce24d52291a8056b855c6e988956821894b695f2afd50b189a581e9cb5a5d1c13b9b1144be9fb6296a62681b209412caf85b42'
dependency_hashes['argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl']='7a77d7e0becc167b7b348b19d5fc65e25fea4dd93a8e26c203b39f88691515a756a78becfb665a4aa965112a9b561be6c4461fe38db422fe20198b3139d652ff'
dependency_hashes['argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl']='1bd48d7a11cb99cc0d8526b2dc156e025002092d1f7c60632a0470275ade2374d3be6138ecf924eae2c33fb5d29fef16729b710294cc0b8f35f50a7544d17cc5'
dependency_hashes['argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_aarch64.whl']='c1f3ede9138689922619a240242a9d11aaf5942058c896be8ddce517d3ebf386767804e93d9dc4055ad04c682560764e9020c87f96f0d949a36e758025d09fe1'
dependency_hashes['argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_i686.whl']='ddb220ba4de3b61cefb657776d64ea0351e89b1f6e2eb04664e024f3e0dc46f1cdb11159b79f8cb9d0530c3dbc4bcfb9d89234fff567d53b06e0564ffa46afea'
dependency_hashes['argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_x86_64.whl']='96c611449056eccf03f2e2cb5f8b6353ddd9f852c607f2fc6cda5f13bf8f8d554f2679b6c221b2114ff0b3ae5cb9ba841a7fc1affde840f5dcda65eb9c319d83'
dependency_hashes['argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl']='7097f92df079b66b1d59b699523e2a87f2864e1e3c59bc2a4c71cf5657e6f3b094c62e626009602d1c44f07b5e1f2fc79ab03e916475cde07b6b6c6494e7388d'
dependency_hashes['argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl']='d8556d123fb96046837c6eb9205cb9fdb817f5ec6aa482f9f6ded77a3e67f1b49a21e1273acc0fcbaed7f4bf46b34fd555ec085ffc9a2daea1345186444f7c7c'
dependency_hashes['argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl']='8940ebaa9466f7810e385d0721042ae9c117480cc659436cd903b5c37af02fd9c9dce8d5ed95fb45ef7305777445cdb8fb00c82671bd577e1eeaf81f2691ae8d'
dependency_hashes['argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl']='9c77fbaecb073de920ed570cb7c7956a04c904e7e9616badde275791048a487fcd7e85eb6bf79d3f6bba6fdbc398e32cc208bda8fe368a84897b8d35da8e6bbd'
dependency_hashes['argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl']='74f931f3262333e3451295075abbfe0c323beadad36ffe73b65143e906f1e8cab924601a56a3f184ca2516fedcb69dc7d8dc6ea1d0ba25bccb21476a89bc46d8'
dependency_hashes['argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl']='740c76d800bf8bea95f800457ef03d4125b262d034e4a62e66615144a8abe35950b37fe5627553e5bc448ffe32f77ebeee5e2a04857409f53604fb30de7d8d72'
dependency_hashes['argon2-cffi-bindings-21.2.0.tar.gz']='71d023ae96073ed78599e4f4e42f8efcc985cc329adeea00b14b54eaac1e6a545e6ad9b7f4cfdc60a4e9c396f95053c0ccb6f6f67d92f70265f91315fff4a390'
dependency_hashes['blinker-1.8.1-py3-none-any.whl']='2bee24786f60124d6e07900213433a2d4b42eb48439f7f435da1e51f0fdb77d137083aa37d0d62d473bdfa24d63d9cf23712a3dd6416a491eaf0e326062ba185'
dependency_hashes['blinker-1.8.1.tar.gz']='b6adada7f314369863070e830364833aade954e4cedca390643223d632c110a314856fb1c050402cad7e64959500c4d27d149ce7b682ee6bde99a69dc66ea495'
dependency_hashes['certifi-2024.2.2-py3-none-any.whl']='7a3bd4849f95e1715fe2e99613df70a0fedd944a9bfde71a0fadb837fe62c3431c30da4f0b75c74de6f1a459f1fdf7cb62eaf404fdbe45e2d121e0b1021f1580'
dependency_hashes['certifi-2024.2.2.tar.gz']='2191710dc2cfdf781df498c3ecd5f38dfc5215e2c2dc402cdcd484376dbd7fe2e442793cc856e93f6033c1fc43cb77c71d2dc785dbfe0d8cd10fd3120ee3c2fd'
dependency_hashes['cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl']='47fc17ba58e9fc2e7829a4c028a0a067f0d2c9a23dec886674fb69098645bfa6e9a67a0a78439216e420b8f63be98818cd6dcd07fd270279385b9c3787710223'
dependency_hashes['cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl']='e76d186d948fa47d5747c116f165fd788e808f40a043ac6bac9a0cecb4d353c1138ca4e2050ddc73c21119b254935a8097a2006f37391ccf525612c305b77dfd'
dependency_hashes['cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl']='c303d9ff3a67f7b6765eafcf0e296456673916a3c4cf4a04c40153c783cf93b380cca78298b0de6b79ba1ad53bf79798887af0cb208ffd34d3bac5528e04fb51'
dependency_hashes['cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl']='38d62fd917816afa5795c1ff68810bda9e197e9b17f351075e03a11d0d84369093a39e0b690e1ae6d933578f90c0f8573f9640e43f9160a7431d854045ad79ef'
dependency_hashes['cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl']='4b6336b34388197360186916b81988e7421d78be4ade5d2a1b0ce19b7a19ce64d2831111ecd89a9e549b4ae4e01a46e146bd2675122c68f4ce92d4e5f865fcbb'
dependency_hashes['cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl']='e0410aa181003dce4ce74a2674450089fda1d954c3eea2ca6852bf32e45e9b1fad7173e67448d448cdc06d3c333e2572fd2ccd0b0d79bc819df452abd5ee9e08'
dependency_hashes['cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl']='f7e5df0d9f9ae2f8621d123c3f1a88f491d42d3491560ae7dcb372da06fc9de8b9f7634c256ad1cba77c04c96d61965ab6418a39c8ce17a52a916de28edbfebc'
dependency_hashes['cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl']='1faf3db5a6078c51b7b674feeed46f507a94fc0acfba6ad7a8e97bcfac5be653873388725fe9ca95481bc465449f692d9044ed76a86fb7eef1e114dd127f268a'
dependency_hashes['cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl']='20be55a756e84c738a854fa017354095d43990db3f2343397f992415f892ba5ed1fbccae829093bfb2f6d1bd6dbe3761a978a705f833e80385fb92d05f7814a0'
dependency_hashes['cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl']='c2a5fbad7ad536cb72af44d1e9c6c9f77ac69ad527f0a3c8473c587e9dfba462cc23e36135c82ba742190b291f8d78d3568cf0fdcf1c0afb43f8eb225e7dbe2a'
dependency_hashes['cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl']='3b1844eb7be44b411014a5728850037b65509e0e17c4583ca31b9b49194396c4053d10ed0c13ffb02bb2913bea422c4a7df9a60d5a51b68a3805ee77e3e36736'
dependency_hashes['cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl']='24fb9a6d2d4d236cfa1a34ce965b199d487a41c333c7814240e5d5cd59dedc59f3a859922079fe68494827f9570f6e0d2fdbda77d6a224bae9912e3016ff73c4'
dependency_hashes['cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl']='8d0ebb1c616c4c5a7c925101974ade3aef21f673c65ee5325276e0956e7469d93e5eb3a1678c81d5a024629274bdccf662c16cb6f8d1a6212aa82f999a3b9428'
dependency_hashes['cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl']='24ab3223045e3ac04e63647f7ae521d7a654408e8857f36a98565a12e2503c1a08af6a97dac974bee61cc80bea1a702cefe44c1da1ab127132e37ce2a5151801'
dependency_hashes['cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl']='13b4ee8013c3768f41f4f1578b40f36c6b5a4ea2402cb2a212229989d9f3129cc0f191c55c81cea54a8bfe4350fc925a767eddf9b223841435f78b596b4dab89'
dependency_hashes['cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl']='a421becd6cb04f593543c7ad322d431621f4daa0369bf61ebd4fff9329610078df1b5e7374af11e7923eaf72b02e358b35d37bc59d9ce7f800698243ac9ef05e'
dependency_hashes['cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl']='35e793f9593208ae347f0470bea29177136da8ec9e146110fa4de1e933a231ba8cf41baf7aa14a4a716e57da17cf1abf2b8bcceb6def065871a63c307476c53d'
dependency_hashes['cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl']='860229f8a400e26fe119a40995da0dec53c6b5ae92c12eb8e7439792e5489f54634a87a7f6b501f9c0b28e92923beddb982e210315cfa9ed14678be3ac75bbf8'
dependency_hashes['cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl']='72119121fd6e047f0e494dbd2155dff87cbbbb97e95ee91e5d5c38d7309495890e9f588de9fb1d7fd277527df38f4752ac946eaf54a43ebe41dff9747b88315f'
dependency_hashes['cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl']='445d4eb5a1ce0a97dba362d53856bbecf9a2b134c49fc04c3310756c429c094ee080d926f5dfb6302fb6cd057c2066a20636a192acb173bd430cedfabcbed105'
dependency_hashes['cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl']='59aa1acabeec0e87c8ba93da669d33ff1012197b8dd11c504287e67da0501420e5394c007b69a6bc327c35378ced73e62a10203d70ba917b585fec8197afe554'
dependency_hashes['cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl']='dad6c5b9a2199d3a14e1cafd6c36b92f3d4dbdbc8189db37025e44bfb2977908a8470e8f9a2896d37e5376aa4d9b3e29888562caeea3edd608c9c782868c17ae'
dependency_hashes['cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl']='81634c38dd4bb2f2be5c239017b89ec8c4dedba2ef2765536673f2b4f666b96fc593406d462d0a8df92b25cde5fe00e7e724f1485a9c4a4b7a968c4c1ce04644'
dependency_hashes['cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl']='bdfa1f8eea7f876ff4e8433a11ad622c6abbc5022a9602a9ed219e5b7e6fc7992b12afcf50e9fc1cfa2a3e21ccf34c64c92a796bb0ff9471ab7e0a28dea52a44'
dependency_hashes['cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl']='083782300fad3c0be0aec6a0eb8d589c25d18a67227c09346f64bdc69b9e41b672ea444d071be82bb7c3fcdeaafdd2959191e4606438cbabc4d566ee5254c0a1'
dependency_hashes['cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl']='2c849efac9a228c5d005bd575c99c1b83289e3a1602baf1fd853d19ff2f25ef4f5536b38837dc76ab416dc3105ee9c4bbbbb6b660ff4a0333a2f0ef2cc7fd0ba'
dependency_hashes['cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl']='8193c0d306f0212d0fbbe44510b2fed7a4abc74409d6f28a87b481ff475f2e00b006bde4fcd28b0fa5c8535e015a9e16337ace0259f72c6df4d8cb9979976b9b'
dependency_hashes['cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl']='e081ee7ae2b49b2704b1d525e6b4c5f53fcca831ddf690a6d47078071525abc5e5c32300b2b76d54e2042f8f299fac0a988474d6e96cab7f8d03b1b46558581a'
dependency_hashes['cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl']='36688299733808953d17daabac798b2b2d143c122ef5355b18068d80c9402b275ca9f65e1082762684269e9c5780ed74b42369db17778492ba1d716742d90153'
dependency_hashes['cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl']='4bbcdd72eb1856516c6235421f1562190e1333349686a079b2ec80ee6a02c370cadcb6b1e0d38fa6fb126368e90dd1b5f9712a92a1fa595a98f471ab2c8486da'
dependency_hashes['cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl']='5dc5ef04aa1b2ef2da537a932b8c11b49ee5e57c6ad214e6bddaef9a61b66a93952cc9f30b805da2c3c028fe58ea11cc25a56bb7fe2b116e7b9349dcc6075b5a'
dependency_hashes['cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl']='e6d8ff3fe823c4d99dc88877e626a9428d554d671d476826bae7117a123074eaae3d42d1f16e7b94bb601ef781c22791e742319f8a9a82599184c23045412da6'
dependency_hashes['cffi-1.16.0.tar.gz']='fd2588115092202aa9289c9d4e0a0b3e264b5e9ec1dc192950f31aeb412fd9f9d4e5c96a3f9c6762987b58ccc1e229f2012ddda89211797104df672d8ed51152'
dependency_hashes['charset_normalizer-2.1.1-py3-none-any.whl']='fe2f3ae5d3c011b314a057456a7b13ba957593b22dbe7f532f9fbe077103e75b3f8b631fb1e2a4d5875a60af678b6779780eff7df0ea7c08144aa88fce34abc0'
dependency_hashes['charset-normalizer-2.1.1.tar.gz']='f52abab683ebda4100d67ec6ee0349713baee453a742d60a1356f405c5ce2c3b4d850b0891527f08f92fa1217d59c46d6b181dc4ff1b962ce60d9c5ef8c913d1'
dependency_hashes['click-8.1.7-py3-none-any.whl']='687ea8c461196b234b0f0db0638ba213304b96bdeb9c9c6334a6cbd78f4e99da9e062bca2f449c88fd7a1de7ea2643e80c8ea571103dd4b2c50424a6fbd5d5e0'
dependency_hashes['click-8.1.7.tar.gz']='c67146ad0112daf8ed4db62a6b0a0065109332eb8fa31962ce40d61e27e2736020a0cadfebdd1656e2f23c20291b069d3a409faffe999a0907e6dbdef77aa014'
dependency_hashes['cryptography-42.0.5-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl']='fd840cb0f6c49078d2484fd2ff75a2c62c6ae58b69a01be0885a7bd088067e5f39f9e0de582e0a824525f7bbfe4d6e5831fe176f40fb01101df3f9a41e3ab14e'
dependency_hashes['cryptography-42.0.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl']='615d99cedb543228cc45a49bde24883e920426cd32c964471149fcb994a74b8ca3edb00d1addd52d19c19d7689f9b978cd10f54ac6ca70368da9dc40c28625fe'
dependency_hashes['cryptography-42.0.5-cp37-abi3-manylinux_2_28_aarch64.whl']='8b98785ff25b2fe0745d867e7055b54bf2ace5a21f9b42eda99c5a5fbd5bb4a6e74bedd6a3cf39c179570b351503ee0e7e937a04e1451f22a4fa0d69dac1f2cf'
dependency_hashes['cryptography-42.0.5-cp37-abi3-manylinux_2_28_x86_64.whl']='8f084fff47efe264edd9101915ea31e1e16cba949b7a0d3be3e72632fd656e5989794c0ef02645192beb3c51be0ab0a3184c554355e241e5060bb1b255cb0983'
dependency_hashes['cryptography-42.0.5-cp37-abi3-musllinux_1_1_aarch64.whl']='687f042d3470af3c6f1e64584a691d9aecff8c589de2a9ae7dc8f7a5738ea76785976ecbc345b838d3023c0cb033476ac86d8d06ecde5ce855ab3a78b465c17a'
dependency_hashes['cryptography-42.0.5-cp37-abi3-musllinux_1_1_x86_64.whl']='2a57528ef4e99daf9956823e994cad12d687e78088b7c10b3e694a859ac70cd66a86e02003f0d1120e75be19a28f299bfc87f590a2157099eb7ad95de8728d2d'
dependency_hashes['cryptography-42.0.5-cp37-abi3-musllinux_1_2_aarch64.whl']='70a54c0660561f898d715cd289be93a52082fcd986d0acbfb2af73cca703661116df34a108cf85da66e3ea1b8fd98e1c8af4f173fd487f54f3a8b840d11d9ccb'
dependency_hashes['cryptography-42.0.5-cp37-abi3-musllinux_1_2_x86_64.whl']='9d180b8749ec2682a868a3f062ed8bec0a6ac4483d23200c50afab4613e167be1e8faddaaad5c841fcc3f06e8c9a5fffd6a33527861c0b1115a624d4961f9cbd'
dependency_hashes['cryptography-42.0.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl']='49b2ba4241bb10ac9a3bca74ccff586abf83d10b2ec641ac9b4d53b334dac60135d6b3ee1bdb300997a3dac78d61abf099ab264d55f152999311588b2a213efb'
dependency_hashes['cryptography-42.0.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl']='0e5dbf32cae786fdcd855c6c065e0ced23de25eb8a667c620d7f2dab9549cb72fa08cc2a2795454d14dfb273a5f0dab4341155dc413cec9049bf2fa5d76f398d'
dependency_hashes['cryptography-42.0.5-cp39-abi3-manylinux_2_28_aarch64.whl']='cd3639bc392ebd062870c90f55ce527d39b354d0e6421d7ef9ead9ce4bfb28eddf3bf8685f9681ed7f23ec0f983f000dfc34b5d6349e825682633306bf29623d'
dependency_hashes['cryptography-42.0.5-cp39-abi3-manylinux_2_28_x86_64.whl']='4b87fe7d1a1f81a3778da62c466f35d763d359b59e79dfc26e971e18ddf4d5d8febfa927d1a7a4a20bcbcf3db8b94ffa73b0aa8e2a8950a1c7d4899f9c95d8a6'
dependency_hashes['cryptography-42.0.5-cp39-abi3-musllinux_1_1_aarch64.whl']='f5149920cc429e841ec88cca9c68cece82172f2a74788b1ce0924b45308df5aa92920c09ec5c93335beb37ddedd09b18444664ee0879a9a72fc432554b8d5e26'
dependency_hashes['cryptography-42.0.5-cp39-abi3-musllinux_1_1_x86_64.whl']='2ee854f1ad3fecd81d2fe2418a7ac804d8e257929f973d8fdba88b8a93362590906eec44ca6e54f9c2173b1d645b2024792f2802320bcb21d7ea7423cdcdd833'
dependency_hashes['cryptography-42.0.5-cp39-abi3-musllinux_1_2_aarch64.whl']='e4ba05cd1f7fe2486dd2041ec2ef31b9642b1d17300025eda181413a1ad7af8d5a1daf4cd45a2a236191e591d74b51c2a63217a737eab680dd09efa8122be5bf'
dependency_hashes['cryptography-42.0.5-cp39-abi3-musllinux_1_2_x86_64.whl']='04805984237fbf618d6d87321432840701ce0bbc1fd7ff65664487faec8724398fccbd8c01ac5c5764e4ac1bc5206325320c6cda74f3ca3045e8739487ea779b'
dependency_hashes['cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl']='3b416b5c915890a8bbe75aaada1c032710dfadd352895effd7e0412ad2e2b91b3b0af087a6dd94af415394506e2598adc9f9deb8ab3820c3d0c21d5048670af1'
dependency_hashes['cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl']='a4b10e9a9665e83434e9035b56542234fbfa07cba85d351a910ad2e81cedd9cab27497d34bf58bb3995d22ef6118438635e881418f58fca5d326b3d3495e890c'
dependency_hashes['cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl']='c8b8fae59303afef81daf85381596e366b60e1d5d499b6001c078bea669b1373fba37aca20a3617c54461f6797a1c655707fdee23cffb7aae8c1d6208dcff894'
dependency_hashes['cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl']='2b5b7a9667fc85c691fc107ba8b32ff8931ee6ee4c34e15784b1882d21c7fb2650d63825f1bdf14626dafe671f67e7816c79a5a8c6b7f1366f20f24ee6f32bd8'
dependency_hashes['cryptography-42.0.5.tar.gz']='5524fd230b55580a2c647a0a78197a783e201fdfa8b3177b72c6d7b689afd76a689e4fe3593120d3adb7ee5cc4adf9211e8deedc8fab355e9ed70076db09f68b'
dependency_hashes['flask-3.0.3-py3-none-any.whl']='efc6990982567332f1d274054a5d2f1e1b78c21f25a4cfb1de6b3a2f96fceab33fb0d31f9eb28f504e3899742fddf5854ee357395e7b66fdd3810eb64b8dff93'
dependency_hashes['flask-3.0.3.tar.gz']='977c0e5fd2d496fae8e34a035f211118642dc6518a14dc5f6c175b72c6a76407aeb9ff0ed1f5db636370a9aa7e0d36baa783713ea7bdabd7f28c8c2b94643cd1'
dependency_hashes['idna-3.7-py3-none-any.whl']='488a437198bcf2d32098d99a71447a514212d28e6149d597a347d85b638d9c1b8a328cf190f2b7872a7869f157160754f560fdbca2f2a9ba035401f0eaa9a87d'
dependency_hashes['idna-3.7.tar.gz']='b50e5ae117b67c7076125d6943e3436200676f85d7dd1b5a5414e217e73904ef077f0b1108d9781ab4afe2a66f7c9e1ce8262ce51edeb2d29e4c504147b6c4cc'
dependency_hashes['itsdangerous-2.2.0-py3-none-any.whl']='3c9d38d4a9a53f6426139969a111cb13f0588e4445173542a5c88a2f85348afc6f709f3523a4169eee6010cec99eed6df3a82cac59ca96a731d39461f88e2b83'
dependency_hashes['itsdangerous-2.2.0.tar.gz']='ed046cc371ea9aa1f7cd3bd201f1b68910a2b008bd8434c425332cecec6539cf031df6e2223a1fdccd68c12ccf5486e80f178d5906911b19417f0ea244e367f8'
dependency_hashes['Jinja2-3.1.3-py3-none-any.whl']='e3e2e6bd511dec484dd0292f4c46c55c88a885eabf15413d53edea2dd4a4dbae1571735b9424f78c0cd7f1082476a8259f31fd3f63990f726175470f636df2b3'
dependency_hashes['Jinja2-3.1.3.tar.gz']='5c36d0cd094b40626511f30c561176c095c49ef4066c2752a9edc3e6feb2430dafa866c17deebddcd0168aa1f0fd3944916d592c5c999639b8152e7c1009c700'
dependency_hashes['MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl']='f32cc6753cfaedeae6392e1f7fc8523ccbbdd996fa10636164004dba3d0d3ed80b8cccb1de3f6660c3114e8f83c260b7f92238b0a2a0bc48580ae00f4fb1f964'
dependency_hashes['MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl']='46d4361fc36ed9f3c78b282eca731c3918c9cc3ed6a74b1dcdb74731075d63152bc5398d64691c08e79e9b7a21f7bb96bf20d7e4ee4feeb7c2dfddae0ea34d40'
dependency_hashes['MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl']='bd0f8577d7909a1186671cf1a2f9f87d0af6597eee61e44c071a4f8d9dc51bda6cb40a8a3e328840b24ac86fbe9cd0ef8f06ec78d08b4ce764194512e0fa0634'
dependency_hashes['MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl']='689c416c3ef1624f97d4213375ee20966cd800d9540d114323c4adafa4e3e5a2e63bae89c8f948d2e940d9bf29f485c8143e25da2a5eef130ef39a039bc5d9af'
dependency_hashes['MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl']='ca60491527e5b2da3ac3adc9ede4d68c64f7d70e1e6f7d061d6a7bf285aba3e5e2c8543dbb9f0203df4503c81b5d53559f1098abe5433f0fc8cabc8b737cc130'
dependency_hashes['MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl']='ec77e3fcb4bf013af838bd3d67ffaf42f614705798c415a89af121326e6107943264059df97a41806036a8e6d70b894c8cdaf49ef8d8060263b61408c5102c61'
dependency_hashes['MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl']='2d138f2261ccec4e009a71d5900d18eddb61875ef9851c63ed3e644ba3c0b5dbd3fd3ef77c39605c43a5b6bf28e5fe9614342e7bf4ed7cff03913187d5ad6017'
dependency_hashes['MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl']='a1dfb9d014e5ac5a6523a3d4751a0d8458d6a3ceeb87aba13d303eface5b6428d62d8c989c74397aecf597545405ac1d1378308057ba1274cca0a436054a8819'
dependency_hashes['MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl']='ccb5a63f69e5681b3eea5646dd6b163241e890d4ea502c29ca782265ef2322428213de5c1dfa37adcb328af24430c22ed49cc2ee2a96e8ead416bc7a569683f6'
dependency_hashes['MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl']='6698232b96e1dafa8c1905b393952f6ab132bbb72bc10b0dbc567abcfcd30205bfc59c6fd9ab66e4c4f28e82f7cc3e871537b086b035dfb46ea1160e2b26cfc2'
dependency_hashes['MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl']='efb08c3eaf132e8d10e3f8a01ff80bd0bc36ac7eec078cf99f9065a9bc4cb7e4b53c9ade1a2ad46b786c26a31662f38c4d70da2c53533c8c5759c8d439c3bc66'
dependency_hashes['MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl']='eeef56f202babb0174ff72a65239d806a1a6070873f94dbf939443df60c259bb403aae2afee7f2e9f199ee9b8bb0e91f5e95ca9029b33730d88cf53457c36674'
dependency_hashes['MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl']='0447b1b39d8eaacbe0bc023ef7a5f0fc304b4b562940cafe150670c7cd8be6c11c23fbd831b9a9f3645e3e085f1662e20b2f2c7114bde23aeaf4b55e9ca6f8d9'
dependency_hashes['MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl']='77bd99171babb05f0b4d8c45936dfc91f9c2c0ab4a7e9215b6788800a13befca9256cf56422a0125f51a8e53928dac66ddc3077e655dfb3a2fc185c3f7646db4'
dependency_hashes['MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl']='7667f36657612cf81f273608c2e5861e0cc412ec5af3ab2a080547a7b97d1670d5caf15aca23d0a2653f01157a9849e5435662f833cca868fe83c51cb17746ac'
dependency_hashes['MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl']='386f9624fe79c40e9e548323d00f2bac16c5d58914afba24a5ba94024fc15a4e531a3203f077fbb3517a6a2c4b3570caed701520933216ecd67c3721415e860e'
dependency_hashes['MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl']='270353f556509467e6183d1c440015f72e0712cb9e3420981aa7e48d09cfa8666966568bbc6530fcbecd73864714cf1bdc52948e2950c565d6b50eeb1d677f75'
dependency_hashes['MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl']='ec755a40cb7427da58dac02ada8f2cefec4a38902140ccfba0bb663f4f4cf54eacf411b9fbbeabb5ad690c4f1140ab38a1d58fd80961adddc04bede32d65dd8d'
dependency_hashes['MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl']='7e0d8d5e85790cefa7b56b5924523a8dda606a0cfd1e9efa2fc67ab13c2eaaa5b4e8e15a45dfbc9dd7759b1f87dc1d13fc35557bf0f9dd38e4c09e039b018aa0'
dependency_hashes['MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl']='e620c9ea18dde478e6d449803e511d7c0db079d3093063fa383bc498618ecd618edd3518f1408762d5262f4663a85d3f5aaefb9408da2c440182281678f6222a'
dependency_hashes['MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl']='81b67d2e236dfba6661aa9f06a0f11e64f2a598d3c3a80b1678eb207b856edb18372dca8d62fa419eb717339c1147fb53bbcfb4b06d8df281c0890020ffb2bfc'
dependency_hashes['MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl']='34392033b008945424d81bb0a2b201c853e3b743dc4b4ad2270903c2a031e36a59518767ceb724ae540c9b03c502c6956f8af048e4a6c1be55428a7cb0676539'
dependency_hashes['MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl']='94233c5f239525bba1c584da6a3bd54c7a9c3d99f6f40affb32484d93d89eed9b6c2eaba522b0388c1fed5405354abccc4a48a8522188ceb1055be1799bce42d'
dependency_hashes['MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl']='ffed1823a6bcff917670c25aa7d249179899ecc9651f2e303a626d1a8e10ea189cb1875dd73bb190914bc2714574e9fa73f62d881f07522e7a50a485a6d71be6'
dependency_hashes['MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl']='54c049098b67a7ab0876f37196d6e76f5702caedb282fb76224b472a27e48c9d3d68f841a9a45514090a55a28d3f94135ea60bcd440a5c27357ea5cc44391176'
dependency_hashes['MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl']='9a51bca810984054d198d4081262be66fb3d05f8356772bc27a01a580d82f0552542a8b18e41e7cd9e6aff2b2065dddf103f0ad87b851b2d36c7459c926e856e'
dependency_hashes['MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl']='54e954e982df1ec116d1ae58e432c36935b582aa6e13e13645916428b918abfb5475d31c8be6308fe04bdad139b0e08667896e26ca909b242b0f104c959b2661'
dependency_hashes['MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl']='3e409b8b9642620925066423c44eb38dbe1ef35c842c39a6e19c0dce620181395effc195ce8a3e5717c76d554a0dfcb0838965358b7190d98ac576ef475e25a5'
dependency_hashes['MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl']='1eb79d3a71d25372dac4b11827986f01d11618baddad7145efda13cc1acccc8e2c7967c4c51fbadbd57e7c93818ac32405721b2d94bd7e3cb519b0cf5ec47423'
dependency_hashes['MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl']='e09fafdcdcfdb7025b1fa803cba5ee04ff7ae8eb599b8d69deb6b37f958f92627b6b2e9e4de30fb338f65eb1a123e1de15afeaa75e0d8dfe0081482d303b11be'
dependency_hashes['MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl']='8a1f91b4f7ffc97332ba757cfbf4bd6d9190235854711b70250c0b0535d6025ed08b5fb5dfbd6288b0c50f04ecfe42ffd9c7d1829bab8bdce26ebf105e95059c'
dependency_hashes['MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl']='1a882d8ff38682ffc5cef0d0e29a01a1f012da22c4a4a12872c812459ceb62bef496aa5debfd68d53e9069ba0d5a33ba58f9d071e0a89cbf568df8eb0343ea8f'
dependency_hashes['MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl']='74c0871ec5ccd70bf4d84cca9f17d36ab3279253a1aadc2e6d8c349c4e381b63eccdc19152028ce26a9f03d58ea61b65780a41460b094b584585ae1708034562'
dependency_hashes['MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl']='913f2a486f2d553a998a7adb836acffa89e3adff031abdb8964638a0a45f8257e90f50678668d624425fe743796c6d272c9b5eb1fcabbdf823aae97b4f303fc0'
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.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'
dependency_hashes['PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl']='01d38ba9cdebf0cc658b187a753f63065cea29ca757296cc1d40da4c2609b8cf96a1df1af7bb75bec61af767cee4ac6a20aa858ba060b4c61807800e57e53fe4'
dependency_hashes['PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_aarch64.whl']='61e07a421705e5c1613cbc888ff594d7e3457090e9654280f2b6e54a84e5d6dcd56292fd3c47b86a59be6eecafa8f17ad5d710f45b7fcda9f57d9c7343328bc2'
dependency_hashes['PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_x86_64.whl']='9720cfe64e70667804c197a1762db2985bf5893ae774418f50da9a3d31135b8935fd497c5fe4f92909f6e0ac70e3c5dd57f6322ab780d29b12741c64c0d2c007'
dependency_hashes['PyNaCl-1.5.0.tar.gz']='cea3e4556432588630382abae6debf9203c7f55da286509da547a7921e4dbad98c915743625c68e5f7187fcaf6d4cdaf7ed2ed3ba60bd4c10ae6e3f88608dc65'
dependency_hashes['pyserial-3.5-py2.py3-none-any.whl']='29bce14c59e60f54ce476d919c9b9477190ef6bb44a6102f71345840f5c0f1d0a323c4c3c302c5f380bfaae32cf04142ee528b6dd7184f17789632a31d5ecab6'
dependency_hashes['pyserial-3.5.tar.gz']='c8df5e50d952d5a6dcf1d9253a6ba953e9763c545a867da66c22c90dfa015aba0194f2a8f29a229d0a5f4dc8bfeeaaab8bcfda4066ed78a18b151bc05e6ae327'
dependency_hashes['PySocks-1.7.1-py27-none-any.whl']='3e0b1775c14fe091d10e30b03f7f0c770861152e493cf3a3143b0de01aadbc73f684f0d4305f1a694932d4bdcac8056c422437130640e19028cd9fba59ff0b3f'
dependency_hashes['PySocks-1.7.1-py3-none-any.whl']='313b954102231d038d52ab58f41e3642579be29f827135b8dd92c06acb362effcb0a7fd5f35de9273372b92d9fe29f38381ae44f8b41aa90d2564d6dd07ecd12'
dependency_hashes['PySocks-1.7.1.tar.gz']='cef4a5ce8c67fb485644696a23bf68a721db47f3211212de2d4431eaf9ebd26077dd5a06f6dfa7fde2dcb9d7c1ed551facd014e999929cb4d7b504972c464016'
dependency_hashes['requests-2.31.0-py3-none-any.whl']='b795abb26ba2f04f1afcfb196f21f638014b26c8186f8f488f1c2d91e8e0220962fbd259dbc9c3875222eb47fc95c73fc0606aaa6602b9ebc524809c9ba3501f'
dependency_hashes['requests-2.31.0.tar.gz']='ce50d64973752f4cf7f7c7c91401669854b55c66d7465bea3689772fae8a6b646cf6720d84a2984bbe6fd78fc8b9ce0aa377f291fb6d7c20c7c2a4be8193acdd'
dependency_hashes['setuptools-69.5.1-py3-none-any.whl']='d212edd21ab99f50c2daf6080c68a3cc0eeed566f10e91f857e7eeb86513f33e9cde25b975db1030110c3b1714cfbfd4d3c9e2937b4a5ff2bb8971e605ecee85'
dependency_hashes['setuptools-69.5.1.tar.gz']='81f4a5184976a0d4aa070261bb56458c54ec6a479786fa469e98cb336e9a1691b281ae771abe41bc5398fac15b2dd039c07cf014ee00572f46908ce6830fcb6a'
dependency_hashes['stem-1.8.2.tar.gz']='f054bbc9a61e04fb7e3b7d1534803b938b855c29795471953661f8fd9c0a5196fe1f9ccfd01e5b3256ea42893a7d57fda34fa54932012e345f74bb3303ff98c5'
dependency_hashes['typing_extensions-4.11.0-py3-none-any.whl']='b169d8dcb01948fa08f4f85d2e224d285e47710b6f2e3924340ad01f26c7ef68588c571b8f6a0fbc6955dae46950420d046057bbba82c26d30de0554494206a4'
dependency_hashes['typing_extensions-4.11.0.tar.gz']='d795fadbe9c0a24c4d3190f375d72576f5b42a14dc3a999127c1356f2ae77d4bffdc04a23b7a05331cc8b703a81f504328aaf5d6c1247ea24cbeed25dccb0a37'
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.2-py3-none-any.whl']='8904c195547630ef173356c8286deb5fa9140f193ea017680b9ea67a57db4d4c7479fe9023f02f78882a5212bf8e79240d994b13322d8a7cb6e4348b6b69b760'
dependency_hashes['werkzeug-3.0.2.tar.gz']='5f9a91684653da17ef419459db11a6a4bd83b6c1f2f6b0e9f0a296bc5c7c63d2b465044a131f579557f1fb240a560793b734f3d0248ecbb7b6af62b1713484c3'


# ----------------------------------------------------------------------------------------
# Package lists (the list must be ordered so that sub-dependencies are listed
# before packages that use them, and the list items must use the same capitalization
# as the dependency filename).

tcb_packages=("pycparser" "cffi" "setuptools" "argon2_cffi_bindings" "argon2_cffi-" "PyNaCl" "cryptography" "pyserial")

# ----------------------------------------------------------------------------------------
# TFC Source file verification

function compare_digest {
    # Compare the BLAKE2b digest of TFC file against the digest pinned in this installer.
    purp_digest=$(b2sum "${INSTALL_DIR}/${2}${3}" | awk '{print $1}')
    if echo "${purp_digest}" | cmp -s <(echo "$1"); then
        echo "OK - Pinned BLAKE2b hash matched file ${INSTALL_DIR}/${2}${3}"
    else
        echo "Error: ${INSTALL_DIR}/${2}${3} had an invalid BLAKE2b hash:"
        echo "${purp_digest}"
        echo "Expected following hash:"
        echo "${1}"
        exit 1
    fi
}


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 ad8cd3eac919da8be4c732c44f31ab3a54c85c1f73fc9c3fc130be69ca7c566e69a9f207511d6ec32ef7957b1e70953996d7e250cfbc54f3cb35d991ff12e5b0 '' requirements.txt
}


function verify_files {
    # Verify the authenticity of the rest of the TFC files.
    compare_digest 4bb6ad0462768b4febe425cfd2873d7ca47075fa9b467d7d3f19f9063e9b0cf941228e5f2e72a884895913e338e5cc8b079d664377169d8e38272dfc8c2917b3 '' dd.py
    compare_digest 74915e048cf8b5207abf603136e7d5fcf5b8ad512cce78a2ebe3c88fc3150155893bf9824e6ed6a86414bbe4511a6bd4a42e8ec643c63353dc8eea4a44a021cd '' LICENSE
    compare_digest da5fad3d2dfcb2bb992df4af9ce5c7a792b489cf9f86f966610c2d5933536a8ff9f1be2ec2bf2cbc3eab46cd469ffba35a739eacd31dadb704a28f14cd75cefa '' LICENSE-3RD-PARTY
    compare_digest 55b954740233846aaed4360b5ec39e5fd52c3050541b221013fd62fe66e65f21c3ef3527dc97f3db1013a26768a9f2cc1400bf84bf4b26bb18f79c00e1f500f5 '' relay.py
    compare_digest 46e2d85d0a7148215385db873a3e9957f2a5f5e26e12bd1265503bdb074c2c807e720e885644b248864885cbc4129acfb4352208a22ecad4bf3c55416dc11684 '' requirements-dev.txt
    compare_digest 2687a45f351f1ccdd7d0a4582b7c54756615f6b66c03703f1bff3ab25fdf4532ce30a9adb0339def64f6e359cad739d511c63fe44c1f18fcb9a49a47927dac85 '' requirements-relay.txt
    compare_digest 0e66cb8609a0f9abc9ca0f76298593a076cd5c8b85abaa0d8b3b765d40a2599a4ea2d33fb4792a97dc5ceee3b71e1682715f71555d5502c87ea20da922d05a7f '' tfc.png
    compare_digest 217c3a4dabba7be2b0d0d5d948b3e02e4caef9c3d1e24e8b62f2fd874a2239bb59d3433b9f92fa8123803d7daeb6a026adaaa06396a5ca5b3e77673d919b1b2f '' tfc.py
    compare_digest 356ef5005cd095c219608255b1ca61365d32ce246c0eea4bd5c565c5a4730e1e9dd0c56b85e5ba9923271db99cd5ae430480d70fbf7e23ab26b362aaaedb3103 '' tfc.yml
    compare_digest 71999fc9fa70313af601e5b7a41cc21f756af9f7f79eaea5ba47a07a8f8c8ca2951bd6cf5fdba8a55604f15e83e26ffda12b71fd2db4dc1916433243471aa164 '' uninstall.sh

    compare_digest fe5b9289761661f1440f97d4f64b4f969842ef4ed3dcc6951ce7e5fbe71f986f5058e7fe4815e0e6b0bca9b644b95d147a2e77d367ebfec8df2733d22ed38bec launchers/ terminator-config-local-test
    compare_digest 19dc85a3e35f7a6261cf8ea09621ffd2dabe0e9a96f4ba504f0127825da0e3239073a0e56e2ab7eedd4053f1cb4b53b6ba98e964988a4d92efa59f0b10937871 launchers/ TFC-Local-test.desktop
    compare_digest 80570937452b77e61cd716a04657a406faac7aab734e654358639714df4e51b0862c968ab05000c438d1fb7319c4faa9862cd763e90f5c0be22e27807231e023 launchers/ tfc-qubes-receiver
    compare_digest ecedbdc6a3ddff31e81da2cc1ad12997411c7ed5f11a1701df443b293f0cb06c005d062f13f1e0ddac39939c5068f1e1833de2238a0efc243b75b3a0b6ca7eb7 launchers/ tfc-qubes-relay
    compare_digest e3b1a1ecac9fccdc4a708351fd71fd0897621446df13fde0e2ebc34d4704d945dcfdd256af1cec92d6ebe6c5497bce3db98d052f8885db8e579345769e5fe758 launchers/ tfc-qubes-transmitter
    compare_digest 2cb288015524281c001c2090c952a607a984568f8d62146a5e7bc4ba8cc8e4105fb411324cc8806a05ad99b501d9585bbded43b5ad8762db698ea6ef7681e1ef launchers/ TFC-RP.desktop
    compare_digest 6bb0b26d0f9cd800fae0a1cdd13c48bb030fa9141591d5f8dde02b55693b47213e1989095f4187f286f515314b50ee25c90364a4dad7099b6bf15cf39cbeb183 launchers/ TFC-RP-Qubes.desktop
    compare_digest f1cdc9d5b3610686a3be43b9fd050521e16780fa59b914036eb90ebbc027bc0bfcea3c38b84b3fe2f5cd63fdcebae18854606ad4cfe27a34c1e2aac95baa9c26 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
    compare_digest 46d22f767b973f095ae1580d31731f0448bbe4a06d645cb8f6c02678e4779c32b7e778d21bae15186b9855ff4a731e1de9c2d77572e2a3e29bf2bd4bda63d954 launchers/ TFC-TxP-Qubes.desktop

    compare_digest 523ae2069b86a30952575f5ca2a2ec46ca7f8e4705602d3a709b9a88dccdd4bd236dcf1aa0988b632e621a18cc546e815eb73946c6db307fa4bfa51d7a51a804 qubes/ service.sh
    compare_digest fe5ae2641998bc31ff723cdcecd85b68373df978c4b8a032058ff636e4445bfa63264fbe309d2be6f664176372d84bbc9b99d39acce447a6d25b51575ecd004c qubes/ writer.py

    compare_digest 90218be097758a94ed60189393756682fa84cac0c025bd9c3c1f26bf21357800398ac505eb9f4b0f565ff3cfe52c48a0463a939a60e1e197a674d88cda314583 src/ __init__.py
    compare_digest 90218be097758a94ed60189393756682fa84cac0c025bd9c3c1f26bf21357800398ac505eb9f4b0f565ff3cfe52c48a0463a939a60e1e197a674d88cda314583 src/common/ __init__.py
    compare_digest f3770e6eaf83f26d95e6dbc7e287bf6959a52753eaf743e4f1f600615d5d85446e49bc6b68083a0bc66ce62c7d12259ec2f7019da65f42c0735311e1be6aa0b0 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 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
    compare_digest db23bde3df574af6de3eeebcefc73169c6932a7c97dcfda004c9a26108f575f3de443be4f069474fb9be89936ebd445c8efd6594da3986141195b65175566ff7 src/common/ exceptions.py
    compare_digest 6b82499438a7c48e1e9e563fdc4e756fb0a0523289c06a554fa7eeecb62b2a63d2c2dee368266483142c5b7394a4c256609c21033a8c26e15a126f270dc126a1 src/common/ gateway.py
    compare_digest 012e3e1be22084e6b43cf66a7c1d81779e4a2317f1558b126923929b0f10b36ebd356e99d386de72731d528884b864fb48a1d7a0c4f77bac94c148af18480df2 src/common/ input.py
    compare_digest 24c7e4d83f5282f05d821808b0cf5412be95a81f30650cfd699c41e5cdacaeacb31f9a6df01a20e9a22cd9d75843e409c745e7a4940ff3ca5f0fa3749b35ca28 src/common/ misc.py
    compare_digest 776b60201db1ccfa593015b9c4e5877adf0c184cc5f41b22e2d3dd54106b01a2f9e817493283c58c416d0d28081bc94f2fe7db9f336cfec2f65e40a85c4d326e src/common/ output.py
    compare_digest f1805ae899c94c80aaaadc7fedb03e09525288542f96cc660ac4bb1da5be4c123bc068cd426c529b99c679e4a1e9a63ac0a8c2eca964af9bcd3a0bfdc8da398e src/common/ path.py
    compare_digest 0cf08cbd61b1be94142414c03f6931df7c06ff944f150855bb52b13abd8f1dd29506be041cd4b77eb83a2c4617702b4dbad3e9a5097256c36b1d444f20ccb4da src/common/ reed_solomon.py
    compare_digest cbb72e8ce58a7bde532aed54260be4ecd421d973f942f56c98539ba6c957c4dad094bd3ce2793324f3fd25375491e5b313786f2ead9dc93a06e88588b043f061 src/common/ statics.py
    compare_digest ba0e77bffa8243ea13565664133e433bf72925d5ddd5c6e599dd24e58c0728aa55f496db06d25fba0d3050aea4163d1b6bc81d1b8fb75e48bace43845e83df70 src/common/ word_list.py

    compare_digest 90218be097758a94ed60189393756682fa84cac0c025bd9c3c1f26bf21357800398ac505eb9f4b0f565ff3cfe52c48a0463a939a60e1e197a674d88cda314583 src/receiver/ __init__.py
    compare_digest 6b3860213104873fbfd59917e4d52e31466696f3494ffbbd5edd4a8bccacfc72e3141b2c6779d52da7d393b501f03a40fb0d4e86270d65fd1129ad3d508ed98e src/receiver/ commands.py
    compare_digest b33c47f47ce37b7f3905ff99f690300cbd4b96e6b2d4b785f06361b47d9993e6a6740435e09313cfacd63650705e85eeb29c49706da369c2424205b9cdf44a45 src/receiver/ commands_g.py
    compare_digest 5838632327e7b3fdd5c5c17f3f2d75db7a62d75f7444b9d2bebda7b72ed3f48c82745d8aa78a74320f8f6c4e6d22b1228cab0a8c0dfb2fa1d642c8cc85ecfefd src/receiver/ files.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
    compare_digest 5ca450c91633d53d4c23ec981ce7343cd67f7264f6530e67d3ef8e61d8e6731b18bb27df07876512d17147bb46e0d84f486e3fda2f1950c7b563000f3a5a88be src/receiver/ receiver_loop.py
    compare_digest d8f4295a9ca479a79d9b07ecd509878dff29bb4b30d51a74bc405f205db4d2eac429a6b1b2091bd031dbb777ff2e2553c6ec0cbb2b33ebb896aa79c9bcae07f5 src/receiver/ windows.py

    compare_digest 90218be097758a94ed60189393756682fa84cac0c025bd9c3c1f26bf21357800398ac505eb9f4b0f565ff3cfe52c48a0463a939a60e1e197a674d88cda314583 src/relay/ __init__.py
    compare_digest d2336f5e6a5aa5c41c39f95b4fe6e1413e5a87ac305cb31916c58df130cff81a6952045942b63f1546c82d6d3b05c38e3d0c6ddd69428d239a9030d150bda562 src/relay/ client.py
    compare_digest a56aa18162a25b2fb665ec81b93745bd9aa8e2b5ef88f75a0984ef50ef2fb7d90cc2287c4e1ce54b2fcbca89f7a89e84024d58904c4d6c948fa356448466bc63 src/relay/ commands.py
    compare_digest a27e65526e0eb40319dd65a2739d94e2aae6c0d67eefc3885f54c8ebdd390d8331f8682aff56aea9911e94a24edecf28435011eed7713dbdca58a9e7f927bf22 src/relay/ diffs.py
    compare_digest a5ff25e6a99dddc87f2fa26bfb7271a108af1b418a0ebae9c90b455226e2c06016c12a01e78d05b40235e87111354f80090294c6bf5a48f74a5a94956c72bb55 src/relay/ onion.py
    compare_digest 49e838d7d5758cd94ff547fbebfe3a5de08bf5b2e1e7c35313d29c20ee24d258484996a11b36402de4113704efcf8fdec05db23a4ab774d5cfb7a4670f7f697b src/relay/ server.py
    compare_digest 0a702b1ef2be7f359b8f43e27ca917d39c36ecf47b98b57829b9d1729a9149e8543089ee332f67d0aece79fd16c12f563090d70ecc1203e355368fb80f5e508f src/relay/ tcb.py

    compare_digest 90218be097758a94ed60189393756682fa84cac0c025bd9c3c1f26bf21357800398ac505eb9f4b0f565ff3cfe52c48a0463a939a60e1e197a674d88cda314583 src/transmitter/ __init__.py
    compare_digest bd7ecc8ac586597823f88ba93b3202fa29e01157ee901091f2d1c574d5e1a693982a26067cf21749de3f794076bca85a7267975b4864b4bb62461be53cb8fe5a src/transmitter/ commands.py
    compare_digest 985a828b4ef321fc90f0486005de8a9b3b522b020af054abbec0670803eb8ee119795c8039ba7f1d7c3f2e7b68b96e6cf2454ef210687d0a0ad7fcf5bb3f4a89 src/transmitter/ commands_g.py
    compare_digest e5fd8ab8978d38fe617606581783d0c9807dfcc8c95e0e31cd3a7d1ce6ad88d3f88ba49c070ec69743c11b8707a6cd1f4ea979f2dfe49621a095e910e14a82a8 src/transmitter/ contact.py
    compare_digest e14e407035e0b8342320f205d1b934c5f521ae2e86aaeb8fc6265572b0aed06e64fc86b0b96253efe45f4499b3ecd80c865e3424c5d2cf46d3e31f051232b8a9 src/transmitter/ files.py
    compare_digest ad73ffa1c20620a01cf846a73cf0f3d0dc786beaa3be00bb3996ca087ca1d8a960f962474989ccf096f98b94d73c65552e0905c8b697c9438ffbcb3fded235b9 src/transmitter/ input_loop.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
    compare_digest d34dbf120e6bd2ec68b1becb719b5cc45a134ac4293abacdcacc992709577fcae41083814cd6b1c9d80eede3faf836fb5c58fd672c8a2fa8c55ee77756436f72 src/transmitter/ user_input.py
    compare_digest 3855e30bb873099b6273138f3b9532d3fbc217b91e9c8953620817c34f7ef7214abf83579e3aa1d47df1997d581516871f9889f3a3bdd8a23d4209b02df659c9 src/transmitter/ window_mock.py
    compare_digest c06e0aae612984abb469b38f5382885e9cd2e122e42957cc3099e062f1d77c834c4aa01d6f470227ee91ae438dd90ec81e6ec41756fbe61445c8a571947f32c9 src/transmitter/ windows.py
}


# ----------------------------------------------------------------------------------------
# Printing utilities

function c_echo {
    # Justify printed text to the center of the terminal.
    printf "%*s\n" "$(( ( $(echo "${1}" | wc -c) + 80 ) / 2 ))" "${1}"
}


function exit_with_message {
    # Print error message and exit the installer with flag 1.
    clear
    echo ''
    c_echo "Error: $* Exiting." 1>&2
    echo ''
    exit 1
}


function install_complete {
    # Notify the user that the installation is complete.
    clear
    c_echo ''
    c_echo "$*"
    c_echo ''
    c_echo "Press any key to close the installer."
    read -r -n 1 -s -p ''
    echo ''

    kill -9 $PPID
}


function install_complete_qubes {
    # Notify the user that the installation for Qubes VM is complete.
    clear
    c_echo ''
    c_echo "Installation of TFC on this Qube is now complete."
    c_echo ''
    c_echo "Press any key to close the installer."
    read -r -n 1 -s -p ''
    clear

    kill -9 $PPID
}


# ----------------------------------------------------------------------------------------
# Installation utilities

function t_sudo {
    # Execute command as root on Tails.
    echo "${sudo_pwd}" | sudo -S "${@}"
}


function check_rm_existing_installation {
    # Remove TFC installation directory if TFC is already installed.
    if [[ -d "${INSTALL_DIR}" ]]; then
        if [[ ${sudo_pwd} ]]; then
            t_sudo rm -r ${INSTALL_DIR}  # Tails
        else
            sudo rm -r ${INSTALL_DIR}    # Debian etc.
        fi
    fi
}


function dpkg_check {
    # Check if the software manager is busy, and if, wait until it completes.
    i=0
    tput sc
    while sudo fuser /var/lib/dpkg/lock >/dev/null 2>&1 ; do
        case $((i % 4)) in
            0 ) j="." ;;
            1 ) j="o" ;;
            2 ) j="O" ;;
            3 ) j="o" ;;
        esac
        tput rc
        echo -en "\rWaiting for other software managers to finish..$j"
        sleep 0.5
        ((i=i+1))
    done
    echo ''
}


function steps_before_network_kill {
    # These steps are identical in TCB/Relay/Local test configurations.
    # This makes it harder to distinguish from network traffic when the
    # user is installing TFC for Source or Destination Computer: By the
    # time `kill_network` is run, it's too late to compromise the TCB.
    # Hopefully this forces adversaries to attempt compromise of more
    # endpoints during installation, which increases their chances of
    # getting caught.
    dpkg_check
    check_rm_existing_installation

    wait_for_tor
    sudo torsocks apt update
    sudo torsocks apt install git gnome-terminal libssl-dev python3-pip python3-tk python3-virtualenv net-tools -y
    sudo torsocks git clone --depth 1 https://github.com/maqp/tfc.git ${INSTALL_DIR}

    verify_tcb_requirements_files
    sudo torsocks python3 -m pip download -r "${INSTALL_DIR}/requirements.txt" --require-hashes --no-deps --no-cache-dir -d ${INSTALL_DIR}/
}


function verify_packages() {
    # Verify authenticity of downloaded dependency file.
    dependency_list=("$@")
    for dependency in "${dependency_list[@]}"; do

        dep_file_name=$(ls "${HOME}/" | grep "^${dependency}")

        # Move the dependency to root controlled dir
        if [[ ${sudo_pwd} ]]; then
            t_sudo mv "${HOME}/${dep_file_name}" "${INSTALL_DIR}/${dep_file_name}"  # Tails
            t_sudo chown root                    "${INSTALL_DIR}/${dep_file_name}"
        else
            sudo mv "${HOME}/${dep_file_name}" "${INSTALL_DIR}/${dep_file_name}"    # Debian etc.
            sudo chown root                    "${INSTALL_DIR}/${dep_file_name}"
        fi

        # Calculate the purported hash from the downloaded file
        purp_hash=$(sha512sum "${INSTALL_DIR}/${dep_file_name}" | awk '{print $1}')

        # Load pinned hash from the hashmap based on filename
        pinned_hash=${dependency_hashes[${dep_file_name}]}

        # Compare the purported hash to the pinned hash
        if echo "${purp_hash}" | cmp -s <(echo "$pinned_hash"); then
            echo "OK - Pinned SHA512 hash matched file ${dep_file_name}"
        else
            echo "Error: ${dep_file_name} had an invalid SHA512 hash:"
            echo "${purp_hash}"
            echo "Expected following hash:"
            echo "${pinned_hash}"
            exit 1
        fi

    done
}


function install_packages_as_root() {
    # Install list of verified packages
    dependency_list=("$@")
    for dependency in "${dependency_list[@]}"; do

        # Find file that starts with the dependency name
        dep_file_name=$(ls "${INSTALL_DIR}" | grep "^${dependency}")

        # Install the dependency
        if [[ ${sudo_pwd} ]]; then
            t_sudo python3 -m pip install "${INSTALL_DIR}/${dep_file_name}" --no-deps  # Tails
        else
            sudo python3 -m pip install "${INSTALL_DIR}/${dep_file_name}" --no-deps    # Debian etc.
        fi

    done
}

function install_to_venv() {
    # Install list of verified packages to virtualenv
    dependency_list=("$@")
    for dependency in "${dependency_list[@]}"; do

        # Find file that starts with the dependency name
        dep_file_name=$(ls "${INSTALL_DIR}" | grep "^${dependency}")

        # Install the dependency to virtualenv
        if [[ ${sudo_pwd} ]]; then
            t_sudo "${INSTALL_DIR}/${VENV_NAME}/bin/pip3" install "${INSTALL_DIR}/${dep_file_name}" --no-deps  # Tails
        else
            sudo "${INSTALL_DIR}/${VENV_NAME}/bin/pip3" install "${INSTALL_DIR}/${dep_file_name}" --no-deps    # Debian etc.
        fi

    done
}


function remove_packages() {
    # Remove the dependency installation files.
    dependency_list=("$@")
    for dependency in "${dependency_list[@]}"; do

        # Find file that starts with the dependency name
        dep_file_name=$(ls "${INSTALL_DIR}" | grep "^${dependency}")

        # Delete the file
        if [[ ${sudo_pwd} ]]; then
            t_sudo -E rm -f "${INSTALL_DIR}/${dep_file_name}"  # Tails
        else
            sudo -E rm -f "${INSTALL_DIR}/${dep_file_name}"    # Debian etc.
        fi

    done
}


function remove_common_files {
    # Remove files that become unnecessary after installation.
    $1 rm -r ${INSTALL_DIR}/.git/
    $1 rm -r ${INSTALL_DIR}/.github/
    $1 rm -r ${INSTALL_DIR}/launchers/
    $1 rm -r ${INSTALL_DIR}/tests/
    $1 rm    ${INSTALL_DIR}/.coveragerc
    $1 rm    ${INSTALL_DIR}/install.sh
    $1 rm    ${INSTALL_DIR}/install.sh.asc
    $1 rm    ${INSTALL_DIR}/pubkey.asc
    $1 rm    ${INSTALL_DIR}/README.md
    $1 rm    ${INSTALL_DIR}/requirements.txt
    $1 rm    ${INSTALL_DIR}/requirements-dev.txt
    $1 rm    ${INSTALL_DIR}/requirements-relay.txt
    $1 rm -f /opt/install.sh
    $1 rm -f /opt/install.sh.asc
    $1 rm -f /opt/pubkey.asc
}


function kill_network {
    # Kill network interfaces to protect the TCB from remote compromise.
    for interface in /sys/class/net/*; do
        name=$(basename "${interface}")
        if [[ ${name} != "lo" ]]; then
            echo "Disabling network interface ${name}"
            sudo ifconfig "${name}" down
        fi
    done

    sleep 1
    clear
    c_echo ''
    c_echo " This computer needs to be air gapped. The installer has "
    c_echo "disabled network interfaces as the first line of defense."
    c_echo ''
    c_echo "Disconnect the Ethernet cable and press any key to continue."
    read -r -n 1 -s -p ''
    echo -e '\n'
}


function add_serial_permissions {
    # Enable serial interface for user-level programs.
    clear
    c_echo ''
    c_echo "Setting serial permissions. If available, please connect the"
    c_echo "USB-to-serial/TTL adapter now and press any key to continue."
    read -r -n 1 -s -p ''
    echo -e '\n'
    sleep 3  # Wait for USB serial interfaces to register

    # Add user to the dialout group to allow serial access after reboot
    sudo adduser "${USER}" dialout

    # Add temporary permissions for serial interfaces until reboot
    mapfile -t arr < <(ls /sys/class/tty | grep USB) || true
    for i in "${arr[@]}"; do
        sudo chmod 666 "/dev/${i}"
    done

    if [[ -e /dev/ttyS0 ]]; then
        sudo chmod 666 "/dev/ttyS0"
    fi
}


function create_user_data_dir {
    # Backup TFC user data directory if it exists and has files in it.
    if [[ -d "$HOME/tfc" ]]; then
        if [[ -n "$(ls -A "${HOME}/tfc/")" ]]; then
            mv "${HOME}/tfc" "${HOME}/tfc_userdata_backup_at_$(date +%Y-%m-%d_%H-%M-%S)"
        fi
    fi
    mkdir -p "${HOME}/tfc" 2>/dev/null
}


function get_screen_width {
    # Output the width of the screen resolution.
    xdpyinfo | grep dimensions | sed -r 's/^[^0-9]*([0-9]+).*$/\1/'
}


function modify_terminator_font_size {
    # Adjust terminator font size for tiling terminal emulator configurations.
    #
    # The default font sizes in terminator config file are for 1920px
    # wide screens. The lowest resolution (width) supported is 1366px.
    width=$(get_screen_width)

    if (( width < 1600 )); then
        $1 sed -i -e 's/font                = Monospace 11/font                = Monospace 8/g'     "${2}"  # Normal config
        $1 sed -i -e 's/font                = Monospace 10.5/font                = Monospace 7/g'   "${2}"  # Data diode config
    elif (( width < 1920 )); then
        $1 sed -i -e 's/font                = Monospace 11/font                = Monospace 9/g'     "${2}"  # Normal config
        $1 sed -i -e 's/font                = Monospace 10.5/font                = Monospace 8.5/g' "${2}"  # Data diode config
    fi
}


# Installation configurations for Debian/PureOS/Ubuntu/LMDE
# ----------------------------------------------------------------------------------------

function install_tcb {
    # Install TFC for Source/Destination Computer.
    steps_before_network_kill
    kill_network

    verify_files
    create_user_data_dir

    VENV_NAME="venv_tcb"

    # Temporary fix for pypa/virtualenv issue #2350
    export DEB_PYTHON_INSTALL_LAYOUT='deb'
    sudo -E python3 -m virtualenv "${INSTALL_DIR}/${VENV_NAME}" --system-site-packages --never-download --always-copy

    . ${INSTALL_DIR}/${VENV_NAME}/bin/activate
    install_to_venv "${tcb_packages[@]}"
    deactivate

    sudo mv ${INSTALL_DIR}/tfc.png                   /usr/share/pixmaps/
    sudo mv ${INSTALL_DIR}/launchers/TFC-TxP.desktop /usr/share/applications/
    sudo mv ${INSTALL_DIR}/launchers/TFC-RxP.desktop /usr/share/applications/

    # Remove unnecessary files
    remove_packages "${tcb_packages[@]}"
    remove_common_files "sudo"
    sudo rm -r "${INSTALL_DIR}/src/relay/"
    sudo rm -r "${INSTALL_DIR}/qubes/"
    sudo rm    "${INSTALL_DIR}/dd.py"
    sudo rm    "${INSTALL_DIR}/relay.py"
    sudo rm    "${INSTALL_DIR}/tfc.yml"

    add_serial_permissions

    install_complete "Installation of TFC on this device is now complete."
}


function install_relay {
    # Install TFC Relay configuration on Networked Computer.
    steps_before_network_kill

    verify_files
    create_user_data_dir

    VENV_NAME="venv_relay"

    # Temporary fix for pypa/virtualenv issue #2350
    export DEB_PYTHON_INSTALL_LAYOUT='deb'
    sudo -E python3 -m virtualenv ${INSTALL_DIR}/${VENV_NAME} --system-site-packages --always-copy

    . ${INSTALL_DIR}/${VENV_NAME}/bin/activate
    sudo torsocks ${INSTALL_DIR}/${VENV_NAME}/bin/pip3 install -r ${INSTALL_DIR}/requirements-relay.txt --require-hashes --no-deps
    deactivate

    sudo mv ${INSTALL_DIR}/tfc.png                  /usr/share/pixmaps/
    sudo mv ${INSTALL_DIR}/launchers/TFC-RP.desktop /usr/share/applications/

    # Remove unnecessary files
    remove_packages "${tcb_packages[@]}"
    remove_common_files "sudo"
    sudo rm -r "${INSTALL_DIR}/src/receiver/"
    sudo rm -r "${INSTALL_DIR}/src/transmitter/"
    sudo rm -r "${INSTALL_DIR}/qubes/"
    sudo rm    "${INSTALL_DIR}/dd.py"
    sudo rm    "${INSTALL_DIR}/tfc.py"
    sudo rm    "${INSTALL_DIR}/tfc.yml"

    add_serial_permissions

    install_complete "Installation of the TFC Relay configuration is now complete."
}


# ----------------------------------------------------------------------------------------
# Installation configuration for Tails

function read_sudo_pwd {
    # Cache the sudo password so that Debian doesn't keep asking
    # for it during the installation (it won't be stored on disk).
    read -r -s -p "[sudo] password for ${USER}: " sudo_pwd
    until (t_sudo echo '' 2>/dev/null)
    do
        echo -e '\nSorry, try again.'
        read -r -s -p "[sudo] password for ${USER}: " sudo_pwd
    done
    echo
}


function install_relay_tails {
    # Install TFC Relay configuration on Networked Computer
    # running Tails live distro (https://tails.boum.org/).
    read_sudo_pwd

    t_sudo apt update
    t_sudo apt install python3-tk -y || true  # Ignore error in case packets can not be persistently installed

    create_user_data_dir

    torsocks git clone --depth 1 https://github.com/maqp/tfc.git "${HOME}/tfc"
    t_sudo mv "${HOME}/tfc/" "${INSTALL_DIR}/"
    t_sudo chown -R root ${INSTALL_DIR}/

    verify_tcb_requirements_files
    verify_files

    t_sudo mv ${INSTALL_DIR}/tfc.png                        /usr/share/pixmaps/
    t_sudo mv ${INSTALL_DIR}/launchers/TFC-RP-Tails.desktop /usr/share/applications/
    t_sudo mv ${INSTALL_DIR}/tfc.yml                        /etc/onion-grater.d/

    # Remove unnecessary files
    remove_common_files "t_sudo"
    t_sudo rm -r "${INSTALL_DIR}/src/receiver/"
    t_sudo rm -r "${INSTALL_DIR}/src/transmitter/"
    t_sudo rm -r "${INSTALL_DIR}/qubes/"
    t_sudo rm    "${INSTALL_DIR}/dd.py"
    t_sudo rm    "${INSTALL_DIR}/tfc.py"

    install_complete "Installation of the TFC Relay configuration is now complete."
}


# ----------------------------------------------------------------------------------------
# Installation configurations for Qubes OS (https://www.qubes-os.org/)

function install_qubes_src {
    # Qubes Source VM installation configuration for Debian 10 domains.
    steps_before_network_kill

    verify_files
    create_user_data_dir

    VENV_NAME="venv_tcb"

    export DEB_PYTHON_INSTALL_LAYOUT='deb'
    sudo -E python3 -m virtualenv "${INSTALL_DIR}/${VENV_NAME}" --system-site-packages --never-download --always-copy

    . ${INSTALL_DIR}/${VENV_NAME}/bin/activate
    install_to_venv "${tcb_packages[@]}"
    deactivate

    sudo mv ${INSTALL_DIR}/tfc.png                         /usr/share/pixmaps/
    sudo mv ${INSTALL_DIR}/launchers/TFC-TxP-Qubes.desktop /usr/share/applications/
    sudo mv ${INSTALL_DIR}/launchers/tfc-qubes-transmitter /usr/bin/tfc-transmitter

    # Remove unnecessary files
    remove_packages "${tcb_packages[@]}"
    remove_common_files "sudo"
    sudo rm -r "${INSTALL_DIR}/src/relay/"
    sudo rm -r "${INSTALL_DIR}/qubes/"  # Listening service only needed on NET/DST
    sudo rm    "${INSTALL_DIR}/dd.py"
    sudo rm    "${INSTALL_DIR}/relay.py"
    sudo rm    "${INSTALL_DIR}/tfc.yml"

    install_complete_qubes
}


function install_qubes_dst {
    # Qubes Destination VM installation configuration for Debian 10 domains.
    steps_before_network_kill

    verify_files
    create_user_data_dir

    VENV_NAME="venv_tcb"

    # Configure listening service for qrexec RPC
    sudo ln -sf /opt/tfc/qubes/service.sh /etc/qubes-rpc/tfc.NetworkerDestination
    sudo chmod a+x /opt/tfc/qubes/writer.py
    sudo chmod a+x /opt/tfc/qubes/service.sh

    export DEB_PYTHON_INSTALL_LAYOUT='deb'
    sudo -E python3 -m virtualenv "${INSTALL_DIR}/${VENV_NAME}" --system-site-packages --never-download --always-copy

    . ${INSTALL_DIR}/${VENV_NAME}/bin/activate
    install_to_venv "${tcb_packages[@]}"
    deactivate

    sudo mv ${INSTALL_DIR}/tfc.png                         /usr/share/pixmaps/
    sudo mv ${INSTALL_DIR}/launchers/TFC-RxP-Qubes.desktop /usr/share/applications/
    sudo mv ${INSTALL_DIR}/launchers/tfc-qubes-receiver    /usr/bin/tfc-receiver

    # Remove unnecessary files
    remove_packages "${tcb_packages[@]}"
    remove_common_files "sudo"
    sudo rm -r "${INSTALL_DIR}/src/relay/"
    sudo rm    "${INSTALL_DIR}/dd.py"
    sudo rm    "${INSTALL_DIR}/relay.py"
    sudo rm    "${INSTALL_DIR}/tfc.yml"

    install_complete_qubes
}


function install_qubes_net {
    # Qubes Networked VM installation configuration for Debian 10 domains.
    steps_before_network_kill

    verify_files
    create_user_data_dir

    VENV_NAME="venv_relay"

    # Configure listening service for qrexec RPC
    sudo ln -sf /opt/tfc/qubes/service.sh /etc/qubes-rpc/tfc.SourceNetworker
    sudo chmod a+x /opt/tfc/qubes/writer.py
    sudo chmod a+x /opt/tfc/qubes/service.sh

    export DEB_PYTHON_INSTALL_LAYOUT='deb'
    sudo -E python3 -m virtualenv ${INSTALL_DIR}/${VENV_NAME} --system-site-packages --always-copy

    . ${INSTALL_DIR}/${VENV_NAME}/bin/activate
    sudo torsocks ${INSTALL_DIR}/${VENV_NAME}/bin/pip3 install -r ${INSTALL_DIR}/requirements-relay.txt --require-hashes --no-deps
    deactivate

    sudo mv ${INSTALL_DIR}/tfc.png                        /usr/share/pixmaps/
    sudo mv ${INSTALL_DIR}/launchers/TFC-RP-Qubes.desktop /usr/share/applications/
    sudo mv ${INSTALL_DIR}/launchers/tfc-qubes-relay      /usr/bin/tfc-relay

    # Remove unnecessary files
    remove_packages "${tcb_packages[@]}"
    remove_common_files "sudo"
    sudo rm -r "${INSTALL_DIR}/src/receiver/"
    sudo rm -r "${INSTALL_DIR}/src/transmitter/"
    sudo rm    "${INSTALL_DIR}/dd.py"
    sudo rm    "${INSTALL_DIR}/tfc.py"
    sudo rm    "${INSTALL_DIR}/tfc.yml"

    install_complete_qubes
}


# ----------------------------------------------------------------------------------------
# Tiling terminal emulator configurations for single OS

function install_local_test {
    # Install TFC for local testing on a single computer.
    steps_before_network_kill

    verify_files
    create_user_data_dir

    VENV_NAME="venv_tfc"

    sudo torsocks apt install terminator -y

    # Temporary fix for pypa/virtualenv issue #2350
    export DEB_PYTHON_INSTALL_LAYOUT='deb'
    sudo -E python3 -m virtualenv ${INSTALL_DIR}/${VENV_NAME} --system-site-packages --always-copy

    . ${INSTALL_DIR}/${VENV_NAME}/bin/activate
    sudo torsocks ${INSTALL_DIR}/${VENV_NAME}/bin/pip3 install -r ${INSTALL_DIR}/requirements.txt       --require-hashes --no-deps
    sudo torsocks ${INSTALL_DIR}/${VENV_NAME}/bin/pip3 install -r ${INSTALL_DIR}/requirements-relay.txt --require-hashes --no-deps
    deactivate

    sudo mv ${INSTALL_DIR}/tfc.png                                /usr/share/pixmaps/
    sudo mv ${INSTALL_DIR}/launchers/TFC-Local-test.desktop       /usr/share/applications/
    sudo mv ${INSTALL_DIR}/launchers/terminator-config-local-test ${INSTALL_DIR}/
    modify_terminator_font_size "sudo" "${INSTALL_DIR}/terminator-config-local-test"

    # Remove unnecessary files
    remove_packages "${tcb_packages[@]}"
    remove_common_files "sudo"
    sudo rm -r "${INSTALL_DIR}/qubes/"
    sudo rm    "${INSTALL_DIR}/tfc.yml"

    install_complete "Installation of TFC for local testing is now complete."
}


function install_developer {
    # Install TFC development configuration.
    #
    # This configuration will install TFC into `$HOME/tfc/`. This allows
    # you (the user) to easily make edits to the source between runs.
    # Note that it also means, that any malicious program with
    # user-level privileges is also able to modify the source files. For
    # more secure use on a single computer, select the local testing
    # install configuration, or preferably use the Qubes configuration.
    dpkg_check

    wait_for_tor
    create_user_data_dir

    VENV_NAME="venv_tfc"

    sudo torsocks apt update
    sudo torsocks apt install git libssl-dev python3-pip python3-virtualenv python3-tk terminator -y

    torsocks git clone https://github.com/maqp/tfc.git "${HOME}/tfc"

    # Temporary fix for pypa/virtualenv issue #2350
    export DEB_PYTHON_INSTALL_LAYOUT='deb'
    python3 -m virtualenv "${HOME}/tfc/${VENV_NAME}" --system-site-packages --always-copy

    . "${HOME}/tfc/${VENV_NAME}/bin/activate"
    torsocks "${HOME}/tfc/${VENV_NAME}/bin/pip3" install -r "${HOME}/tfc/requirements-dev.txt"
    deactivate

    sudo cp "${HOME}/tfc/tfc.png"                   "/usr/share/pixmaps/"
    sudo cp "${HOME}/tfc/launchers/TFC-Dev.desktop" "/usr/share/applications/"
    sudo sed -i "s|\$HOME|${HOME}|g"                "/usr/share/applications/TFC-Dev.desktop"
    modify_terminator_font_size "" "${HOME}/tfc/launchers/terminator-config-dev"
    chmod a+rwx -R "${HOME}/tfc/"

    # Remove unnecessary files
    sudo rm -f "/opt/install.sh"
    sudo rm -f "/opt/install.sh.asc"
    sudo rm -f "/opt/pubkey.asc"

    add_serial_permissions

    install_complete "Installation of the TFC dev environment is now complete."
}


function arg_error {
    # Print help message if the user launches the
    # installer with missing or invalid argument.
    clear
    echo -e "\nUsage: bash install.sh [OPTION]\n"
    echo    "Mandatory arguments"
    echo    "  tcb      Install Transmitter/Receiver Program (Debian 12 / PureOS 10.3 / *buntu 24.04 / Pop!_OS 22.04 / LMDE 6 / Mint 21.3 / Zorin OS 17.1)"
    echo    "  relay    Install Relay Program                (Debian 12 / PureOS 10.3 / *buntu 24.04 / Pop!_OS 22.04 / LMDE 6 / Mint 21.3 / Zorin OS 17.1 / Tails 6.2)"
    echo -e "  local    Install insecure local testing mode  (Debian 12 / PureOS 10.3 / *buntu 24.04 / Pop!_OS 22.04 / LMDE 6 / Mint 21.3 / Zorin OS 17.1)\n"
    echo    "  qsrc     Install Transmitter Program          (Qubes 4.2.1)"
    echo    "  qdst     Install Receiver Program             (Qubes 4.2.1)"
    echo -e "  qnet     Install Relay Program                (Qubes 4.2.1)\n"
    exit 1
}


# ----------------------------------------------------------------------------------------
# Pre-install checks

function root_check {
    # Check that the installer was not launched as root.
    if [[ ! $EUID -ne 0 ]]; then
        exit_with_message "This installer must not be run as root."
    fi
}


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 grep -q "Tails" /etc/os-release; then
        return
    fi

    # QubesOS also allows sudo without the user `user` being on sudoers list.
    if [ -d "/etc/qubes-rpc" ]; then
        return
    fi

    sudoers=$(getent group sudo |cut -d: -f4 | tr "," "\n")
    user_is_sudoer=false

    for sudoer in ${sudoers}; do
        if [[ ${sudoer} == "${USER}" ]]; then
            user_is_sudoer=true
            break
        fi
    done

    if ! ${user_is_sudoer}; then
        exit_with_message "User ${USER} must be on the sudoers list."
    fi
}


function test_installer {
    # Test that the installer's hashes match the files.
    # Note: This function is only used as part of the release pipeline.
    INSTALL_DIR='.'
    verify_tcb_requirements_files
    verify_files
}


function wait_for_tor {
    # Wait until Torsocks connects properly to GitHub
    c_echo "Waiting for Tor..."
    until torsocks wget -T 10 -q https://raw.githubusercontent.com -O /dev/null; do
        sleep 1
    done
}


# ----------------------------------------------------------------------------------------
# Main routine

set -e
root_check
sudoer_check
sudo_pwd=''

case $1 in
    tcb    ) install_tcb;;
    relay  ) install_relay;;
    tails  ) install_relay_tails;;
    local  ) install_local_test;;
    qsrc   ) install_qubes_src;;
    qdst   ) install_qubes_dst;;
    qnet   ) install_qubes_net;;
    dev    ) install_developer;;
    test   ) test_installer;;
    *      ) arg_error;;
esac
