|
@@ -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:
|