diff --git a/ugly_hacks/fbx/known_services.py b/ugly_hacks/fbx/known_services.py new file mode 100644 index 000000000..6c2725aeb --- /dev/null +++ b/ugly_hacks/fbx/known_services.py @@ -0,0 +1 @@ +{ "plinth": { "james": [ "http://192.168.0.12:8080" ], }, } diff --git a/ugly_hacks/fbx/serving_to.py b/ugly_hacks/fbx/serving_to.py new file mode 100644 index 000000000..a8130ae57 --- /dev/null +++ b/ugly_hacks/fbx/serving_to.py @@ -0,0 +1,2 @@ +{ "james": { "wiki": "wiki" }, + "ian": { "web": "plinth" }, } diff --git a/ugly_hacks/fbx/serving_what.py b/ugly_hacks/fbx/serving_what.py new file mode 100644 index 000000000..e2b428d3f --- /dev/null +++ b/ugly_hacks/fbx/serving_what.py @@ -0,0 +1,2 @@ +{ "plinth": "http://192.168.0.13:8080", + "wiki": "http://192.168.0.13/wiki", } diff --git a/ugly_hacks/fbx/settings.py b/ugly_hacks/fbx/settings.py new file mode 100644 index 000000000..56491a2d6 --- /dev/null +++ b/ugly_hacks/fbx/settings.py @@ -0,0 +1,5 @@ +{ "me": "nick", + "socket_port": 8080, + "max_hops": 3, + "proxy_list": ("tor") + } diff --git a/ugly_hacks/santiago.html b/ugly_hacks/santiago.html new file mode 100644 index 000000000..e5453692a --- /dev/null +++ b/ugly_hacks/santiago.html @@ -0,0 +1,625 @@ + + + + + + +Santiago + + + +
+

Santiago

+

Less Discoverable Discovery?

+ + +
+

Disclaimer

+

The following is an ugly hack. Beware!

+
+
+

Santiago's Map

+

Santiago manages service discovery between FreedomBoxen, coordinating +connections between arbitrary servers and services. In essence, A requests a +service from B, B replies with the service's location, and A uses that location +for the service.

+
    +
  1. A sends a signed (and encrypted?) message to B's Santiago, requesting +information, in the form of:
      +
    • Will X do Y for me?
    • +
    • Optional: Reply to my Santiago at Z.
    • +
    +
  2. +
  3. If B does not recognize A or does not trust A, it stays silent.
  4. +
  5. If B recognizes and trusts A, it replies with a signed (and encrypted?) +message to A's Santiago, giving the location of A's usable service. If no +service is available, it replies with a rejection.
  6. +
+

In a nutshell, that's the important part. There are additional details to +manage, but they're implied and built on the system above.

+
+

Our Cheats

+

Right now, we're cheating. We start by pairing boxes, exchanging both +box-specific PGP keys and Tor Hidden Service IDs. This allows boxes to trust +and communicate with one another, regardless of any adverserial interference. +Or, rather, any adverserial interference will be obvious and ignorable.

+
+
+

Message Exchange

+

The Santiago service is running on B, waiting for connections. When it +receives a request message, that message must be signed by a known and trusted +party. If it is acceptably signed (with a known, and valid ID), B will +reply to A's Santiago with an acceptably signed message.

+

The contents of the request message are as follows:

+
    +
  • I am requesting service X.
  • +
  • I am requesting that the service be performed by Y.
  • +
  • Optional: Reply to this message at Z.
  • +
+

The message is signed by A, and optionally encrypted (if the message is +proxied, it must contain a "To" header). If A includes a location stanza, +B MUST respect that location in its response and update that location for +its Santiago service from A going forward.

+

In this document, I elide the Santiago acknowledgements (because they add a lot +of unnecessary noise - we can assume communication failures are failures of +acknolwedgement receipt). But, after each message that needs a response, an +acceptably signed acknowledgement message is returned. Otherwise the sender +preferentially moves on to the recipient's next Santiago address after a +sufficient amount of time has passed. An example of this communication, with +these details specified, follows:

+ +++ + + + + + + + + + +
B -> A:I'll gladly serve X for you, at Z, my good fellow.
A -> B:(No response)
B -> A:(using a different Santiago address) I'm serving X for A at Z.
A -> B:(Acknowledgment)
+
+
+

Storing Service Data and Network Structure

+

How are these data stored, to prevent both A and B from having to dance the +Santiago for each and every request?

+

Each node contains two dictionaries/hash-tables listing (1) what they serve and +who they serve it to, and (2) what services they use, who from, and where that +service is located.

+
+

What I Host and Serve

+

I offer these services to others.

+

These data are stored as pair of dictionaries:

+
    +
  • The GPG ID to Service dictionary. This lists which service each user is +authorized for:

    +
    +0x0928: [ "proxy": "proxy", "wiki": "wiki",
    +          "drinking buddy": "drinking buddy" ]
    +0x7747: [ "wiki": "wiki", "proxy": "restricted_proxy" ]
    +
    +
  • +
  • The Service to Location dictionary. This lists the locations each service +runs on:

    +
    +"wiki": [ 192.168.1.1, "superduperwiki.onion" ]
    +"proxy": [ 8.8.8.8 ]
    +"restricted_proxy": [ 4.4.4.4 ]
    +
    +
  • +
+
+
+

Others' Services I Know Of

+

I consume these services, they are offered by others.

+

These data are stored as a triple-key dictionary, with the following mappings:

+
+Service X: { GPG ID1: [ location, location, location ],
+             GPG ID2: [ location, location ], }
+Service Y: { GPG ID2: [ location, location, location ],
+             GPG ID3: [ location, location ], }
+
+

This allows fast lookup from the service desired to the users that host the +service, to the actual locations that service is offered. This allows the user +to quickly decide which service provider to use and to try all locations +controlled by that service provider very quickly and easily.

+
+
+
+

Data Use

+ +++ + + + +
TODO:Revise to reduce communication to logical minimum number of connections, +exchanges, and communications.
+

When A is connecting to B's service, it will attempt to connect to that +service, which B will validate before permitting the connection. If the service +is non-responsive, A will query B for the service. If B is generally +non-responsive, A will move on to C. A will ask C for the service. If +C cannot provide the service, A will ask C to request the service from +B. If C can reach B and B authorizes A, B will respond +affirmatively to A with the service's location.

+ +++ + + + + + + + + + + + + + + + + + + + + + +
A -> B:(Connecting to Service!)
B:(Validating Service and rejecting for some reason, e.x., A hasn't been +reauthorized for this service recently enough, and because it's Wednesday.)
B -> A:(No response)
A -> B:Will you serve X?
B -> A:(No response, A can't reach B's Santiago.)
A -> C:Will you serve X?
C -> A:No!
A -> C:Will B serve X?
C -> B:Will you serve X for A?
B -> A:Hey, buddy, here's X!
+
+
+

Proxied service requesting

+
+

The Simple Case

+

I'm looking for somebody to provide a service, X.

+

A sends a request to C, and C doesn't respond. A requests the +service from B and B NBKs. A requests that B proxy my request +to C, in case B can reach C. C replies directly to A, and +we begin communicating on that service:

+ +++ + + + + + + + + + + + + + + + +
A -> C:Will you serve X?
C -> A:(No response)
A -> B:Will you serve X?
B -> A:No!
A -> B:Will C serve X?
B -> C:Will you serve X for A?
C -> A:Hey, buddy, here's X! Let's go out for beer later.
+
+
+

More Complicated Cases

+

I know D offers a service, X, but I can't get in touch with it.

+

A requests X from D, and D never responds. A requests that B find +D. B doesn't know D and forwards the request to a friend C. C knows +D and sends the message along. D tries to respond directly to A, but +can't, so it sends replies back up the chain.

+ +++ + + + + + + + + + + + + + + + + + + + + + +
A -> D:Will you serve X?
D -> A:(No response)
A -> B:Will D serve X for me?
B -> C:Will D serve X for A?
C -> D:Will you serve X for A?
D -> A:Hey, buddy, here's X!
A -> D:(No response)
D -> C:I'm serving X for A.
C -> B:D's serving X for A.
B -> A:D's serving X for you.
+

Each message is signed, but only the first message (A's message) is inviolable. +Each client then passes the message, stripping off intermediary signatures, and +then signing the message for each of its friends.

+

A message looks like:

+
+---- A's Signed Message Starts Here ----
+    To: D's GPG key.
+    ---- D's Encrypted Message Starts Here ----
+        Hey *D*, will you serve *X* for me?
+        Please reply to 5.onion.
+    ---- D's Encrypted Message Ends Here ----
+---- A's Signed Message Ends Here ----
+
+

A forwarded message, from B to C, looks like:

+
+---- B's Signed Message Starts Here ----
+---- A's Signed Message Starts Here ----
+    To: D's GPG key.
+    ---- D's Encrypted Message Starts Here ----
+        Hey *D*, will you serve *X* for me?
+        Please reply to 5.onion.
+    ---- D's Encrypted Message Ends Here ----
+---- A's Signed Message Ends Here ----
+---- B's Signed Message Ends Here ----
+
+

When forwarded over again, from C to D, it looks like:

+
+---- C's Signed Message Starts Here ----
+---- A's Signed Message Starts Here ----
+    To: D's GPG key.
+    ---- D's Encrypted Message Starts Here ----
+        Hey *D*, will you serve *X* for me?
+        Please reply to 5.onion.
+    ---- D's Encrypted Message Ends Here ----
+---- A's Signed Message Ends Here ----
+---- C's Signed Message Ends Here ----
+
+
+
+
+
+

Unit Tests

+

These buggers are neat. We need to fake known and pre-determined communications +to verify the servers and clients are correctly and independently responding +according to the protocol.

+
+
+

Attacks

+

Of course this is vulnerable. It's on the internet, isn't it?

+
+

Discovery

+

A discovered box is shut down or compromised. It can lie to its requestors and +not perform its functions. It can also allow connections and expose +connecting clients. If the client is compromisable (within reach), it also can +be compromised. We can try, but every service that isn't run directly over Tor +identifies one user to another.

+
+
+

Deception

+

This is probably the largest worry, where B fakes A's responses.

+
+
+
+

Mitigations

+

We gain a lot by relying on the WOT, and only direct links in the WOT. We also +gain a lot by requiring every communication to be signed (and maximally +encrypted).

+

Once a key is compromised, we're vulnerable, but to what exactly? What attacks +can an adversary who's compromised a secret key perform? As long as you don't +have any publicly identifiable (or public-facing) services in your Santiago, +then not much. If you're identifiable by your Santiago, and you've permitted +the attacker to see an identifiable service (including your Santiago instances), +that service and all co-located services could be shut down. If the service +identifies you (and not just your box), you're vulnerable. An attacker will +shortly identify all the services you've given it access to.

+

An attacker can try to identify your friends, though will have trouble if you +send your proxied requests with non-public methods, or you don't proxy at all.

+
+
+

Outstanding Questions

+
+
+ + diff --git a/ugly_hacks/santiago.py b/ugly_hacks/santiago.py new file mode 100644 index 000000000..44fbba062 --- /dev/null +++ b/ugly_hacks/santiago.py @@ -0,0 +1,260 @@ +#! /usr/bin/python # -*- fill-column: 80 -*- + +"""The Santiago service. + +It runs on a port (the Port of Santiago) with sending and receiving services +(the Santiagi) with a simple authentication mechanism ("the Dance of the +Santiagi" or "Santiago's Dance"). The idea is that systems can share +identification information without ever revealing their location (where in the +world is Santiago?). + +This *is* signed identity statements, just on a +service-by-service-for-individual-people level. Granularity, bitches! + +hackyhackyhacky hacks, but the right structure sans details (a tracer-bullet +structure, see "The Pragmatic Programmer", Hunt & Thomas, 2000, pg. 48). If +you have a copy of TPP around, also see pgs: 111 120 161 186 192 238 248 258. + +This file is distributed under the GPL, version 3 or later, at your discretion. + + Santiago, he + smiles like a Buddah, 'neath + his red sombrero. + +""" + +# If you're crazy like me, you can turn this script into its own +# documentation by running:: +# +# python pylit.py -c backup - | rst2html > backup.html +# +# You'll need PyLit_ and ReStructuredText_ for this to work correctly. +# +# .. _pylit: http://pylit.berlios.de/ +# .. _restructuredtext: http://docutils.sourceforge.net/rst.html +# +# .. contents:: + +import cherrypy +import os +from simplejson import JSONEncoder + + +DEBUG = 0 +encoder = JSONEncoder() + + +if DEBUG: + for x in range(0, 3): + for y in range(0, 7): + print "WARNING", + print "" + print "You're in DEBUG MODE! You are surprisingly vulnerable! Raar!" + +def fix_old_cherrypy(): + """Make Lenny's CherryPy forward-compatible.""" + + for x in range(0,3): + for y in range(0, 7): + print "WARNING", + print "" + + print("You're using an old CherryPy version! We're faking it!") + print("Expect the unexpected! Raar!") + + def jsonify_tool_callback(*args, **kwargs): + response = cherrypy.response + response.headers['Content-Type'] = 'application/json' + response.body = encoder.iterencode(response.body) + + cherrypy.tools.json_out = cherrypy.Tool('before_finalize', jsonify_tool_callback, priority=30) + +if cherrypy.__version__ < "3.2": + fix_old_cherrypy() + + +class Santiago(object): + """Santiago's base class, containing listener and sender defaults.""" + + DEFAULT_RESPONSE = """\ +Use it right. + +

Nice try, now try again with a request like:

+

http://localhost:8080/santiago/(gpgId)/(service)/(server)

+ +
+
gpgId
james, ian
+
service
wiki, web
+
server
nick
+
+ +

This'll get you good results:

+ + http://localhost:8080/santiago/james/wiki/nick + +

See the serving_to, serving_what, and + me variables.

+ +""" + + def __init__(self, instance): + self.load_instance(instance) + + # TODO Does the listener need to know what services others are running? + # TODO Does the sender need to know what services I'm running? + self.load_serving_to() + self.load_serving_what() + self.load_known_services() + + def am_i(self, server): + return str(self.me) == str(server) + + def load_instance(self, instance): + """Load instance settings from a file. + + A terrible, unforgivable hack. But it's a pretty effective prototype, + allowing us to add and remove attributes really easily. + + """ + settings = run_file("%s%ssettings.py" % (instance, os.sep)) + for key, value in settings.iteritems(): + setattr(self, key, value) + + self.instance = instance + + # FIXME I need to handle instance vs controller correctly. this is the + # wrong place. + # + # I'm putting instance data into the controller, which is nutters. Too + # tired to fix tonight, though. + # + # These data should probably be loaded in the server and listener, + # respectively, but I don't know whether one needs to know about the other's + # services. + + def load_serving_to(self): + """Who can see which of my services?""" + + self.serving_to = run_file("%s%sserving_to.py" % (self.instance, + os.sep)) + def load_serving_what(self): + """What location does that service translate to?""" + + self.serving_what = run_file("%s%sserving_what.py" % (self.instance, + os.sep)) + def load_known_services(self): + """What services do I know of?""" + + self.known_services = run_file("%s%sknown_services.py" % (self.instance, + os.sep)) + @cherrypy.expose + def index(self): + """Do nothing, unless we're debugging.""" + + if DEBUG: + return DEFAULT_RESPONSE + + +class SantiagoListener(Santiago): + """Listens for requests on the santiago port.""" + + def __init__(self, instance, port=8080): + super(SantiagoListener, self).__init__(instance=instance) + + self.socket_port = port + + @cherrypy.expose + @cherrypy.tools.json_out() + def santiago(self, from_id=None, to_id=None, service=None, hops=0): #, new_santiago_id=""): + """Handles an incoming request. + + FIXME: handle new Santiago ID list. + + """ + message = { "requester": from_id, + "server": to_id, + "service": service, + "hops": hops, } + #"santiago": new_santiago_id } + + # FIXME: this is being dumb and not working how I expect it. later. + # if not self.i_am(message["server"]): + # return self.proxy_santiago_request(message) + + try: + return self.serving_what[ + self.serving_to[message["requester"]][message["service"]]] + except KeyError: + # TODO: handle responses. should a fail just timeout? + return None + + @cherrypy.tools.json_out() + def proxy_santiago_request(self, message, hops=3): + """Passes a Santiago request off to another known host. + + We're trying to search the friend list for the target server. + + """ + # handle crap input. + if (hops > self.max_hops): + hops = self.max_hops + if (hops < 1): + return + + # this counts as a hop. + hops -= 1 + + # TODO pull this off, another day. + return str(message) + + +class SantiagoSender(Santiago): + """Sendss the Santiago request to a Santiago service.""" + + def __init__(self, instance, proxy): + super(SantiagoSender, self).__init__(instance=instance) + + self.proxy = proxy if proxy in self.proxy_list else None + + def request(self, destination, resource): + """Sends a request for a resource to a known Santiago. + + The request MUST include the following: + + - A service. + - A server. + + The request MAY include the following: + + - Other Santiago listeners. + - An action. + + post: + not (__return__["destination"] is None) + not (__return__["service"] is None) + # TODO my request is signed with my GPG key, recipient encrypted. + + """ + pass # TODO: do. + + +# utility functions +# ================= + +def run_file(filename): + """Returns the result of executing the Python file. Terrible idea. Effective + hack. + + TODO: Replace this with James's database stuff! + + If you try to use this in the wild, I will hunt you down and cut you. + + """ + with open(filename) as in_file: + return eval("".join(in_file.readlines())) + + +if __name__ == "__main__": + santiago = SantiagoListener("fbx") + + cherrypy.quickstart(santiago) diff --git a/ugly_hacks/santiago.rst b/ugly_hacks/santiago.rst index 35bd165d4..5ea83a2a0 100644 --- a/ugly_hacks/santiago.rst +++ b/ugly_hacks/santiago.rst @@ -24,31 +24,56 @@ for the service. information, in the form of: - Will *X* do *Y* for me? - - (By the way I'm at *Z*) + - Optional: Reply to my Santiago at *Z*. + #. If B does not recognize A or does not trust A, it stays silent. + #. If B recognizes and trusts A, it replies with a signed (and encrypted?) message to A's Santiago, giving the location of A's usable service. If no - service is available, it reples with a rejection. + service is available, it replies with a rejection. In a nutshell, that's the important part. There are additional details to -manage, but they're implied and build on the system above. +manage, but they're implied and built on the system above. + +Our Cheats +---------- + +Right now, we're cheating. We start by pairing boxes, exchanging both +box-specific PGP keys and Tor Hidden Service IDs. This allows boxes to trust +and communicate with one another, regardless of any adverserial interference. +Or, rather, any adverserial interference will be obvious and ignorable. Message Exchange ---------------- -The Santiago service is running on ``B``, waiting for connections. When it +The Santiago service is running on *B*, waiting for connections. When it receives a request message, that message must be signed by a known and trusted -party. If it is acceptably signed (with a known, and valid ID), ``B`` will -reply to ``A``'s Santiago with an acceptably signed message. +party. If it is acceptably signed (with a known, and valid ID), *B* will +reply to *A*'s Santiago with an acceptably signed message. The contents of the request message are as follows: - I am requesting service *X*. - I am requesting that the service be performed by *Y*. -- Optional: I am located at *Z*. +- Optional: Reply to this message at *Z*. -The message is signed by ``A``, and optionally encrypted (if the message is -proxied, it must contain a "To" header). +The message is signed by *A*, and optionally encrypted (if the message is +proxied, it must contain a "To" header). If *A* includes a location stanza, +*B* MUST respect that location in its response and update that location for +its Santiago service from *A* going forward. + +In this document, I elide the Santiago acknowledgements (because they add a lot +of unnecessary noise - we can assume communication failures are failures of +acknolwedgement receipt). But, after each message that needs a response, an +acceptably signed acknowledgement message is returned. Otherwise the sender +preferentially moves on to the recipient's next Santiago address after a +sufficient amount of time has passed. An example of this communication, with +these details specified, follows: + +:B -> A: I'll gladly serve *X* for you, at *Z*, my good fellow. +:A -> B: (No response) +:B -> A: *(using a different Santiago address)* I'm serving *X* for *A* at *Z*. +:A -> B: (Acknowledgment) Storing Service Data and Network Structure ------------------------------------------ @@ -88,8 +113,10 @@ I consume these services, they are offered by others. These data are stored as a triple-key dictionary, with the following mappings:: - Service -> [ GPG, ID ] - GPG ID -> [ Location, location, location ] + Service X: { GPG ID1: [ location, location, location ], + GPG ID2: [ location, location ], } + Service Y: { GPG ID2: [ location, location, location ], + GPG ID3: [ location, location ], } This allows fast lookup from the service desired to the users that host the service, to the actual locations that service is offered. This allows the user @@ -102,36 +129,111 @@ Data Use :TODO: Revise to reduce communication to logical minimum number of connections, exchanges, and communications. -When ``A`` is connecting to ``B``'s service, it will attempt to connect to that -service (and verify ``B``'s identity with a signed handshake before using the -service?). If the service is non-responsive, ``A`` will query ``B`` for the -service. If ``B`` is generally non-responsive, ``A`` will move on to ``C``. -``A`` will ask ``C`` for the service. If ``C`` cannot provide the service, -``A`` will ask ``C`` to request the service from ``B``. If ``C`` can reach -``B`` and ``B`` authorizes ``A``, ``B`` will respond affirmatively to ``A`` with -the service's location. +When *A* is connecting to *B*'s service, it will attempt to connect to that +service, which B will validate before permitting the connection. If the service +is non-responsive, *A* will query *B* for the service. If *B* is generally +non-responsive, *A* will move on to *C*. *A* will ask *C* for the service. If +*C* cannot provide the service, *A* will ask *C* to request the service from +*B*. If *C* can reach *B* and *B* authorizes *A*, *B* will respond +affirmatively to *A* with the service's location. -Our Cheats ----------- - -Right now, we're cheating. We start by pairing boxes, exchanging both -box-specific PGP keys and Tor Hidden Service IDs. This allows boxes to trust -and communicate with one another, regardless of any adverserial interference. -Or, rather, any adverserial interference will be obvious and ignorable. - -Next Steps -========== +:A -> B: (Connecting to Service!) +:B: (Validating Service and rejecting for some reason, e.x., A hasn't been + reauthorized for this service recently enough, and because it's Wednesday.) +:B -> A: (No response) +:A -> B: Will you serve X? +:B -> A: (No response, A can't reach B's Santiago.) +:A -> C: Will you serve X? +:C -> A: No! +:A -> C: Will B serve X? +:C -> B: Will you serve X for A? +:B -> A: Hey, buddy, here's X! Proxied service requesting -------------------------- -``Me`` sends a request to ``B``, and ``B`` doesn't respond. ``Me`` requests the -service from ``A`` and ``A`` NAKs. ``Me`` requests that ``A`` proxy my request -to ``B``, in case ``A`` can reach ``B``. ``B`` replies directly to ``Me``, and -we begin communicating on that service. +The Simple Case +~~~~~~~~~~~~~~~ -:TODO: What is the maximum number of hops that we can securely achieve from this - system while keeping intermedia hops secret? One? More? +I'm looking for somebody to provide a service, *X*. + +*A* sends a request to *C*, and *C* doesn't respond. *A* requests the +service from *B* and *B* NBKs. *A* requests that *B* proxy my request +to *C*, in case *B* can reach *C*. *C* replies directly to *A*, and +we begin communicating on that service: + +:A -> C: Will you serve X? +:C -> A: (No response) +:A -> B: Will you serve X? +:B -> A: No! +:A -> B: Will C serve X? +:B -> C: Will you serve X for A? +:C -> A: Hey, buddy, here's X! Let's go out for beer later. + +More Complicated Cases +~~~~~~~~~~~~~~~~~~~~~~ + +I know *D* offers a service, *X*, but I can't get in touch with it. + +*A* requests *X* from *D*, and *D* never responds. *A* requests that *B* find +*D*. *B* doesn't know *D* and forwards the request to a friend *C*. *C* knows +*D* and sends the message along. *D* tries to respond directly to *A*, but +can't, so it sends replies back up the chain. + +:A -> D: Will you serve X? +:D -> A: (No response) +:A -> B: Will D serve X for me? +:B -> C: Will D serve X for A? +:C -> D: Will you serve X for A? +:D -> A: Hey, buddy, here's X! +:A -> D: (No response) +:D -> C: I'm serving X for A. +:C -> B: D's serving X for A. +:B -> A: D's serving X for you. + +Each message is signed, but only the first message (A's message) is inviolable. +Each client then passes the message, stripping off intermediary signatures, and +then signing the message for each of its friends. + +A message looks like:: + + ---- A's Signed Message Starts Here ---- + To: D's GPG key. + ---- D's Encrypted Message Starts Here ---- + Hey *D*, will you serve *X* for me? + Please reply to 5.onion. + ---- D's Encrypted Message Ends Here ---- + ---- A's Signed Message Ends Here ---- + +A forwarded message, from B to C, looks like:: + + ---- B's Signed Message Starts Here ---- + ---- A's Signed Message Starts Here ---- + To: D's GPG key. + ---- D's Encrypted Message Starts Here ---- + Hey *D*, will you serve *X* for me? + Please reply to 5.onion. + ---- D's Encrypted Message Ends Here ---- + ---- A's Signed Message Ends Here ---- + ---- B's Signed Message Ends Here ---- + +When forwarded over again, from C to D, it looks like:: + + ---- C's Signed Message Starts Here ---- + ---- A's Signed Message Starts Here ---- + To: D's GPG key. + ---- D's Encrypted Message Starts Here ---- + Hey *D*, will you serve *X* for me? + Please reply to 5.onion. + ---- D's Encrypted Message Ends Here ---- + ---- A's Signed Message Ends Here ---- + ---- C's Signed Message Ends Here ---- + +Note that: + +- The original message is unchanged. +- Irrelevant signatures (intermediate links in the WOT) are stripped, hiding the + WOT's structure from friends. Unit Tests ========== @@ -157,4 +259,26 @@ identifies one user to another. Deception --------- -This is probably the largest worry, where B spoofs A's responses. +This is probably the largest worry, where B fakes A's responses. + +Mitigations +=========== + +We gain a lot by relying on the WOT, and only direct links in the WOT. We also +gain a lot by requiring every communication to be signed (and maximally +encrypted). + +Once a key is compromised, we're vulnerable, but to what exactly? What attacks +can an adversary who's compromised a secret key perform? As long as you don't +have any publicly identifiable (or public-facing) services in your Santiago, +then not much. If you're identifiable by your Santiago, and you've permitted +the attacker to see an identifiable service (including your Santiago instances), +that service and all co-located services could be shut down. If the service +identifies you (and not just your box), you're vulnerable. An attacker will +shortly identify all the services you've given it access to. + +An attacker can try to identify your friends, though will have trouble if you +send your proxied requests with non-public methods, or you don't proxy at all. + +Outstanding Questions +===================== diff --git a/ugly_hacks/setup.sh b/ugly_hacks/setup.sh new file mode 100644 index 000000000..4fac23e12 --- /dev/null +++ b/ugly_hacks/setup.sh @@ -0,0 +1,15 @@ +#! /bin/bash + +# A crappy, but effective setup script. +# +# This: +# +# - Installs Tor. +# - Adds a hidden service. +# - Sets up Santiago as a hidden service. +# +# This file is distributed under the GPL, version 3 or later, at your +# discretion. +# +# Also see the plugserverr scripts: setup/tor + diff --git a/ugly_hacks/test_santiago.py b/ugly_hacks/test_santiago.py new file mode 100644 index 000000000..9de53bd3b --- /dev/null +++ b/ugly_hacks/test_santiago.py @@ -0,0 +1,43 @@ +"""Making Santiago dance, in 4 parts: + +- Validating the initial request (playing B). +- Validating the initial response (playing A). + - Validating the silent response. + - Validating the rejection response. + - Validating the acceptance response. + - Validating the forwarded request (playing C). +- Validating the forwarded request (playing D, when C isn't the target). +- Validating the forwarded response. + - Validating the direct response (playing A). + - Validating the indirect response (playing C, B, and A). + +""" + +import unittest +import santiago +# TODO import some http client and validate my responses! + +class TestInitialRequest(unittest.TestCase): + """Testing the initial request. + + Does Santiago produce well-formed output when creating a service request? + + """ + def setup(self): + requester = santiago.SantiagoSender("initial_requester") + destination = santiago.SantiagoListener("initial_destination",8081) + + def test_valid_request(self): + """When requested, does Santiago send out an appropriate message?""" + + # TODO finish these and otehr tests + requester.request(destination="james", service="wiki") + +class TestInitialResponse(unittest.TestCase): + pass + +class TestForwardedRequest(unittest.TestCase): + pass + +class TestForwardedResponse(unittest.TestCase): + pass