commit d249c7ff695b0efdb4dd23a0a4bc6756024f7f19 Author: Arturo Filastò hellais@torproject.org Date: Wed Jul 11 12:37:23 2012 +0200
Implement censorship detection for fingerprint * Move part of b0wser to a ooni protocol --- ooni/protocols/b0wser.py | 104 ++++++++++++++++++++++++++++++++++++++++ oonib/b0wser.py | 118 ++++++++++++---------------------------------- 2 files changed, 134 insertions(+), 88 deletions(-)
diff --git a/ooni/protocols/b0wser.py b/ooni/protocols/b0wser.py new file mode 100644 index 0000000..ae6b002 --- /dev/null +++ b/ooni/protocols/b0wser.py @@ -0,0 +1,104 @@ +from ooni.utils import log + +class Mutator: + idx = 0 + step = 0 + + waiting = False + waiting_step = 0 + + def __init__(self, steps): + """ + @param steps: array of dicts containing as keys data and wait. Data is + the content of the ith packet to be sent and wait is how + much we should wait before mutating the packet of the + next step. + """ + self.steps = steps + + def _mutate(self, data, idx): + """ + Mutate the idx bytes by increasing it's value by one + + @param data: the data to be mutated. + + @param idx: what byte should be mutated. + """ + print "idx: %s, data: %s" % (idx, data) + ret = data[:idx] + ret += chr(ord(data[idx]) + 1) + ret += data[idx+1:] + return ret + + def state(self): + """ + Return the current mutation state. As in what bytes are being mutated. + + Returns a dict containg the packet index and the step number. + """ + return {'idx': self.idx, 'step': self.step} + + def next_mutation(self): + """ + Increases by one the mutation state. + + ex. (* is the mutation state, i.e. the byte to be mutated) + before [___*] [____] + step1 step2 + after [____] [*___] + + Should be called every time you need to proceed onto the next mutation. + It changes the internal state of the mutator to that of the next + mutatation. + + returns True if another mutation is available. + returns False if all the possible mutations have been done. + """ + if self.step > len(self.steps): + self.waiting = True + return False + + self.idx += 1 + current_idx = self.idx + current_step = self.step + current_data = self.steps[current_step]['data'] + data_to_receive = self.steps[current_step]['wait'] + + if self.waiting and self.waiting_step == data_to_receive: + log.debug("I am no longer waiting.") + self.waiting = False + self.waiting_step = 0 + self.idx = 0 + + elif self.waiting: + log.debug("Waiting some more.") + self.waiting_step += 1 + + elif current_idx >= len(current_data): + log.debug("Entering waiting mode.") + self.step += 1 + self.idx = 0 + self.waiting = True + log.debug("current index %s" % current_idx) + log.debug("current data %s" % len(current_data)) + return True + + def get_mutation(self, step): + """ + Returns the current packet to be sent to the wire. + If no mutation is necessary it will return the plain data. + Should be called when you are interested in obtaining the data to be + sent for the selected state. + + @param step: the current step you want the mutation for + + returns the mutated packet for the specified step. + """ + if step != self.step or self.waiting: + log.debug("I am not going to do anything :)") + return self.steps[step]['data'] + + data = self.steps[step]['data'] + print "Mutating %s with idx %s" % (data, self.idx) + return self._mutate(data, self.idx) + diff --git a/oonib/b0wser.py b/oonib/b0wser.py index 186c9a4..6da1997 100644 --- a/oonib/b0wser.py +++ b/oonib/b0wser.py @@ -1,100 +1,19 @@ from twisted.internet import protocol +from twisted.internet.error import ConnectionDone
-class Mutator: - idx = 0 - step = 0 - - waiting = False - waiting_step = 0 - - def __init__(self, steps): - self.steps = steps - - def _mutate(self, data, idx): - """ - Mutate the idx bytes by increasing it's value by one - - @param data: the data to be mutated. - - @param idx: what byte should be mutated. - """ - print "idx: %s, data: %s" % (idx, data) - ret = data[:idx] - ret += chr(ord(data[idx]) + 1) - ret += data[idx+1:] - return ret - - def next_mutation(self): - """ - Increases by one the mutation state. - - ex. (* is the mutation state, i.e. the byte to be mutated) - before [___*] [____] - step1 step2 - after [____] [*___] - - Should be called every time you need to proceed onto the next mutation. - It changes the internal state of the mutator to that of the next - mutatation. - - returns True if another mutation is available. - returns False if all the possible mutations have been done. - """ - if self.step > len(self.steps): - self.waiting = True - return False - - self.idx += 1 - current_idx = self.idx - current_step = self.step - current_data = self.steps[current_step]['data'] - data_to_receive = self.steps[current_step]['wait'] - - if self.waiting and self.waiting_step == data_to_receive: - print "I am no longer waiting..." - self.waiting = False - self.waiting_step = 0 - self.idx = 0 - - elif self.waiting: - print "Waiting some more..." - self.waiting_step += 1 - - elif current_idx >= len(current_data): - print "Entering waiting mode..." - self.step += 1 - self.idx = 0 - self.waiting = True - print "current index %s current data %s" % (current_idx, len(current_data)) - return True - - def get_mutation(self, state): - """ - Returns the current packet to be sent to the wire. - If no mutation is necessary it will return the plain data. - Should be called when you are interested in obtaining the data to be - sent for the selected state. - - @param step: the current step you want the mutation for - - returns the mutated packet for the specified step. - """ - if step != self.step or self.waiting: - print "I am not going to do anything :)" - return self.steps[step]['data'] - - data = self.steps[step]['data'] - print "Mutating %s with idx %s" % (data, self.idx) - return self._mutate(data, self.idx) +from ooni.plugoo import reports +from ooni.protocols.b0wser import Mutator
class B0wserProtocol(protocol.Protocol): steps = [{'data': "STEP1", 'wait': 4}, {'data': "STEP2", 'wait': 4}, {'data': "STEP3", 'wait': 4}] - mutator = None + state = 0 + total_states = len(steps) - 1 received_data = 0 + report = reports.Report('b0wser', 'b0wser.yamlooni')
def next_state(self): data = self.mutator.get_mutation(self.state) @@ -111,6 +30,28 @@ class B0wserProtocol(protocol.Protocol): print "Moving to next state %s" % self.state self.next_state()
+ def censorship_detected(self, report): + print "The connection was closed because of %s" % report['reason'] + print "I may have matched the censorship fingerprint" + print "State %s, Mutator %s" % (report['proto_state'], + report['mutator_state']) + self.report(report) + + + def connectionLost(self, reason): + report = {'reason': reason, 'proto_state': self.state, + 'mutator_state': self.mutator.state(), 'trigger': None} + + if self.state < self.total_states: + report['trigger'] = 'did not finish state walk' + self.censorship_detected(report) + + if reason.check(ConnectionDone): + print "Connection closed cleanly" + else: + report['trigger'] = 'unclean connection closure' + self.censorship_detected(report) + class B0wserServer(protocol.ServerFactory): protocol = B0wserProtocol mutations = {} @@ -122,7 +63,8 @@ class B0wserServer(protocol.ServerFactory): self.mutations[addr.host] = Mutator(p.steps) else: print "Moving on to next mutation" - self.mutations[addr.host].next_mutation() + if not self.mutations[addr.host].next_mutation(): + self.mutations.pop(addr.host) p.mutator = self.mutations[addr.host] return p
tor-commits@lists.torproject.org