Opened 7 years ago

Closed 4 years ago

Last modified 10 months ago

#5463 closed enhancement (fixed)

BridgeDB must GPG-sign outgoing mails

Reported by: rransom Owned by: isis
Priority: Medium Milestone:
Component: Circumvention/BridgeDB Version:
Severity: Blocker Keywords: bridgegb-email, bridgedb-ui
Cc: aagbsn, kaner, mikeperry, isis@…, mrphs Actual Points:
Parent ID: #3893 Points:
Reviewer: Sponsor:

Description

To protect users against attacks in which someone forges an e-mail message which appears to be sent by BridgeDB, but which contains malicious bridges intended to target a specific user, BridgeDB must start GPG-signing its outgoing e-mail messages.

BridgeDB must also include the address to which it sent a message in the GPG-signed text, and warn users that they should verify that BridgeDB messages are GPG-signed and that the e-mail address in the signed message matches the e-mail address which the user requested bridges with.

Child Tickets

TicketTypeStatusOwnerSummary
#9443taskclosedisisGenerate and secure pgp keys for bridges.tpo

Change History (28)

comment:1 Changed 7 years ago by runa

BridgeDB should also include information about (1) why it is important users verify the signature of the email, and (2) how users can verify the signature (pointing to the short user manual should be fine).

comment:2 Changed 7 years ago by rransom

Cc: aagbsn kaner mikeperry added

This is slightly related to #5462.

comment:3 Changed 7 years ago by aagbsn

Status: newneeds_information

I wrote some (untested) code as starting point, using gpgpme (python-gpgme)

https://gitweb.torproject.org/user/aagbsn/bridgedb.git/commit/c166119dec14584ad14dcf50b2a98ff9f719892a

Now for some questions:

Is it OK to use unprotected protected keyfile?
Is gpg clearsign fine here?
What sort of friendly and encouraging text do we want to include to inspire users to actually verify messages? And, if the instructions we link to are on www.tpo, and *.tpo is blocked, what now?

comment:4 in reply to:  3 Changed 7 years ago by rransom

Replying to aagbsn:

I wrote some (untested) code as starting point, using gpgpme (python-gpgme)

https://gitweb.torproject.org/user/aagbsn/bridgedb.git/commit/c166119dec14584ad14dcf50b2a98ff9f719892a

Now for some questions:

Is it OK to use unprotected protected keyfile?

Using a GPG key with no passphrase is fine. (I assume the HTTPS certificate has no passphrase either.)

Is gpg clearsign fine here?

Yes.

What sort of friendly and encouraging text do we want to include to inspire users to actually verify messages?

This is the hard part -- I don't have that text written.

And, if the instructions we link to are on www.tpo, and *.tpo is blocked, what now?

The Tor Short User Manual is sent out by GetTor with every Tor package. Perhaps GetTor should also be able to send a copy of TSUM only, or perhaps BridgeDB should attach a copy of TSUM to its messages (or have an extra command to ask for one).

comment:5 Changed 6 years ago by isis

Cc: isis@… added

For my work with LEAP, I ended up auditing a bunch of the python GnuPG tools, and then fixing one of them. If help is still needed on this ticket, it is easy for me to do so.

aagbsn: Did your branch for this ever get merged into the main bridgedb? Would you like me to help by reviewing/testing, or by writing something using the library I´m maintaining?

comment:6 Changed 6 years ago by aagbsn

gpg signing support was added in: ca5398470108b8f9444b5c0f823411eb735dcfd0
5463 - Adds GPG clearsign to email distributor.

What remains to be done is to create and distribute keys, and review any messages and translations that BridgeDB uses to inform users.

comment:7 Changed 6 years ago by sysrqb

Keywords: important added

comment:8 Changed 6 years ago by sysrqb

I think it will be beneficial if we reorganize/extend the verifying-signatures[0] page to include a section that describes how one verifies our signed mail, too. Assuming we do this, then we don't need to describe the various methods for doing this on the various OSs inline. We may still want to distribute the TSUM, but presently it does not describe everything we need, so it will similarly need to be updated/extended to say how to verify our sig. This maybe worthwhile though, for places where tpo is blocked.

With regard to the email message, I think we can add something like this:

Please consider verifying that this email was sent by the Tor Project
and that it has not changed since its creation. A malicious bridge can
destroy your anonymity, so you may want to confirm that this email is
legitimate and was not altered. You can follow the instructions at 
https://www.torproject.org/docs/verifying-signatures.html.en to verify
this email.

If you don't need step-by-step instructions, then our public signing
key is also available at
https://bridges.torproject.org/sig and our fingerprint is [fpr]. If
you don't know how to use this information, then please go to the above
mentioned website or contact help@rt.torproject.org for assistance.

(I dialed-back the scariness/pushiness of my original message) Suggestions?

Thus making:

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

[This is an automated message; please do not reply.]

Here are your bridge relays:

  [bridges x 3]

Bridge relays (or "bridges" for short) are Tor relays that aren't listed
in the main directory. Since there is no complete public list of them,
even if your ISP is filtering connections to all the known Tor relays,
they probably won't be able to block all the bridges.

To use the above lines, go to Vidalia's Network settings page, and click
"My ISP blocks connections to the Tor network". Then add each bridge
address one at a time.

Configuring more than one bridge address will make your Tor connection
more stable, in case some of the bridges become unreachable.

The following commands are also supported:

  ipv6 : request ipv6 bridges.
  transport NAME : request transport NAME. Example: 'transport obfs2'

Another way to find public bridge addresses is to visit
https://bridges.torproject.org/. The answers you get from that page
will change every few days, so check back periodically if you need more
bridge addresses.

Please consider verifying that this email was sent by the Tor Project
and that it has not changed since its creation. A malicious bridge can
destroy your anonymity, so you may want to confirm that this email is
legitimate and was not altered. You can follow the instructions at 
https://www.torproject.org/docs/verifying-signatures.html.en to verify
this email.

If you don't need step-by-step instructions, then our public signing
key is also available at
https://bridges.torproject.org/sig and our fingerprint is [fpr]. If
you don't know how to use this information, then please go to the above
mentioned website or contact help@rt.torproject.org for assistance.

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.19 (GNU/Linux)

[sig]
-----END PGP SIGNATURE-----

[0] https://www.torproject.org/docs/verifying-signatures.html.en

comment:9 Changed 6 years ago by sysrqb

Looks like I missed the second part of this ticket, that's what I get for not reading the description for a couple months. We should also copy the destination email address into the signed portion of the response, I think that's a good idea.

comment:10 Changed 6 years ago by mrphs

Cc: mrphs added

comment:11 Changed 6 years ago by isis

Keywords: bridgegb-email added; important removed
Owner: set to isis
Priority: criticalmajor
Status: needs_informationassigned

I wrote several tests for the functionality of this, and I'll spare the details, but python-gpgme is a horrible, horrible monster and we should not be using it.

Essentially, the TravisCI builds are failing right now (with TESTING gpg keys in place, and EMAIL_GPG_SIGN_KEY and enabled) simply because gpgme, when you do:

gpgme.Context().import_(open(cfg.EMAIL_GPG_SIGN_KEY))

doesn't import that key. Instead, it imports every key in the EUID's home directory. For the continuous integration tests, this means that (because the tor package from the Debian repositories is installed as part of the CI build script) the deb.torproject.org archive signing key ends up as the first key, gpgme tries to sign a test email with it, and craps its pants.

It is about 50 lines of code to iterate though the fingerprint of every uid of every key and find the one that matches the signing subkey...I don't trust this thing. I think it is buggy and poorly designed and way too many things are going to go wrong with it. Perhaps this is just me complaining because I spent a good three months last year writing a python gnupg module, but I would actually be worried to deploy using python-gpgme. I've not yet assessed how much work it would be to replace it.

comment:12 Changed 6 years ago by isis

Status: assignedneeds_revision

comment:13 Changed 6 years ago by isis

Priority: majornormal
Type: defectenhancement

comment:14 Changed 5 years ago by isis

Status: needs_revisionneeds_review

This is fixed, and there are unittests for problems encountered, in my fix/5463-7547-7550-8241-11475-11753-email-rewrite branch (which rewrites the entirety of the old lib/bridgedb/EmailServer.py module).

There are additional fixes for the issue where libgpgme was attempting to load private keys from the process owner's $HOME directory in my fix/5463-gpgme-homedir branch, which is currently based on the fix/5463-7547-7550-8241-11475-11753-email-rewrite branch and should be merged after it. Additionally, since all the strings were changed to be (mostly) the same as the ones which are currently in use on the HTTPS distributor, there is a translations/2014-05-07-update branch which should be merged which updates the gettext bridgedb.pot file.

There still is not a mechanism to include the client's email address in the signed portion of the message. I'm not exactly sure what adversarial behaviours that was intended to protect against.

comment:15 in reply to:  14 ; Changed 5 years ago by rransom

Replying to isis:

There still is not a mechanism to include the client's email address in the signed portion of the message. I'm not exactly sure what adversarial behaviours that was intended to protect against.

Signing the intended recipient's e-mail address prevents the attacker from querying BridgeDB until it receives a signed message containing a malicious bridge, and then re-sending that message to one or more targeted users. (If you don't sign the destination e-mail address, there's not much point in signing BridgeDB's e-mails at all.)

comment:16 Changed 5 years ago by isis

Currently, the way the responses look like in the new email distributor are as follows:

  1. The client sends an email to the distributor.

1a. If it was a valid request, the client gets the response they wanted. For example, if the client sent:

18:16:04 DEBUG    L670:server.validateFrom()    ORIGIN: "twisted.mail.smtp.Address('isis@127.0.0.1')"
18:16:04 DEBUG    L679:server.validateFrom()    Got canonical domain: '127.0.0.1'
18:16:04 DEBUG    L487:server.lineReceived()    > Received: from localhost (localhost [127.0.0.1]) for <bridges+fa@127.0.0.1>; Mon, 05 May 2014 18:16:04 +0000
18:16:04 DEBUG    L487:server.lineReceived()    > From: isis@127.0.0.1
18:16:04 DEBUG    L487:server.lineReceived()    > To: bridges+fa@127.0.0.1
18:16:04 DEBUG    L487:server.lineReceived()    > Subject: testing
18:16:04 DEBUG    L487:server.lineReceived()    >
18:16:04 DEBUG    L487:server.lineReceived()    > get transport obfs3

Then they would get something quite similar to this in response:

18:16:04 INFO     L591:server.reply()           Got an email; deciding whether to reply.
18:16:04 INFO     L626:server.reply()           Client requested email translation: fa
18:16:04 DEBUG     L42:request.determineBridg() Email request was valid.
18:16:04 DEBUG    L106:request.withPluggableT() Parsing 'transport' line: 'get transport obfs3'
18:16:04 INFO     L110:request.withPluggableT() Email requested transport type: 'obfs3'
18:16:04 DEBUG     L53:request.determineBridg() Generating hashring filters for request.
18:16:04 INFO     L420:Dist.getBridgesForEmai() Attempting to return for 3 bridges for isis@127.0.0.1...
18:16:05 INFO    L1378:Bridges.addRing()        Bridges inserted into Email-Transpo subring: 285
18:16:05 DEBUG     L75:Dist.getNumBridgesPerA() Returning 3 bridges from ring of len: 285
18:16:05 DEBUG   L1034:Bridges.getBridges()     Got duplicate bridge 'f966e90fd0135a00300da797577322645b7de9fd' in main hashring for position 'f8f6fd62d22b6d29e684b0149eff7ef7a7aaf177'.
18:16:05 DEBUG    L181:server.generateRespons() Email contents:
From: bridges@torproject.org
To: isis@127.0.0.1
Message-ID: <20140505181605.30859.509475106.1@wintermute.patternsinthevoid.net>
Content-Type: text/plain; charset="utf-8"
Date: Mon, 05 May 2014 18:16:05 +0000
Subject: Re: testing


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

[This is an automated message; please do not reply.]

Here are your bridges:

  obfs3 60.224.31.170:32425 fda5c4cbf4c50e099e66522b6ddd2f7088611411
  obfs3 118.174.139.237:36576 46ea8d81cb30d6f0faa1064f796694e30bc4b306
  obfs3 24.50.111.88:64810 ccbd222d4840a3b2c865fc558139e4c6864c17b5


To enter bridges into Tor Browser, follow the instructions on the  Tor
Browser download page [0] to start Tor Browser.

When the 'Tor Network Settings' dialogue pops up, click 'Configure' and follow
the wizard until it asks:

> Does your Internet Service Provider (ISP) block or otherwise censor connections
> to the Tor network?

Select 'Yes' and then click 'Next'. To configure your new bridges, copy and
paste the bridge lines into the text input box. Finally, click 'Connect', and
you should be good to go! If you experience trouble, try clicking the 'Help'
button in the 'Tor Network Settings' wizard for further assistance.

[0]: https://www.torproject.org/projects/torbrowser.html.en#downloads-beta



COMMANDs: (combine COMMANDs to specify multiple options simultaneously)
  get bridges            Request vanilla bridges.
  get transport [TYPE]   Request a Pluggable Transport by TYPE.
  get help               Displays this message.
  get key                Get a copy of BridgeDB's public GnuPG key.
  get ipv6               Request IPv6 bridges.

Currently supported tranport TYPEs:
  obfs2
  obfs3
  scramblesuit

-----BEGIN PGP SIGNATURE-----

iQMhBAEBCgELBQJTZ9VlBYMB4TOAVhSAAAAAACUAKGlzaXMrc2lnbnN1YmtleUBw
YXR0ZXJuc2ludGhldm9pZC5uZXQ5RkUzOUQxQTc0Mzg5MjIzM0IzRjY2RjIyMUI1
NTRFOTU5MzhGNEQwSxSAAAAAABoAKGlzaXNAcGF0dGVybnNpbnRoZXZvaWQubmV0
REY4MTExMDlFMTdDOEJGMTM0QjVFRUI2OERDNDNBMjg0ODgyMUUzMi4aaHR0cHM6
Ly9ibG9nLnBhdHRlcm5zaW50aGV2b2lkLm5ldC9wb2xpY3kudHh0LJhodHRwczov
L2Jsb2cucGF0dGVybnNpbnRoZXZvaWQubmV0L2lzaXMudHh0AAoJECG1VOlZOPTQ
xZYQAJVzYB0V10nUHmHqxAzkXyU5IsUGJe+mnVdB+m5FS2ALqIE/+cQo75SFQeaa
wWrP0Qr6vrPMbNSmIm0W2aVKvtrCVGoUAdg20lr61TCj/WknGVYjkzp1SePM21ZS
Xh6W4YkzWTKvKjLZHpc7aNYUuuqJNSKd2nOeQ4X/2QT4MXLMEagn/oFcQPxzcW7d
ilGxqCCS0uxSTFpHlhLjSezNfzYFapqRq4W9poZET3azAvNk/diYfW1RF6YDqgT0
NZlgbHxzaqWM8JTQeNR1RoStTd1pWZOIKIiAr7S2Hc/FV6k3lVtggEEz/89gGMH3
wCm9RadUxtTWpgipCvXI8HtRjv+zFmAqk4n+YKISAXIHFB6ptL6wjrBP8i/5R8Uy
37//1j80iHyAz49Qu/o9hu1Dy2Iai+hS+LraJF4KMVINes4cFr6OnplYvlM5ZjvK
HI9Qis++/mmKtruss+M02pSNAQzmSo6YGbq3WpilCTjUwrAbumdWXTAg0NNq0fbA
aOCvq9mF1TnqHc05GpXIi6/ggdpn4kzzc1OG60SAc/AIvh88HjOVXoHrW1d6cT75
THgkTobwBSmgdDbYqIHe3t4FjNBOZX2F3vXDWjAtEuXq3h3/vTYbUDmg0TAlWHpC
7yYmSzj2uHI45X2Tlx/JGr+D+ES+0TFpqgCuVRNOAk4xNjJX
=sIGg
-----END PGP SIGNATURE-----


18:16:05 INFO     L635:server.reply()           Sending reply to isis@127.0.0.1

1b. The request was invalid, or the client said "get help":

18:04:11 DEBUG    L670:server.validateFrom()    ORIGIN: "twisted.mail.smtp.Address('isis@127.0.0.1')"
18:04:11 DEBUG    L679:server.validateFrom()    Got canonical domain: '127.0.0.1'
18:04:11 DEBUG    L487:server.lineReceived()    > Received: from localhost (localhost [127.0.0.1]) for <bridges+fa@127.0.0.1>; Mon, 05 May 2014 18:04:11 +0000
18:04:11 DEBUG    L487:server.lineReceived()    > From: isis@127.0.0.1
18:04:11 DEBUG    L487:server.lineReceived()    > To: bridges+fa@127.0.0.1
18:04:11 DEBUG    L487:server.lineReceived()    > Subject: testing
18:04:11 DEBUG    L487:server.lineReceived()    >
18:04:11 DEBUG    L487:server.lineReceived()    > get help

Then the response would look like this:

18:04:11 INFO     L591:server.reply()           Got an email; deciding whether to reply.
18:04:11 INFO     L626:server.reply()           Client requested email translation: fa
18:04:11 INFO     L122:server.createResponseB() Client requested help.
18:04:12 DEBUG    L181:server.generateRespons() Email contents:
From: bridges@torproject.org
To: isis@127.0.0.1
Message-ID: <20140505180411.28572.546159386.0@wintermute.patternsinthevoid.net>
Content-Type: text/plain; charset="utf-8"
Date: Mon, 05 May 2014 18:04:11 +0000
Subject: Re: testing


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

Welcome to BridgeDB!

COMMANDs: (combine COMMANDs to specify multiple options simultaneously)
  get bridges            Request vanilla bridges.
  get transport [TYPE]   Request a Pluggable Transport by TYPE.
  get help               Displays this message.
  get key                Get a copy of BridgeDB's public GnuPG key.
  get ipv6               Request IPv6 bridges.

Currently supported tranport TYPEs:
  obfs2
  obfs3
  scramblesuit


BridgeDB can provide bridges with several types of Pluggable Transports[0],
which can help obfuscate your connections to the Tor Network, making it more
difficult for anyone watching your internet traffic to determine that you are
using Tor.

Some bridges with IPv6 addresses are also available, though some Pluggable
Transports aren't IPv6 compatible.

Additionally, BridgeDB has plenty of plain-ol'-vanilla bridges - without any
Pluggable Transports - which maybe doesn't sound as cool, but they can still
help to circumvent internet censorship in many cases.

[0]: https://www.torproject.org/docs/pluggable-transports.html

-----BEGIN PGP SIGNATURE-----

iQMhBAEBCgELBQJTZ9KbBYMB4TOAVhSAAAAAACUAKGlzaXMrc2lnbnN1YmtleUBw
YXR0ZXJuc2ludGhldm9pZC5uZXQ5RkUzOUQxQTc0Mzg5MjIzM0IzRjY2RjIyMUI1
NTRFOTU5MzhGNEQwSxSAAAAAABoAKGlzaXNAcGF0dGVybnNpbnRoZXZvaWQubmV0
REY4MTExMDlFMTdDOEJGMTM0QjVFRUI2OERDNDNBMjg0ODgyMUUzMi4aaHR0cHM6
Ly9ibG9nLnBhdHRlcm5zaW50aGV2b2lkLm5ldC9wb2xpY3kudHh0LJhodHRwczov
L2Jsb2cucGF0dGVybnNpbnRoZXZvaWQubmV0L2lzaXMudHh0AAoJECG1VOlZOPTQ
PB8QAJD82VUKet9GtmEqk/i1EioR3ZJYSXKqVPMoo7a/NdI4UenSbIYN0Rsy7SBx
HJmOzZ1RJ4lf2mKr/2YEGdPiIuzo/ngAYKnVXmPd4FUjTRQrIJTcnu3sOVgSbklO
7c5NqoCbs6sDfHE46f/BcIhYdVFp/iPBKG8PjuE2lzqRbIqi6fn0q0+OZpxqnIg/
ZC1GCPgoKuA8b1HHfu9ndvMMDiSWrFk0HWDS5fMJuOBu1ro/pjFBvvtBHBHo+0gv
/86a1CWSOhMdxyNSn3Crof/XEebHD7aBgCc/eFm6EFrJMRpGg4pbsG+4cn+DoOpv
J+jMm4/EJUurOMwR9DYgjy/9uxfI4OAiJq/Ed5cGv+p3AmsNEZRQsNCHlRDTqbex
XlEd8v/4mS14XqcB3aBilgkNpcKDraxtkzR7vg5te8Eno75ktQgTyUWIIa8uXkuR
4lM2XExgwQ0y+4Yidj7V731DIe9ik4uLwE4V95Tf+P3u65bBYxDCQO0hQyOtKcdP
6BIgjjc54VC9vm33RrVrRfXiEj+UsfFpfnR+jU6dB4xIBqebgNDF/4FcYL+egCIG
GnTqUFA9SbWSrhPIxU7t9f7ghjc8BuETymWcQxWsjDwgZDa/5nSvAb4AeQLBCMPe
bKAJGpaHzepRKLwhI6WSY2R1m1QAQqhtPiyW2Us1pNAnAgtx
=ZZ9B
-----END PGP SIGNATURE-----


18:04:12 INFO     L635:server.reply()           Sending reply to isis@127.0.0.1
  1. They requested a copy of our public GnuPG keys:
18:15:09 DEBUG    L487:server.lineReceived()    > Received: from localhost (localhost [127.0.0.1]) for <bridges+fa@127.0.0.1>; Mon, 05 May 2014 18:15:09 +0000
18:15:09 DEBUG    L487:server.lineReceived()    > From: isis@127.0.0.1
18:15:09 DEBUG    L487:server.lineReceived()    > To: bridges+fa@127.0.0.1
18:15:09 DEBUG    L487:server.lineReceived()    > Subject: testing
18:15:09 DEBUG    L487:server.lineReceived()    >
18:15:09 DEBUG    L487:server.lineReceived()    > get key

Results in this response:

18:15:09 DEBUG     L42:request.determineBridg() Email request was valid.
18:15:09 INFO     L125:server.createResponseB() Email requested a copy of our GnuPG key.
18:15:09 DEBUG    L181:server.generateRespons() Email contents:
From: bridges@torproject.org
To: isis@127.0.0.1
Message-ID: <20140505181509.30859.369688437.0@wintermute.patternsinthevoid.net>
Content-Type: text/plain; charset="utf-8"
Date: Mon, 05 May 2014 18:15:09 +0000
Subject: Re: testing


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

# 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: 2014-09-11]
#       Key fingerprint = DF81 1109 E17C 8BF1 34B5  EEB6 8DC4 3A28 4882 1E32
# uid                           BridgeDB <bridges@bridges.torproject.org>
# sub   4096R/21B554E95938F4D0 2013-09-11 [expires: 2014-09-11]
#       Key fingerprint = 9FE3 9D1A 7438 9223 3B3F  66F2 21B5 54E9 5938 F4D0
# sub   4096R/E7793047C5B54232 2013-09-11 [expires: 2014-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
AQoBDwUCUi/xgEgUgAAAAAAXACh2ZXJpZmllZEB0b3Jwcm9qZWN0Lm9yZ0RGODEx
MTA5RTE3QzhCRjEzNEI1RUVCNjhEQzQzQTI4NDg4MjFFMzJPFIAAAAAAHgAoYnJp
ZGdlc0BicmlkZ2VzLnRvcnByb2plY3Qub3JnREY4MTExMDlFMTdDOEJGMTM0QjVF
RUI2OERDNDNBMjg0ODgyMUUzMioaaHR0cHM6Ly9icmlkZ2VzLnRvcnByb2plY3Qu
b3JnL3BvbGljeS50eHQCGwEFCQHhM4ADCw0JBBUKCQgEFgIBAAIeAQIXgCcYaHR0
cHM6Ly9icmlkZ2VzLnRvcnByb2plY3Qub3JnL2tleS5hc2MACgkQjcQ6KEiCHjIW
qw//WM8jAeuZaNl+2Cdt8Mbe4TCS4jggy4IfRgVqOiKEdIcMXUrDIUXsfg1tc+Jg
Gk6Ztcw4Mt8HYIEn2MyhuSWOlRoPlLNzN8i8bVBsPHUGKoX0Z1xIahBhkcXVJTJe
cxRHhvXOpUe+vd54fGYBz/7ViXwwZOjCA7e1zsdkhkpjAkWdJgPynH93bXMU8PYA
XlMR4rJDTs2LCvMIw3BKfv3Z5zE1a3vQFE1dKsKj6Q3fBYw0gZuobF5Z7fgbN9L+
f9H9bz0A9K7z6AH+6/C9kHwsjWa2zI7Ei+jexQX+v9udNeR1GphLorgewe+1X+9z
UsWhBnczSQU/lQFiFuMFgbzFsuwqD8lmDPrO7Ek3b2R0Klv8ge0cA2Y6z7cVZ8Px
BIEJ5Z9FEqhNBsaLvbbCGbeXUnvzue3Gqcv2WJ8aNfDnPV7BBrtgRSTMP+ErEqyq
DTPWdOASZmEgqox62zMFP42U7UrgHrGKO801Z6P50ELYAganCOEDFC8YM/R1l+Sq
ZezRjjNYj0qNUEm2bBNzLDhi7VvMpoxr2D5oIIrm2i+WeankO2hQuPGuRc0rvASb
tOazhVWqRSddVxeFGlIye8J93NLgEzlHaU1yBwsq/y09/uvmpW65+424HaYzdWVq
s3H4GCa7VyxH6GoQJeXxqQbJP3gsNkSeEyuXF7LyuFl6hVSJCOYEEwEKANAFAlJZ
D9AFgwG4FTBIFIAAAAAAFwAodmVyaWZpZWRAdG9ycHJvamVjdC5vcmdERjgxMTEw
OUUxN0M4QkYxMzRCNUVFQjY4REM0M0EyODQ4ODIxRTMyTxSAAAAAAB4AKGJyaWRn
ZXNAYnJpZGdlcy50b3Jwcm9qZWN0Lm9yZzdCNzg0MzcwMTVFNjNERjQ3QkIxMjcw
QUNCRDk3QUEyNEU4RTQ3MkUqGmh0dHBzOi8vYnJpZGdlcy50b3Jwcm9qZWN0Lm9y
Zy9wb2xpY3kudHh0AAoJEMvZeqJOjkcuVyY//3Gqkvf84Mo3cLcpux1L6F1UqyQ7
ZwgFAVbrtsR/8HbDD0BUIEoKUbqkqdMhhNeE+yNzlZHLMqgUxveb4sgfYNStMTJ/
VSlNPTf/T25FShmS6BghWF8qubyKi4cpqyt3zD1INvvTWdRVzRpwxYRBKKWdALl2
7J5PqvDSvevjx6h6Gc4FoO97WbK5qaQFOc2Nnw2CKCiEiyL3RciMR4IkvAPoxv+E
Wi6uhCD72s4apqkPiSnsdDqWI/MMkxP7ZKwyyAy/8qMsI79DqwEr2dmLs6AOIXgb
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
klHqPZwzyHw0O+tBuQINBFIv8oEBEAC2f6e8USDR2OzDH9/26PyNFkcVh3dQBPbU
U6MhINUtrtWK1li+TPQ/LGkbKfXKZ6RhLoVt77uxuJ2rGNu6js+G0irtWDAFSAuf
L9bRJwanaNcxKzY4bN/D65iSm27PbQ32GDotsdBpYTGyKzUrXGaZwnS2ulOjSzG4
IAy9RsYzD2ATIuE7S4HCHkQ1Eo7GbHGZICsbOUJElXN0q2Mq2msiHPFrSKQy3MT7
ev4PdK22rWcwXzvvd0c+6lFx5oM7jNIctRb5+tfaNyOQ7Fq3zs/bf0Ls56DgcOD3
8wzzU+1Zc2TeMRwHK+fuwfgx/SLL5QreTZEBbmKRWQqVzWg9lzrZiKo0wnizX3IE
xI9KqFCBCsimwSIHz/u0trS18vCSlE8uYPZMV0Hh7ni8pEODLdfCR9JzB2x20JdC
tyxk+LnEQzpozbZUApbVS5CJNxopsqirP73WfXPnZuAJMdarCXML73l/opiXtnvU
VKHXMVl8Vq2J7j2uW+Z30dYvhn1dKJ/z7QEPIHPQpCaTIquU0AzhYRT+skHKu+7F
7lJBJeampqbXnc9vBNCOvd7pZ38HLBPNKcZKSBup99w2SUhMqoLVoxNT9qZ7FXzu
SC7N5ii3u2ztb96jF3ApDAhh/5MM77aMKmL2OIGmTA2bUBWPFO5SPRXUVL1XXhwA
duMtvdZ4cwARAQABiQWDBBgBCgDTBQJSL/KBSBSAAAAAABcAKHZlcmlmaWVkQHRv
cnByb2plY3Qub3JnREY4MTExMDlFMTdDOEJGMTM0QjVFRUI2OERDNDNBMjg0ODgy
MUUzMk8UgAAAAAAeAChicmlkZ2VzQGJyaWRnZXMudG9ycHJvamVjdC5vcmdERjgx
MTEwOUUxN0M4QkYxMzRCNUVFQjY4REM0M0EyODQ4ODIxRTMyKhpodHRwczovL2Jy
aWRnZXMudG9ycHJvamVjdC5vcmcvcG9saWN5LnR4dAIbAgUJAeEzgAKkCRCNxDoo
SIIeMsHYIAQZAQoAgQUCUi/ygU8UgAAAAAAeAChicmlkZ2VzQGJyaWRnZXMudG9y
cHJvamVjdC5vcmc5RkUzOUQxQTc0Mzg5MjIzM0IzRjY2RjIyMUI1NTRFOTU5MzhG
NEQwKhpodHRwczovL2JyaWRnZXMudG9ycHJvamVjdC5vcmcvcG9saWN5LnR4dAAK
CRAhtVTpWTj00Ns/D/kBN79Pz55pOgLe/RNg1Efme95C/MBZ2eyEPeWe9+yAOFuK
XCJ8qlzkscpdTeu9vjYcfZy7RyS6Sx0NO8gJkP1wmNGr8X4zObSk0IAmDSlCcdpp
YVCPD4IdgSiLNqLdL6JjGE0d5J0DpPLvGbj5sc1/QCpuVt3p0rCr0S+PAAjrNmwA
e2yYdFQx2sXAZ0pp7oJJyFRPYlAUE8RYyG/N2kMYAJZVpI+zFgq5iD0uCmzAZyh/
62mtGMpGXwy1KHS5+NtUGVN5Iqkvlj59xNEPc8jzDs5yvfOOvpelgbhJQ0exbDHK
78NNeDvouTX/4F1ofbmEM4BqwtQ5TvJEvzMP9zszQWQZd2OvkjYMQSrDMn1w8daO
mBvw319kug8U/0k6YoYcg2rE17tN/CJN0Pq4Mqz+79ArVT6jeJgFcFNER2ik9R0F
Mz3BhFSHIZdMQouFQ/cwaldiWB5g+tJ2nvpXI3cHMtNsy/77uvHIrEv1xZhTSmdc
eA8LmfIdfmepeaEyeTEAPcGebyf1cN+CLJMMTGqSwSHZVe3KFV8t7PKUQ/Z/C13z
+YAEEKxOPNlboVF9FECNRlZkYWkWDyejXHklL26KzBE/mDWoeKM0VakbJO0laXlU
13R334u1VRfWNGkNSmyNlzGqzd+y/Dan4f/NDXAW8Ouyhq+Go5Or4ASclztkbznu
D/9mG59d6ZeeT+1u+dYeWNUjAYkIt3B/TMOs0KQjxHgvbogto1jy4MMvSfyygkCq
zDURZslBXw6gZbSHu1XZrfApaX0tphhOmiwztbwOr4aEDKv5MsT9MgkXEEh8BesF
g/3yUnZ34I8laKfWJQqxVI+reKz/aJYCx9gcoY2jJAaMQoSsxjtFU/KAPvkRhgq/
QobO5BS/DPWn8SqMzLXlrG2UjR/E5mb6aQU5yDEtSrMuIHQk0hnc3OrgoNX8mt/B
uhVirtqmsJmrftcfvAfXFWp0RIzsh9qixYeE4yAUSX5mZ4a0zheq/jVAnL0KJEEn
p2Mw8pkBSmfJS0QpHtMxoaOkZSZO/Ofah751X2Hlr7KVG9ZUdqm8kKtDMJc/JCDz
tJSpzioUBcXcuG31sfUOayYtS3RG385SuxaC1IiY6QVQvVSh90UVtatfv/jiuWmb
ddgM7Eifto5ybTnPFNkQASRiFmz3NyrWbinmjnMfC94JJRJVwgOUree3KahjopLC
r6WiZEknm8SqaipkH7o7xKN7HIe0kvx67YqwTmCH1wK81pQQrbqZPh6xzCys+AXb
5VK8yoVX4duo7cQGYiODGaw0SqNqXFkzsJ0fy00LDfmKF7KVvtF2qSK50kgAe3Fc
tQRxUklWHHz5IEviG8nBETtXERQLhilK4E3BPc9BA+aPm7kCDQRSL/KeARAA0zi0
eXuTSikT6Ywm+Af/GGJFofqK2+9BKICGeo+b/Hf3Yy8oY6mfQSJIeNf4WCjMJ7Gu
CpA5E2l9HjbtwRQdsxoGdRkne+duREsYqrLlyJQINqXse28rGhX7mtJ1MeuLZmkJ
tlwK3ZI/8CNKW4PonVspt1kEJmS8k0285fK6UxLU7fktpus9xHgIRB7jRR9eoRA1
MsJXn9xKLV9547hYi6mjb581GA8YVLyndtitXrNYIIrKYD/nm9W5DAKcXnH9q6pk
lBHL7Lk1oqeKBmTwSxg0HjSh5AlFW4+ME8lgyEbbaN8IvuAhTxFc8PobY9XEH07L
T6lHRuzUjpi0IIHnaFHb+wCZuDCSYshGsZfiAH+gH8K65t3jkr9D4KFMTq1G/b4S
KC/IofILYqr0PamGkUH0AP8N2dErtmH6HH0EvFMPfahvqE4InhuhernfPoTGQGFW
Ram1Ps7czMgxOoeB35ZLelTSdB5NFaBd/E07O+6R11wWyw+ks0erWzbkfa/5W5gv
EH9gNWQE4UdIrhoTURbVD6NvBsWpMRnS2fZByMY5exDf4LJmE8LhIa/snzbS8LUh
9KI3qmw5oLn74IfkiEwrcjQwFLKY62WBLRdDR0aUqJtjRVRCzKjEcnVP53KYdaxm
YFaev81Y+sSJYSpqo+T9cv/QdHOE7tQsyaZCecMAEQEAAYkC6QQYAQoA0wUCUi/y
nkgUgAAAAAAXACh2ZXJpZmllZEB0b3Jwcm9qZWN0Lm9yZ0RGODExMTA5RTE3QzhC
RjEzNEI1RUVCNjhEQzQzQTI4NDg4MjFFMzJPFIAAAAAAHgAoYnJpZGdlc0Bicmlk
Z2VzLnRvcnByb2plY3Qub3JnREY4MTExMDlFMTdDOEJGMTM0QjVFRUI2OERDNDNB
Mjg0ODgyMUUzMioaaHR0cHM6Ly9icmlkZ2VzLnRvcnByb2plY3Qub3JnL3BvbGlj
eS50eHQCGwwFCQHhM4AACgkQjcQ6KEiCHjIPZhAAs5l8T2mWL+kG8WbNyjdISe/W
D0zZ5E2vAnCq5DcxZ4rax3GblJWRxo1frRT5xoq21adGs8TC5NG1u/40KYn30aB2
9YE6ida9yCR2R2EYKgX5sKGTXXOpnbFa9og4gXHrgU0f9vwT0rY7CyaRmjBfBxRX
fCy7i/+dnsuwhMHn0RHJL4yIEdmQk/HFA6Taep6ACl8Wfrebg0TX5zi63v1RbC4e
KBt39p+l46+UzGWGqP7yfLrHpYNMUIGJOgImvOWeHa2QWJWojHjCuXt5oIuER4ib
p0lUO2B56wljoAjYvgPHWLAVIAMaxQMriMalF4Nlmcgj0I4aWcFuKMXZVUuie/Ps
Epj8U2JpIWmGQhzUNN2eUF5WNV/8VPF/WqzcYBBlmGz1lMzRCmQ/Ws1DlJz2i5Pj
L2cOVatJrRH7Iwz4YPHxgtv+T777GfezuqaIq+ZDKlICxFjalg1gHe40hxsUDdH1
oJrb5Tg6RD7QV//wDxHDi43p4iL57d3V8LLl/dICA03n/c4ilBBfoh0gWTCjw3pb
sgww4Q39lcUmcittWG5/xS1OLD9WVLSQ42rUu3c5hPL35zAOhFZsFJSIPuZ2ip8E
T94oJR/vE4KRRZca7Uy5A0TcPmB4qTMfaCmWjQOyCxPyJKc2ECx+thiHKglL62Ww
qgQ42nQM/debtU+2aTo=
=ieIZ
-----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) <bridges@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-----



-----BEGIN PGP SIGNATURE-----

iQMhBAEBCgELBQJTZ9UtBYMB4TOAVhSAAAAAACUAKGlzaXMrc2lnbnN1YmtleUBw
YXR0ZXJuc2ludGhldm9pZC5uZXQ5RkUzOUQxQTc0Mzg5MjIzM0IzRjY2RjIyMUI1
NTRFOTU5MzhGNEQwSxSAAAAAABoAKGlzaXNAcGF0dGVybnNpbnRoZXZvaWQubmV0
REY4MTExMDlFMTdDOEJGMTM0QjVFRUI2OERDNDNBMjg0ODgyMUUzMi4aaHR0cHM6
Ly9ibG9nLnBhdHRlcm5zaW50aGV2b2lkLm5ldC9wb2xpY3kudHh0LJhodHRwczov
L2Jsb2cucGF0dGVybnNpbnRoZXZvaWQubmV0L2lzaXMudHh0AAoJECG1VOlZOPTQ
dXMP/A78b7eekpJD0zH3TwGPMQCX0iM67fL2N1VH4WcrqkUmYQM2N+NNO5qc94iG
YZyros2Oi+TDc0es0Xve3Tck3o6IdxpksKlZ9tbbdeP9ruBD/q50V0eKfvOAEu+B
LSIxEzBYMGoCHWNP788rTu1Y+R5TsgsIdkfla4rbpq5ocsCXxp2Tsj9gdi/uUH1o
h5ZEk9GwBVbuWzaJtKHl6f07HMzrKQwBHfsix7dPW+FRVgNKl5Vl3x2MijLr77bo
OF7PzAawYqYE0C7ze8N+Q6ReTTjNEyBsHkc30Jek2ZcZstNbHDDUXFUI0fcXbqN4
atRE3v68JUhWvEps7NAuH6SrmD7KLg/Nd6vrFp3uVmJxIlkb5r98iOlabIGi1db+
ZN8FR52ueVOqcU+Qz4C/oQ2xWKfkaoqCLfdNe+89u5izqzbGDiMQBp6nGAS+S8bK
G3Zs0lIcX00ZeIFOWU7tW79FvJPmOHD2gE2ZF4bXGfZuFEbyjXzsPnjRiODl4ju7
SyINnWLUnjKCHYxz5Ie56td75Xzup9Q554bHRKxwOmFiMEteXKhQZdqVcEARS02k
b9wXYuJhbrMewwc/15LeyvfGMk2sbcjawbteVtGemy+ruPFOTMCMprCfimIS+nJL
p6LLmSu4L+uvzrYdfLgSHeCTGLUSkTQvodpnN+YomPSSbTaU
=bJPk
-----END PGP SIGNATURE-----


18:15:09 INFO     L635:server.reply()           Sending reply to isis@127.0.0.1
  1. The client sends too many valid requests (after they have already received help). In this case, they'll get the normal rate-limiting informational reply (except it's signed):
18:17:11 DEBUG     L42:request.determineBridg() Email request was valid.
18:17:11 DEBUG    L106:request.withPluggableT() Parsing 'transport' line: 'get transport obfs3'
18:17:11 INFO     L110:request.withPluggableT() Email requested transport type: 'obfs3'
18:17:11 DEBUG     L53:request.determineBridg() Generating hashring filters for request.
18:17:11 INFO     L420:Dist.getBridgesForEmai() Attempting to return for 3 bridges for isis@127.0.0.1...
18:17:11 INFO     L426:Dist.getBridgesForEmai() Client isis@127.0.0.1 must wait another 10728 seconds.
18:17:11 INFO     L430:Dist.getBridgesForEmai() Sending duplicate request warning.
18:17:11 INFO     L128:server.createResponseB() Got a mail too frequently: Must wait 10728 seconds.
18:17:11 DEBUG    L181:server.generateRespons() Email contents:
From: bridges@torproject.org
To: isis@127.0.0.1
Message-ID: <20140505181711.30859.1077507167.2@wintermute.patternsinthevoid.net>
Content-Type: text/plain; charset="utf-8"
Date: Mon, 05 May 2014 18:17:11 +0000
Subject: Re: testing


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

[This is an automated message; please do not reply.]

You have exceeded the rate limit. Please slow down! The minimum time between
emails is 3 hours. All further emails during this time period will be ignored.

-----BEGIN PGP SIGNATURE-----

iQMhBAEBCgELBQJTZ9WnBYMB4TOAVhSAAAAAACUAKGlzaXMrc2lnbnN1YmtleUBw
YXR0ZXJuc2ludGhldm9pZC5uZXQ5RkUzOUQxQTc0Mzg5MjIzM0IzRjY2RjIyMUI1
NTRFOTU5MzhGNEQwSxSAAAAAABoAKGlzaXNAcGF0dGVybnNpbnRoZXZvaWQubmV0
REY4MTExMDlFMTdDOEJGMTM0QjVFRUI2OERDNDNBMjg0ODgyMUUzMi4aaHR0cHM6
Ly9ibG9nLnBhdHRlcm5zaW50aGV2b2lkLm5ldC9wb2xpY3kudHh0LJhodHRwczov
L2Jsb2cucGF0dGVybnNpbnRoZXZvaWQubmV0L2lzaXMudHh0AAoJECG1VOlZOPTQ
WMoP/1b4C1klisjrm5B7J2Uz+ClxW2jWO03Ec6VyZd7GGyb1texDefGaiiG4GEUJ
lEq7IccZ++OZxkkQk7cx9/m53Q+FHM/jAOIIeOpP+q8sWb0iH8X+UVxkzxKWFbqD
+sUDI9YL/o3dEq48Cfy0Qd4ox4d8ubwgw1K8eL1wGHeUkiqCySvVTPRgVd2cyefB
DJCP9B+zvJupayqqnllAl2WnqawNNuS+06xuVhMn3dY2o6hZe08e7YZfLUQbr0fC
eV3CnDh4Axo48RU5M9dQHKn7D523+NO0ByjCNtmLHBppU0EsNUvkhM4N2XtDyZSe
yrdamvPzfxaIKAsOfYrUvE8ADbAw3niVQoPTLza7WJoTwCKD0cyJJ9e0UDcgg6/k
j1lUUlFYAetPDNQv7l9OFCzKreWA43LQfH8csCUagN6Z7uaX6CyqCU0yLIQOlpHm
4Odk+kLwvJ2U6goJ3MaB9tnTav4MdLT/KL23UyxJeeF3WPBWRlnN9a6Irfp4Pryr
Y5PUoEj0iqtHFx6VCmAoot3A2kpo517PbFTP3wsnL2N7XV4o0sEMCKPW4xjoIV5d
CvVYSJSmjjG4ebu882tenbKW5aidbBWb+SVAcchnvUdr/FTILRI7+kiB/Z+lfujv
xTwb/EjPI//r/QwGk9Lf7YILZWzNJ2HuD0QIvkamrNuCjpOB
=CqjB
-----END PGP SIGNATURE-----


18:17:11 INFO     L635:server.reply()           Sending reply to isis@127.0.0.1
  1. And finally, the server logs show the following if a client has already received the rate-limit warning and is no longer being replied to:
8:17:48 DEBUG    L670:server.validateFrom()    ORIGIN: "twisted.mail.smtp.Address('isis@127.0.0.1')"
18:17:48 DEBUG    L679:server.validateFrom()    Got canonical domain: '127.0.0.1'
18:17:48 DEBUG    L487:server.lineReceived()    > Received: from localhost (localhost [127.0.0.1]) for <bridges+fa@127.0.0.1>; Mon, 05 May 2014 18:17:48 +0000
18:17:48 DEBUG    L487:server.lineReceived()    > From: isis@127.0.0.1
18:17:48 DEBUG    L487:server.lineReceived()    > To: bridges+fa@127.0.0.1
18:17:48 DEBUG    L487:server.lineReceived()    > Subject: testing
18:17:48 DEBUG    L487:server.lineReceived()    >
18:17:48 DEBUG    L487:server.lineReceived()    > get transport obfs3
18:17:48 INFO     L591:server.reply()           Got an email; deciding whether to reply.
18:17:48 INFO     L626:server.reply()           Client requested email translation: fa
18:17:48 DEBUG     L42:request.determineBridg() Email request was valid.
18:17:48 DEBUG    L106:request.withPluggableT() Parsing 'transport' line: 'get transport obfs3'
18:17:48 INFO     L110:request.withPluggableT() Email requested transport type: 'obfs3'
18:17:48 DEBUG     L53:request.determineBridg() Generating hashring filters for request.
18:17:48 INFO     L420:Dist.getBridgesForEmai() Attempting to return for 3 bridges for isis@127.0.0.1...
18:17:48 INFO     L426:Dist.getBridgesForEmai() Client isis@127.0.0.1 must wait another 10691 seconds.
18:17:48 INFO     L131:server.createResponseB() Client was warned.

comment:17 in reply to:  15 ; Changed 5 years ago by isis

Replying to rransom:

Replying to isis:

There still is not a mechanism to include the client's email address in the signed portion of the message. I'm not exactly sure what adversarial behaviours that was intended to protect against.

Signing the intended recipient's e-mail address prevents the attacker from querying BridgeDB until it receives a signed message containing a malicious bridge, and then re-sending that message to one or more targeted users. (If you don't sign the destination e-mail address, there's not much point in signing BridgeDB's e-mails at all.)

Good point. I agree completely, and I'll hack it in right now. :)

comment:18 in reply to:  17 ; Changed 5 years ago by isis

Replying to isis:

Replying to rransom:

Replying to isis:

There still is not a mechanism to include the client's email address in the signed portion of the message. I'm not exactly sure what adversarial behaviours that was intended to protect against.

Signing the intended recipient's e-mail address prevents the attacker from querying BridgeDB until it receives a signed message containing a malicious bridge, and then re-sending that message to one or more targeted users. (If you don't sign the destination e-mail address, there's not much point in signing BridgeDB's e-mails at all.)

Good point. I agree completely, and I'll hack it in right now. :)


I'm going to add timestamps too, so that an earlier email cannot be replayed. I.e., when the NSA is like "Yo', we got the extra wiretaps installed around the boxes with those IPs. Let's resend and get 'em."

comment:19 in reply to:  18 Changed 5 years ago by rransom

Replying to isis:

I'm going to add timestamps too, so that an earlier email cannot be replayed. I.e., when the NSA is like "Yo', we got the extra wiretaps installed around the boxes with those IPs. Let's resend and get 'em."

OpenPGP already includes a timestamp in the signature packet, but adding a timestamp in the text is a good idea too, if users won't be confused by the time zone. (MUAs which display the timestamp are likely to display it adjusted to the user's TZ; unfortunately, the one MUA I used with OpenPGP support didn't display the timestamp.)

comment:20 Changed 5 years ago by isis

Done. It's in my fix/5463-sign-client-email-addr branch. It's based on fix/5463-gpgme-homedir, which in turn is based on fix/5463-7547-7550-8241-11475-11753-email-rewrite which are the two branches mentioned above which fix all these tickets.

comment:21 Changed 5 years ago by sysrqb

nice job, isis. i'm thankful that your comment my fix/5463-7547-7550-8241-11475-11753-email-rewrite ​branch (which ​rewrites the entirety of the old lib/bridgedb/EmailServer.py module) was not actually a complete rewrite, otherwise this review would take a lot longer :)

This review doesn't include anything more than a cursory skimming of the HTML template modifications or a proper review of the unit tests. Most of what follows are a bunch of nitpicky details that are not blockers. A few of them should be separate tickets, tbh, too. The only parts that I think should be reconsidered are the unconditional logging of email addresses. I really think those should be scrubbed except for when safe logging is disabled, unless there's a good reason?

This only includes fix/5463-7547-7550-8241-11475-11753-email-rewrite. fix/5463-sign-client-email-addr and fix/5463-gpgme-homedir will (hopefully) follow in a few hours.

  • lib/bridgedb/Dist.py:419 - need to scrub email address
  • lib/bridgedb/Dist.py:426 - need to scrub email address
  • lib/bridgedb/Dist.py:428 - need to scrub email address
  • lib/bridgedb/Dist.py:433 - need to scrub email address
  • lib/bridgedb/bridgerequest.py:29 - s/bridges/bridges'/s
  • Bridges.isBlocked() is buggy. It returns true if any of a bridges ipaddr:port pair are not known to be blocked in the country. 1) When the filter returns true, we don't know which pair, 2) we should somehow mark the bridge as unhealthy and only return it if there aren't other options
  • lib/bridgedb/email/request.py - missing file header?
  • determineBridgeRequestOptions(): the presence of "get" defines an email as valid? I don't have a significantly better answer except that maybe only set it when a valid command is found? Not much is gained by this, though.
  • EmailBridgeRequest:withoutBlockInCountry(): Should we support multiple country codes per line? space and/or comma separated?
  • Should our response inform the user that we only used the last transport specified in their email request?
  • determineBridgeRequestOptions(): There's no harm in setting request.isValid(True) and request.wantsKey(True) prior to raises the exception, but is there a reason for doing this?
  • lib/bridgedb/email/server.py:183 - Do we want to unconditionally log the client's email address?
  • lib/bridgedb/email/server.py:464 - s/param/var/
  • MailMessage:getClientAddress() - Missing docstring
  • MailMessage:getRecipient() - incoming - DOCDOC, and s/param/var/
  • lib/bridgedb/email/server.py:636 - unconditionally logging client email address
  • While the email server is getting an upgrade, should we also bump it to use the ESMTPFactory? We won't see any benefit from this, but I wonder if there's a reason for us to continue using SMTP.
  • lib/bridgedb/email/server.py:316,328,332 - trailing white space after read(), seek(), tell()
  • lib/bridgedb/email/server.py:732 - The schedule was originally going to be used to partition the hash-rings so that bridgedb can rotate through the bridges over a given period, (obviously) it's not doing this right now. If we had higher bridge churn then this would be more useful.
  • lib/bridgedb/email/templates.py:33

Is there a benefit to

        while not len(command) >= 25:

rather than

        while len(command) < 25:

?

  • template.gettext(strings.HOWTO_TBB[1]) % strings.EMAIL_SPRINTF[\"HOWTO_TBB1\"] - adds an extra space between the words. "the %s Tor"
  • lib/bridgedb/strings.py:116 - s/the word the word/the word/ and s/tranlate/translate/

comment:22 in reply to:  21 Changed 5 years ago by cypherpunks

Replying to sysrqb:

You already fixed this in c68a04ed3

  • MailMessage:getClientAddress() - Missing docstring


As for the other two, they both lgtm but I haven't tested them yet. I'm wondering if we should add another line to addBridgeAnswer() that asks the user to check the email address and timestamp in the footer and discard the email if either of them are incorrect. I doubt most users will think twice about an old timestamp or wrong email address unless we explicitly mention that these should be correct.

comment:23 in reply to:  description Changed 5 years ago by cypherpunks

Replying to rransom:

BridgeDB must also include the address to which it sent a message in the GPG-signed text, and warn users that they should verify that BridgeDB messages are GPG-signed and that the e-mail address in the signed message matches the e-mail address which the user requested bridges with.

After re-reading the ticket's description I realized that, in addition to my last comment, I'm not sure we sufficiently warn users about the unfortunate side effects of not verifying the signature. As far as I see, bridgedb.asc is the only place we mention verifying the sig, and a user only sees this if they explicitly request the key. We should try to find a place to mention this in the other templates. I think we should be realistic and take into account the fact that only a small fraction of end-users will check the sig, regardless of what the email says, and if we add too much information then most users won't read any of it. This may be a small challenge.

comment:24 in reply to:  21 Changed 5 years ago by isis

Resolution: fixed
Status: needs_reviewclosed

Replying to sysrqb:

nice job, isis.


Thanks for the review!

I really think those should be scrubbed except for when safe logging is disabled, unless there's a good reason?


Safelogging is automagic now! :D (see #9199)

  • lib/bridgedb/Dist.py:419 - need to scrub email address
  • lib/bridgedb/Dist.py:426 - need to scrub email address
  • lib/bridgedb/Dist.py:428 - need to scrub email address
  • lib/bridgedb/Dist.py:433 - need to scrub email address


Automagic, as mentioned!

  • lib/bridgedb/bridgerequest.py:29 - s/bridges/bridges'/s


Thanks, fixed!

  • Bridges.isBlocked() is buggy. It returns true if any of a bridges ipaddr:port pair are not known to be blocked in the country. 1) When the filter returns true, we don't know which pair, 2) we should somehow mark the bridge as unhealthy and only return it if there aren't other options


Yeah, none of the blocking check code currently works. I believe there is a ticket for this...

  • lib/bridgedb/email/request.py - missing file header?


Doh. Added!

  • determineBridgeRequestOptions(): the presence of "get" defines an email as valid? I don't have a significantly better answer except that maybe only set it when a valid command is found? Not much is gained by this, though.


Yeah... so I mostly added that because I assumed that other subclasses of bridgedb.bridgerequest.BridgeRequest would need a way to check that the request was valid. Because get was the only thing common to all request strings, it was what I chose to determine if the person/robot was able to follow basic instruction... not really necessary, and if it turns out to be a problem then we can just automatically set isValid(True).

  • EmailBridgeRequest:withoutBlockInCountry(): Should we support multiple country codes per line? space and/or comma separated?

But why would you be traveling between multiple countries which block Tor bridges so fast that you need bridges which support both countries?

  • Should our response inform the user that we only used the last transport specified in their email request?


Perhaps. But if they were doing things like what's in the test_createResponseBody_bridges_obfsobfswebzipv6() unittest, then they are probably trying to mess with us somehow. That, or get multiple bridge types simultaneously. If someone wants to wreck theirs and the bridges' anonymity by probing the internet with every PT type on the wiki everytime they fire up tor or TBB, then they can slowly accumulate the collection of different PTs they need to do it over the course of a day or so.

I guess what I mean is: "I'd rather not help/inform/enable users who appear to be somewhat-abusing BridgeDB in to destroy their anonymity and burn bridges as fast as possible."

Also, in general, I'm for less wall-of-text emails, as someone who currently can't handle reading/answering the amount of email they receive.

  • determineBridgeRequestOptions(): There's no harm in setting request.isValid(True) and request.wantsKey(True) prior to raises the exception, but is there a reason for doing this?


Another somewhat awkward API design choice in this case. I assumed that normally, a distributor would want to set wantsKey(True), and then include the keys with whatever response it gives later. However, since we don't currently support PGP/MIME and/or MIME/multipart, we can't attach the key, and it has to occupy the entire body of the email (ugh, gross).

Later, if someone hacks in MIME support, the EmailRequestedKey exception should be rather unnecessary, and we could just tack the key on as an attachment to whatever else we're sending.

I'm not sure if any other distributors in the future will want a wantsKey() method.

  • lib/bridgedb/email/server.py:183 - Do we want to unconditionally log the client's email address?
  • lib/bridgedb/email/server.py:636 - unconditionally logging client email address


Automagick magick! :)

  • lib/bridgedb/email/server.py:464 - s/param/var/
  • MailMessage:getClientAddress() - Missing docstring
  • MailMessage:getRecipient() - incoming - DOCDOC, and s/param/var/


Fixed, thanks again.

  • While the email server is getting an upgrade, should we also bump it to use the ESMTPFactory? We won't see any benefit from this, but I wonder if there's a reason for us to continue using SMTP.


Hmm. Not sure? I thought that no mail servers actually support full ESMTP, and there's risk with not being able to talk to them, but we should only be talking to the local Postfix, IIRC.

Though, did you have a specific extension in mind that would help with something?

  • lib/bridgedb/email/server.py:316,328,332 - trailing white space after read(), seek(), tell()
  • lib/bridgedb/email/server.py:732 - The schedule was originally going to be used to partition the hash-rings so that bridgedb can rotate through the bridges over a given period, (obviously) it's not doing this right now. If we had higher bridge churn then this would be more useful.


Yeah... I still keep wondering if we should re-enable that.

I ended up using a schedule for implementing the gimp-captcha timeouts in #11215 though, so they're still useful.

  • lib/bridgedb/strings.py:116 - s/the word the word/the word/ and s/tranlate/translate/


Robert Ransom gave me a patch for this one, and it's already merged. Thanks, both of you!

I'll respond to the rest of this tomorrow:

  • lib/bridgedb/email/templates.py:33

Is there a benefit to

        while not len(command) >= 25:

rather than

        while len(command) < 25:

?

  • template.gettext(strings.HOWTO_TBB[1]) % strings.EMAIL_SPRINTF[\"HOWTO_TBB1\"] - adds an extra space between the words. "the %s Tor"

comment:25 Changed 5 years ago by isis

Resolution: fixed
Status: closedreopened

This appears not to be fixed.

comment:26 Changed 4 years ago by isis

Keywords: bridgedb-ui added

comment:27 Changed 4 years ago by isis

Resolution: fixed
Status: reopenedclosed

After switching to python-gnupg in #10385, this is working again. I just received the following email from BridgeDB after deploying 0.3.0:

From: bridges+de@torproject.org
To: isis@riseup.net
Cc:
Bcc:
Subject: Re: (no subject)
Reply-To:
In-Reply-To: <30168958158219d97dfcba15c42b1634@riseup.net>
Delivered-To: <isis@patternsinthevoid.net>


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

Hey, isis! Welcome to BridgeDB!



COMMANDs: (combine COMMANDs to specify multiple options simultaneously)
  get bridges            Request vanilla bridges.
  get transport [TYPE]   Request a Pluggable Transport by TYPE.
  get help               Displays this message.
  get key                Get a copy of BridgeDB's public GnuPG key.
  get ipv6               Request IPv6 bridges.

Currently supported transport TYPEs:
  obfs2
  obfs3
  obfs4
  scramblesuit
  fte


BridgeDB can provide bridges with several types of Pluggable Transports[0],
which can help obfuscate your connections to the Tor Network, making it more
difficult for anyone watching your internet traffic to determine that you are
using Tor.

Some bridges with IPv6 addresses are also available, though some Pluggable
Transports aren't IPv6 compatible.

Additionally, BridgeDB has plenty of plain-ol'-vanilla bridges - without any
Pluggable Transports - which maybe doesn't sound as cool, but they can still
help to circumvent internet censorship in many cases.

[0]: https://www.torproject.org/docs/pluggable-transports.html

 --
 <3 BridgeDB
______________________________________________________________________
Public Keys: https://bridges.torproject.org/keys
This email was generated with rainbows, unicorns, and sparkles
for isis@riseup.net on Saturday, 21 March, 2015 at 03:28:48.
-----BEGIN PGP SIGNATURE-----

iQIcBAEBCgAGBQJVDOVwAAoJECG1VOlZOPTQFX8QAI3IF1Ce/77Q0WeI7M1v2d2e
ZJzLuxRRjQO0TiWUrRouOA3+gXTlhVs+bqCDxf52POd7OOWj6ojBptesf0gy+6X6
vw647fD/cJFKxqEkU34aV1s3SK991G30oGMMKguIDlw3jdtyNSemqw9rrYP33JHP
/G/5SLGLYnuZgek0WntOGcikCqikECxRpD7//MZPH0MoqkgPSh8MUKTAWLYJ0g3+
xFq7WMIfVMnyaR2UOq1uEYpfJ+Pli/TeYphvPtmeTcrLmFXDtm/0rtjxgCDyX67Y
0QEXAeyMJWDE2gTD0P3Ur+IVTZanIZsXyyGrmiggOIPo0JakDUnDUjAaKaBwDlAn
bMJ/t+5Bnl8NAUahDOltThCLE9432P4vyRrvnZPYE+55Bc5AX078dUNxoaWD74DH
xCKf84VFVIHzK9wyCPFdmxtsi0UUi7rc8kr0n1Rnwe4XDfb3EDSsNZyGLNNDOjsM
Ouxjnl0KhuVThPR/xlnb3Zic7BsRHoZhU4eBn/k6Ex+BkrNAC4QCWqY8AOgHXQXk
5HMjQzQsJs/GrM60OK05+3yjVIFaM/a7d1RTAutt07uR9JeV3fj2TgA1s7/xjTOj
HkxGtVpOq+WfwuWC3hXrrGcdxRXEkj4VDzd6p2Wu16+jcsX6zuhLgLcgYBb77fgC
PwRwpUowl8FDAZL7U159
=BEzx
-----END PGP SIGNATURE-----

comment:28 Changed 10 months ago by traumschule

Parent ID: #3893
Severity: Blocker
Note: See TracTickets for help on using tickets.