|
@@ -10,25 +10,46 @@ 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')
|
|
|
+# Simulated VRF. The output and proof are of the correct size (32 bytes
|
|
|
+# for the output, 48 bytes for the proof), and we charge the right
|
|
|
+# number of group operations (1 keygen, 2 DH for proving, 3 DH for
|
|
|
+# verifiying -- that last number charges 1 DH for a (s*G+c*Y) multiexp,
|
|
|
+# but 2 for a (s*A+c*B) multiexp)
|
|
|
+class VRF:
|
|
|
+ @staticmethod
|
|
|
+ def get_output(vrf_privkey, vrf_input, perfstats):
|
|
|
+ """Given the VRF private key and the input, produce
|
|
|
+ the output value and the proof."""
|
|
|
+ # nacl can't do the group operations we need to actually
|
|
|
+ # compute the VRF, so we just fake it for the simulation. The
|
|
|
+ # output value is sha256(privkey, input), and the proof is
|
|
|
+ # sha256(pubkey, input, output) + 16 bytes of 0.
|
|
|
+ # ***THIS IS NOT A REAL VRF!***
|
|
|
+ val = nacl.hash.sha256(bytes(vrf_privkey) + vrf_input)
|
|
|
+ vrf_pubkey = vrf_privkey.public_key
|
|
|
+ proof = nacl.hash.sha256(bytes(vrf_pubkey) + val + vrf_input) + \
|
|
|
+ bytes(16)
|
|
|
+ perfstats.keygens += 1
|
|
|
+ perfstats.dhs += 2
|
|
|
+ return val, proof
|
|
|
|
|
|
-class VRF():
|
|
|
- def __init__(self, private_key, relaypicker):
|
|
|
- self.private_key = private_key
|
|
|
- self.relaypicker = relaypicker
|
|
|
+ @staticmethod
|
|
|
+ def check_output(vrf_pubkey, vrf_input, vrf_output, perfstats):
|
|
|
+ """Given the VRF public key, the input, and the claimed output
|
|
|
+ and proof, raise an exception if the proof fails to check.
|
|
|
+ Returns the VRF output."""
|
|
|
+ # Again, NOT A REAL VRF!
|
|
|
+ val, proof = vrf_output
|
|
|
+ if nacl.hash.sha256(vrf_pubkey + val + vrf_input) + bytes(16) != \
|
|
|
+ proof:
|
|
|
+ raise ValueError("VRF proof did not verify")
|
|
|
+ perfstats.dhs += 3
|
|
|
+ return val
|
|
|
|
|
|
- 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
|
|
@@ -989,21 +1010,22 @@ class RelayChannelManager(ChannelManager):
|
|
|
deckey = nacl.hash.sha256(secret + b'upstream')
|
|
|
createdkey = nacl.hash.sha256(secret + b'created')
|
|
|
|
|
|
- # here, we will directly extend the circuit ourselves, after
|
|
|
+ # 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
|
|
|
pathsel_rand, blinded_client_path_selection_key = \
|
|
|
Sphinx.server(nacl.public.PublicKey(msg.clipathselkey),
|
|
|
- self.path_selection_key, b'pathsel', False, self.perfstats)
|
|
|
+ self.onionkey, b'pathsel', 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.")
|
|
|
+ # Simulate the VRF output for now (but it has the right
|
|
|
+ # size, and charges the right number of group operations to
|
|
|
+ # the perfstats)
|
|
|
+ vrf_output = VRF.get_output(self.path_selection_key,
|
|
|
+ pathsel_rand, self.perfstats)
|
|
|
|
|
|
- # simulate the VRF output for now
|
|
|
- vrf_output = VRF(self.path_selection_key,
|
|
|
- self.relaypicker).get_output(pathsel_rand)
|
|
|
+ index = int.from_bytes(vrf_output[0][:4], 'big', signed=False)
|
|
|
|
|
|
- next_hop = self.relaypicker.pick_relay_by_uniform_index(vrf_output.output)
|
|
|
+ next_hop = self.relaypicker.pick_relay_by_uniform_index(index)
|
|
|
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()
|