|
@@ -10,10 +10,26 @@ import nacl.signing
|
|
|
import nacl.public
|
|
|
import nacl.hash
|
|
|
import nacl.bindings
|
|
|
+import hashlib
|
|
|
+
|
|
|
+from collections import namedtuple
|
|
|
|
|
|
import network
|
|
|
import dirauth
|
|
|
|
|
|
+VRFOutput = namedtuple('VRFOutput', 'output proof')
|
|
|
+
|
|
|
+class VRF():
|
|
|
+ def __init__(self, private_key, relaypicker):
|
|
|
+ self.private_key = private_key
|
|
|
+ self.relaypicker = relaypicker
|
|
|
+
|
|
|
+ def get_output(self, vrf_input):
|
|
|
+ output = self.relaypicker.pick_weighted_relay_index()
|
|
|
+ m = hashlib.sha256()
|
|
|
+ m.update(str(output).encode())
|
|
|
+ return VRFOutput(output, m.digest())
|
|
|
+
|
|
|
class RelayNetMsg(network.NetMsg):
|
|
|
"""The subclass of NetMsg for messages between relays and either
|
|
|
relays or clients."""
|
|
@@ -160,7 +176,7 @@ class TelescopingCreatedCircuitCell(RelayCell):
|
|
|
|
|
|
class TelescopingExtendCircuitCell(RelayCell):
|
|
|
"""The message for requesting circuit extension in Telescoping Walking
|
|
|
- Onionss."""
|
|
|
+ Onions."""
|
|
|
|
|
|
def __init__(self, idx, ntor_request):
|
|
|
self.idx = idx
|
|
@@ -176,20 +192,26 @@ class TelescopingExtendedCircuitCell(RelayCell):
|
|
|
|
|
|
class SinglePassCreateCircuitMsg(RelayNetMsg):
|
|
|
"""The message for requesting circuit creation in Single Pass Onion
|
|
|
- Routing."""
|
|
|
+ Routing. This is used to extend a Single-Pass circuit by clients."""
|
|
|
|
|
|
- def __init__(self, circid, ntor_request, client_path_selection_key):
|
|
|
+ def __init__(self, circid, ntor_request, client_path_selection_key, ttl):
|
|
|
self.circid = circid
|
|
|
self.ntor_request = ntor_request
|
|
|
self.client_path_selection_key = client_path_selection_key
|
|
|
+ self.ttl = ttl
|
|
|
|
|
|
class SinglePassCreatedCircuitCell(RelayCell):
|
|
|
"""The message for responding to circuit creation in Single-Pass Walking
|
|
|
Onions."""
|
|
|
|
|
|
- def __init__(self, ntor_reply, circuit_snips):
|
|
|
+ def __init__(self, ntor_reply, snip, vrf_output, ttl, encrypted_cell):
|
|
|
self.ntor_reply = ntor_reply
|
|
|
- self.circuit_snips = circuit_snips
|
|
|
+ self.snip = snip
|
|
|
+ self.vrf_output = vrf_output
|
|
|
+ self.ttl = ttl
|
|
|
+ # the above fields are sent in plaintext, the below is encrypted
|
|
|
+ # information
|
|
|
+ self.encrypted_cell = encrypted_cell
|
|
|
|
|
|
|
|
|
class EncryptedCell(RelayCell):
|
|
@@ -498,7 +520,7 @@ class TelescopingCreatedRelayHandler:
|
|
|
def received_cell(self, circhandler, cell):
|
|
|
logging.debug("Handle a TelescopingCreatedCircuit received by a relay")
|
|
|
# Remove ourselves from handling a second
|
|
|
- # VanillaCreatedCircuitCell on this circuit
|
|
|
+ # TelescopingCreatedCircuitCell on this circuit
|
|
|
circhandler.replace_celltype_handler(TelescopingCreatedCircuitCell, None)
|
|
|
|
|
|
# Just forward a TelescopingExtendedCircuitCell back towards the
|
|
@@ -506,6 +528,30 @@ class TelescopingCreatedRelayHandler:
|
|
|
circhandler.adjacent_circuit_handler.send_cell(
|
|
|
TelescopingExtendedCircuitCell(cell.ntor_reply, self.next_snip))
|
|
|
|
|
|
+class SinglePassCreatedRelayHandler:
|
|
|
+ """Handle a SinglePassCreatedCircuitCell received by a _relay_ that
|
|
|
+ recently received a SinglePassCreateCircuitMsg from this relay."""
|
|
|
+
|
|
|
+ def __init__(self, ntorreply, next_snip, vrf_output, ttl):
|
|
|
+ self.ntorreply = ntorreply
|
|
|
+ self.next_snip = next_snip
|
|
|
+ self.vrf_output = vrf_output
|
|
|
+ self.ttl = ttl
|
|
|
+
|
|
|
+ def received_cell(self, circhandler, cell):
|
|
|
+ logging.debug("Handle a SinglePassCreatedCircuitCell received by a relay, my ttl is %s", str(self.ttl))
|
|
|
+ # Remove ourselves from handling a second
|
|
|
+ # SinglePassCreatedCircuitCell on this circuit
|
|
|
+ circhandler.replace_celltype_handler(SinglePassCreatedCircuitCell, None)
|
|
|
+ logging.debug("Sending a SinglePassCreatedCell after receiving one from %s; my ttl is %s", self.next_snip.snipdict['addr'], str(self.ttl))
|
|
|
+
|
|
|
+ # Just forward a SinglePassCreatedCircuitCell back towards the
|
|
|
+ # client
|
|
|
+ circhandler.adjacent_circuit_handler.channel_send_cell(
|
|
|
+ SinglePassCreatedCircuitCell(cell.ntor_reply, self.next_snip,
|
|
|
+ self.vrf_output, self.ttl, cell))
|
|
|
+
|
|
|
+
|
|
|
class CircuitHandler:
|
|
|
"""A class for managing sending and receiving encrypted cells on a
|
|
|
particular circuit."""
|
|
@@ -879,11 +925,36 @@ class RelayChannelManager(ChannelManager):
|
|
|
# Send the ntor reply
|
|
|
self.send_msg(CircuitCellMsg(msg.circid,
|
|
|
TelescopingCreatedCircuitCell(reply)), peeraddr)
|
|
|
- elif isinstance(msg, SinglePassCreateCircuitMsg):
|
|
|
+ elif isinstance(msg, SinglePassCreateCircuitMsg) and msg.ttl == 0:
|
|
|
+ # we are the end of the circuit, just establish a shared key and
|
|
|
+ # return
|
|
|
+ logging.debug("RelayChannelManager: Single-Pass TTL is 0, replying without extending")
|
|
|
+
|
|
|
# A new circuit has arrived
|
|
|
circhandler = channel.new_circuit_with_circid(msg.circid)
|
|
|
- # Create the ntor reply for the circuit-extension key
|
|
|
- (reply, secret), blinded_client_pubkey = NTor.reply(self.onionkey, self.idpubkey,
|
|
|
+
|
|
|
+ # Create the ntor reply
|
|
|
+ reply, secret = NTor.reply(self.onionkey, self.idpubkey,
|
|
|
+ msg.ntor_request, self.perfstats)
|
|
|
+ # Set up the circuit to use the shared 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, but no need to send the snip for the next
|
|
|
+ # relay or vrf proof, as this is the last relay in the circuit.
|
|
|
+ self.send_msg(CircuitCellMsg(msg.circid,
|
|
|
+ SinglePassCreatedCircuitCell(reply, None, None, 0, None)), peeraddr)
|
|
|
+
|
|
|
+ elif isinstance(msg, SinglePassCreateCircuitMsg) and msg.ttl > 0:
|
|
|
+ logging.debug("RelayChannelManager: Single-Pass TTL is greater than 0; extending")
|
|
|
+
|
|
|
+ # A new circuit has arrived
|
|
|
+ circhandler = channel.new_circuit_with_circid(msg.circid)
|
|
|
+
|
|
|
+ # Create the ntor reply for the circuit-extension key, and derive
|
|
|
+ # the client's next blinded key
|
|
|
+ (ntorreply, secret), blinded_client_encr_key = NTor.reply(self.onionkey, self.idpubkey,
|
|
|
msg.ntor_request, self.perfstats, b'circuit')
|
|
|
|
|
|
# Set up the circuit to use the shared secret established from the
|
|
@@ -892,45 +963,47 @@ class RelayChannelManager(ChannelManager):
|
|
|
deckey = nacl.hash.sha256(secret + b'upstream')
|
|
|
circhandler.add_crypt_layer(enckey, deckey)
|
|
|
|
|
|
- logging.debug("WARNING: Unimplemented! Should check the TTL, if it isn't \
|
|
|
- zero, decriment it and pass it along. Otherwise, just reply \
|
|
|
- without extending.")
|
|
|
-
|
|
|
- # here, we will directly extend the circuit ourselves, after doing
|
|
|
- # the following:
|
|
|
- # 1. determining the next relay using the client's path selection
|
|
|
- # key in conjunction with our own
|
|
|
- # 2. blinding each of the client's public keys to send to the next
|
|
|
- # hop.
|
|
|
- idx_as_hex, blinded_client_pubkey = Sphinx.server(msg.client_path_selection_key,
|
|
|
+ # here, we will directly extend the circuit ourselves, after
|
|
|
+ # determining the next relay using the client's path selection
|
|
|
+ # key in conjunction with our own
|
|
|
+ idx_as_hex, blinded_client_path_selection_key = Sphinx.server(msg.client_path_selection_key,
|
|
|
self.path_selection_key, b'circuit', False, self.perfstats)
|
|
|
|
|
|
logging.debug("RelayChannelManager: Unimplemented! need to translate idx into endive index")
|
|
|
+ logging.debug("RelayChannelManager: Unimplemented! need to pick the next relay using the shared secret between the client and the relay.")
|
|
|
+
|
|
|
+ # simpulate the VRF output for now
|
|
|
+ vrf_output = VRF(self.path_selection_key,
|
|
|
+ self.relaypicker).get_output(idx_as_hex)
|
|
|
|
|
|
- sys.exit("TODO convert shared secret into integer type modulo alpha")
|
|
|
- nexthop = self.relaypicker.pick_relay_by_uniform_index(idx)
|
|
|
- if nexthop == None:
|
|
|
- logging.debug("WARNING: Unimplemented! Need to validate next hop is not null, if it is, we should send a CLOSE cell.")
|
|
|
+ next_hop = self.relaypicker.pick_relay_by_uniform_index(vrf_output.output)
|
|
|
+ if next_hop == None:
|
|
|
+ logging.debug("Client requested extending the circuit to a relay index that results in None, aborting. my circid: %s", str(circhandler.circid))
|
|
|
+ circhandler.close()
|
|
|
+ elif next_hop.snipdict["idkey"] == self.idpubkey or next_hop.snipdict["addr"] == peeraddr:
|
|
|
+ logging.debug("Client requested extending the circuit to a relay already in the path; aborting. my circid: %s", str(circhandler.circid))
|
|
|
+ circhandler.close()
|
|
|
+
|
|
|
+ # Allocate a new circuit id to the requested next hop
|
|
|
+ channelmgr = circhandler.channel.channelmgr
|
|
|
+ nexthopchannel = channelmgr.get_channel_to(next_hop.snipdict["addr"])
|
|
|
+ newcircid, newcirchandler = nexthopchannel.new_circuit()
|
|
|
+
|
|
|
+ # Connect the existing and new circuits together
|
|
|
+ circhandler.adjacent_circuit_handler = newcirchandler
|
|
|
+ newcirchandler.adjacent_circuit_handler = circhandler
|
|
|
|
|
|
# Add a handler for once the next relay replies to say that the
|
|
|
# circuit has been created
|
|
|
- # be at most one on this circuit).
|
|
|
- circhandler.replace_celltype_handler(
|
|
|
+ newcirchandler.replace_celltype_handler(
|
|
|
SinglePassCreatedCircuitCell,
|
|
|
- SinglePassCreatedCircuitHandler(ntorreply, next_hop))
|
|
|
-
|
|
|
-# # Allocate a new circuit id to the requested next hop
|
|
|
-# channelmgr = circhandler.channel.channelmgr
|
|
|
-# nexthopchannel = channelmgr.get_channel_to(next_snip.snipdict["addr"])
|
|
|
-# newcircid, newcirchandler = nexthopchannel.new_circuit()
|
|
|
-#
|
|
|
-# # Send the next create message to the next hop
|
|
|
-# # TODO add the correct interface here
|
|
|
-# self.send_msg(CircuitCellMsg(msg.circid,
|
|
|
-# SinglePassCreateCircuitCell(newcircid, ntorrequest,
|
|
|
-# next_client_path_selection_key)), peeraddr)
|
|
|
-#
|
|
|
- sys.exit("have not yet implemented circuit handling for single-pass in relays")
|
|
|
+ SinglePassCreatedRelayHandler(ntorreply, next_hop,
|
|
|
+ vrf_output, msg.ttl))
|
|
|
+
|
|
|
+ # Send the next create message to the next hop
|
|
|
+ nexthopchannel.send_msg(SinglePassCreateCircuitMsg(newcircid,
|
|
|
+ blinded_client_encr_key, blinded_client_path_selection_key,
|
|
|
+ msg.ttl-1))
|
|
|
else:
|
|
|
return super().received_msg(msg, peeraddr, channel)
|
|
|
|