relay.py 52 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294
  1. #!/usr/bin/env python3
  2. import random # For simulation, not cryptography!
  3. import math
  4. import sys
  5. import logging
  6. import nacl.utils
  7. import nacl.signing
  8. import nacl.public
  9. import nacl.hash
  10. import nacl.bindings
  11. import network
  12. import dirauth
  13. class RelayNetMsg(network.NetMsg):
  14. """The subclass of NetMsg for messages between relays and either
  15. relays or clients."""
  16. class RelayGetConsensusMsg(RelayNetMsg):
  17. """The subclass of RelayNetMsg for fetching the consensus. Sent by
  18. clients to relays."""
  19. class RelayConsensusMsg(RelayNetMsg):
  20. """The subclass of RelayNetMsg for returning the consensus from
  21. relays to clients, in response to RelayGetConsensusMsg."""
  22. def __init__(self, consensus):
  23. self.consensus = consensus
  24. class RelayGetConsensusDiffMsg(RelayNetMsg):
  25. """The subclass of RelayNetMsg for fetching the consensus, if the
  26. requestor already has the previous consensus. Sent by clients to
  27. relays."""
  28. class RelayConsensusDiffMsg(RelayNetMsg):
  29. """The subclass of RelayNetMsg for returning the consensus, if the
  30. requestor already has the previous consensus. We don't _actually_
  31. produce the diff at this time; we just charge fewer bytes for this
  32. message. Sent by relays to clients in response to
  33. RelayGetConsensusDiffMsg."""
  34. def __init__(self, consensus):
  35. self.consensus = consensus
  36. def size(self):
  37. if network.symbolic_byte_counters:
  38. return super().size()
  39. return math.ceil(RelayConsensusMsg(self.consensus).size() \
  40. * network.P_Delta)
  41. class RelayRandomHopMsg(RelayNetMsg):
  42. """A message used for testing, that hops from relay to relay
  43. randomly until its TTL expires."""
  44. def __init__(self, ttl):
  45. self.ttl = ttl
  46. def __str__(self):
  47. return "RandomHop TTL=%d" % self.ttl
  48. class CircuitCellMsg(RelayNetMsg):
  49. """Send a message tagged with a circuit id. This is the container
  50. class for all RelayCell messages."""
  51. def __init__(self, circuitid, cell):
  52. self.circid = circuitid
  53. self.cell = cell
  54. def __str__(self):
  55. return "C%d:%s" % (self.circid, self.cell)
  56. def size(self):
  57. # circuitids are 4 bytes
  58. return 4 + self.cell.size()
  59. class RelayCell(RelayNetMsg):
  60. """All cells (which are sent inside a CircuitCellMsg, and so do not
  61. need their own circuitid) should be a subclass of this class."""
  62. class StringCell(RelayCell):
  63. """Send an arbitrary string as a cell."""
  64. def __init__(self, str):
  65. self.data = str
  66. def __str__(self):
  67. return self.data.__str__()
  68. class CloseCell(RelayCell):
  69. """Close the circuit this cell was sent on. It should be sent
  70. _unencrypted_ (not within an EncryptedCell), and relays that receive
  71. one should forward it along the adjacent circuit, then close both
  72. the circuit it was received on and the adjacent one."""
  73. # It is intentional that VanillaCreateCircuitMsg is a RelayNetMsg and
  74. # not a RelayCell. This is the message that _creates_ the circuit, so
  75. # it can't be sent as a cell _within_ the circuit.
  76. class VanillaCreateCircuitMsg(RelayNetMsg):
  77. """The message for requesting circuit creation in Vanilla Onion
  78. Routing."""
  79. def __init__(self, circid, ntor_request):
  80. self.circid = circid
  81. self.ntor_request = ntor_request
  82. class VanillaCreatedCircuitCell(RelayCell):
  83. """The message for responding to circuit creation in Vanilla Onion
  84. Routing."""
  85. def __init__(self, ntor_reply):
  86. self.ntor_reply = ntor_reply
  87. class VanillaExtendCircuitCell(RelayCell):
  88. """The message for requesting circuit extension in Vanilla Onion
  89. Routing."""
  90. def __init__(self, hopaddr, ntor_request):
  91. self.hopaddr = hopaddr
  92. self.ntor_request = ntor_request
  93. class VanillaExtendedCircuitCell(RelayCell):
  94. """The message for responding to circuit extension in Vanilla Onion
  95. Routing."""
  96. def __init__(self, ntor_reply):
  97. self.ntor_reply = ntor_reply
  98. # It is intentional that TelescopingCreateCircuitMsg is a RelayNetMsg and
  99. # not a RelayCell. This is the message that _creates_ the circuit, so
  100. # it can't be sent as a cell _within_ the circuit.
  101. class TelescopingCreateCircuitMsg(RelayNetMsg):
  102. """The message for requesting circuit creation in Telescoping Onion
  103. Routing."""
  104. def __init__(self, circid, ntor_request):
  105. self.circid = circid
  106. self.ntor_request = ntor_request
  107. class TelescopingCreatedCircuitCell(RelayCell):
  108. """The message for responding to circuit creation in Telescoping Walking
  109. Onions."""
  110. def __init__(self, ntor_reply):
  111. self.ntor_reply = ntor_reply
  112. class TelescopingExtendCircuitCell(RelayCell):
  113. """The message for requesting circuit extension in Telescoping Walking
  114. Onionss."""
  115. def __init__(self, idx, ntor_request):
  116. self.idx = idx
  117. self.ntor_request = ntor_request
  118. class TelescopingExtendedCircuitCell(RelayCell):
  119. """The message for responding to circuit extension in Telescoping Walking
  120. Onions."""
  121. def __init__(self, ntor_reply, snip):
  122. self.ntor_reply = ntor_reply
  123. self.snip = snip
  124. class SinglePassCreateCircuitMsg(RelayNetMsg):
  125. """The message for requesting circuit creation in Single Pass Onion
  126. Routing."""
  127. def __init__(self, circid, ntor_request, client_path_selection_key):
  128. self.circid = circid
  129. self.ntor_request = ntor_request
  130. self.client_path_selection_key = client_path_selection_key
  131. class SinglePassCreatedCircuitCell(RelayCell):
  132. """The message for responding to circuit creation in Single-Pass Walking
  133. Onions."""
  134. def __init__(self, ntor_reply, circuit_snips):
  135. self.ntor_reply = ntor_reply
  136. self.circuit_snips = circuit_snips
  137. class EncryptedCell(RelayCell):
  138. """Send a message encrypted with a symmetric key. In this
  139. implementation, the encryption is not really done. A hash of the
  140. key is stored with the message so that it can be checked at
  141. decryption time."""
  142. def __init__(self, key, msg):
  143. self.keyhash = nacl.hash.sha256(key)
  144. self.plaintext = msg
  145. def decrypt(self, key):
  146. keyhash = nacl.hash.sha256(key)
  147. if keyhash != self.keyhash:
  148. raise ValueError("EncryptedCell key mismatch")
  149. return self.plaintext
  150. def size(self):
  151. # Current Tor actually has no overhead for encryption
  152. return self.plaintext.size()
  153. class RelayFallbackTerminationError(Exception):
  154. """An exception raised when someone tries to terminate a fallback
  155. relay."""
  156. class Sphinx:
  157. """Implement the public-key reblinding technique based on Sphinx.
  158. This does a few more public-key operations than it would strictly
  159. need to if we were using a group implementation that (unlike nacl)
  160. supported the operations we needed directly. The biggest issue is
  161. that nacl insists the high bit is set on private keys, which means
  162. we can't just multiply private keys together to get a new private
  163. key, and do a single DH operation with that resulting key; we have
  164. to perform a linear number of DH operations instead, per node in the
  165. circuit, so a quadratic number of DH operations total."""
  166. @staticmethod
  167. def makeblindkey(shared_secret, domain_separator, perfstats):
  168. """Create a Sphinx reblinding key (a PrivateKey) out of a shared
  169. secret and a domain separator (both bytestrings). The domain
  170. separator is just a constant bytestring like b'data' or
  171. b'circuit' for the data-protecting and circuit-protecting
  172. public-key elements respectively."""
  173. rawkey = nacl.hash.sha256(domain_separator + shared_secret,
  174. encoder=nacl.encoding.RawEncoder)
  175. perfstats.keygens += 1
  176. # The PrivateKey constructor does the Curve25519 pinning of
  177. # certain bits of the key to 0 and 1
  178. return nacl.public.PrivateKey(rawkey)
  179. @staticmethod
  180. def reblindpubkey(blindkey, pubkey, perfstats):
  181. """Create a Sphinx reblinded PublicKey out of a reblinding key
  182. (output by makeblindkey) and a (possibly already reblinded)
  183. PublicKey."""
  184. new_pubkey = nacl.bindings.crypto_scalarmult(bytes(blindkey),
  185. bytes(pubkey))
  186. perfstats.dhs += 1
  187. return nacl.public.PublicKey(new_pubkey)
  188. @staticmethod
  189. def client(client_privkey, blindkey_list, server_pubkey,
  190. domain_separator, is_last, perfstats):
  191. """Given the client's PrivateKey, a (possibly empty) list of
  192. reblinding keys, and the server's PublicKey, produce the shared
  193. secret and the new blinding key (to add to the list). The
  194. domain separator is as above. If is_last is true, don't bother
  195. creating the new blinding key, since this is the last iteration,
  196. and we won't be using it."""
  197. reblinded_server_pubkey = server_pubkey
  198. for blindkey in blindkey_list:
  199. reblinded_server_pubkey = Sphinx.reblindpubkey(blindkey,
  200. reblinded_server_pubkey, perfstats)
  201. sharedsecret = nacl.public.Box(client_privkey,
  202. reblinded_server_pubkey).shared_key()
  203. perfstats.dhs += 1
  204. if is_last:
  205. blindkey = None
  206. else:
  207. blindkey = Sphinx.makeblindkey(sharedsecret,
  208. domain_separator, perfstats)
  209. return sharedsecret, blindkey
  210. @staticmethod
  211. def server(client_pubkey, server_privkey, domain_separator, is_last,
  212. perfstats):
  213. """Given the client's PublicKey and the server's PrivateKey,
  214. produce the shared secret and the new reblinded client
  215. PublicKey. The domain separator is as above. If is_last is
  216. True, don't bother generating the new PublicKey, since we're the
  217. last server in the chain, and won't be using it."""
  218. sharedsecret = nacl.public.Box(server_privkey,
  219. client_pubkey).shared_key()
  220. perfstats.dhs += 1
  221. if is_last:
  222. blinded_pubkey = None
  223. else:
  224. blindkey = Sphinx.makeblindkey(sharedsecret, domain_separator,
  225. perfstats)
  226. blinded_pubkey = Sphinx.reblindpubkey(blindkey, client_pubkey,
  227. perfstats)
  228. return sharedsecret, blinded_pubkey
  229. class NTor:
  230. """A class implementing the ntor one-way authenticated key agreement
  231. scheme. The details are not exactly the same as either the ntor
  232. paper or Tor's implementation, but it will agree on keys and have
  233. the same number of public key operations."""
  234. def __init__(self, perfstats):
  235. self.perfstats = perfstats
  236. # Only used for Single-Pass Walking Onions; it is the sequence
  237. # of blinding keys used by Sphinx
  238. self.blinding_keys = []
  239. def request(self):
  240. """Create the ntor request message: X = g^x."""
  241. self.client_ephem_key = nacl.public.PrivateKey.generate()
  242. self.perfstats.keygens += 1
  243. return self.client_ephem_key.public_key
  244. @staticmethod
  245. def reply(onion_privkey, idpubkey, client_pubkey, perfstats,
  246. sphinx_domainsep=None):
  247. """The server calls this static method to produce the ntor reply
  248. message: (Y = g^y, B = g^b, A = H(M, "verify")) and the shared
  249. secret S = H(M, "secret") for M = (X^y,X^b,ID,B,X,Y). If
  250. sphinx_domainsep is not None, also compute and return the Sphinx
  251. reblinded client request to pass to the next server."""
  252. server_ephem_key = nacl.public.PrivateKey.generate()
  253. perfstats.keygens += 1
  254. xykey = nacl.public.Box(server_ephem_key, client_pubkey).shared_key()
  255. xbkey = nacl.public.Box(onion_privkey, client_pubkey).shared_key()
  256. perfstats.dhs += 2
  257. M = xykey + xbkey + \
  258. idpubkey.encode(encoder=nacl.encoding.RawEncoder) + \
  259. onion_privkey.public_key.encode(encoder=nacl.encoding.RawEncoder) + \
  260. server_ephem_key.public_key.encode(encoder=nacl.encoding.RawEncoder)
  261. A = nacl.hash.sha256(M + b'verify', encoder=nacl.encoding.RawEncoder)
  262. S = nacl.hash.sha256(M + b'secret', encoder=nacl.encoding.RawEncoder)
  263. if sphinx_domainsep is not None:
  264. blindkey = Sphinx.makeblindkey(S, sphinx_domainsep, perfstats)
  265. blinded_client_pubkey = Sphinx.reblindpubkey(blindkey,
  266. client_pubkey, perfstats)
  267. return ((server_ephem_key.public_key, onion_privkey.public_key, A),
  268. S), blinded_client_pubkey
  269. else:
  270. return ((server_ephem_key.public_key, onion_privkey.public_key, A),
  271. S)
  272. def verify(self, reply, onion_pubkey, idpubkey, sphinx_domainsep=None):
  273. """The client calls this method to verify the ntor reply
  274. message, passing the onion and id public keys for the server
  275. it's expecting to be talking to. If sphinx_domainsep is not
  276. None, also compute the reblinding key so that the client can
  277. reuse this same NTor object for the next server. Returns the
  278. shared secret on success, or raises ValueError on failure."""
  279. server_ephem_pubkey, server_onion_pubkey, authtag = reply
  280. if onion_pubkey != server_onion_pubkey:
  281. raise ValueError("NTor onion pubkey mismatch")
  282. # We use the blinding keys if present; if they're not present
  283. # (because we're not in Single-Pass Walking Onions), the loops
  284. # are just empty anyway, so everything will work in the usual
  285. # unblinded way.
  286. reblinded_server_ephem_pubkey = server_ephem_pubkey
  287. for blindkey in self.blinding_keys:
  288. reblinded_server_ephem_pubkey = Sphinx.reblindpubkey(blindkey,
  289. reblinded_server_ephem_pubkey, self.perfstats)
  290. xykey = nacl.public.Box(self.client_ephem_key,
  291. reblinded_server_ephem_pubkey).shared_key()
  292. reblinded_onion_pubkey = onion_pubkey
  293. for blindkey in self.blinding_keys:
  294. reblinded_onion_pubkey = Sphinx.reblindpubkey(blindkey,
  295. reblinded_onion_pubkey, self.perfstats)
  296. xbkey = nacl.public.Box(self.client_ephem_key,
  297. reblinded_onion_pubkey).shared_key()
  298. self.perfstats.dhs += 2
  299. M = xykey + xbkey + \
  300. idpubkey.encode(encoder=nacl.encoding.RawEncoder) + \
  301. onion_pubkey.encode(encoder=nacl.encoding.RawEncoder) + \
  302. server_ephem_pubkey.encode(encoder=nacl.encoding.RawEncoder)
  303. Acheck = nacl.hash.sha256(M + b'verify', encoder=nacl.encoding.RawEncoder)
  304. S = nacl.hash.sha256(M + b'secret', encoder=nacl.encoding.RawEncoder)
  305. if Acheck != authtag:
  306. raise ValueError("NTor auth mismatch")
  307. if sphinx_domainsep is not None:
  308. blindkey = Sphinx.makeblindkey(S, sphinx_domainsep,
  309. self.perfstats)
  310. self.blinding_keys.append(blindkey)
  311. return S
  312. class VanillaExtendCircuitHandler:
  313. """A handler for VanillaExtendCircuitCell cells. It allocates a new
  314. circuit id on the Channel to the requested next hop, connects the
  315. existing and new circuits together, and forwards a
  316. VanillaCreateCircuitMsg to the next hop."""
  317. def received_cell(self, circhandler, cell):
  318. # Remove ourselves from handling a second
  319. # VanillaExtendCircuitCell on this circuit
  320. circhandler.replace_celltype_handler(VanillaExtendCircuitCell, None)
  321. # Allocate a new circuit id to the requested next hop
  322. channelmgr = circhandler.channel.channelmgr
  323. nexthopchannel = channelmgr.get_channel_to(cell.hopaddr)
  324. newcircid, newcirchandler = nexthopchannel.new_circuit()
  325. # Connect the existing and new circuits together
  326. circhandler.adjacent_circuit_handler = newcirchandler
  327. newcirchandler.adjacent_circuit_handler = circhandler
  328. # Set up a handler for when the VanillaCreatedCircuitCell comes
  329. # back
  330. newcirchandler.replace_celltype_handler(
  331. VanillaCreatedCircuitCell,
  332. VanillaCreatedRelayHandler())
  333. # Forward a VanillaCreateCircuitMsg to the next hop
  334. nexthopchannel.send_msg(
  335. VanillaCreateCircuitMsg(newcircid, cell.ntor_request))
  336. class TelescopingExtendCircuitHandler:
  337. """A handler for TelescopingExtendCircuitCell cells. It allocates a new
  338. circuit id on the Channel to the requested next hop, connects the
  339. existing and new circuits together, and forwards a
  340. TelescopingCreateCircuitMsg to the next hop."""
  341. def __init__(self, relaypicker, current_relay_idkey):
  342. self.relaypicker = relaypicker
  343. self.current_relay_idkey = current_relay_idkey
  344. def received_cell(self, circhandler, cell):
  345. # Remove ourselves from handling a second
  346. # TelescopingExtendCircuitCell on this circuit
  347. circhandler.replace_celltype_handler(TelescopingExtendCircuitCell, None)
  348. # Find the SNIP corresponding to the index sent by the client
  349. next_snip = self.relaypicker.pick_relay_by_uniform_index(cell.idx)
  350. # Check to make sure that we aren't extending to ourselves. If we are,
  351. # close the circuit.
  352. if next_snip.snipdict["idkey"] == self.current_relay_idkey:
  353. logging.debug("Client requested extending the circuit to a relay already in the path; aborting. my circid: %s", str(circhandler.circid))
  354. circhandler.close()
  355. return
  356. # Allocate a new circuit id to the requested next hop
  357. channelmgr = circhandler.channel.channelmgr
  358. nexthopchannel = channelmgr.get_channel_to(next_snip.snipdict["addr"])
  359. newcircid, newcirchandler = nexthopchannel.new_circuit()
  360. # Connect the existing and new circuits together
  361. circhandler.adjacent_circuit_handler = newcirchandler
  362. newcirchandler.adjacent_circuit_handler = circhandler
  363. # Set up a handler for when the TelescopingCreatedCircuitCell comes
  364. # back
  365. newcirchandler.replace_celltype_handler(
  366. TelescopingCreatedCircuitCell,
  367. TelescopingCreatedRelayHandler(next_snip))
  368. # Forward a TelescopingCreateCircuitMsg to the next hop
  369. nexthopchannel.send_msg(
  370. TelescopingCreateCircuitMsg(newcircid, cell.ntor_request))
  371. class VanillaCreatedRelayHandler:
  372. """Handle a VanillaCreatedCircuitCell received by a _relay_ that
  373. recently received a VanillaExtendCircuitCell from a client, and so
  374. forwarded a VanillaCreateCircuitCell to the next hop."""
  375. def received_cell(self, circhandler, cell):
  376. # Remove ourselves from handling a second
  377. # VanillaCreatedCircuitCell on this circuit
  378. circhandler.replace_celltype_handler(VanillaCreatedCircuitCell, None)
  379. # Just forward a VanillaExtendedCircuitCell back towards the
  380. # client
  381. circhandler.adjacent_circuit_handler.send_cell(
  382. VanillaExtendedCircuitCell(cell.ntor_reply))
  383. class TelescopingCreatedRelayHandler:
  384. """Handle a TelescopingCreatedCircuitCell received by a _relay_ that
  385. recently received a TelescopingExtendCircuitCell from a client, and so
  386. forwarded a TelescopingCreateCircuitCell to the next hop."""
  387. def __init__(self, next_snip):
  388. self.next_snip = next_snip
  389. def received_cell(self, circhandler, cell):
  390. logging.debug("Handle a TelescopingCreatedCircuit received by a relay")
  391. # Remove ourselves from handling a second
  392. # VanillaCreatedCircuitCell on this circuit
  393. circhandler.replace_celltype_handler(TelescopingCreatedCircuitCell, None)
  394. # Just forward a TelescopingExtendedCircuitCell back towards the
  395. # client
  396. circhandler.adjacent_circuit_handler.send_cell(
  397. TelescopingExtendedCircuitCell(cell.ntor_reply, self.next_snip))
  398. class CircuitHandler:
  399. """A class for managing sending and receiving encrypted cells on a
  400. particular circuit."""
  401. class NoCryptLayer:
  402. def encrypt_msg(self, msg): return msg
  403. def decrypt_msg(self, msg): return msg
  404. class CryptLayer:
  405. def __init__(self, enckey, deckey, next_layer):
  406. self.enckey = enckey
  407. self.deckey = deckey
  408. self.next_layer = next_layer
  409. def encrypt_msg(self, msg):
  410. return self.next_layer.encrypt_msg(EncryptedCell(self.enckey, msg))
  411. def decrypt_msg(self, msg):
  412. return self.next_layer.decrypt_msg(msg).decrypt(self.deckey)
  413. def __init__(self, channel, circid):
  414. self.channel = channel
  415. self.circid = circid
  416. # The list of relay descriptors that form the circuit so far
  417. # (client side only)
  418. self.circuit_descs = []
  419. # The dispatch table is indexed by type, and the values are
  420. # objects with received_cell(circhandler, cell) methods.
  421. self.cell_dispatch_table = dict()
  422. # The topmost crypt layer. This is an object with
  423. # encrypt_msg(msg) and decrypt_msg(msg) methods that returns the
  424. # en/decrypted messages respectively. Messages are encrypted
  425. # starting with the last layer that was added (the keys for the
  426. # furthest away relay in the circuit) and are decrypted starting
  427. # with the first layer that was added (the keys for the guard).
  428. self.crypt_layer = self.NoCryptLayer()
  429. # The adjacent CircuitHandler that's connected to this one. If
  430. # we get a cell on one, we forward it to the other (if it's not
  431. # meant for us to handle directly)
  432. self.adjacent_circuit_handler = None
  433. # The function to call when this circuit closes
  434. self.closer = lambda: self.channel.circuithandlers.pop(circid)
  435. def close(self):
  436. """Close the circuit. Sends a CloseCell on the circuit (and its
  437. adjacent circuit, if present) and closes both."""
  438. adjcirchandler = self.adjacent_circuit_handler
  439. self.adjacent_circuit_handler = None
  440. logging.debug("Closing circuit. circid: %s", str(self.circid))
  441. if adjcirchandler is not None:
  442. adjcirchandler.adjacent_circuit_handler = None
  443. self.closer()
  444. self.channel_send_cell(CloseCell())
  445. if adjcirchandler is not None:
  446. adjcirchandler.closer()
  447. adjcirchandler.channel_send_cell(CloseCell())
  448. def send_cell(self, cell):
  449. """Send a cell on this circuit, encrypting as needed."""
  450. self.channel_send_cell(self.crypt_layer.encrypt_msg(cell))
  451. def channel_send_cell(self, cell):
  452. """Send a cell on this circuit directly without encrypting it
  453. first."""
  454. self.channel.send_msg(CircuitCellMsg(self.circid, cell))
  455. def received_cell(self, cell):
  456. """A cell has been received on this circuit. Dispatch it
  457. according to its type."""
  458. if isinstance(cell, EncryptedCell):
  459. cell = self.crypt_layer.decrypt_msg(cell)
  460. logging.debug("CircuitHandler: %s received cell %s on circuit %d from %s" % (self.channel.channelmgr.myaddr, cell, self.circid, self.channel.peer.channelmgr.myaddr))
  461. # If it's still encrypted, it's for sure meant for forwarding to
  462. # our adjacent hop, which had better exist.
  463. if isinstance(cell, EncryptedCell):
  464. self.adjacent_circuit_handler.send_cell(cell)
  465. else:
  466. # This is a plaintext cell meant for us. Handle it
  467. # according to the table.
  468. celltype = type(cell)
  469. if celltype in self.cell_dispatch_table:
  470. self.cell_dispatch_table[celltype].received_cell(self, cell)
  471. elif isinstance(cell, StringCell):
  472. # Default handler; just print the message in the cell
  473. logging.debug("CircuitHandler: %s received '%s' on circuit %d from %s" \
  474. % (self.channel.channelmgr.myaddr, cell,
  475. self.circid, self.channel.peer.channelmgr.myaddr))
  476. elif isinstance(cell, CloseCell):
  477. logging.debug("Received CloseCell on circuit %s", str(self.circid))
  478. # Forward the CloseCell (without encryption) to the
  479. # adjacent circuit, if any, and close both this and the
  480. # adjacent circuit
  481. adjcirchandler = self.adjacent_circuit_handler
  482. self.adjacent_circuit_handler = None
  483. if adjcirchandler is not None:
  484. adjcirchandler.adjacent_circuit_handler = None
  485. self.closer()
  486. if adjcirchandler is not None:
  487. adjcirchandler.closer()
  488. adjcirchandler.channel_send_cell(cell)
  489. else:
  490. # I don't know how to handle this cell?
  491. raise ValueError("CircuitHandler: %s received unknown cell type %s on circuit %d from %s" \
  492. % (self.channel.channelmgr.myaddr, cell,
  493. self.circid, self.channel.peer.channelmgr.myaddr))
  494. def replace_celltype_handler(self, celltype, handler):
  495. """Add an object with a received_cell(circhandler, cell) method
  496. to the cell dispatch table. It replaces anything that's already
  497. there. Passing None as the handler removes the dispatcher for
  498. that cell type."""
  499. if handler is None:
  500. del self.cell_dispatch_table[celltype]
  501. else:
  502. self.cell_dispatch_table[celltype] = handler
  503. def add_crypt_layer(self, enckey, deckey):
  504. """Add a processing layer to this CircuitHandler so that cells
  505. we send will get encrypted with the first given key, and cells
  506. we receive will be decrypted with the other given key."""
  507. current_crypt_layer = self.crypt_layer
  508. self.crypt_layer = self.CryptLayer(enckey, deckey, current_crypt_layer)
  509. class Channel(network.Connection):
  510. """A class representing a channel between a relay and either a
  511. client or a relay, transporting cells from various circuits."""
  512. def __init__(self):
  513. super().__init__()
  514. # The RelayChannelManager managing this Channel
  515. self.channelmgr = None
  516. # The Channel at the other end
  517. self.peer = None
  518. # The function to call when the connection closes
  519. self.closer = lambda: None
  520. # The next circuit id to use on this channel. The party that
  521. # opened the channel uses even numbers; the receiving party uses
  522. # odd numbers.
  523. self.next_circid = None
  524. # A map for CircuitHandlers to use for each open circuit on the
  525. # channel
  526. self.circuithandlers = dict()
  527. def closed(self):
  528. # Close each circuithandler we're managing
  529. while self.circuithandlers:
  530. chitems = iter(self.circuithandlers.items())
  531. circid, circhandler = next(chitems)
  532. logging.debug('closing circuit %s', circid)
  533. circhandler.close()
  534. self.closer()
  535. self.peer = None
  536. def close(self):
  537. peer = self.peer
  538. self.closed()
  539. if peer is not None and peer is not self:
  540. peer.closed()
  541. def new_circuit(self):
  542. """Allocate a new circuit on this channel, returning the new
  543. circuit's id and the new CircuitHandler."""
  544. circid = self.next_circid
  545. self.next_circid += 2
  546. circuithandler = CircuitHandler(self, circid)
  547. self.circuithandlers[circid] = circuithandler
  548. return circid, circuithandler
  549. def is_circuit_open(self, circid):
  550. is_open = (circid in self.circuithandlers) and (self.circuithandlers[circid] is not None)
  551. return is_open
  552. def new_circuit_with_circid(self, circid):
  553. """Allocate a new circuit on this channel, with the circuit id
  554. received from our peer. Return the new CircuitHandler"""
  555. circuithandler = CircuitHandler(self, circid)
  556. self.circuithandlers[circid] = circuithandler
  557. return circuithandler
  558. def send_cell(self, circid, cell):
  559. """Send the given message on the given circuit, encrypting or
  560. decrypting as needed."""
  561. self.circuithandlers[circid].send_cell(cell)
  562. def send_raw_cell(self, circid, cell):
  563. """Send the given message, tagged for the given circuit id. No
  564. encryption or decryption is done."""
  565. self.send_msg(CircuitCellMsg(self.circid, self.cell))
  566. def send_msg(self, msg):
  567. """Send the given NetMsg on the channel."""
  568. self.channelmgr.perfstats.bytes_sent += msg.size()
  569. self.peer.received(self.channelmgr.myaddr, msg)
  570. def received(self, peeraddr, msg):
  571. """Callback when a message is received from the network."""
  572. logging.debug('Channel: %s received %s from %s' % (self.channelmgr.myaddr, msg, peeraddr))
  573. self.channelmgr.perfstats.bytes_received += msg.size()
  574. if isinstance(msg, CircuitCellMsg):
  575. circid, cell = msg.circid, msg.cell
  576. self.circuithandlers[circid].received_cell(cell)
  577. else:
  578. self.channelmgr.received_msg(msg, peeraddr, self)
  579. class ChannelManager:
  580. """The class that manages the channels to other relays and clients.
  581. Relays and clients both use subclasses of this class to both create
  582. on-demand channels to relays, to gracefully handle the closing of
  583. channels, and to handle commands received over the channels."""
  584. def __init__(self, myaddr, dirauthaddrs, perfstats):
  585. # A dictionary of Channels to other hosts, indexed by NetAddr
  586. self.channels = dict()
  587. self.myaddr = myaddr
  588. self.dirauthaddrs = dirauthaddrs
  589. self.consensus = None
  590. self.relaypicker = None
  591. self.perfstats = perfstats
  592. def terminate(self):
  593. """Close all connections we're managing."""
  594. while self.channels:
  595. channelitems = iter(self.channels.items())
  596. addr, channel = next(channelitems)
  597. logging.debug('closing channel %s %s', addr, channel)
  598. channel.close()
  599. def add_channel(self, channel, peeraddr):
  600. """Add the given channel to the list of channels we are
  601. managing. If we are already managing a channel to the same
  602. peer, close it first."""
  603. if peeraddr in self.channels:
  604. self.channels[peeraddr].close()
  605. channel.channelmgr = self
  606. self.channels[peeraddr] = channel
  607. channel.closer = lambda: self.channels.pop(peeraddr)
  608. def get_channel_to(self, addr):
  609. """Get the Channel connected to the given NetAddr, creating one
  610. if none exists right now."""
  611. if addr in self.channels:
  612. return self.channels[addr]
  613. # Create the new channel
  614. logging.debug('getting channel from %s to %s',self.myaddr,addr)
  615. newchannel = network.thenetwork.connect(self.myaddr, addr,
  616. self.perfstats)
  617. logging.debug('got channel from %s to %s',self.myaddr,addr)
  618. self.channels[addr] = newchannel
  619. newchannel.closer = lambda: self.channels.pop(addr)
  620. newchannel.channelmgr = self
  621. return newchannel
  622. def received_msg(self, msg, peeraddr, channel):
  623. """Callback when a NetMsg not specific to a circuit is
  624. received."""
  625. logging.debug("ChannelManager: Node %s received msg %s from %s" % (self.myaddr, msg, peeraddr))
  626. def received_cell(self, circid, cell, peeraddr, channel):
  627. """Callback with a circuit-specific cell is received."""
  628. logging.debug("ChannelManager: Node %s received cell on circ %d: %s from %s" % (self.myaddr, circid, cell, peeraddr))
  629. def send_msg(self, msg, peeraddr):
  630. """Send a message to the peer with the given address."""
  631. channel = self.get_channel_to(peeraddr)
  632. channel.send_msg(msg)
  633. def send_cell(self, circid, cell, peeraddr):
  634. """Send a cell on the given circuit to the peer with the given
  635. address."""
  636. channel = self.get_channel_to(peeraddr)
  637. channel.send_cell(circid, cell)
  638. class RelayChannelManager(ChannelManager):
  639. """The subclass of ChannelManager for relays."""
  640. def __init__(self, myaddr, dirauthaddrs, onionprivkey, idpubkey,
  641. path_selection_key, perfstats):
  642. super().__init__(myaddr, dirauthaddrs, perfstats)
  643. self.onionkey = onionprivkey
  644. self.idpubkey = idpubkey
  645. if network.thenetwork.womode != network.WOMode.VANILLA:
  646. self.endive = None
  647. if network.thenetwork.womode == network.WOMode.SINGLEPASS:
  648. self.path_selection_key = path_selection_key
  649. def get_consensus(self):
  650. """Download a fresh consensus (and ENDIVE if using Walking
  651. Onions) from a random dirauth."""
  652. a = random.choice(self.dirauthaddrs)
  653. c = network.thenetwork.connect(self, a, self.perfstats)
  654. if network.thenetwork.womode == network.WOMode.VANILLA:
  655. if self.consensus is not None and \
  656. len(self.consensus.consdict['relays']) > 0:
  657. self.consensus = c.getconsensusdiff()
  658. else:
  659. self.consensus = c.getconsensus()
  660. self.relaypicker = dirauth.Consensus.verify(self.consensus,
  661. network.thenetwork.dirauthkeys(), self.perfstats)
  662. else:
  663. self.consensus = c.getconsensus()
  664. if self.endive is not None and \
  665. len(self.endive.enddict['snips']) > 0:
  666. self.endive = c.getendivediff()
  667. else:
  668. self.endive = c.getendive()
  669. self.relaypicker = dirauth.ENDIVE.verify(self.endive,
  670. self.consensus, network.thenetwork.dirauthkeys(),
  671. self.perfstats)
  672. c.close()
  673. def received_msg(self, msg, peeraddr, channel):
  674. """Callback when a NetMsg not specific to a circuit is
  675. received."""
  676. logging.debug("RelayChannelManager: Node %s received msg %s from %s" % (self.myaddr, msg, peeraddr))
  677. if isinstance(msg, RelayRandomHopMsg):
  678. if msg.ttl > 0:
  679. # Pick a random next hop from the consensus
  680. nexthop = self.relaypicker.pick_weighted_relay()
  681. if network.thenetwork.womode == network.WOMode.VANILLA:
  682. nextaddr = nexthop.descdict["addr"]
  683. else:
  684. nextaddr = nexthop.snipdict["addr"]
  685. self.send_msg(RelayRandomHopMsg(msg.ttl-1), nextaddr)
  686. elif isinstance(msg, RelayGetConsensusMsg):
  687. self.send_msg(RelayConsensusMsg(self.consensus), peeraddr)
  688. elif isinstance(msg, RelayGetConsensusDiffMsg):
  689. self.send_msg(RelayConsensusDiffMsg(self.consensus), peeraddr)
  690. elif isinstance(msg, VanillaCreateCircuitMsg):
  691. # A new circuit has arrived
  692. circhandler = channel.new_circuit_with_circid(msg.circid)
  693. # Create the ntor reply
  694. reply, secret = NTor.reply(self.onionkey, self.idpubkey,
  695. msg.ntor_request, self.perfstats)
  696. # Set up the circuit to use the shared secret
  697. enckey = nacl.hash.sha256(secret + b'downstream')
  698. deckey = nacl.hash.sha256(secret + b'upstream')
  699. circhandler.add_crypt_layer(enckey, deckey)
  700. # Add a handler for if an Extend Cell arrives (there should
  701. # be at most one on this circuit).
  702. circhandler.replace_celltype_handler(
  703. VanillaExtendCircuitCell, VanillaExtendCircuitHandler())
  704. # Send the ntor reply
  705. self.send_msg(CircuitCellMsg(msg.circid,
  706. VanillaCreatedCircuitCell(reply)), peeraddr)
  707. elif isinstance(msg, TelescopingCreateCircuitMsg):
  708. # A new circuit has arrived
  709. circhandler = channel.new_circuit_with_circid(msg.circid)
  710. # Create the ntor reply
  711. reply, secret = NTor.reply(self.onionkey, self.idpubkey,
  712. msg.ntor_request, self.perfstats)
  713. # Set up the circuit to use the shared secret
  714. enckey = nacl.hash.sha256(secret + b'downstream')
  715. deckey = nacl.hash.sha256(secret + b'upstream')
  716. circhandler.add_crypt_layer(enckey, deckey)
  717. # Add a handler for if an Extend Cell arrives (there should
  718. # be at most one on this circuit).
  719. circhandler.replace_celltype_handler(
  720. TelescopingExtendCircuitCell,
  721. TelescopingExtendCircuitHandler(self.relaypicker,
  722. self.idpubkey))
  723. # Send the ntor reply
  724. self.send_msg(CircuitCellMsg(msg.circid,
  725. TelescopingCreatedCircuitCell(reply)), peeraddr)
  726. elif isinstance(msg, SinglePassCreateCircuitMsg):
  727. # A new circuit has arrived
  728. circhandler = channel.new_circuit_with_circid(msg.circid)
  729. # Create the ntor reply for the circuit-extension key
  730. (reply, secret), blinded_client_pubkey = NTor.reply(self.onionkey, self.idpubkey,
  731. msg.ntor_request, self.perfstats, b'circuit')
  732. # Set up the circuit to use the shared secret established from the
  733. # circuit extension key
  734. enckey = nacl.hash.sha256(secret + b'downstream')
  735. deckey = nacl.hash.sha256(secret + b'upstream')
  736. circhandler.add_crypt_layer(enckey, deckey)
  737. print("WARNING: Unimplemented! Should check the TTL, if it isn't \
  738. zero, decriment it and pass it along. Otherwise, just reply \
  739. without extending.")
  740. # here, we will directly extend the circuit ourselves, after doing
  741. # the following:
  742. # 1. determining the next relay using the client's path selection
  743. # key in conjunction with our own
  744. # 2. blinding each of the client's public keys to send to the next
  745. # hop.
  746. idx, blinded_client_pubkey = Sphinx.server(msg.client_path_selection_key,
  747. self.path_selection_key, b'circuit', False, self.perfstats)
  748. sys.exit("TODO convert shared secret into integer type modulo alpha")
  749. nexthop = self.relaypicker.pick_relay_by_uniform_index(idx)
  750. if nexthop == None:
  751. print("WARNING: Unimplemented! Need to validate next hop is not null, if it is, we should send a CLOSE cell.")
  752. # Add a handler for once the next relay replies to say that the
  753. # circuit has been created
  754. # be at most one on this circuit).
  755. circhandler.replace_celltype_handler(
  756. SinglePassCreatedCircuitCell,
  757. SinglePassCreatedCircuitHandler(ntorreply, next_hop))
  758. # # Allocate a new circuit id to the requested next hop
  759. # channelmgr = circhandler.channel.channelmgr
  760. # nexthopchannel = channelmgr.get_channel_to(next_snip.snipdict["addr"])
  761. # newcircid, newcirchandler = nexthopchannel.new_circuit()
  762. #
  763. # # Send the next create message to the next hop
  764. # # TODO add the correct interface here
  765. # self.send_msg(CircuitCellMsg(msg.circid,
  766. # SinglePassCreateCircuitCell(newcircid, ntorrequest,
  767. # next_client_path_selection_key)), peeraddr)
  768. #
  769. sys.exit("have not yet implemented circuit handling for single-pass in relays")
  770. else:
  771. return super().received_msg(msg, peeraddr, channel)
  772. def received_cell(self, circid, cell, peeraddr, channel):
  773. """Callback with a circuit-specific cell is received."""
  774. logging.debug("RelayChannelManager: Node %s received cell on circ %d: %s from %s" % (self.myaddr, circid, cell, peeraddr))
  775. return super().received_cell(circid, cell, peeraddr, channel)
  776. class Relay(network.Server):
  777. """The class representing an onion relay."""
  778. def __init__(self, dirauthaddrs, bw, flags):
  779. # Gather performance statistics
  780. self.perfstats = network.PerfStats(network.EntType.RELAY)
  781. self.perfstats.is_bootstrapping = True
  782. # Create the identity and onion keys
  783. self.idkey = nacl.signing.SigningKey.generate()
  784. self.onionkey = nacl.public.PrivateKey.generate()
  785. self.perfstats.keygens += 2
  786. self.name = self.idkey.verify_key.encode(encoder=nacl.encoding.HexEncoder).decode("ascii")
  787. # Bind to the network to get a network address
  788. self.netaddr = network.thenetwork.bind(self)
  789. self.perfstats.name = "Relay at %s" % self.netaddr
  790. # Our bandwidth and flags
  791. self.bw = bw
  792. self.flags = flags
  793. # Register for epoch change notification
  794. network.thenetwork.wantepochticks(self, True, end=True)
  795. network.thenetwork.wantepochticks(self, True)
  796. if network.thenetwork.womode == network.WOMode.SINGLEPASS:
  797. self.path_selection_key = nacl.public.PrivateKey.generate()
  798. else:
  799. self.path_selection_key = None
  800. # Create the RelayChannelManager connection manager
  801. self.channelmgr = RelayChannelManager(self.netaddr, dirauthaddrs,
  802. self.onionkey, self.idkey.verify_key, self.path_selection_key, self.perfstats)
  803. # Initially, we're not a fallback relay
  804. self.is_fallbackrelay = False
  805. self.uploaddesc()
  806. def terminate(self):
  807. """Stop this relay."""
  808. if self.is_fallbackrelay:
  809. # Fallback relays must not (for now) terminate
  810. raise RelayFallbackTerminationError(self)
  811. # Stop listening for epoch ticks
  812. network.thenetwork.wantepochticks(self, False, end=True)
  813. network.thenetwork.wantepochticks(self, False)
  814. # Tell the dirauths we're going away
  815. self.uploaddesc(False)
  816. # Close connections to other relays
  817. self.channelmgr.terminate()
  818. # Stop listening to our own bound port
  819. self.close()
  820. def set_is_fallbackrelay(self, isfallback = True):
  821. """Set this relay to be a fallback relay (or unset if passed
  822. False)."""
  823. self.is_fallbackrelay = isfallback
  824. def epoch_ending(self, epoch):
  825. # Download the new consensus, which will have been created
  826. # already since the dirauths' epoch_ending callbacks happened
  827. # before the relays'.
  828. self.channelmgr.get_consensus()
  829. def newepoch(self, epoch):
  830. self.uploaddesc()
  831. def uploaddesc(self, upload=True):
  832. # Upload the descriptor for the epoch to come, or delete a
  833. # previous upload if upload=False
  834. descdict = dict();
  835. descdict["epoch"] = network.thenetwork.getepoch() + 1
  836. descdict["idkey"] = self.idkey.verify_key
  837. descdict["onionkey"] = self.onionkey.public_key
  838. descdict["addr"] = self.netaddr
  839. descdict["bw"] = self.bw
  840. descdict["flags"] = self.flags
  841. if network.thenetwork.womode == network.WOMode.SINGLEPASS:
  842. descdict["path_selection_key"] = self.path_selection_key
  843. desc = dirauth.RelayDescriptor(descdict)
  844. desc.sign(self.idkey, self.perfstats)
  845. dirauth.RelayDescriptor.verify(desc, self.perfstats)
  846. if upload:
  847. descmsg = dirauth.DirAuthUploadDescMsg(desc)
  848. else:
  849. # Note that this relies on signatures being deterministic;
  850. # otherwise we'd need to save the descriptor we uploaded
  851. # before so we could tell the airauths to delete the exact
  852. # one
  853. descmsg = dirauth.DirAuthDelDescMsg(desc)
  854. # Upload them
  855. for a in self.channelmgr.dirauthaddrs:
  856. c = network.thenetwork.connect(self, a, self.perfstats)
  857. c.sendmsg(descmsg)
  858. c.close()
  859. def connected(self, peer):
  860. """Callback invoked when someone (client or relay) connects to
  861. us. Create a pair of linked Channels and return the peer half
  862. to the peer."""
  863. # Create the linked pair
  864. if peer is self.netaddr:
  865. # A self-loop? We'll allow it.
  866. peerchannel = Channel()
  867. peerchannel.peer = peerchannel
  868. peerchannel.next_circid = 2
  869. return peerchannel
  870. peerchannel = Channel()
  871. ourchannel = Channel()
  872. peerchannel.peer = ourchannel
  873. peerchannel.next_circid = 2
  874. ourchannel.peer = peerchannel
  875. ourchannel.next_circid = 1
  876. # Add our channel to the RelayChannelManager
  877. self.channelmgr.add_channel(ourchannel, peer)
  878. return peerchannel
  879. if __name__ == '__main__':
  880. perfstats = network.PerfStats(network.EntType.NONE)
  881. # Initialize the (non-cryptographic) random seed
  882. random.seed(1)
  883. if len(sys.argv) < 3:
  884. print("Must pass in network mode and snip auth mode!")
  885. print("Network options are vanilla, telescoping, or single-pass.")
  886. print("SNIP auth options are merkle or threshold.")
  887. sys.exit(0)
  888. network_mode = network.WOMode.string_to_type(sys.argv[1])
  889. if network_mode == -1:
  890. print("Not a valid network mode: " + network_mode)
  891. sys.exit(0)
  892. snipauth_mode = network.SNIPAuthMode.string_to_type(sys.argv[2])
  893. if network_mode == -1:
  894. print("Not a valid SNIP authentication mode: " + snipauth_mode)
  895. sys.exit(0)
  896. if network_mode == network.WOMode.VANILLA:
  897. network.thenetwork.set_wo_style(network.WOMode.VANILLA,
  898. network.SNIPAuthMode.NONE)
  899. elif network_mode == network.WOMode.TELESCOPING:
  900. if snipauth_mode == network.SNIPAuthMode.MERKLE:
  901. network.thenetwork.set_wo_style(network.WOMode.TELESCOPING,
  902. network.SNIPAuthMode.MERKLE)
  903. else:
  904. network.thenetwork.set_wo_style(network.WOMode.TELESCOPING,
  905. network.SNIPAuthMode.THRESHSIG)
  906. elif network_mode == network.WOMode.SINGLEPASS:
  907. if snipauth_mode == network.SNIPAuthMode.MERKLE:
  908. network.thenetwork.set_wo_style(network.WOMode.SINGLEPASS,
  909. network.SNIPAuthMode.MERKLE)
  910. else:
  911. network.thenetwork.set_wo_style(network.WOMode.SINGLEPASS,
  912. network.SNIPAuthMode.THRESHSIG)
  913. else:
  914. sys.exit("Received unsupported network mode, exiting.")
  915. # Start some dirauths
  916. numdirauths = 9
  917. dirauthaddrs = []
  918. for i in range(numdirauths):
  919. dira = dirauth.DirAuth(i, numdirauths)
  920. dirauthaddrs.append(dira.netaddr)
  921. # Start some relays
  922. numrelays = 10
  923. relays = []
  924. for i in range(numrelays):
  925. # Relay bandwidths (at least the ones fast enough to get used)
  926. # in the live Tor network (as of Dec 2019) are well approximated
  927. # by (200000-(200000-25000)/3*log10(x)) where x is a
  928. # uniform integer in [1,2500]
  929. x = random.randint(1,2500)
  930. bw = int(200000-(200000-25000)/3*math.log10(x))
  931. relays.append(Relay(dirauthaddrs, bw, 0))
  932. # The fallback relays are a hardcoded list of about 5% of the
  933. # relays, used by clients for bootstrapping
  934. numfallbackrelays = int(numrelays * 0.05) + 1
  935. fallbackrelays = random.sample(relays, numfallbackrelays)
  936. for r in fallbackrelays:
  937. r.set_is_fallbackrelay()
  938. network.thenetwork.setfallbackrelays(fallbackrelays)
  939. # Tick the epoch
  940. network.thenetwork.nextepoch()
  941. print(dirauth.DirAuth.consensus)
  942. print(dirauth.DirAuth.endive)
  943. if network.thenetwork.womode == network.WOMode.VANILLA:
  944. relaypicker = dirauth.Consensus.verify(dirauth.DirAuth.consensus,
  945. network.thenetwork.dirauthkeys(), perfstats)
  946. else:
  947. relaypicker = dirauth.ENDIVE.verify(dirauth.DirAuth.endive,
  948. dirauth.DirAuth.consensus,
  949. network.thenetwork.dirauthkeys(), perfstats)
  950. if network.thenetwork.snipauthmode == \
  951. network.SNIPAuthMode.THRESHSIG:
  952. for s in dirauth.DirAuth.endive.enddict['snips']:
  953. dirauth.SNIP.verify(s, dirauth.DirAuth.consensus,
  954. network.thenetwork.dirauthkeys()[0], perfstats)
  955. print('ticked; epoch=', network.thenetwork.getepoch())
  956. relays[3].channelmgr.send_msg(RelayRandomHopMsg(30), relays[5].netaddr)
  957. # See what channels exist and do a consistency check
  958. for r in relays:
  959. print("%s: %s" % (r.netaddr, [ str(k) for k in r.channelmgr.channels.keys()]))
  960. raddr = r.netaddr
  961. for ad, ch in r.channelmgr.channels.items():
  962. if ch.peer.channelmgr.myaddr != ad:
  963. print('address mismatch:', raddr, ad, ch.peer.channelmgr.myaddr)
  964. if ch.peer.channelmgr.channels[raddr].peer is not ch:
  965. print('asymmetry:', raddr, ad, ch, ch.peer.channelmgr.channels[raddr].peer)
  966. # Stop some relays
  967. relays[3].terminate()
  968. del relays[3]
  969. relays[5].terminate()
  970. del relays[5]
  971. relays[7].terminate()
  972. del relays[7]
  973. # Tick the epoch
  974. network.thenetwork.nextepoch()
  975. print(dirauth.DirAuth.consensus)
  976. print(dirauth.DirAuth.endive)
  977. # See what channels exist and do a consistency check
  978. for r in relays:
  979. print("%s: %s" % (r.netaddr, [ str(k) for k in r.channelmgr.channels.keys()]))
  980. raddr = r.netaddr
  981. for ad, ch in r.channelmgr.channels.items():
  982. if ch.peer.channelmgr.myaddr != ad:
  983. print('address mismatch:', raddr, ad, ch.peer.channelmgr.myaddr)
  984. if ch.peer.channelmgr.channels[raddr].peer is not ch:
  985. print('asymmetry:', raddr, ad, ch, ch.peer.channelmgr.channels[raddr].peer)
  986. channel = relays[3].channelmgr.get_channel_to(relays[5].netaddr)
  987. circid, circhandler = channel.new_circuit()
  988. peerchannel = relays[5].channelmgr.get_channel_to(relays[3].netaddr)
  989. peerchannel.new_circuit_with_circid(circid)
  990. relays[3].channelmgr.send_cell(circid, StringCell("test"), relays[5].netaddr)
  991. idpubkey = relays[1].idkey.verify_key
  992. onionpubkey = relays[1].onionkey.public_key
  993. nt = NTor(perfstats)
  994. req = nt.request()
  995. R, S = NTor.reply(relays[1].onionkey, idpubkey, req, perfstats)
  996. S2 = nt.verify(R, onionpubkey, idpubkey)
  997. print(S == S2)
  998. # Test the Sphinx class: DH version (for the path selection keys)
  999. server1_key = nacl.public.PrivateKey.generate()
  1000. server2_key = nacl.public.PrivateKey.generate()
  1001. server3_key = nacl.public.PrivateKey.generate()
  1002. client_key = nacl.public.PrivateKey.generate()
  1003. # Check that normal DH is working
  1004. ckey = nacl.public.Box(client_key, server1_key.public_key).shared_key()
  1005. skey = nacl.public.Box(server1_key, client_key.public_key).shared_key()
  1006. assert(ckey == skey)
  1007. # Transform the client pubkey with Sphinx as it passes through the
  1008. # servers and record the resulting shared secrets
  1009. blinded_client_pubkey = client_key.public_key
  1010. s1secret, blinded_client_pubkey = Sphinx.server(blinded_client_pubkey,
  1011. server1_key, b'circuit', False, perfstats)
  1012. s2secret, blinded_client_pubkey = Sphinx.server(blinded_client_pubkey,
  1013. server2_key, b'circuit', False, perfstats)
  1014. s3secret, _ = Sphinx.server(blinded_client_pubkey,
  1015. server3_key, b'circuit', True, perfstats)
  1016. # Hopefully matching keys on the client side
  1017. blinding_keys = []
  1018. c1secret, blind_key = Sphinx.client(client_key, blinding_keys,
  1019. server1_key.public_key, b'circuit', False, perfstats)
  1020. blinding_keys.append(blind_key)
  1021. c2secret, blind_key = Sphinx.client(client_key, blinding_keys,
  1022. server2_key.public_key, b'circuit', False, perfstats)
  1023. blinding_keys.append(blind_key)
  1024. c3secret, _ = Sphinx.client(client_key, blinding_keys,
  1025. server3_key.public_key, b'circuit', True, perfstats)
  1026. assert(s1secret == c1secret)
  1027. assert(s2secret == c2secret)
  1028. assert(s3secret == c3secret)
  1029. print('Sphinx DH test successful')
  1030. # End test of Sphinx (DH version)
  1031. # Test the Sphinx class: NTor version (for the path selection keys)
  1032. server1_idkey = nacl.signing.SigningKey.generate().verify_key
  1033. server2_idkey = nacl.signing.SigningKey.generate().verify_key
  1034. server3_idkey = nacl.signing.SigningKey.generate().verify_key
  1035. server1_onionkey = nacl.public.PrivateKey.generate()
  1036. server2_onionkey = nacl.public.PrivateKey.generate()
  1037. server3_onionkey = nacl.public.PrivateKey.generate()
  1038. client_ntor = NTor(perfstats)
  1039. # Client's initial message
  1040. client_pubkey = client_ntor.request()
  1041. # Transform the client pubkey with Sphinx as it passes through the
  1042. # servers and record the resulting shared secrets
  1043. blinded_client_pubkey = client_pubkey
  1044. (s1reply, s1secret), blinded_client_pubkey = NTor.reply(
  1045. server1_onionkey, server1_idkey, blinded_client_pubkey,
  1046. perfstats, b'data')
  1047. (s2reply, s2secret), blinded_client_pubkey = NTor.reply(
  1048. server2_onionkey, server2_idkey, blinded_client_pubkey,
  1049. perfstats, b'data')
  1050. (s3reply, s3secret) = NTor.reply(
  1051. server3_onionkey, server3_idkey, blinded_client_pubkey,
  1052. perfstats)
  1053. # Hopefully matching keys on the client side
  1054. c1secret = client_ntor.verify(s1reply, server1_onionkey.public_key,
  1055. server1_idkey, b'data')
  1056. c2secret = client_ntor.verify(s2reply, server2_onionkey.public_key,
  1057. server2_idkey, b'data')
  1058. c3secret = client_ntor.verify(s3reply, server3_onionkey.public_key,
  1059. server3_idkey)
  1060. assert(s1secret == c1secret)
  1061. assert(s2secret == c2secret)
  1062. assert(s3secret == c3secret)
  1063. print('Sphinx NTor test successful')
  1064. # End test of Sphinx (NTor version)