Kaynağa Gözat

(Simulated) layered symmetric crypto of cells in a circuit

Ian Goldberg 4 yıl önce
ebeveyn
işleme
12ce56e2a9
2 değiştirilmiş dosya ile 75 ekleme ve 5 silme
  1. 14 1
      client.py
  2. 61 4
      relay.py

+ 14 - 1
client.py

@@ -7,6 +7,8 @@ import network
 import dirauth
 import relay
 
+import nacl.hash
+
 class CreatedHandler:
     """A handler for VanillaCreatedCircuitMsg cells."""
 
@@ -18,8 +20,17 @@ class CreatedHandler:
 
     def received_cell(self, circhandler, cell):
         secret = self.ntor.verify(cell.ntor_reply, self.onionkey, self.idkey)
+        enckey = nacl.hash.sha256(secret + b'upstream')
+        deckey = nacl.hash.sha256(secret + b'downstream')
+        circhandler.add_crypt_layer(enckey, deckey)
         circhandler.circuit_descs.append(self.expecteddesc)
-        print('client secret=',secret)
+
+        # Are we done building the circuit?
+        if len(circhandler.circuit_descs) == 3:
+            # Yes!
+            del circhandler.cell_dispatch_table[relay.VanillaCreatedCircuitMsg]
+            return
+
 
 
 class CellClient(relay.CellHandler):
@@ -268,6 +279,8 @@ if __name__ == '__main__':
     network.thenetwork.nextepoch()
 
     clients[0].cellhandler.new_circuit()
+    circhandler = clients[0].cellhandler.channels[clients[0].cellhandler.guardaddr].circuithandlers[2]
+    circhandler.send_cell(network.StringNetMsg("hello world"))
 
     # See what channels exist and do a consistency check
     for r in relays:

+ 61 - 4
relay.py

@@ -57,6 +57,7 @@ class VanillaCreatedCircuitMsg(RelayNetMsg):
 
 class CircuitCellMsg(RelayNetMsg):
     """Send a message tagged with a circuit id."""
+
     def __init__(self, circuitid, cell):
         self.circid = circuitid
         self.cell = cell
@@ -69,6 +70,23 @@ class CircuitCellMsg(RelayNetMsg):
         return 4 + self.cell.size()
 
 
+class EncryptedMsg(RelayNetMsg):
+    """Send a message encrypted with a symmetric key.  In this
+    implementation, the encryption is not really done.  A hash of the
+    key is stored with the message so that it can be checked at
+    decryption time."""
+
+    def __init__(self, key, msg):
+        self.keyhash = nacl.hash.sha256(key)
+        self.plaintext = msg
+
+    def decrypt(self, key):
+        keyhash = nacl.hash.sha256(key)
+        if keyhash != self.keyhash:
+            raise ValueError("EncryptedMsg key mismatch")
+        return self.plaintext
+
+
 class RelayFallbackTerminationError(Exception):
     """An exception raised when someone tries to terminate a fallback
     relay."""
@@ -133,28 +151,65 @@ class CircuitHandler:
     """A class for managing sending and receiving encrypted cells on a
     particular circuit."""
 
+    class NoCryptLayer:
+        def encrypt_msg(self, msg): return msg
+        def decrypt_msg(self, msg): return msg
+
+    class CryptLayer:
+        def __init__(self, enckey, deckey, next_layer):
+            self.enckey = enckey
+            self.deckey = deckey
+            self.next_layer = next_layer
+        def encrypt_msg(self, msg):
+            return self.next_layer.encrypt_msg(EncryptedMsg(self.enckey, msg))
+        def decrypt_msg(self, msg):
+            return self.next_layer.decrypt_msg(msg).decrypt(self.deckey)
+
     def __init__(self, channel, circid):
         self.channel = channel
         self.circid = circid
-        self.send_cell = self.channel_send_cell
         # The list of relay descriptors that form the circuit so far
         # (client side only)
         self.circuit_descs = []
         # The dispatch table is indexed by type, and the values are
         # objects with received_cell(circhandler, cell) methods.
         self.cell_dispatch_table = dict()
+        # The topmost crypt layer.  This is an object with
+        # encrypt_msg(msg) and decrypt_msg(msg) methods that returns the
+        # en/decrypted messages respectively.  Messages are encrypted
+        # starting with the last layer that was added (the keys for the
+        # furthest away relay in the circuit) and are decrypted starting
+        # with the first layer that was added (the keys for the guard).
+        self.crypt_layer = self.NoCryptLayer()
+
+    def send_cell(self, cell):
+        """Send a cell on this circuit, encrypting as needed."""
+        self.channel_send_cell(self.crypt_layer.encrypt_msg(cell))
 
     def channel_send_cell(self, cell):
-        """Send a cell on this circuit."""
+        """Send a cell on this circuit directly without encrypting it
+        first."""
         self.channel.send_msg(CircuitCellMsg(self.circid, cell))
 
     def received_cell(self, cell):
         """A cell has been received on this circuit.  Dispatch it
         according to its type."""
+        if isinstance(cell, EncryptedMsg):
+            cell = self.crypt_layer.decrypt_msg(cell)
+
         celltype = type(cell)
         if celltype in self.cell_dispatch_table:
             self.cell_dispatch_table[celltype].received_cell(self, cell)
 
+        print("CircuitHandler: %s received cell %s on circuit %d from %s" % (self.channel.cellhandler.myaddr, cell, self.circid, self.channel.peer.cellhandler.myaddr))
+
+    def add_crypt_layer(self, enckey, deckey):
+        """Add a processing layer to this CircuitHandler so that cells
+        we send will get encrypted with the first given key, and cells
+        we receive will be decrypted with the other given key."""
+        current_crypt_layer = self.crypt_layer
+        self.crypt_layer = self.CryptLayer(enckey, deckey, current_crypt_layer)
+
 
 class Channel(network.Connection):
     """A class representing a channel between a relay and either a
@@ -217,6 +272,7 @@ class Channel(network.Connection):
 
     def received(self, peeraddr, msg):
         """Callback when a message is received from the network."""
+        print('Channel: %s received %s from %s' % (self.cellhandler.myaddr, msg, peeraddr))
         self.cellhandler.perfstats.bytes_received += msg.size()
         if isinstance(msg, CircuitCellMsg):
             circid, cell = msg.circid, msg.cell
@@ -330,8 +386,9 @@ class CellRelay(CellHandler):
             reply, secret = NTor.reply(self.onionkey, self.idpubkey, \
                     msg.ntor_request, self.perfstats)
             # Set up the circuit to use the shared secret
-            # TODO
-            print('relay secret=', secret)
+            enckey = nacl.hash.sha256(secret + b'downstream')
+            deckey = nacl.hash.sha256(secret + b'upstream')
+            circhandler.add_crypt_layer(enckey, deckey)
             # Send the ntor reply
             self.send_msg(CircuitCellMsg(msg.circid, VanillaCreatedCircuitMsg(reply)), peeraddr)
         else: