def handshake(self, circuit): """This function is invoked after a circuit was established. The server generates a time-lock puzzle and sends it to the client. The client does nothing during the handshake.""" log.debug("Entering handshake().") if self.circuit == None: self.circuit = circuit # Only the server is generating and transmitting a puzzle. if self.weAreServer: # Generate master key and derive client -and server key. masterKey = mycrypto.strong_random(const.MASTER_KEY_SIZE) self._deriveSecrets(masterKey) # Append random padding to obfuscate length and transmit blurb. padding = mycrypto.weak_random(random.randint(0, \ const.MAX_PADDING_LENGTH)) log.debug("Sending puzzle with %d-byte of padding." % len(padding)) puzzle, nonce = timelock.encryptPuzzle( \ timelock.generateRawPuzzle(masterKey)) circuit.downstream.write(nonce + puzzle + padding) log.debug("Switching to state ST_WAIT_FOR_TICKET.") self.state = const.ST_WAIT_FOR_TICKET # Send a session ticket to the server (if we have one). elif self.weAreClient: stop = False try: with open(const.DATA_DIRECTORY + const.TICKET_FILE, "rb") as fd: masterKey = fd.read(const.MASTER_KEY_SIZE) ticket = fd.read(const.TICKET_LENGTH) fd.close() except IOError as e: log.error("Could not read session ticket from \"%s\"." % \ (const.DATA_DIRECTORY + const.TICKET_FILE)) stop = True if not stop: log.debug("Trying to redeem session ticket: 0x%s..." % \ ticket.encode('hex')[:10]) self._deriveSecrets(masterKey) padding = mycrypto.weak_random(random.randint(0, \ const.MAX_PADDING_LENGTH)) circuit.downstream.write(ticket + padding) self.redeemedTicket = True # Now start transmitting cover traffic. self.stopCoverTraffic = False reactor.callLater(0, self._sendCoverTraffic, circuit)
def handshake(self, circuit): """This function is invoked after a circuit was established. The server generates a time-lock puzzle and sends it to the client. The client does nothing during the handshake.""" log.debug("Entering handshake().") if self.circuit == None: self.circuit = circuit # Only the server is generating and transmitting a puzzle. if self.weAreServer: # Generate master key and derive client -and server key. masterKey = mycrypto.strong_random(const.MASTER_KEY_SIZE) self._deriveSecrets(masterKey) # Append random padding to obfuscate length and transmit blurb. padding = mycrypto.weak_random(random.randint(0, const.MAX_PADDING_LENGTH)) log.debug("Sending puzzle with %d-byte of padding." % len(padding)) puzzle, nonce = timelock.encryptPuzzle(timelock.generateRawPuzzle(masterKey)) circuit.downstream.write(nonce + puzzle + padding) log.debug("Switching to state ST_WAIT_FOR_TICKET.") self.state = const.ST_WAIT_FOR_TICKET # Send a session ticket to the server (if we have one). elif self.weAreClient: stop = False try: with open(const.DATA_DIRECTORY + const.TICKET_FILE, "rb") as fd: masterKey = fd.read(const.MASTER_KEY_SIZE) ticket = fd.read(const.TICKET_LENGTH) fd.close() except IOError as e: log.error('Could not read session ticket from "%s".' % (const.DATA_DIRECTORY + const.TICKET_FILE)) stop = True if not stop: log.debug("Trying to redeem session ticket: 0x%s..." % ticket.encode("hex")[:10]) self._deriveSecrets(masterKey) padding = mycrypto.weak_random(random.randint(0, const.MAX_PADDING_LENGTH)) circuit.downstream.write(ticket + padding) self.redeemedTicket = True # Now start transmitting cover traffic. self.stopCoverTraffic = False reactor.callLater(0, self._sendCoverTraffic, circuit)
def decryptedPuzzleCallback(self, masterKey): """This method is invoked as soon as the puzzle is unlocked. The argument `masterKey' is the content of the unlocked puzzle.""" log.debug("Callback invoked after solved puzzle.") # Sanity check to verify that we solved a real puzzle. if not const.MASTER_KEY_PREFIX in masterKey: log.critical("No MASTER_KEY_PREFIX in puzzle. What did we just " "solve?") return masterKey = masterKey[len(const.MASTER_KEY_PREFIX) :] assert len(masterKey) == const.MASTER_KEY_SIZE self._deriveSecrets(masterKey) log.debug("Stopping cover traffic generation.") self.stopCoverTraffic = True # Send bridge randomness || magic value. log.debug("Sending magic value to server.") assert self.circuit self.circuit.downstream.write( mycrypto.weak_random(random.randint(0, const.MAX_PADDING_LENGTH)) + self.sendMagic ) log.debug("Switching to state ST_WAIT_FOR_MAGIC.") self.state = const.ST_WAIT_FOR_MAGIC
def decryptedPuzzleCallback(self, masterKey): """This method is invoked as soon as the puzzle is unlocked. The argument `masterKey' is the content of the unlocked puzzle.""" log.debug("Callback invoked after solved puzzle.") # Sanity check to verify that we solved a real puzzle. if not const.MASTER_KEY_PREFIX in masterKey: log.critical("No MASTER_KEY_PREFIX in puzzle. What did we just " \ "solve?") return masterKey = masterKey[len(const.MASTER_KEY_PREFIX):] assert len(masterKey) == const.MASTER_KEY_SIZE self._deriveSecrets(masterKey) log.debug("Stopping cover traffic generation.") self.stopCoverTraffic = True # Send bridge randomness || magic value. log.debug("Sending magic value to server.") assert self.circuit self.circuit.downstream.write( mycrypto.weak_random(random.randint(0, const.MAX_PADDING_LENGTH)) + self.sendMagic) log.debug("Switching to state ST_WAIT_FOR_MAGIC.") self.state = const.ST_WAIT_FOR_MAGIC
def _sendMagicValue(self, circuit, magic): """Sends the given `magic' to the remote machine using `circuit'. Before that, the cover traffic generator is stopped.""" log.debug("Stopping cover traffic generation.") self.stopCoverTraffic = True # FIXME - Use packet morpher oracle for padding. circuit.downstream.write(mycrypto.weak_random(random.randint(0, const.MAX_PADDING_LENGTH)) + magic)
def _sendMagicValue(self, circuit, magic): """Sends the given `magic' to the remote machine using `circuit'. Before that, the cover traffic generator is stopped.""" log.debug("Stopping cover traffic generation.") self.stopCoverTraffic = True # FIXME - Use packet morpher oracle for padding. circuit.downstream.write(mycrypto.weak_random(random.randint(0, \ const.MAX_PADDING_LENGTH)) + magic)
def _sendCoverTraffic(self, circuit): """Send random cover traffic using `circuit'. This is done to make DPI boxes believe that we are communicating when in fact the client is busy solving the puzzle.""" if self.stopCoverTraffic == True: return coverTraffic = mycrypto.weak_random(self.pktMorpher.randomSample()) log.debug("Sending %d bytes of cover traffic." % len(coverTraffic)) circuit.downstream.write(coverTraffic) # When should we send the next chunk of bytes? if random.random() > 0.3: delay = self.iatMorpher.randomSample() else: delay = random.random() * 5 reactor.callLater(delay, self._sendCoverTraffic, circuit)