commit b366bb4af8786ca06437f7f7ce5e340c69f0f6e4 Author: Philipp Winter <p...@nymity.ch> Date: Mon Oct 14 20:49:03 2019 -0700
Remove PGP support. --- .gnupg/TESTING.pub | Bin 4622 -> 0 bytes .gnupg/TESTING.subkeys.sec | Bin 7212 -> 0 bytes .gnupg/bridgedb-offline-key.pub.asc | 113 ---------- .gnupg/bridgedb-online-key.pub.asc | 100 --------- .gnupg/gpg.conf | 96 --------- .gnupg/pubring.gpg | Bin 4634 -> 0 bytes .gnupg/secring.gpg | Bin 7224 -> 0 bytes .travis.requirements.txt | 1 - CHANGELOG | 6 + README.rst | 29 +-- bridgedb.conf | 45 ---- bridgedb/configure.py | 3 +- bridgedb/crypto.py | 99 +-------- bridgedb/distributors/email/autoresponder.py | 41 +--- bridgedb/distributors/email/distributor.py | 6 +- bridgedb/distributors/email/request.py | 22 -- bridgedb/distributors/email/server.py | 10 - bridgedb/distributors/email/templates.py | 7 - bridgedb/distributors/https/server.py | 2 - bridgedb/distributors/https/templates/base.html | 2 - bridgedb/i18n/templates/bridgedb.pot | 84 ++++---- bridgedb/strings.py | 268 +----------------------- bridgedb/test/email_helpers.py | 23 +- bridgedb/test/test_crypto.py | 137 ------------ bridgedb/test/test_email_autoresponder.py | 15 -- bridgedb/test/test_email_request.py | 36 +--- bridgedb/test/test_email_templates.py | 11 - bridgedb/test/test_main.py | 5 - requirements.txt | 1 - scripts/setup-tests | 2 +- 30 files changed, 63 insertions(+), 1101 deletions(-) diff --git a/.gnupg/TESTING.pub b/.gnupg/TESTING.pub deleted file mode 100644 index 764bf47..0000000 Binary files a/.gnupg/TESTING.pub and /dev/null differ diff --git a/.gnupg/TESTING.subkeys.sec b/.gnupg/TESTING.subkeys.sec deleted file mode 100644 index 688f7e9..0000000 Binary files a/.gnupg/TESTING.subkeys.sec and /dev/null differ diff --git a/.gnupg/bridgedb-offline-key.pub.asc b/.gnupg/bridgedb-offline-key.pub.asc deleted file mode 100644 index 47d1310..0000000 --- a/.gnupg/bridgedb-offline-key.pub.asc +++ /dev/null @@ -1,113 +0,0 @@ ------BEGIN PGP PUBLIC KEY BLOCK----- - -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 -ILUXBkpIDfVSoZD7qWlntai53BDQr5pfMJhv15di4XAhtqv43vAmA57ifd+QJS2U -AfYc4CdX0lk2BZ4jRD8jCZ4Uxw15E3RqhnXsWDRxtD4fwsb2ZFi0DDuPlwBdGgh5 -Rm2Bz9JjSV6gDEuXr/JtAzjSz1Jdh8wPkSofiHGTfxysVhlGlg+YPRziRlzML8A2 -0xY+9mPxEEin5ZQ9wmrDyiiOBvPTbG3O9+Sp5VZDuD4ivW/DHumPWGVSRdjcAQDe -HMXUVGjhBDnj06XNrgJPhODdJeYq0EnGTt15ofZQSswD7TTTRPDOn0Cz/QARAQAB -tDpCcmlkZ2VEQiAoT2ZmbGluZSBJRCBLZXkpIDxicmlkZ2VzQGJyaWRnZXMudG9y -cHJvamVjdC5vcmc+iQkfBBMBCgEJBQJSWQfkSBSAAAAAABcAKHZlcmlmaWVkQHRv -cnByb2plY3Qub3JnN0I3ODQzNzAxNUU2M0RGNDdCQjEyNzBBQ0JEOTdBQTI0RThF -NDcyRU8UgAAAAAAeAChicmlkZ2VzQGJyaWRnZXMudG9ycHJvamVjdC5vcmc3Qjc4 -NDM3MDE1RTYzREY0N0JCMTI3MEFDQkQ5N0FBMjRFOEU0NzJFKhpodHRwczovL2Jy -aWRnZXMudG9ycHJvamVjdC5vcmcvcG9saWN5LnR4dAIbAQMLDQkEFQoJCAQWAgEA -Ah4BAheAJxhodHRwczovL2JyaWRnZXMudG9ycHJvamVjdC5vcmcva2V5LmFzYwAK -CRDL2XqiTo5HLoqEP/48rFpJCVStn8xo+KkHSVvsqpxDRlb/nNheI+ov2UxILdwl -NIU6kLsvKECKPe1AHKdS/MzANbkTF35Y4QgZsNpVXaCVL7adGBSzOdPFupDJJVOu -wa+uFRc/FuNJyH/TIn56/+R5J5C54OxIYNxvW3WF4eHKLJYk/JZOMMfy4iWm7Sql -0nDC5O435nK4F4Jb4GLPlUIzioIy2OWqGoFHXymbGhL1tWaqasYmED4n3AMqlYw6 -xnNhdWOc/KZelPl9nanybyh0IIdZqUKZleRt4BxSgIT8FqC2sZuZ8z7O9s987Naz -Q32SKaP4i2M9lai/Y2QYfKo+wlG+egmxtujz7etQMGlpgBZzFLdJ8/w4U11ku1ai -om74RIn8zl/LHjMQHnCKGoVlscTI1ZPt+p+p8hO2/9vOwTR8y8O/3DQSOfTSipwc -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 -YnJpZGdlc0BicmlkZ2VzLnRvcnByb2plY3Qub3JnREY4MTExMDlFMTdDOEJGMTM0 -QjVFRUI2OERDNDNBMjg0ODgyMUUzMioaaHR0cHM6Ly9icmlkZ2VzLnRvcnByb2pl -Y3Qub3JnL3BvbGljeS50eHQACgkQjcQ6KEiCHjIaqBAA0BuEs7horx6iCq4cjAhv -YPLrxuC4fKEfVyhAjCJMJSFFCPAlGgU+BjyPNDD57wzKAmUkdJG+Ss25mwWXa53w -5R2kDqDnHocOdZGtxZ7zx/uUd2eWLNBfVuK7nHOk1d1Hs0OZBnckc+MCqnLtuYe5 -68pa9+jW6cNIjAnzMIListmoXWgYYWJvMKeBMG4DGtYJ8w7CJQjOHc5yar12DrX3 -wnQ7hXtFuuqQblpEUnLnZGvHf2NKMZfBBMcP96h9OmLGNa+vmNYsMyPKU7n5hPgX -nTgmQ4xrv1G7JukjppZRA8SFoxupcaQeTixyWERGBhBiAbwZsbQz8L/TVZKierzg -sdNngHcFzE8MyjuJDvTos7qXPmgSRXFqJLRn0ZxpR5V1V8BVZUqCGuSZT89TizsD -z5vyv8c9r7HKD4pRjw32P2dgcEqyGRkqERAgSuFpObP+juty+kxYyfnadBNCyjgP -s7u0GmsTt4CZi7BbowNRL6bynrwrmQI9LJI1bPhgqfdDUbqG3HXwHz80oRFfKou8 -JTYKxK4Iumfw2l/uAACma5ZyrwIDBX/H5XEQqch4sORzQnuhlTmZRf6ldVIIWjdJ -ef+DpOt12s+cS2F4D5g8G6t9CprCLYyrXiHwM/U8N5ywL9IeYKSWJxa7si3l9A6o -ZxOds8F/UJYDSIB97MQFzBo= -=JdC7 ------END PGP PUBLIC KEY BLOCK----- diff --git a/.gnupg/bridgedb-online-key.pub.asc b/.gnupg/bridgedb-online-key.pub.asc deleted file mode 100644 index a848a2b..0000000 --- a/.gnupg/bridgedb-online-key.pub.asc +++ /dev/null @@ -1,100 +0,0 @@ ------BEGIN PGP PUBLIC KEY BLOCK----- - -mQINBFIv8YABEADRqvfLB4xWj3Fz+HEmUUt/qbJnZhqIjo5WBHaBJOmrzx1c9fLN -aYG36Hgo6A7NygI1oQmFnDinSrZAtrPaT63d1Jg49yZwr/OhMaxHYJElMFHGJ876 -kLZHmQTysquYKDHhv+fH51t7UVaZ9NkP5cI+V3pqck0DW5DwMsVJXNaU317kk9me -mPJUDMb5FM4d2Vtk1N+54bHJgpgmnukNtpJmRyHRbZBqNMln5nWF7vdZ4u5PGPWj -bA0rPZhayeE3FQ0MHiGL12kHAy30pfg54QfPJDQBCywjABetRE+xaM9TcS+R31Pf -2VbLeb+Km7QpHMwOXI5xZLss9BAWm9EBbmXxuqaRBHyi830jjCrK9UYuzzOqKoUV -Mk1BRelZTFnGPWeVTE+Ps+pwJ0Dwx4ghppJBCoArmEbkNliblxR/2wYOOFi/ZVA4 -Zc2ok9T3rBLVg07b7ezFUScGiTnc7ac7hp6r8Qsh09ZbhRr9erK/n194aEvkXTfr -qepwrAE7YeF4YuR206UOFFWDhxWDLbRu0gIWgrevEQu/cvQPrO9uH5fL6Gw/+mNP -Q/NIteejhkDyvyTUKyBu7x+Gls71zT2u/X13eOAJ8IxBkSVRKQ8tRD+oqJkWplOf -+BpaGU+g6u4kT2AzFDxTOupfrYcPvORTAV/V3suys2YQE4x422GASXDivQARAQAB -tClCcmlkZ2VEQiA8YnJpZGdlc0BicmlkZ2VzLnRvcnByb2plY3Qub3JnPokDJQQT -AQoBD0gUgAAAAAAXACh2ZXJpZmllZEB0b3Jwcm9qZWN0Lm9yZ0RGODExMTA5RTE3 -QzhCRjEzNEI1RUVCNjhEQzQzQTI4NDg4MjFFMzJPFIAAAAAAHgAoYnJpZGdlc0Bi -cmlkZ2VzLnRvcnByb2plY3Qub3JnREY4MTExMDlFMTdDOEJGMTM0QjVFRUI2OERD -NDNBMjg0ODgyMUUzMioaaHR0cHM6Ly9icmlkZ2VzLnRvcnByb2plY3Qub3JnL3Bv -bGljeS50eHQCGwEDCw0JBBUKCQgEFgIBAAIeAQIXgCcYaHR0cHM6Ly9icmlkZ2Vz -LnRvcnByb2plY3Qub3JnL2tleS5hc2MFAlSKBKIFCQPDTiIACgkQjcQ6KEiCHjIs -jg//bJ12eRnBMfIGzOGh+T4wz7/YyKLfARAMnqDnSxhTxuE+M5hWm3QbxP03R6eY -x+PKwQaDJSmm7HhRhltb7QXUe8dqjnocFwwagpoLZ/81mBLxByqg5TKHGGIGy+DX -omIzCq5ijx1IUkHlgh708a5alG7bjRTqedT4Wxxyl6psGzDhGQdS8bqx/f32nQaE -h41l+A/EY1g2HVqky63ZHAP3S2v+mWCrk5DnkElc0229MXqaBuEr4nbYMXRkahMb -E2gnCmdSoeD21AY6bNyz7IcJGpyKCx9+hVgPjpm3J23JEYyPL+s48jn6QcI/Q2gD -yAtgU65y6IrdYn8SwkABI1FIq9WAwG7DaInxvkqkYqyBQLaZJEMyX8NTBvFoT5JS -jnkxG0xu61Vxq0BLYBIOJE0VFHAJ40/jOvSxQJkQhu9G4BK7htnADbtstmMDMM3q -xuuO5pcj2rl7YthNunyZ1yhPHXijUUyKrwR9piENpptztFBVN6+ijqU/TmWMOtbH -X7p9F+3tXCHHqwO5U/JMtsb/9M39MR8BrdcLc7m6dCpeuSUuR2LLroh+MoMJGviI -iesxHF95kFqkJAecW1Z3eKL9vrlbfO3waeuCi18k1TePnZuG5lmf2KjKDW5vHK4O -WFqvvfK2kxkCUjvGdLeTOAVOV+X+PQ23jvBJO2bS7YbOb9C5Ag0EUi/ygQEQALZ/ -p7xRINHY7MMf3/bo/I0WRxWHd1AE9tRToyEg1S2u1YrWWL5M9D8saRsp9cpnpGEu -hW3vu7G4nasY27qOz4bSKu1YMAVIC58v1tEnBqdo1zErNjhs38PrmJKbbs9tDfYY -Oi2x0GlhMbIrNStcZpnCdLa6U6NLMbggDL1GxjMPYBMi4TtLgcIeRDUSjsZscZkg -Kxs5QkSVc3SrYyraayIc8WtIpDLcxPt6/g90rbatZzBfO+93Rz7qUXHmgzuM0hy1 -Fvn619o3I5DsWrfOz9t/QuznoOBw4PfzDPNT7VlzZN4xHAcr5+7B+DH9IsvlCt5N -kQFuYpFZCpXNaD2XOtmIqjTCeLNfcgTEj0qoUIEKyKbBIgfP+7S2tLXy8JKUTy5g -9kxXQeHueLykQ4Mt18JH0nMHbHbQl0K3LGT4ucRDOmjNtlQCltVLkIk3GimyqKs/ -vdZ9c+dm4Akx1qsJcwvveX+imJe2e9RUodcxWXxWrYnuPa5b5nfR1i+GfV0on/Pt -AQ8gc9CkJpMiq5TQDOFhFP6yQcq77sXuUkEl5qamptedz28E0I693ulnfwcsE80p -xkpIG6n33DZJSEyqgtWjE1P2pnsVfO5ILs3mKLe7bO1v3qMXcCkMCGH/kwzvtowq -YvY4gaZMDZtQFY8U7lI9FdRUvVdeHAB24y291nhzABEBAAGJBYMEGAEKANNIFIAA -AAAAFwAodmVyaWZpZWRAdG9ycHJvamVjdC5vcmdERjgxMTEwOUUxN0M4QkYxMzRC -NUVFQjY4REM0M0EyODQ4ODIxRTMyTxSAAAAAAB4AKGJyaWRnZXNAYnJpZGdlcy50 -b3Jwcm9qZWN0Lm9yZ0RGODExMTA5RTE3QzhCRjEzNEI1RUVCNjhEQzQzQTI4NDg4 -MjFFMzIqGmh0dHBzOi8vYnJpZGdlcy50b3Jwcm9qZWN0Lm9yZy9wb2xpY3kudHh0 -AhsCBQJUigTTBQkDw01SAqTB2CAEGQEKAIEFAlIv8oFPFIAAAAAAHgAoYnJpZGdl -c0BicmlkZ2VzLnRvcnByb2plY3Qub3JnOUZFMzlEMUE3NDM4OTIyMzNCM0Y2NkYy -MjFCNTU0RTk1OTM4RjREMCoaaHR0cHM6Ly9icmlkZ2VzLnRvcnByb2plY3Qub3Jn -L3BvbGljeS50eHQACgkQIbVU6Vk49NDbPw/5ATe/T8+eaToC3v0TYNRH5nveQvzA -WdnshD3lnvfsgDhbilwifKpc5LHKXU3rvb42HH2cu0ckuksdDTvICZD9cJjRq/F+ -Mzm0pNCAJg0pQnHaaWFQjw+CHYEoizai3S+iYxhNHeSdA6Ty7xm4+bHNf0Aqblbd -6dKwq9EvjwAI6zZsAHtsmHRUMdrFwGdKae6CSchUT2JQFBPEWMhvzdpDGACWVaSP -sxYKuYg9LgpswGcof+tprRjKRl8MtSh0ufjbVBlTeSKpL5Y+fcTRD3PI8w7Ocr3z -jr6XpYG4SUNHsWwxyu/DTXg76Lk1/+BdaH25hDOAasLUOU7yRL8zD/c7M0FkGXdj -r5I2DEEqwzJ9cPHWjpgb8N9fZLoPFP9JOmKGHINqxNe7TfwiTdD6uDKs/u/QK1U+ -o3iYBXBTREdopPUdBTM9wYRUhyGXTEKLhUP3MGpXYlgeYPrSdp76VyN3BzLTbMv+ -+7rxyKxL9cWYU0pnXHgPC5nyHX5nqXmhMnkxAD3Bnm8n9XDfgiyTDExqksEh2VXt -yhVfLezylEP2fwtd8/mABBCsTjzZW6FRfRRAjUZWZGFpFg8no1x5JS9uiswRP5g1 -qHijNFWpGyTtJWl5VNd0d9+LtVUX1jRpDUpsjZcxqs3fsvw2p+H/zQ1wFvDrsoav -hqOTq+AEnJc7ZG8JEI3EOihIgh4ych8P/3GTyWb29+43YVibbSPPvEv4gFqziC+9 -1p92FJ0V4XdaT7TW3qaZVp5edUNwB/jjF0SxBybwaMX2ZIGXOjnjF6/Zby4ynuTX -vZkS1mKRA0KWupB3e9PSMY3ZtssnqpGna/+3qlpxtunW7HcW4nCF/f59WHhlVjaO -MXjtuWj59yB56Dd1sNjwhcNCyp4/NpzGnRW97ZV3Pp4oqIOqcGzCQXkVPcnaqcOh -Cs9vIDJlMtn/IWBzUGimuRllDSSVSWkYkyJcG2NUHUwgXYpLwQz7sScvmCPchf4K -qarpX0FpkUDfqaVVuQ7A2XbPUAVFzIk930G1WzgOuOdg9vhWSEjou+SKrAoMz90w -3xHwEvmPDTTVJQft9ytoRbwZkIPfzzhII3mr4agbORAfzDaj5g/f6CVRdg6D3ME1 -Etg9ZrfLgRY993g/arfIME6OOsiNcy5+PunN96Rw0o1xoD+97NmZuQrs/p4Mfn5o -8EwXHutREhahin+3/SV3hz9ReeLYmClq+OVhjPzPdtwZsFoyQyUJoFVHPTuSdChZ -FPaqN68FjlNMugmxnvski3ZDVT7pw3B6otjjaL3rr6q0PC2yhEb2ntb3IFUizHjn -80SmfE1Bqwit7ZHu8r/Gt/0iecGk5h84VzSgiGZGF/7m1i5UMVlNSeWnsInGa5Su -7HSzfMq+YmkzuQINBFIv8p4BEADTOLR5e5NKKRPpjCb4B/8YYkWh+orb70EogIZ6 -j5v8d/djLyhjqZ9BIkh41/hYKMwnsa4KkDkTaX0eNu3BFB2zGgZ1GSd7525ESxiq -suXIlAg2pex7bysaFfua0nUx64tmaQm2XArdkj/wI0pbg+idWym3WQQmZLyTTbzl -8rpTEtTt+S2m6z3EeAhEHuNFH16hEDUywlef3EotX3njuFiLqaNvnzUYDxhUvKd2 -2K1es1ggispgP+eb1bkMApxecf2rqmSUEcvsuTWip4oGZPBLGDQeNKHkCUVbj4wT -yWDIRtto3wi+4CFPEVzw+htj1cQfTstPqUdG7NSOmLQggedoUdv7AJm4MJJiyEax -l+IAf6Afwrrm3eOSv0PgoUxOrUb9vhIoL8ih8gtiqvQ9qYaRQfQA/w3Z0Su2Yfoc -fQS8Uw99qG+oTgieG6F6ud8+hMZAYVZFqbU+ztzMyDE6h4Hflkt6VNJ0Hk0VoF38 -TTs77pHXXBbLD6SzR6tbNuR9r/lbmC8Qf2A1ZAThR0iuGhNRFtUPo28GxakxGdLZ -9kHIxjl7EN/gsmYTwuEhr+yfNtLwtSH0ojeqbDmgufvgh+SITCtyNDAUspjrZYEt -F0NHRpSom2NFVELMqMRydU/ncph1rGZgVp6/zVj6xIlhKmqj5P1y/9B0c4Tu1CzJ -pkJ5wwARAQABiQLpBBgBCgDTSBSAAAAAABcAKHZlcmlmaWVkQHRvcnByb2plY3Qu -b3JnREY4MTExMDlFMTdDOEJGMTM0QjVFRUI2OERDNDNBMjg0ODgyMUUzMk8UgAAA -AAAeAChicmlkZ2VzQGJyaWRnZXMudG9ycHJvamVjdC5vcmdERjgxMTEwOUUxN0M4 -QkYxMzRCNUVFQjY4REM0M0EyODQ4ODIxRTMyKhpodHRwczovL2JyaWRnZXMudG9y -cHJvamVjdC5vcmcvcG9saWN5LnR4dAIbDAUCVIoE4QUJA8NNQwAKCRCNxDooSIIe -Mo7JEADDBtQpYxPhbj3MT0xpk96EDlon/5yHVf+cnk1pNisc+HkJsVe1nh7gAWOz -wJKdeqOVpgxiJTxIQdl6mipKwwFi0DreP7h56s1WQkuSSWJzqssAwWHfVAsX13fV -zWd0XyxN/OF9ZKQjX4qwpJ/na631PSwZLvHYhMaZnb9pjNwC5/PEKRmFqLbQT6Px -12miZT6ToPDCczHxJ4BxbEGVU+PtRsHwmTRT3JhxFNDfeVd+uwsQIMidJbUoqVW7 -fe2zNd0TaWyz4Rw087oZE2OXdctjvtsu8fzXx6d/tkazI6cUOqoaMTR41KEu5X0T -BpWSAMADBYjNs9QRWXX7ZlsJRUSCX1EKbMhgoL6KIGceIkjH61M/LF6HqDgSgSWt -h+LIYGa+LrB/6819o32QSOSHHJ5+NJrbCSaLgKE/LKnf92V2QbZE8IGY6EOSjHqn -n1+j+CLRKY/kUyvk+1TumTghjg/aDs/8Jv8PvgSWLQ0q1rxHYbX7q9ZJhYC/4LdR -ya/Cho6w2l0N3tV/IMAwvFNHsaiIiiwfoOQbkBUvkyzBwjKt96Ai4I0QKt/63uH0 -drQhlJEgIyGkOrorBByVqZAQdnoLENYIu6tDUj0bTbGObKqua4iPlSK3/g40zCm4 -9OgcN7A8kFuNpgp2EHqj1/jrwd7mZYKsWTuGiR/7fwXf+4xbvg== -=raCx ------END PGP PUBLIC KEY BLOCK----- diff --git a/.gnupg/gpg.conf b/.gnupg/gpg.conf deleted file mode 100644 index d4a9913..0000000 --- a/.gnupg/gpg.conf +++ /dev/null @@ -1,96 +0,0 @@ -## -## Options for GnuPG -## -## :author: isis <i...@patternsinthevoid.net> - -no-greeting -no-emit-version -charset utf-8 -display-charset utf-8 -utf8-strings -keyid-format long -default-key 8DC43A2848821E32 -trusted-key CBD97AA24E8E472E - -# When outputting certificates, view user IDs distinctly from keys: -fixed-list-mode - -verify-options no-show-photos show-uid-validity show-notations show-user-notations show-policy-urls show-keyserver-urls no-pka-lookups no-pka-trust-increase - -list-options no-show-photos show-uid-validity no-show-unusable-uids show-unusable-subkeys show-notations show-user-notations show-policy-urls show-keyserver-urls show-sig-expire show-sig-subpackets - -export-options no-export-attributes - -# Because some mailers change lines starting with "From " to ">From " -# it is good to handle such lines in a special way when creating -# cleartext signatures; all other PGP versions do it this way too. -#no-escape-from-lines - -## Indymedia Keyservers: -##---------------------- -keyserver hkps://2eghzlv2wwcq7u7y.onion -keyserver hkp://2eghzlv2wwcq7u7y.onion -keyserver hkps://keys.indymedia.org -keyserver hkp://keys.indymedia.org -keyserver https://keys.indymedia.org -keyserver http://keys.indymedia.org -keyserver https://qtt2yl5jocgrk7nu.onion -keyserver http://qtt2yl5jocgrk7nu.onion -keyserver-options ca-cert-file=~/scripts/certs/keys.indymedia.org - -## Normal keyservers: -##-------------------- -keyserver hkp://subkeys.pgp.net -keyserver mailto:pgp-public-k...@keys.pgp.net -keyserver pgp.mit.edu - -keyserver-options verbose verbose verbose no-include-revoked no-include-disabled no-auto-key-retrieve no-honor-keyserver-url no-honor-pka-record include-subkeys no-include-attributes - -#keyserver-options http-proxy=socks://127.0.0.1:59050 - -expert -allow-freeform-uid -cert-digest-algo SHA512 -digest-algo SHA512 -default-preference-list SHA512 SHA384 SHA256 CAMELLIA256 AES256 ZLIB ZIP Uncompressed -personal-cipher-preferences CAMELLIA256 AES256 -personal-digest-preferences SHA512 SHA384 SHA256 -personal-compress-preferences ZLIB ZIP -compress-level 9 -default-cert-expire 2y -ask-cert-expire -ask-cert-level -default-sig-expire 1y -no-ask-sig-expire - -## algorithm to protect the key in memory: -s2k-cipher-algo AES256 - -## use this one to mangle the passphrases: -s2k-digest-algo SHA512 - -## passphrase mangling mode: -## 0=plaintest -## 1=salt -## 3=salt+iteration -s2k-mode 3 - -## how mangly should we mangle it? 1024 < mangle < 65011712 -## -## try "python -c'import random;a=random.randint(32505856, 65011712);print a' -s2k-count 48454407 - -## Don't run if we can't secure mempages -require-secmem - -## Check the back sig on subkey which has made a signature -require-cross-certification - -## The notation on certifications we make: -## see http://thread.gmane.org/gmane.mail.notmuch.general/3721/focus=7234 -sig-notation brid...@bridges.torproject.org=%g -cert-notation brid...@bridges.torproject.org=%g -cert-notation verif...@torproject.org=%f -set-policy-url https://bridges.torproject.org/policy.txt -sig-keyserver-url !https://bridges.torproject.org/key.asc -default-keyserver-url https://bridges.torproject.org/key.asc diff --git a/.gnupg/pubring.gpg b/.gnupg/pubring.gpg deleted file mode 100644 index 82c5477..0000000 Binary files a/.gnupg/pubring.gpg and /dev/null differ diff --git a/.gnupg/secring.gpg b/.gnupg/secring.gpg deleted file mode 100644 index 5e0fb5d..0000000 Binary files a/.gnupg/secring.gpg and /dev/null differ diff --git a/.travis.requirements.txt b/.travis.requirements.txt index 8308ce2..a74821a 100644 --- a/.travis.requirements.txt +++ b/.travis.requirements.txt @@ -21,7 +21,6 @@ pycryptodome==3.9.6 Twisted==20.3.0 coverage==5.0.3 coveralls==1.10.0 -gnupg==2.3.1 ipaddr==2.2.0 mechanize==0.4.5 Pillow==6.2.2 diff --git a/CHANGELOG b/CHANGELOG index f85283b..7524344 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,9 @@ + * FIXES https://bugs.torproject.org/17548 + This patch removes PGP support. BridgeDB's signing key expired on + 2015-09-11. Nobody ever complained and maintaining the bits and pieces + necessary to sign emails isn't worth the effort, so this patch removes + that feature. + * FIXES https://bugs.torproject.org/30941 Make our email responder more usable. This patch removes the concept of "valid" email commands and returns bridges (obfs4, for now) no matter diff --git a/README.rst b/README.rst index 1a338ea..858562e 100644 --- a/README.rst +++ b/README.rst @@ -82,7 +82,6 @@ BridgeDB requires the following OS-level dependencies: - python-dev - `python3-dkim <https://pypi.org/project/dkimpy/>`__ (it contains the ``dkimverify`` binary) - build-essential -- gnupg (preferrably, gnupg2) - OpenSSL>=1.0.1g - `SQLite3 <http://www.maxmind.com/app/python>`__ - `MaxMind GeoIP <https://www.maxmind.com/en/geolocation_landing>`__ @@ -111,7 +110,7 @@ BridgeDB should work with or without a Python virtualenv. can do:: sudo apt-get install build-essential openssl python python-dev \ - python-setuptools sqlite3 gnupg2 libgeoip-dev geoip-database + python-setuptools sqlite3 libgeoip-dev geoip-database - Install Pip 1.3.1 or later. Debian has this version, but if for some @@ -254,32 +253,6 @@ To enable using a local cache of CAPTCHAs, set the following options:: ------- --------------------- -GnuPG email signing: --------------------- - -In your ``bridgedb.conf`` file, make sure that:: - - EMAIL_GPG_SIGNING_ENABLED = True - -and edit the following option to add the full fingerprint of the GnuPG key -that should be used to by BridgeDB to sign outgoing emails:: - - EMAIL_GPG_PRIMARY_KEY_FINGERPRINT - -The key specified by ``EMAIL_GPG_PRIMARY_KEY_FINGERPRINT`` can be a master -key, or a subkey (with or without the private portions of its corresponding -master key), but it **must** be inside the ``secring.gpg`` and ``pubring.gpg`` -keyrings inside the directory specified in the ``bridgedb.conf`` option:: - - EMAIL_GPG_HOMEDIR - -If the key has requires a passphrase for signing, you'll also need to set -either of:: - - EMAIL_GPG_PASSPHRASE - EMAIL_GPG_PASSPHRASE_FILE - ---------------------------------------------------------- Preventing already-blocked bridges from being distributed: diff --git a/bridgedb.conf b/bridgedb.conf index 5a8a38e..31eb3fa 100644 --- a/bridgedb.conf +++ b/bridgedb.conf @@ -642,51 +642,6 @@ EMAIL_N_BRIDGES_PER_ANSWER = 3 # once we have the vidalia/tor interaction fixed for everbody. EMAIL_INCLUDE_FINGERPRINTS = True -# -# Configuration options for OpenPGP signing and encryption -# ------------------------------------------------------------------------------ - -# Should we sign all email responses to clients with the key specified by -# EMAIL_GPG_PRIMARY_KEY_FINGERPRINT (or one of its subkeys)? -EMAIL_GPG_SIGNING_ENABLED = True - -# The directory, relative to BridgeDB's runtime directory, in which to store -# OpenPGP keyrings and associated files. -EMAIL_GPG_HOMEDIR = '.gnupg' - -# This should be a 40-character hexadecimal string containing the OpenPGP -# fingerprint (without spaces) of the default primary key to use. The key -# should be capable of both signing and encryption, or have subkeys capable of -# such. -# -# The default primary key fingerprint below is the test key contained in the -# '.gnupg/TESTING.subkeys.sec' and '.gnupg/TESTING.pub' files: -EMAIL_GPG_PRIMARY_KEY_FINGERPRINT = '0017098C5DF4197E3C884DCFF1B240D43F148C21' - -# If the key referred to by EMAIL_GPG_PRIMARY_KEY_FINGERPRINT requires a -# passphrase for signing or encryption, then the passphrase may be given in -# the EMAIL_GPG_PASSPHRASE option (as a string), or it may be contained within -# the file pointed to by EMAIL_GPG_PASSPHRASE_FILE. Currently, only one -# passphrase is supported, so if the key specified by -# EMAIL_GPG_PRIMARY_KEY_FINGERPRINT has multiple subkeys, those subkeys MUST -# all have the same passphrase. -# -# If EMAIL_GPG_PASSPHRASE_FILE is used, and the filepath is not absolute, the -# path is interpreted as being relative to BridgeDB's runtime directory. -# (Note: be sure not to put any newlines after the phassphrase in the -# EMAIL_GPG_PASSPHRASE_FILE, or else they will be interpreted as part of the -# passphrase.) -# -# There are currently no safety checks on the permissions of either this -# configuration file or the EMAIL_GPG_PASSPHRASE_FILE, so beware and use at -# your own risk. -# -# If both EMAIL_GPG_PASSPHRASE and EMAIL_GPG_PASSPHRASE_FILE are ``None``, -# then it is assumed that the key specified by -# EMAIL_GPG_PRIMARY_KEY_FINGERPRINT does not require a passphrase. -EMAIL_GPG_PASSPHRASE = None -EMAIL_GPG_PASSPHRASE_FILE = None - #------------------------------- # Hashring Allocation Options \ #------------------------------------------------------------------------------ diff --git a/bridgedb/configure.py b/bridgedb/configure.py index e069ea3..ab8e08a 100644 --- a/bridgedb/configure.py +++ b/bridgedb/configure.py @@ -113,8 +113,7 @@ def loadConfig(configFile=None, configCls=None): "MOAT_CERT_FILE", "MOAT_KEY_FILE", "LOG_FILE", "COUNTRY_BLOCK_FILE", "GIMP_CAPTCHA_DIR", "GIMP_CAPTCHA_HMAC_KEYFILE", - "GIMP_CAPTCHA_RSA_KEYFILE", "EMAIL_GPG_HOMEDIR", - "EMAIL_GPG_PASSPHRASE_FILE", "NO_DISTRIBUTION_FILE"]: + "GIMP_CAPTCHA_RSA_KEYFILE", "NO_DISTRIBUTION_FILE"]: setting = getattr(config, attr, None) if setting is None: setattr(config, attr, setting) diff --git a/bridgedb/crypto.py b/bridgedb/crypto.py index db3d083..9bdf729 100644 --- a/bridgedb/crypto.py +++ b/bridgedb/crypto.py @@ -9,9 +9,9 @@ # :license: 3-clause BSD, see included LICENSE for information """This module contains general utilities for working with external -cryptographic tools and libraries, including OpenSSL and GnuPG. It also -includes utilities for creating callable HMAC functions, generating HMACs for -data, and generating and/or storing key material. +cryptographic tools and libraries, like OpenSSL. It also includes utilities for +creating callable HMAC functions, generating HMACs for data, and generating +and/or storing key material. .. py:module:: bridgedb.crypto :synopsis: BridgeDB general cryptographic utilities. @@ -19,12 +19,10 @@ data, and generating and/or storing key material. :: bridgedb.crypto - |_getGPGContext() - Get a pre-configured GPGME context. |_getHMAC() - Compute an HMAC with some key for some data. |_getHMACFunc() - Get a callable for producing HMACs with the given key. |_getKey() - Load the master HMAC key from a file, or create a new one. |_getRSAKey() - Load an RSA key from a file, or create a new one. - |_gpgSignMessage() - Sign a message string according to a GPGME context. |_writeKeyToFile() - Write to a file readable only by the process owner. | \_SSLVerifyingContextFactory - OpenSSL.SSL.Context factory which verifies @@ -40,7 +38,6 @@ data, and generating and/or storing key material. from __future__ import absolute_import from __future__ import unicode_literals -import gnupg import hashlib import hmac import io @@ -264,96 +261,6 @@ def removePKCS1Padding(message): return unpadded -def initializeGnuPG(config): - """Initialize a GnuPG interface and test our configured keys. - - .. note:: This function uses python-gnupg_. - - :type config: :class:`bridgedb.persistent.Conf` - :param config: The loaded config file. - :rtype: 2-tuple - :returns: If ``EMAIL_GPG_SIGNING_ENABLED`` isn't ``True``, or we couldn't - initialize GnuPG and make a successful test signature with the - specified key, then a 2-tuple of ``None`` is returned. Otherwise, the - first item in the tuple is a :class:`gnupg.GPG` interface_ with the - GnuPG homedir set to the ``EMAIL_GPG_HOMEDIR`` option and the signing - key specified by the ``EMAIL_GPG_SIGNING_KEY_FINGERPRINT`` option in - bridgedb.conf set as the default key. The second item in the tuple is - a signing function with the passphrase (as specified in either - ``EMAIL_GPG_PASSPHRASE`` or ``EMAIL_GPG_PASSPHRASE_FILE``) already - set. - - .. _python-gnupg: https://pypi.python.org/pypi/gnupg/ - .. _interface: https://python-gnupg.readthedocs.org/en/latest/gnupg.html#gnupg-module - """ - ret = (None, None) - - if not config.EMAIL_GPG_SIGNING_ENABLED: - return ret - - homedir = config.EMAIL_GPG_HOMEDIR - primary = config.EMAIL_GPG_PRIMARY_KEY_FINGERPRINT - passphrase = config.EMAIL_GPG_PASSPHRASE - passFile = config.EMAIL_GPG_PASSPHRASE_FILE - - logging.info("Using %s as our GnuPG home directory..." % homedir) - gpg = gnupg.GPG(homedir=homedir) - logging.info("Initialized GnuPG interface using %s binary with version %s." - % (gpg.binary, gpg.binary_version)) - - primarySK = None - primaryPK = None - secrets = gpg.list_keys(secret=True) - publics = gpg.list_keys() - - if not secrets: - logging.warn("No secret keys found in %s!" % gpg.secring) - return ret - - primarySK = list(filter(lambda key: key['fingerprint'] == primary, secrets)) - primaryPK = list(filter(lambda key: key['fingerprint'] == primary, publics)) - - if primarySK and primaryPK: - logging.info("Found GnuPG primary key with fingerprint: %s" % primary) - - for sub in primaryPK[0]['subkeys']: - logging.info(" Subkey: %s Usage: %s" % (sub[0], sub[1].upper())) - else: - logging.warn("GnuPG key %s could not be found in %s!" % (primary, gpg.secring)) - return ret - - if passphrase: - logging.info("Read GnuPG passphrase from config.") - elif passFile: - try: - with open(passFile) as fh: - passphrase = fh.read() - except (IOError, OSError): - logging.error("Could not open GnuPG passphrase file: %s!" % passFile) - else: - logging.info("Read GnuPG passphrase from file: %s" % passFile) - - def gpgSignMessage(message): - """Sign **message** with the default key specified by - ``EMAIL_GPG_PRIMARY_KEY_FINGERPRINT``. - - :param str message: A message to sign. - :rtype: str or ``None``. - :returns: A string containing the clearsigned message, or ``None`` if - the signing failed. - """ - sig = gpg.sign(message, default_key=primary, passphrase=passphrase) - if sig and sig.data: - return sig.data - - logging.debug("Testing signature created with GnuPG key...") - sig = gpgSignMessage("Testing 1 2 3") - if sig: - logging.info("Test signature with GnuPG key %s okay:\n%s" % (primary, sig)) - return (gpg, gpgSignMessage) - - return ret - class SSLVerifyingContextFactory(ssl.CertificateOptions): """``OpenSSL.SSL.Context`` factory which does full certificate-chain and diff --git a/bridgedb/distributors/email/autoresponder.py b/bridgedb/distributors/email/autoresponder.py index 5e1fcd0..9b0ac53 100644 --- a/bridgedb/distributors/email/autoresponder.py +++ b/bridgedb/distributors/email/autoresponder.py @@ -55,7 +55,6 @@ from bridgedb import safelog from bridgedb.distributors.email import dkim from bridgedb.distributors.email import request from bridgedb.distributors.email import templates -from bridgedb.distributors.email.distributor import EmailRequestedKey from bridgedb.distributors.email.distributor import TooSoonEmail from bridgedb.distributors.email.distributor import IgnoreEmail from bridgedb.parse import addr @@ -100,9 +99,6 @@ def createResponseBody(lines, context, client, lang='en'): # Otherwise they must have requested bridges: interval = context.schedule.intervalStart(time.time()) bridges = context.distributor.getBridges(bridgeRequest, interval) - except EmailRequestedKey as error: - logging.info(error) - return templates.buildKeyMessage(translator, client) except TooSoonEmail as error: logging.info("Got a mail too frequently: %s." % error) return templates.buildSpamWarning(translator, client) @@ -120,7 +116,7 @@ def createResponseBody(lines, context, client, lang='en'): return templates.buildAnswerMessage(translator, client, answer) def generateResponse(fromAddress, client, body, subject=None, - messageID=None, gpgSignFunc=None): + messageID=None): """Create an :class:`EmailResponse`, which acts like an :class:`io.StringIO` instance, by creating and writing all headers and the email body into the file-like :attr:`EmailResponse.mailfile`. @@ -131,21 +127,15 @@ def generateResponse(fromAddress, client, body, subject=None, :param client: The client's email address which should be in the ``'To:'`` header of the response email. :param str subject: The string to write to the ``'Subject:'`` header. - :param str body: The body of the email. If a **gpgSignFunc** is also - given, then :meth:`EmailResponse.writeBody` will generate and include - an ascii-armored OpenPGP signature in the **body**. + :param str body: The body of the email. :type messageID: ``None`` or :any:`str` :param messageID: The :rfc:`2822` specifier for the ``'Message-ID:'`` header, if including one is desirable. - :type gpgSignFunc: callable - :param gpgSignFunc: If given, this should be a callable function for - signing messages. See :func:`bridgedb.crypto.initializeGnuPG` for - obtaining a pre-configured **gpgSignFunc**. :returns: An :class:`EmailResponse` which contains the entire email. To obtain the contents of the email, including all headers, simply use :meth:`EmailResponse.readContents`. """ - response = EmailResponse(gpgSignFunc) + response = EmailResponse() response.to = client response.writeHeaders(fromAddress.encode('utf-8'), str(client), subject, inReplyTo=messageID) @@ -168,10 +158,7 @@ class EmailResponse(object): .. todo:: At some point, we may want to change this class to optionally handle creating Multipart MIME encoding messages, so that we can - include attachments. (This would be useful for attaching our GnuPG - keyfile, for example, rather than simply pasting it into the body of - the email.) - + include attachments. :var str delimiter: Delimiter between lines written to the :data:`mailfile`. @@ -180,18 +167,12 @@ class EmailResponse(object): :var to: The client's email address, to which this response should be sent. """ - def __init__(self, gpgSignFunc=None): + def __init__(self): """Create a response to an email we have recieved. This class deals with correctly formatting text for the response email headers and the response body into an instance of :data:`mailfile`. - - :type gpgSignFunc: callable - :param gpgSignFunc: If given, this should be a callable function for - signing messages. See :func:`bridgedb.crypto.initializeGnuPG` for - obtaining a pre-configured **gpgSignFunc**. """ - self.gpgSign = gpgSignFunc self.mailfile = io.StringIO() self.delimiter = '\n' self.closed = False @@ -338,18 +319,9 @@ class EmailResponse(object): def writeBody(self, body): """Write the response body into the :attr:`mailfile`. - If :attr:`EmailResponse.gpgSignFunc` is set, and signing is configured, - the **body** will be automatically signed before writing its contents - into the :attr:`mailfile`. - :param str body: The body of the response email. """ logging.info("Writing email body...") - if self.gpgSign: - logging.info("Attempting to sign email...") - sig = self.gpgSign(body) - if sig: - body = sig self.writelines(body) @@ -433,8 +405,7 @@ class SMTPAutoresponder(smtp.SMTPClient): messageID = self.incoming.message.get("Message-ID", None) subject = self.incoming.message.get("Subject", None) response = generateResponse(recipient, client, - body, subject, messageID, - self.incoming.context.gpgSignFunc) + body, subject, messageID) return response def getMailTo(self): diff --git a/bridgedb/distributors/email/distributor.py b/bridgedb/distributors/email/distributor.py index c6ac2fd..77bcdb9 100644 --- a/bridgedb/distributors/email/distributor.py +++ b/bridgedb/distributors/email/distributor.py @@ -20,7 +20,7 @@ bridgedb.distributors.email.autoresponder A :class:`~bridgedb.distribute.Distributor` which hands out :class:`bridges <bridgedb.bridges.Bridge>` to clients via an email interface. -.. inheritance-diagram:: IgnoreEmail TooSoonEmail EmailRequestedKey EmailDistributor +.. inheritance-diagram:: IgnoreEmail TooSoonEmail EmailDistributor :parts: 1 """ @@ -55,10 +55,6 @@ class TooSoonEmail(addr.BadEmail): """Raised when we got a request from this address too recently.""" -class EmailRequestedKey(Exception): - """Raised when an incoming email requested a copy of our GnuPG keys.""" - - class EmailDistributor(Distributor): """Object that hands out bridges based on the email address of an incoming request and the current time period. diff --git a/bridgedb/distributors/email/request.py b/bridgedb/distributors/email/request.py index 90576c1..6484fe3 100644 --- a/bridgedb/distributors/email/request.py +++ b/bridgedb/distributors/email/request.py @@ -45,7 +45,6 @@ import re from bridgedb import strings from bridgedb import bridgerequest -from bridgedb.distributors.email.distributor import EmailRequestedKey #: A regular expression for matching the Pluggable Transport method TYPE in @@ -62,7 +61,6 @@ UNBLOCKED_PATTERN = re.compile(UNBLOCKED_REGEXP) #: valid as long as it wasn't quoted, i.e., the line didn't start with a '>' #: character. GET_LINE = re.compile("([^>].*)?get") -KEY_LINE = re.compile("([^>].*)?key") IPV6_LINE = re.compile("([^>].*)?ipv6") TRANSPORT_LINE = re.compile("([^>].*)?transport") UNBLOCKED_LINE = re.compile("([^>].*)?unblocked") @@ -75,7 +73,6 @@ def determineBridgeRequestOptions(lines): and/or ``CC`` will *always* be stored as a *lowercase* string. :param list lines: A list of lines from an email, including the headers. - :raises EmailRequestedKey: if the client requested our GnuPG key. :rtype: :class:`EmailBridgeRequest` :returns: A :class:`~bridgerequest.BridgeRequest` with all of the requested parameters set. The returned ``BridgeRequest`` will have already had @@ -93,9 +90,6 @@ def determineBridgeRequestOptions(lines): if GET_LINE.match(line) is not None: request.isValid(True) logging.debug("Email request was valid.") - if KEY_LINE.match(line) is not None: - request.wantsKey(True) - raise EmailRequestedKey("Email requested a copy of our GnuPG key.") if IPV6_LINE.match(line) is not None: request.withIPv6() if TRANSPORT_LINE.match(line) is not None: @@ -127,22 +121,6 @@ class EmailBridgeRequest(bridgerequest.BridgeRequestBase): :class:`~bridgedb.distributors.email.distributor.EmailDistributor`. """ super(EmailBridgeRequest, self).__init__() - self._wantsKey = False - - def wantsKey(self, wantsKey=None): - """Get or set whether this bridge request wanted our GnuPG key. - - If called without parameters, this method will return the current - state, otherwise (if called with the **wantsKey** parameter set), it - will set the current state for whether or not this request wanted our - key. - - :param bool wantsKey: If given, set the validity state of this - request. Otherwise, get the current state. - """ - if wantsKey is not None: - self._wantsKey = bool(wantsKey) - return self._wantsKey def withoutBlockInCountry(self, line): """This request was for bridges not blocked in **country**. diff --git a/bridgedb/distributors/email/server.py b/bridgedb/distributors/email/server.py index 063b640..f18a5ad 100644 --- a/bridgedb/distributors/email/server.py +++ b/bridgedb/distributors/email/server.py @@ -66,7 +66,6 @@ from zope.interface import implementer from bridgedb import __version__ from bridgedb import safelog -from bridgedb.crypto import initializeGnuPG from bridgedb.distributors.email import autoresponder from bridgedb.distributors.email import templates from bridgedb.distributors.email import request @@ -97,13 +96,6 @@ class MailServerContext(object): :ivar int fuzzyMatch: An integer specifying the maximum Levenshtein Distance from an incoming email address to a blacklisted email address for the incoming email to be dropped. - :ivar gpg: A :class:`gnupg.GPG` interface_, as returned by - :func:`~bridgedb.crypto.initialiseGnuPG`, or ``None`` if we couldn't - initialize GnuPG for some reason. - :ivar gpgSignFunc: A callable which signs a message, e.g. the one returned - from :func:`~bridgedb.crypto.initialiseGnuPG`. - - .. _interface: https://pythonhosted.org/gnupg/gnupg.html#gnupg-module """ def __init__(self, config, distributor, schedule): @@ -139,8 +131,6 @@ class MailServerContext(object): self.blacklist = config.EMAIL_BLACKLIST or [] self.fuzzyMatch = config.EMAIL_FUZZY_MATCH or 0 - self.gpg, self.gpgSignFunc = initializeGnuPG(config) - def buildCanonicalDomainMap(self): """Build a map for all email provider domains from which we will accept emails to their canonical domain name. diff --git a/bridgedb/distributors/email/templates.py b/bridgedb/distributors/email/templates.py index 2eb0a31..44450ea 100644 --- a/bridgedb/distributors/email/templates.py +++ b/bridgedb/distributors/email/templates.py @@ -67,9 +67,6 @@ def addGreeting(template): return greeting -def addKeyfile(template): - return u'%s\n\n' % strings.BRIDGEDB_OPENPGP_KEY - def addBridgeAnswer(template, answer): # Give the user their bridges, i.e. the `answer`: bridgeLines = u"" @@ -88,10 +85,6 @@ def addHowto(template): """ return template.gettext(strings.HOWTO_TBB[2]) -def buildKeyMessage(template, clientAddress=None): - message = addKeyfile(template) - return message - def buildAnswerMessage(template, clientAddress=None, answer=None): try: message = addGreeting(template) diff --git a/bridgedb/distributors/https/server.py b/bridgedb/distributors/https/server.py index be99262..b2de4b1 100644 --- a/bridgedb/distributors/https/server.py +++ b/bridgedb/distributors/https/server.py @@ -1143,7 +1143,6 @@ def addWebServer(config, distributor): info = InfoResource() robots = static.File(os.path.join(TEMPLATE_DIR, 'robots.txt')) assets = static.File(os.path.join(TEMPLATE_DIR, 'assets/')) - keys = static.Data(strings.BRIDGEDB_OPENPGP_KEY.encode('utf-8'), 'text/plain') csp = CSPResource(enabled=config.CSP_ENABLED, includeSelf=config.CSP_INCLUDE_SELF, reportViolations=config.CSP_REPORT_ONLY, @@ -1152,7 +1151,6 @@ def addWebServer(config, distributor): root = CustomErrorHandlingResource() root.putChild(b'', index) root.putChild(b'robots.txt', robots) - root.putChild(b'keys', keys) root.putChild(b'assets', assets) root.putChild(b'options', options) root.putChild(b'howto', howto) diff --git a/bridgedb/distributors/https/templates/base.html b/bridgedb/distributors/https/templates/base.html index b42d252..2f502a9 100644 --- a/bridgedb/distributors/https/templates/base.html +++ b/bridgedb/distributors/https/templates/base.html @@ -98,8 +98,6 @@ ${next.body(strings, langs, rtl=rtl, lang=lang, langOverride=langOverride, **kwa · <a href="https://gitweb.torproject.org/bridgedb.git/tree/CHANGELOG"> <i class="icon icon-large icon-rocket"><span id="footer-changelog"> ${_("Changelog")}</span></i></a> - · - <a href="../keys"><i class="icon icon-large icon-key"><span id="footer-keys"> ${_("Public Keys")}</span></i></a> </p> <br /> <p>© The Tor Project</p> diff --git a/bridgedb/i18n/templates/bridgedb.pot b/bridgedb/i18n/templates/bridgedb.pot index 85958bd..bb78824 100644 --- a/bridgedb/i18n/templates/bridgedb.pot +++ b/bridgedb/i18n/templates/bridgedb.pot @@ -5,11 +5,11 @@ # msgid "" msgstr "" -"Project-Id-Version: bridgedb 0.10.0+7.gbca6496.dirty\n" +"Project-Id-Version: bridgedb 0.10.0+10.g96de503\n" "Report-Msgid-Bugs-To: " "'https://trac.torproject.org/projects/tor/newticket?component=BridgeDB&keywords" "=bridgedb-reported,msgid&cc=isis,sysrqb&owner=isis'\n" -"POT-Creation-Date: 2020-04-07 10:14-0700\n" +"POT-Creation-Date: 2020-04-09 14:45-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <l...@li.org>\n" @@ -66,10 +66,6 @@ msgstr "" msgid "Changelog" msgstr "" -#: bridgedb/distributors/https/templates/base.html:102 -msgid "Public Keys" -msgstr "" - #: bridgedb/distributors/https/templates/bridges.html:35 msgid "Select All" msgstr "" @@ -180,15 +176,15 @@ msgstr "" msgid "%sG%set Bridges" msgstr "" -#: bridgedb/strings.py:42 +#: bridgedb/strings.py:33 msgid "[This is an automated email.]" msgstr "" -#: bridgedb/strings.py:44 +#: bridgedb/strings.py:35 msgid "Here are your bridges:" msgstr "" -#: bridgedb/strings.py:46 +#: bridgedb/strings.py:37 #, python-format msgid "" "You have exceeded the rate limit. Please slow down! The minimum time between\n" @@ -196,7 +192,7 @@ msgid "" "ignored." msgstr "" -#: bridgedb/strings.py:49 +#: bridgedb/strings.py:40 msgid "" "If these bridges are not what you need, reply to this email with one of\n" "the following commands in the message body:" @@ -206,7 +202,7 @@ msgstr "" #. TRANSLATORS: Please DO NOT translate "Pluggable Transports". #. TRANSLATORS: Please DO NOT translate "Tor". #. TRANSLATORS: Please DO NOT translate "Tor Network". -#: bridgedb/strings.py:59 +#: bridgedb/strings.py:50 #, python-format msgid "" "BridgeDB can provide bridges with several %stypes of Pluggable Transports%s,\n" @@ -218,7 +214,7 @@ msgid "" msgstr "" #. TRANSLATORS: Please DO NOT translate "Pluggable Transports". -#: bridgedb/strings.py:66 +#: bridgedb/strings.py:57 msgid "" "Some bridges with IPv6 addresses are also available, though some Pluggable\n" "Transports aren't IPv6 compatible.\n" @@ -230,7 +226,7 @@ msgstr "" #. regular, or unexciting". Like vanilla ice cream. It refers to bridges #. which do not have Pluggable Transports, and only speak the regular, #. boring Tor protocol. Translate it as you see fit. Have fun with it. -#: bridgedb/strings.py:75 +#: bridgedb/strings.py:66 #, python-format msgid "" "Additionally, BridgeDB has plenty of plain-ol'-vanilla bridges %s without any" @@ -241,21 +237,21 @@ msgid "" "\n" msgstr "" -#: bridgedb/strings.py:87 bridgedb/test/test_https.py:356 +#: bridgedb/strings.py:78 bridgedb/test/test_https.py:356 msgid "What are bridges?" msgstr "" -#: bridgedb/strings.py:88 +#: bridgedb/strings.py:79 #, python-format msgid "%s Bridges %s are Tor relays that help you circumvent censorship." msgstr "" -#: bridgedb/strings.py:93 +#: bridgedb/strings.py:84 msgid "I need an alternative way of getting bridges!" msgstr "" #. TRANSLATORS: Please DO NOT translate "get transport obfs4". -#: bridgedb/strings.py:95 +#: bridgedb/strings.py:86 #, python-format msgid "" "Another way to get bridges is to send an email to %s. Leave the email subject" @@ -267,32 +263,32 @@ msgid "" "providers: %s or %s." msgstr "" -#: bridgedb/strings.py:103 +#: bridgedb/strings.py:94 msgid "My bridges don't work! I need help!" msgstr "" #. TRANSLATORS: Please DO NOT translate "Tor Browser". #. TRANSLATORS: The two '%s' are substituted with "Tor Browser Manual" and #. "Support Portal", respectively. -#: bridgedb/strings.py:107 +#: bridgedb/strings.py:98 #, python-format msgid "If your Tor Browser cannot connect, please take a look at the %s and our %s." msgstr "" -#: bridgedb/strings.py:111 +#: bridgedb/strings.py:102 msgid "Here are your bridge lines:" msgstr "" -#: bridgedb/strings.py:112 +#: bridgedb/strings.py:103 msgid "Get Bridges!" msgstr "" -#: bridgedb/strings.py:116 +#: bridgedb/strings.py:107 msgid "Bridge distribution mechanisms" msgstr "" #. TRANSLATORS: Please DO NOT translate "BridgeDB", "HTTPS", and "Moat". -#: bridgedb/strings.py:118 +#: bridgedb/strings.py:109 #, python-format msgid "" "BridgeDB implements four mechanisms to distribute bridges: \"HTTPS\", " @@ -306,7 +302,7 @@ msgid "" "mechanisms is." msgstr "" -#: bridgedb/strings.py:124 +#: bridgedb/strings.py:115 #, python-format msgid "" "The \"HTTPS\" distribution mechanism hands out bridges over this website. To" @@ -316,7 +312,7 @@ msgid "" "solve the subsequent CAPTCHA." msgstr "" -#: bridgedb/strings.py:128 +#: bridgedb/strings.py:119 #, python-format msgid "" "The \"Moat\" distribution mechanism is part of Tor Browser, allowing users to" @@ -329,7 +325,7 @@ msgid "" "bridges." msgstr "" -#: bridgedb/strings.py:134 +#: bridgedb/strings.py:125 #, python-format msgid "" "Users can request bridges from the \"Email\" distribution mechanism by " @@ -339,11 +335,11 @@ msgid "" "email body." msgstr "" -#: bridgedb/strings.py:138 +#: bridgedb/strings.py:129 msgid "Reserved" msgstr "" -#: bridgedb/strings.py:139 +#: bridgedb/strings.py:130 #, python-format msgid "" "BridgeDB maintains a small number of bridges that are not distributed\n" @@ -357,11 +353,11 @@ msgid "" "called \"Unallocated\" in %sbridge pool assignment%s files." msgstr "" -#: bridgedb/strings.py:146 +#: bridgedb/strings.py:137 msgid "None" msgstr "" -#: bridgedb/strings.py:147 +#: bridgedb/strings.py:138 msgid "" "Bridges whose distribution mechanism is \"None\" are not distributed by " "BridgeDB.\n" @@ -372,33 +368,33 @@ msgid "" "it will then change to the bridge's actual distribution mechanism.\n" msgstr "" -#: bridgedb/strings.py:157 +#: bridgedb/strings.py:148 msgid "Please select options for bridge type:" msgstr "" -#: bridgedb/strings.py:158 +#: bridgedb/strings.py:149 msgid "Do you need IPv6 addresses?" msgstr "" -#: bridgedb/strings.py:159 +#: bridgedb/strings.py:150 #, python-format msgid "Do you need a %s?" msgstr "" -#: bridgedb/strings.py:163 +#: bridgedb/strings.py:154 msgid "Your browser is not displaying images properly." msgstr "" -#: bridgedb/strings.py:164 +#: bridgedb/strings.py:155 msgid "Enter the characters from the image above..." msgstr "" -#: bridgedb/strings.py:168 +#: bridgedb/strings.py:159 msgid "How to start using your bridges" msgstr "" #. TRANSLATORS: Please DO NOT translate "Tor Browser". -#: bridgedb/strings.py:170 +#: bridgedb/strings.py:161 #, python-format msgid "" " First, you need to %sdownload Tor Browser%s. Our Tor Browser User\n" @@ -407,28 +403,22 @@ msgid "" " are using Android, %sclick here%s." msgstr "" -#: bridgedb/strings.py:175 +#: bridgedb/strings.py:166 msgid "" "Add these bridges to your Tor Browser by opening your browser\n" "preferences, clicking on \"Tor\", and then adding them to the \"Provide a\n" "bridge\" field." msgstr "" -#: bridgedb/strings.py:182 +#: bridgedb/strings.py:173 msgid "(Request unobfuscated Tor bridges.)" msgstr "" -#: bridgedb/strings.py:183 +#: bridgedb/strings.py:174 msgid "(Request IPv6 bridges.)" msgstr "" -#: bridgedb/strings.py:184 +#: bridgedb/strings.py:175 msgid "(Request obfs4 obfuscated bridges.)" msgstr "" -#. TRANSLATORS: Please DO NOT translate "BridgeDB". -#. TRANSLATORS: Please DO NOT translate "GnuPG". -#: bridgedb/strings.py:187 -msgid "(Get a copy of BridgeDB's public GnuPG key.)" -msgstr "" - diff --git a/bridgedb/strings.py b/bridgedb/strings.py index 6bccf81..2a1e60d 100644 --- a/bridgedb/strings.py +++ b/bridgedb/strings.py @@ -8,16 +8,7 @@ # (c) 2007-2017, all entities within the AUTHORS file # :license: 3-clause BSD, see included LICENSE for information -"""Commonly used string constants. - -.. todo:: The instructions for the OpenPGP keys in - :data:`BRIDGEDB_OPENPGP_KEY` are not translated⦠should we translate them? - Should we tell users where to obtain GPG4Win/GPGTools/gnupg? Should those - instruction be that verbose? Or should we get rid of the instructions - altogether, and assume that any encouragement towards using GPG will just - make users more frustrated, and (possibly) (mis-)direct that frustration - at Tor or BridgeDB? -""" +"""Commonly used string constants.""" from __future__ import unicode_literals @@ -183,8 +174,6 @@ EMAIL_COMMANDS = { "get ipv6": _("(Request IPv6 bridges.)"), "get transport obfs4": _("(Request obfs4 obfuscated bridges.)"), # TRANSLATORS: Please DO NOT translate "BridgeDB". - # TRANSLATORS: Please DO NOT translate "GnuPG". - "get key": _("(Get a copy of BridgeDB's public GnuPG key.)"), } #----------------------------------------------------------------------------- @@ -318,258 +307,3 @@ EMAIL_REFERENCE_LINKS = { "HOWTO_TBB2": "[1]: https://tb-manual.torproject.org/bridges/#entering-bridge-addresses", "HOWTO_TBB3": "[2]: https://tb-manual.torproject.org/mobile-tor/#circumvention", } - -BRIDGEDB_OPENPGP_KEY = """\ -# This keypair contains BridgeDB's online signing and encryption subkeys. This -# keypair rotates because it is kept online. However, the current online -# keypair will *ALWAYS* be certified by the offline keypair (at the bottom of -# this file). -# -# If you receive an email from BridgeDB, it should be signed with the -# 21B554E95938F4D0 subkey from the following keypair: - -# pub 4096R/8DC43A2848821E32 2013-09-11 [expires: 2015-09-11] -# Key fingerprint = DF81 1109 E17C 8BF1 34B5 EEB6 8DC4 3A28 4882 1E32 -# uid BridgeDB <brid...@bridges.torproject.org> -# sub 4096R/21B554E95938F4D0 2013-09-11 [expires: 2015-09-11] -# Key fingerprint = 9FE3 9D1A 7438 9223 3B3F 66F2 21B5 54E9 5938 F4D0 -# sub 4096R/E7793047C5B54232 2013-09-11 [expires: 2015-09-11] -# Key fingerprint = CFFB 8469 9048 37E7 8CAE 322C E779 3047 C5B5 4232 - ------BEGIN PGP PUBLIC KEY BLOCK----- - -mQINBFIv8YABEADRqvfLB4xWj3Fz+HEmUUt/qbJnZhqIjo5WBHaBJOmrzx1c9fLN -aYG36Hgo6A7NygI1oQmFnDinSrZAtrPaT63d1Jg49yZwr/OhMaxHYJElMFHGJ876 -kLZHmQTysquYKDHhv+fH51t7UVaZ9NkP5cI+V3pqck0DW5DwMsVJXNaU317kk9me -mPJUDMb5FM4d2Vtk1N+54bHJgpgmnukNtpJmRyHRbZBqNMln5nWF7vdZ4u5PGPWj -bA0rPZhayeE3FQ0MHiGL12kHAy30pfg54QfPJDQBCywjABetRE+xaM9TcS+R31Pf -2VbLeb+Km7QpHMwOXI5xZLss9BAWm9EBbmXxuqaRBHyi830jjCrK9UYuzzOqKoUV -Mk1BRelZTFnGPWeVTE+Ps+pwJ0Dwx4ghppJBCoArmEbkNliblxR/2wYOOFi/ZVA4 -Zc2ok9T3rBLVg07b7ezFUScGiTnc7ac7hp6r8Qsh09ZbhRr9erK/n194aEvkXTfr -qepwrAE7YeF4YuR206UOFFWDhxWDLbRu0gIWgrevEQu/cvQPrO9uH5fL6Gw/+mNP -Q/NIteejhkDyvyTUKyBu7x+Gls71zT2u/X13eOAJ8IxBkSVRKQ8tRD+oqJkWplOf -+BpaGU+g6u4kT2AzFDxTOupfrYcPvORTAV/V3suys2YQE4x422GASXDivQARAQAB -tClCcmlkZ2VEQiA8YnJpZGdlc0BicmlkZ2VzLnRvcnByb2plY3Qub3JnPokDJQQT -AQoBD0gUgAAAAAAXACh2ZXJpZmllZEB0b3Jwcm9qZWN0Lm9yZ0RGODExMTA5RTE3 -QzhCRjEzNEI1RUVCNjhEQzQzQTI4NDg4MjFFMzJPFIAAAAAAHgAoYnJpZGdlc0Bi -cmlkZ2VzLnRvcnByb2plY3Qub3JnREY4MTExMDlFMTdDOEJGMTM0QjVFRUI2OERD -NDNBMjg0ODgyMUUzMioaaHR0cHM6Ly9icmlkZ2VzLnRvcnByb2plY3Qub3JnL3Bv -bGljeS50eHQCGwEDCw0JBBUKCQgEFgIBAAIeAQIXgCcYaHR0cHM6Ly9icmlkZ2Vz -LnRvcnByb2plY3Qub3JnL2tleS5hc2MFAlSKBKIFCQPDTiIACgkQjcQ6KEiCHjIs -jg//bJ12eRnBMfIGzOGh+T4wz7/YyKLfARAMnqDnSxhTxuE+M5hWm3QbxP03R6eY -x+PKwQaDJSmm7HhRhltb7QXUe8dqjnocFwwagpoLZ/81mBLxByqg5TKHGGIGy+DX -omIzCq5ijx1IUkHlgh708a5alG7bjRTqedT4Wxxyl6psGzDhGQdS8bqx/f32nQaE -h41l+A/EY1g2HVqky63ZHAP3S2v+mWCrk5DnkElc0229MXqaBuEr4nbYMXRkahMb -E2gnCmdSoeD21AY6bNyz7IcJGpyKCx9+hVgPjpm3J23JEYyPL+s48jn6QcI/Q2gD -yAtgU65y6IrdYn8SwkABI1FIq9WAwG7DaInxvkqkYqyBQLaZJEMyX8NTBvFoT5JS -jnkxG0xu61Vxq0BLYBIOJE0VFHAJ40/jOvSxQJkQhu9G4BK7htnADbtstmMDMM3q -xuuO5pcj2rl7YthNunyZ1yhPHXijUUyKrwR9piENpptztFBVN6+ijqU/TmWMOtbH -X7p9F+3tXCHHqwO5U/JMtsb/9M39MR8BrdcLc7m6dCpeuSUuR2LLroh+MoMJGviI -iesxHF95kFqkJAecW1Z3eKL9vrlbfO3waeuCi18k1TePnZuG5lmf2KjKDW5vHK4O -WFqvvfK2kxkCUjvGdLeTOAVOV+X+PQ23jvBJO2bS7YbOb9C5Ag0EUi/ygQEQALZ/ -p7xRINHY7MMf3/bo/I0WRxWHd1AE9tRToyEg1S2u1YrWWL5M9D8saRsp9cpnpGEu -hW3vu7G4nasY27qOz4bSKu1YMAVIC58v1tEnBqdo1zErNjhs38PrmJKbbs9tDfYY -Oi2x0GlhMbIrNStcZpnCdLa6U6NLMbggDL1GxjMPYBMi4TtLgcIeRDUSjsZscZkg -Kxs5QkSVc3SrYyraayIc8WtIpDLcxPt6/g90rbatZzBfO+93Rz7qUXHmgzuM0hy1 -Fvn619o3I5DsWrfOz9t/QuznoOBw4PfzDPNT7VlzZN4xHAcr5+7B+DH9IsvlCt5N -kQFuYpFZCpXNaD2XOtmIqjTCeLNfcgTEj0qoUIEKyKbBIgfP+7S2tLXy8JKUTy5g -9kxXQeHueLykQ4Mt18JH0nMHbHbQl0K3LGT4ucRDOmjNtlQCltVLkIk3GimyqKs/ -vdZ9c+dm4Akx1qsJcwvveX+imJe2e9RUodcxWXxWrYnuPa5b5nfR1i+GfV0on/Pt -AQ8gc9CkJpMiq5TQDOFhFP6yQcq77sXuUkEl5qamptedz28E0I693ulnfwcsE80p -xkpIG6n33DZJSEyqgtWjE1P2pnsVfO5ILs3mKLe7bO1v3qMXcCkMCGH/kwzvtowq -YvY4gaZMDZtQFY8U7lI9FdRUvVdeHAB24y291nhzABEBAAGJBYMEGAEKANNIFIAA -AAAAFwAodmVyaWZpZWRAdG9ycHJvamVjdC5vcmdERjgxMTEwOUUxN0M4QkYxMzRC -NUVFQjY4REM0M0EyODQ4ODIxRTMyTxSAAAAAAB4AKGJyaWRnZXNAYnJpZGdlcy50 -b3Jwcm9qZWN0Lm9yZ0RGODExMTA5RTE3QzhCRjEzNEI1RUVCNjhEQzQzQTI4NDg4 -MjFFMzIqGmh0dHBzOi8vYnJpZGdlcy50b3Jwcm9qZWN0Lm9yZy9wb2xpY3kudHh0 -AhsCBQJUigTTBQkDw01SAqTB2CAEGQEKAIEFAlIv8oFPFIAAAAAAHgAoYnJpZGdl -c0BicmlkZ2VzLnRvcnByb2plY3Qub3JnOUZFMzlEMUE3NDM4OTIyMzNCM0Y2NkYy -MjFCNTU0RTk1OTM4RjREMCoaaHR0cHM6Ly9icmlkZ2VzLnRvcnByb2plY3Qub3Jn -L3BvbGljeS50eHQACgkQIbVU6Vk49NDbPw/5ATe/T8+eaToC3v0TYNRH5nveQvzA -WdnshD3lnvfsgDhbilwifKpc5LHKXU3rvb42HH2cu0ckuksdDTvICZD9cJjRq/F+ -Mzm0pNCAJg0pQnHaaWFQjw+CHYEoizai3S+iYxhNHeSdA6Ty7xm4+bHNf0Aqblbd -6dKwq9EvjwAI6zZsAHtsmHRUMdrFwGdKae6CSchUT2JQFBPEWMhvzdpDGACWVaSP -sxYKuYg9LgpswGcof+tprRjKRl8MtSh0ufjbVBlTeSKpL5Y+fcTRD3PI8w7Ocr3z -jr6XpYG4SUNHsWwxyu/DTXg76Lk1/+BdaH25hDOAasLUOU7yRL8zD/c7M0FkGXdj -r5I2DEEqwzJ9cPHWjpgb8N9fZLoPFP9JOmKGHINqxNe7TfwiTdD6uDKs/u/QK1U+ -o3iYBXBTREdopPUdBTM9wYRUhyGXTEKLhUP3MGpXYlgeYPrSdp76VyN3BzLTbMv+ -+7rxyKxL9cWYU0pnXHgPC5nyHX5nqXmhMnkxAD3Bnm8n9XDfgiyTDExqksEh2VXt -yhVfLezylEP2fwtd8/mABBCsTjzZW6FRfRRAjUZWZGFpFg8no1x5JS9uiswRP5g1 -qHijNFWpGyTtJWl5VNd0d9+LtVUX1jRpDUpsjZcxqs3fsvw2p+H/zQ1wFvDrsoav -hqOTq+AEnJc7ZG8JEI3EOihIgh4ych8P/3GTyWb29+43YVibbSPPvEv4gFqziC+9 -1p92FJ0V4XdaT7TW3qaZVp5edUNwB/jjF0SxBybwaMX2ZIGXOjnjF6/Zby4ynuTX -vZkS1mKRA0KWupB3e9PSMY3ZtssnqpGna/+3qlpxtunW7HcW4nCF/f59WHhlVjaO -MXjtuWj59yB56Dd1sNjwhcNCyp4/NpzGnRW97ZV3Pp4oqIOqcGzCQXkVPcnaqcOh -Cs9vIDJlMtn/IWBzUGimuRllDSSVSWkYkyJcG2NUHUwgXYpLwQz7sScvmCPchf4K -qarpX0FpkUDfqaVVuQ7A2XbPUAVFzIk930G1WzgOuOdg9vhWSEjou+SKrAoMz90w -3xHwEvmPDTTVJQft9ytoRbwZkIPfzzhII3mr4agbORAfzDaj5g/f6CVRdg6D3ME1 -Etg9ZrfLgRY993g/arfIME6OOsiNcy5+PunN96Rw0o1xoD+97NmZuQrs/p4Mfn5o -8EwXHutREhahin+3/SV3hz9ReeLYmClq+OVhjPzPdtwZsFoyQyUJoFVHPTuSdChZ -FPaqN68FjlNMugmxnvski3ZDVT7pw3B6otjjaL3rr6q0PC2yhEb2ntb3IFUizHjn -80SmfE1Bqwit7ZHu8r/Gt/0iecGk5h84VzSgiGZGF/7m1i5UMVlNSeWnsInGa5Su -7HSzfMq+YmkzuQINBFIv8p4BEADTOLR5e5NKKRPpjCb4B/8YYkWh+orb70EogIZ6 -j5v8d/djLyhjqZ9BIkh41/hYKMwnsa4KkDkTaX0eNu3BFB2zGgZ1GSd7525ESxiq -suXIlAg2pex7bysaFfua0nUx64tmaQm2XArdkj/wI0pbg+idWym3WQQmZLyTTbzl -8rpTEtTt+S2m6z3EeAhEHuNFH16hEDUywlef3EotX3njuFiLqaNvnzUYDxhUvKd2 -2K1es1ggispgP+eb1bkMApxecf2rqmSUEcvsuTWip4oGZPBLGDQeNKHkCUVbj4wT -yWDIRtto3wi+4CFPEVzw+htj1cQfTstPqUdG7NSOmLQggedoUdv7AJm4MJJiyEax -l+IAf6Afwrrm3eOSv0PgoUxOrUb9vhIoL8ih8gtiqvQ9qYaRQfQA/w3Z0Su2Yfoc -fQS8Uw99qG+oTgieG6F6ud8+hMZAYVZFqbU+ztzMyDE6h4Hflkt6VNJ0Hk0VoF38 -TTs77pHXXBbLD6SzR6tbNuR9r/lbmC8Qf2A1ZAThR0iuGhNRFtUPo28GxakxGdLZ -9kHIxjl7EN/gsmYTwuEhr+yfNtLwtSH0ojeqbDmgufvgh+SITCtyNDAUspjrZYEt -F0NHRpSom2NFVELMqMRydU/ncph1rGZgVp6/zVj6xIlhKmqj5P1y/9B0c4Tu1CzJ -pkJ5wwARAQABiQLpBBgBCgDTSBSAAAAAABcAKHZlcmlmaWVkQHRvcnByb2plY3Qu -b3JnREY4MTExMDlFMTdDOEJGMTM0QjVFRUI2OERDNDNBMjg0ODgyMUUzMk8UgAAA -AAAeAChicmlkZ2VzQGJyaWRnZXMudG9ycHJvamVjdC5vcmdERjgxMTEwOUUxN0M4 -QkYxMzRCNUVFQjY4REM0M0EyODQ4ODIxRTMyKhpodHRwczovL2JyaWRnZXMudG9y -cHJvamVjdC5vcmcvcG9saWN5LnR4dAIbDAUCVIoE4QUJA8NNQwAKCRCNxDooSIIe -Mo7JEADDBtQpYxPhbj3MT0xpk96EDlon/5yHVf+cnk1pNisc+HkJsVe1nh7gAWOz -wJKdeqOVpgxiJTxIQdl6mipKwwFi0DreP7h56s1WQkuSSWJzqssAwWHfVAsX13fV -zWd0XyxN/OF9ZKQjX4qwpJ/na631PSwZLvHYhMaZnb9pjNwC5/PEKRmFqLbQT6Px -12miZT6ToPDCczHxJ4BxbEGVU+PtRsHwmTRT3JhxFNDfeVd+uwsQIMidJbUoqVW7 -fe2zNd0TaWyz4Rw087oZE2OXdctjvtsu8fzXx6d/tkazI6cUOqoaMTR41KEu5X0T -BpWSAMADBYjNs9QRWXX7ZlsJRUSCX1EKbMhgoL6KIGceIkjH61M/LF6HqDgSgSWt -h+LIYGa+LrB/6819o32QSOSHHJ5+NJrbCSaLgKE/LKnf92V2QbZE8IGY6EOSjHqn -n1+j+CLRKY/kUyvk+1TumTghjg/aDs/8Jv8PvgSWLQ0q1rxHYbX7q9ZJhYC/4LdR -ya/Cho6w2l0N3tV/IMAwvFNHsaiIiiwfoOQbkBUvkyzBwjKt96Ai4I0QKt/63uH0 -drQhlJEgIyGkOrorBByVqZAQdnoLENYIu6tDUj0bTbGObKqua4iPlSK3/g40zCm4 -9OgcN7A8kFuNpgp2EHqj1/jrwd7mZYKsWTuGiR/7fwXf+4xbvg== -=raCx ------END PGP PUBLIC KEY BLOCK----- - -# The following keypair is BridgeDB's offline certification-only keypair. It -# is used to sign new online signing/encryption keypairs. -# -# If you import this key and mark it as trusted, emails from BridgeDB (if -# signed correctly with the online keypair above) should always be trusted. To -# do this, open a shell and do: -# -# $ curl -O https://bridges.torproject.org/keys -# $ gpg --import keys -# $ gpg --check-sigs 7B78437015E63DF47BB1270ACBD97AA24E8E472E -# $ gpg --edit-key 7B78437015E63DF47BB1270ACBD97AA24E8E472E -# -# Then type 'trust' to set the trust level. Choose a number that you like. -# Next type 'quit'. Finally, to create a local signature which will will not -# be uploaded to keyservers: -# -# $ gpg --lsign-key 7B78437015E63DF47BB1270ACBD97AA24E8E472E -# - -# pub 16384R/CBD97AA24E8E472E 2013-10-12 -# Key fingerprint = 7B78 4370 15E6 3DF4 7BB1 270A CBD9 7AA2 4E8E 472E -# uid BridgeDB (Offline ID Key) <brid...@bridges.torproject.org> ------BEGIN PGP PUBLIC KEY BLOCK----- - -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 -ILUXBkpIDfVSoZD7qWlntai53BDQr5pfMJhv15di4XAhtqv43vAmA57ifd+QJS2U -AfYc4CdX0lk2BZ4jRD8jCZ4Uxw15E3RqhnXsWDRxtD4fwsb2ZFi0DDuPlwBdGgh5 -Rm2Bz9JjSV6gDEuXr/JtAzjSz1Jdh8wPkSofiHGTfxysVhlGlg+YPRziRlzML8A2 -0xY+9mPxEEin5ZQ9wmrDyiiOBvPTbG3O9+Sp5VZDuD4ivW/DHumPWGVSRdjcAQDe -HMXUVGjhBDnj06XNrgJPhODdJeYq0EnGTt15ofZQSswD7TTTRPDOn0Cz/QARAQAB -tDpCcmlkZ2VEQiAoT2ZmbGluZSBJRCBLZXkpIDxicmlkZ2VzQGJyaWRnZXMudG9y -cHJvamVjdC5vcmc+iQkfBBMBCgEJBQJSWQfkSBSAAAAAABcAKHZlcmlmaWVkQHRv -cnByb2plY3Qub3JnN0I3ODQzNzAxNUU2M0RGNDdCQjEyNzBBQ0JEOTdBQTI0RThF -NDcyRU8UgAAAAAAeAChicmlkZ2VzQGJyaWRnZXMudG9ycHJvamVjdC5vcmc3Qjc4 -NDM3MDE1RTYzREY0N0JCMTI3MEFDQkQ5N0FBMjRFOEU0NzJFKhpodHRwczovL2Jy -aWRnZXMudG9ycHJvamVjdC5vcmcvcG9saWN5LnR4dAIbAQMLDQkEFQoJCAQWAgEA -Ah4BAheAJxhodHRwczovL2JyaWRnZXMudG9ycHJvamVjdC5vcmcva2V5LmFzYwAK -CRDL2XqiTo5HLoqEP/48rFpJCVStn8xo+KkHSVvsqpxDRlb/nNheI+ov2UxILdwl -NIU6kLsvKECKPe1AHKdS/MzANbkTF35Y4QgZsNpVXaCVL7adGBSzOdPFupDJJVOu -wa+uFRc/FuNJyH/TIn56/+R5J5C54OxIYNxvW3WF4eHKLJYk/JZOMMfy4iWm7Sql -0nDC5O435nK4F4Jb4GLPlUIzioIy2OWqGoFHXymbGhL1tWaqasYmED4n3AMqlYw6 -xnNhdWOc/KZelPl9nanybyh0IIdZqUKZleRt4BxSgIT8FqC2sZuZ8z7O9s987Naz -Q32SKaP4i2M9lai/Y2QYfKo+wlG+egmxtujz7etQMGlpgBZzFLdJ8/w4U11ku1ai -om74RIn8zl/LHjMQHnCKGoVlscTI1ZPt+p+p8hO2/9vOwTR8y8O/3DQSOfTSipwc -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 -YnJpZGdlc0BicmlkZ2VzLnRvcnByb2plY3Qub3JnREY4MTExMDlFMTdDOEJGMTM0 -QjVFRUI2OERDNDNBMjg0ODgyMUUzMioaaHR0cHM6Ly9icmlkZ2VzLnRvcnByb2pl -Y3Qub3JnL3BvbGljeS50eHQACgkQjcQ6KEiCHjIaqBAA0BuEs7horx6iCq4cjAhv -YPLrxuC4fKEfVyhAjCJMJSFFCPAlGgU+BjyPNDD57wzKAmUkdJG+Ss25mwWXa53w -5R2kDqDnHocOdZGtxZ7zx/uUd2eWLNBfVuK7nHOk1d1Hs0OZBnckc+MCqnLtuYe5 -68pa9+jW6cNIjAnzMIListmoXWgYYWJvMKeBMG4DGtYJ8w7CJQjOHc5yar12DrX3 -wnQ7hXtFuuqQblpEUnLnZGvHf2NKMZfBBMcP96h9OmLGNa+vmNYsMyPKU7n5hPgX -nTgmQ4xrv1G7JukjppZRA8SFoxupcaQeTixyWERGBhBiAbwZsbQz8L/TVZKierzg -sdNngHcFzE8MyjuJDvTos7qXPmgSRXFqJLRn0ZxpR5V1V8BVZUqCGuSZT89TizsD -z5vyv8c9r7HKD4pRjw32P2dgcEqyGRkqERAgSuFpObP+juty+kxYyfnadBNCyjgP -s7u0GmsTt4CZi7BbowNRL6bynrwrmQI9LJI1bPhgqfdDUbqG3HXwHz80oRFfKou8 -JTYKxK4Iumfw2l/uAACma5ZyrwIDBX/H5XEQqch4sORzQnuhlTmZRf6ldVIIWjdJ -ef+DpOt12s+cS2F4D5g8G6t9CprCLYyrXiHwM/U8N5ywL9IeYKSWJxa7si3l9A6o -ZxOds8F/UJYDSIB97MQFzBo= -=JdC7 ------END PGP PUBLIC KEY BLOCK----- -""" diff --git a/bridgedb/test/email_helpers.py b/bridgedb/test/email_helpers.py index edc8196..cd805f9 100644 --- a/bridgedb/test/email_helpers.py +++ b/bridgedb/test/email_helpers.py @@ -25,11 +25,6 @@ from . import util EMAIL_DIST = True EMAIL_ROTATION_PERIOD = "1 day" EMAIL_INCLUDE_FINGERPRINTS = True -EMAIL_GPG_SIGNING_ENABLED = True -EMAIL_GPG_HOMEDIR = '.gnupg' -EMAIL_GPG_PRIMARY_KEY_FINGERPRINT = '0017098C5DF4197E3C884DCFF1B240D43F148C21' -EMAIL_GPG_PASSPHRASE = None -EMAIL_GPG_PASSPHRASE_FILE = None EMAIL_DOMAIN_MAP = { 'googlemail.com': 'gmail.com', 'mail.google.com': 'gmail.com', @@ -56,11 +51,6 @@ TEST_CONFIG_FILE = io.StringIO("""\ EMAIL_DIST = %s EMAIL_ROTATION_PERIOD = %s EMAIL_INCLUDE_FINGERPRINTS = %s -EMAIL_GPG_SIGNING_ENABLED = %s -EMAIL_GPG_HOMEDIR = %s -EMAIL_GPG_PRIMARY_KEY_FINGERPRINT = %s -EMAIL_GPG_PASSPHRASE = %s -EMAIL_GPG_PASSPHRASE_FILE = %s EMAIL_DOMAIN_MAP = %s EMAIL_DOMAIN_RULES = %s EMAIL_DOMAINS = %s @@ -78,11 +68,6 @@ EMAIL_PORT = %s """ % (repr(EMAIL_DIST), repr(EMAIL_ROTATION_PERIOD), repr(EMAIL_INCLUDE_FINGERPRINTS), - repr(EMAIL_GPG_SIGNING_ENABLED), - repr(EMAIL_GPG_HOMEDIR), - repr(EMAIL_GPG_PRIMARY_KEY_FINGERPRINT), - repr(EMAIL_GPG_PASSPHRASE), - repr(EMAIL_GPG_PASSPHRASE_FILE), repr(EMAIL_DOMAIN_MAP), repr(EMAIL_DOMAIN_RULES), repr(EMAIL_DOMAINS), @@ -150,10 +135,10 @@ class DummyEmailDistributorWithState(DummyEmailDistributor): :exc:`bridgedb.distributors.email.distributor.IgnoreEmail` on the third. Note that the state tracking is done in a really dumb way. For example, we - currently don't consider requests for help text or GnuPG keys to be a - "real" request, so in the real email distributor they won't trigger either - a TooSoonEmail or IgnoreEmail. Here we only track the total number of - *any* type of request per client. + currently don't consider requests for help text to be a "real" request, so + in the real email distributor they won't trigger either a TooSoonEmail or + IgnoreEmail. Here we only track the total number of *any* type of request + per client. """ def __init__(self, *args, **kwargs): diff --git a/bridgedb/test/test_crypto.py b/bridgedb/test/test_crypto.py index 45c06ae..f57d4f3 100644 --- a/bridgedb/test/test_crypto.py +++ b/bridgedb/test/test_crypto.py @@ -91,143 +91,6 @@ class GetKeyTests(unittest.TestCase): % (binascii.hexlify(key).decode('utf-8'), binascii.hexlify(SEKRIT_KEY).decode('utf-8'))) -class InitializeGnuPGTests(unittest.TestCase): - """Unittests for :func:`bridgedb.crypto.initializeGnupG`.""" - - def _moveGnuPGHomedir(self): - """Move the .gnupg/ directory from the top-level of this repo to the - current working directory. - - :rtype: str - :returns: The full path to the new gnupg home directory. - """ - here = os.getcwd() - topDir = here.rstrip('_trial_temp') - gnupghome = os.path.join(topDir, '.gnupg') - gnupghomeNew = os.path.join(here, '.gnupg') - - if os.path.isdir(gnupghomeNew): - shutil.rmtree(gnupghomeNew) - - shutil.copytree(gnupghome, gnupghomeNew) - - return gnupghomeNew - - def _writePassphraseToFile(self, passphrase, filename): - """Write **passphrase** to the file at **filename**. - - :param str passphrase: The GnuPG passphase. - :param str filename: The file to write the passphrase to. - """ - fh = open(filename, 'w') - fh.write(passphrase) - fh.flush() - fh.close() - - def setUp(self): - """Create a config object and setup our gnupg home directory.""" - self.config = _createConfig() - self.gnupghome = self._moveGnuPGHomedir() - self.config.EMAIL_GPG_HOMEDIR = self.gnupghome - - self.passphraseFile = 'gpg-passphrase-file' - self._writePassphraseToFile('sekrit', self.passphraseFile) - - def test_crypto_initializeGnuPG(self): - """crypto.initializeGnuPG() should return a 2-tuple with a gpg object - and a signing function. - """ - gpg, signfunc = crypto.initializeGnuPG(self.config) - self.assertIsNotNone(gpg) - self.assertIsNotNone(signfunc) - - def test_crypto_initializeGnuPG_disabled(self): - """When EMAIL_GPG_SIGNING_ENABLED=False, crypto.initializeGnuPG() - should return a 2-tuple of None. - """ - self.config.EMAIL_GPG_SIGNING_ENABLED = False - gpg, signfunc = crypto.initializeGnuPG(self.config) - - self.assertIsNone(gpg) - self.assertIsNone(signfunc) - - def test_crypto_initializeGnuPG_no_secrets(self): - """When the secring.gpg is missing, crypto.initializeGnuPG() should - return a 2-tuple of None. - """ - secring = os.path.join(self.gnupghome, 'secring.gpg') - if os.path.isfile(secring): - os.remove(secring) - - gpg, signfunc = crypto.initializeGnuPG(self.config) - self.assertIsNone(gpg) - self.assertIsNone(signfunc) - - def test_crypto_initializeGnuPG_no_publics(self): - """When the pubring.gpg is missing, crypto.initializeGnuPG() should - return a 2-tuple of None. - """ - pubring = os.path.join(self.gnupghome, 'pubring.gpg') - if os.path.isfile(pubring): - os.remove(pubring) - - gpg, signfunc = crypto.initializeGnuPG(self.config) - self.assertIsNone(gpg) - self.assertIsNone(signfunc) - - def test_crypto_initializeGnuPG_with_passphrase(self): - """crypto.initializeGnuPG() should initialize correctly when a - passphrase is given but no passphrase is needed. - """ - self.config.EMAIL_GPG_PASSPHRASE = 'password' - gpg, signfunc = crypto.initializeGnuPG(self.config) - self.assertIsNotNone(gpg) - self.assertIsNotNone(signfunc) - - def test_crypto_initializeGnuPG_with_passphrase_file(self): - """crypto.initializeGnuPG() should initialize correctly when a - passphrase file is given but no passphrase is needed. - """ - self.config.EMAIL_GPG_PASSPHRASE_FILE = self.passphraseFile - gpg, signfunc = crypto.initializeGnuPG(self.config) - self.assertIsNotNone(gpg) - self.assertIsNotNone(signfunc) - - def test_crypto_initializeGnuPG_missing_passphrase_file(self): - """crypto.initializeGnuPG() should initialize correctly if a passphrase - file is given but that file is missing (when no passphrase is actually - necessary). - """ - self.config.EMAIL_GPG_PASSPHRASE_FILE = self.passphraseFile - os.remove(self.passphraseFile) - gpg, signfunc = crypto.initializeGnuPG(self.config) - self.assertIsNotNone(gpg) - self.assertIsNotNone(signfunc) - - def test_crypto_initializeGnuPG_signingFunc(self): - """crypto.initializeGnuPG() should return a signing function which - produces OpenPGP signatures. - """ - gpg, signfunc = crypto.initializeGnuPG(self.config) - self.assertIsNotNone(gpg) - self.assertIsNotNone(signfunc) - - sig = signfunc("This is a test of the public broadcasting system.") - print(sig) - self.assertIsNotNone(sig) - self.assertTrue(sig.startswith(b'-----BEGIN PGP SIGNED MESSAGE-----')) - - def test_crypto_initializeGnuPG_nonexistent_default_key(self): - """When the key specified by EMAIL_GPG_PRIMARY_KEY_FINGERPRINT doesn't - exist in the keyrings, crypto.initializeGnuPG() should return a 2-tuple - of None. - """ - self.config.EMAIL_GPG_PRIMARY_KEY_FINGERPRINT = 'A' * 40 - gpg, signfunc = crypto.initializeGnuPG(self.config) - self.assertIsNone(gpg) - self.assertIsNone(signfunc) - - class RemovePKCS1PaddingTests(unittest.TestCase): """Unittests for :func:`bridgedb.crypto.removePKCS1Padding`.""" diff --git a/bridgedb/test/test_email_autoresponder.py b/bridgedb/test/test_email_autoresponder.py index d5023e0..9b49c67 100644 --- a/bridgedb/test/test_email_autoresponder.py +++ b/bridgedb/test/test_email_autoresponder.py @@ -35,16 +35,8 @@ from bridgedb.test.email_helpers import DummyEmailDistributorWithState class CreateResponseBodyTests(unittest.TestCase): """Tests for :func:`bridgedb.distributors.email.autoresponder.createResponseBody`.""" - def _moveGPGTestKeyfile(self): - here = os.getcwd() - topDir = here.rstrip('_trial_temp') - self.gpgFile = os.path.join(topDir, '.gnupg', 'TESTING.subkeys.sec') - self.gpgMoved = os.path.join(here, 'TESTING.subkeys.sec') - shutil.copy(self.gpgFile, self.gpgMoved) - def setUp(self): """Create fake email, distributor, and associated context data.""" - self._moveGPGTestKeyfile() self.toAddress = "u...@example.com" self.config = _createConfig() self.ctx = _createMailServerContext(self.config) @@ -62,13 +54,6 @@ class CreateResponseBodyTests(unittest.TestCase): ] return lines - def test_createResponseBody_getKey(self): - """A request for 'get key' should receive our GPG key.""" - lines = self._getIncomingLines() - lines[4] = "get key" - ret = autoresponder.createResponseBody(lines, self.ctx, self.toAddress) - self.assertSubstring('-----BEGIN PGP PUBLIC KEY BLOCK-----', ret) - def test_createResponseBody_bridges_invalid(self): """An invalid request for 'transport obfs3' should still return bridges.""" diff --git a/bridgedb/test/test_email_request.py b/bridgedb/test/test_email_request.py index e0bb642..1cbcbc0 100644 --- a/bridgedb/test/test_email_request.py +++ b/bridgedb/test/test_email_request.py @@ -27,32 +27,21 @@ class DetermineBridgeRequestOptionsTests(unittest.TestCase): def test_determineBridgeRequestOptions_multiline_invalid(self): lines = ['', 'help', - 'i need bridges', - 'give me your gpgs'] + 'i need bridges'] reqvest = request.determineBridgeRequestOptions(lines) # We consider every request valid... self.assertEqual(reqvest.isValid(), True) - self.assertFalse(reqvest.wantsKey()) # ...so by default, we return a bridge. self.assertEqual(len(reqvest.transports), 1) - def test_determineBridgeRequestOptions_get_key(self): - """Requesting 'get key' should raise EmailRequestedKey.""" - lines = ['', - 'get key'] - self.assertRaises(request.EmailRequestedKey, - request.determineBridgeRequestOptions, lines) - def test_determineBridgeRequestOptions_multiline_invalid(self): """Requests without a 'get' are incorrect but still valid, and should return bridges.""" lines = ['', 'transport obfs3', - 'ipv6 vanilla bridges', - 'give me your gpgs'] + 'ipv6 vanilla bridges'] reqvest = request.determineBridgeRequestOptions(lines) self.assertEqual(reqvest.isValid(), True) - self.assertFalse(reqvest.wantsKey()) # Though they did request IPv6, technically. self.assertIs(reqvest.ipVersion, 6) # And they did request a transport, technically. @@ -67,7 +56,6 @@ class DetermineBridgeRequestOptionsTests(unittest.TestCase): reqvest = request.determineBridgeRequestOptions(lines) # It's valid because it included a 'get'. self.assertEqual(reqvest.isValid(), True) - self.assertFalse(reqvest.wantsKey()) # Though they didn't request IPv6, so it should default to IPv4. self.assertIs(reqvest.ipVersion, 4) # And they requested two transports. @@ -90,7 +78,6 @@ class DetermineBridgeRequestOptionsTests(unittest.TestCase): reqvest = request.determineBridgeRequestOptions(lines) # It's valid because it included a 'get'. self.assertEqual(reqvest.isValid(), True) - self.assertFalse(reqvest.wantsKey()) # Though they didn't request IPv6, so it should default to IPv4. self.assertIs(reqvest.ipVersion, 4) # And they requested two transports. @@ -156,25 +143,6 @@ class EmailBridgeRequestTests(unittest.TestCase): self.request.isValid(False) self.assertEqual(self.request.isValid(), False) - def test_EmailBridgeRequest_wantsKey_initial(self): - """Initial value of EmailBridgeRequest.wantsKey() should be False.""" - self.request.wantsKey(None) - self.assertEqual(self.request.wantsKey(), False) - - def test_EmailBridgeRequest_wantsKey_True(self): - """The value of EmailBridgeRequest.wantsKey() should be True, after it - has been called with ``True`` as an argument. - """ - self.request.wantsKey(True) - self.assertEqual(self.request.wantsKey(), True) - - def test_EmailBridgeRequest_wantsKey_False(self): - """The value of EmailBridgeRequest.wantsKey() should be False, after - it has been called with ``False`` as an argument. - """ - self.request.wantsKey(False) - self.assertEqual(self.request.wantsKey(), False) - def test_EmailBridgeRequest_withIPv6(self): """IPv6 requests should have ``ipVersion == 6``.""" self.assertEqual(self.request.ipVersion, 4) diff --git a/bridgedb/test/test_email_templates.py b/bridgedb/test/test_email_templates.py index 4a5979d..a948c91 100644 --- a/bridgedb/test/test_email_templates.py +++ b/bridgedb/test/test_email_templates.py @@ -30,10 +30,6 @@ class EmailTemplatesTests(unittest.TestCase): self.t = NullTranslations(StringIO('test')) self.client = Address('blackh...@torproject.org') self.answer = 'obfs3 1.1.1.1:1111\nobfs3 2.2.2.2:2222' - # This is the fingerprint of BridgeDB's offline, certification-only - # GnuPG key. It should be present in any responses to requests for our - # public keys. - self.offlineFingerprint = '7B78437015E63DF47BB1270ACBD97AA24E8E472E' def shouldIncludeCommands(self, text): self.assertSubstring('commands', text) @@ -51,9 +47,6 @@ class EmailTemplatesTests(unittest.TestCase): def shouldIncludeAutomationNotice(self, text): self.assertSubstring('automated email', text) - def shouldIncludeKey(self, text): - self.assertSubstring('-----BEGIN PGP PUBLIC KEY BLOCK-----', text) - def test_templates_addCommands(self): text = templates.addCommands(self.t) self.shouldIncludeCommands(text) @@ -76,10 +69,6 @@ class EmailTemplatesTests(unittest.TestCase): self.shouldIncludeAutomationNotice(text) self.shouldIncludeCommands(text) - def test_templates_buildKeyMessage(self): - text = templates.buildKeyMessage(self.t, self.client) - self.assertSubstring(self.offlineFingerprint, text) - def test_templates_buildSpamWarning(self): text = templates.buildSpamWarning(self.t, self.client) self.shouldIncludeGreeting(text) diff --git a/bridgedb/test/test_main.py b/bridgedb/test/test_main.py index 82b609f..64b65a4 100644 --- a/bridgedb/test/test_main.py +++ b/bridgedb/test/test_main.py @@ -444,11 +444,6 @@ EMAIL_BIND_IP = "127.0.0.1" EMAIL_PORT = 55557 EMAIL_N_BRIDGES_PER_ANSWER = 3 EMAIL_INCLUDE_FINGERPRINTS = True -EMAIL_GPG_SIGNING_ENABLED = False -EMAIL_GPG_HOMEDIR = '../.gnupg' -EMAIL_GPG_PRIMARY_KEY_FINGERPRINT = '0017098C5DF4197E3C884DCFF1B240D43F148C21' -EMAiL_GPG_PASSPHRASE = None -EMAIL_GPG_PASSPHRASE_FILE = None HTTPS_SHARE = 10 EMAIL_SHARE = 5 RESERVED_SHARE = 2""" diff --git a/requirements.txt b/requirements.txt index ef72ed5..79bb01d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,6 @@ html5lib==1.0b8 Mako==1.1.1 pycryptodome==3.9.6 Twisted==20.3.0 -gnupg==2.3.1 ipaddr==2.2.0 Pillow==6.2.2 pyOpenSSL==19.1.0 diff --git a/scripts/setup-tests b/scripts/setup-tests index 1de3c71..fc89d8f 100755 --- a/scripts/setup-tests +++ b/scripts/setup-tests @@ -13,7 +13,7 @@ cd $THERE mkdir -p run/from-authority mkdir -p run/from-bifroest -cp -R -t run bridgedb.conf captchas .gnupg +cp -R -t run bridgedb.conf captchas # Add '127.0.0.1' to EMAIL_DOMAINS in bridgedb.conf. This should ONLY be # done on testing servers, never on production servers. sed -r -i -e "s/(EMAIL_DOMAINS)(.*)(])/\1\2\, '127.0.0.1']/" run/bridgedb.conf
_______________________________________________ tor-commits mailing list tor-commits@lists.torproject.org https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits