client.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745
  1. #!/usr/bin/env python3
  2. import random # For simulation, not cryptography!
  3. import math
  4. import sys
  5. import logging
  6. import network
  7. import dirauth
  8. import relay
  9. import nacl.hash
  10. class VanillaCreatedExtendedHandler:
  11. """A handler for VanillaCreatedCircuitCell and
  12. VanillaExtendedCircuitCell cells."""
  13. def __init__(self, channelmgr, ntor, expecteddesc):
  14. self.channelmgr = channelmgr
  15. self.ntor = ntor
  16. self.expecteddesc = expecteddesc
  17. self.onionkey = expecteddesc.descdict['onionkey']
  18. self.idkey = expecteddesc.descdict['idkey']
  19. def received_cell(self, circhandler, cell):
  20. secret = self.ntor.verify(cell.ntor_reply, self.onionkey, self.idkey)
  21. enckey = nacl.hash.sha256(secret + b'upstream')
  22. deckey = nacl.hash.sha256(secret + b'downstream')
  23. circhandler.add_crypt_layer(enckey, deckey)
  24. if len(circhandler.circuit_descs) == 0:
  25. # This was a VanillaCreatedCircuitCell
  26. circhandler.replace_celltype_handler(
  27. relay.VanillaCreatedCircuitCell, None)
  28. else:
  29. # This was a VanillaExtendedCircuitCell
  30. circhandler.replace_celltype_handler(
  31. relay.VanillaExtendedCircuitCell, None)
  32. circhandler.circuit_descs.append(self.expecteddesc)
  33. # Are we done building the circuit?
  34. if len(circhandler.circuit_descs) == 3:
  35. # Yes!
  36. return
  37. nexthop = None
  38. while nexthop is None:
  39. nexthop = self.channelmgr.relaypicker.pick_weighted_relay()
  40. if nexthop.descdict['addr'] in \
  41. [ desc.descdict['addr'] \
  42. for desc in circhandler.circuit_descs ]:
  43. nexthop = None
  44. # Construct the VanillaExtendCircuitCell
  45. ntor = relay.NTor(self.channelmgr.perfstats)
  46. ntor_request = ntor.request()
  47. circextendmsg = relay.VanillaExtendCircuitCell(
  48. nexthop.descdict['addr'], ntor_request)
  49. # Set up the reply handler
  50. circhandler.replace_celltype_handler(
  51. relay.VanillaExtendedCircuitCell,
  52. VanillaCreatedExtendedHandler(self.channelmgr, ntor, nexthop))
  53. # Send the cell
  54. circhandler.send_cell(circextendmsg)
  55. class TelescopingCreatedHandler:
  56. """A handler for TelescopingCreatedCircuitCell cells; this will only always
  57. communicate with the client's guard."""
  58. def __init__(self, channelmgr, ntor):
  59. self.channelmgr = channelmgr
  60. self.ntor = ntor
  61. if type(self.channelmgr.guard) is dirauth.RelayDescriptor:
  62. guardd = self.channelmgr.guard.descdict
  63. else:
  64. guardd = self.channelmgr.guard.snipdict
  65. self.onionkey = guardd["onionkey"]
  66. self.idkey = guardd["idkey"]
  67. def received_cell(self, circhandler, cell):
  68. logging.debug("Received cell in TelescopingCreatedHandler")
  69. secret = self.ntor.verify(cell.ntor_reply, self.onionkey, self.idkey)
  70. enckey = nacl.hash.sha256(secret + b'upstream')
  71. deckey = nacl.hash.sha256(secret + b'downstream')
  72. circhandler.add_crypt_layer(enckey, deckey)
  73. circhandler.replace_celltype_handler(relay.TelescopingCreatedCircuitCell, None)
  74. circhandler.circuit_descs.append(self.channelmgr.guard)
  75. nexthopidx = None
  76. while nexthopidx is None:
  77. nexthopidx = self.channelmgr.relaypicker.pick_weighted_relay_index()
  78. #print("WARNING: Unimplemented! Need to check if this idx is in the list of circhandlers idxs")
  79. # TODO verify we don't need to do the above
  80. # Construct the TelescopingExtendCircuitCell
  81. ntor = relay.NTor(self.channelmgr.perfstats)
  82. ntor_request = ntor.request()
  83. circextendmsg = relay.TelescopingExtendCircuitCell(
  84. nexthopidx, ntor_request)
  85. # Set up the reply handler
  86. circhandler.replace_celltype_handler(
  87. relay.TelescopingExtendedCircuitCell,
  88. TelescopingExtendedHandler(self.channelmgr, ntor))
  89. # Send the cell
  90. circhandler.send_cell(circextendmsg)
  91. class TelescopingExtendedHandler:
  92. """A handler for TelescopingExtendedCircuitCell cells."""
  93. def __init__(self, channelmgr, ntor):
  94. self.channelmgr = channelmgr
  95. self.ntor = ntor
  96. def received_cell(self, circhandler, cell):
  97. logging.debug("Received cell in TelescopingExtendedHandler")
  98. # Validate the SNIP
  99. dirauth.SNIP.verify(cell.snip, self.channelmgr.consensus,
  100. network.thenetwork.dirauthkeys()[0],
  101. self.channelmgr.perfstats)
  102. onionkey = cell.snip.snipdict['onionkey']
  103. idkey = cell.snip.snipdict['idkey']
  104. secret = self.ntor.verify(cell.ntor_reply, onionkey, idkey)
  105. enckey = nacl.hash.sha256(secret + b'upstream')
  106. deckey = nacl.hash.sha256(secret + b'downstream')
  107. circhandler.add_crypt_layer(enckey, deckey)
  108. circhandler.replace_celltype_handler(
  109. relay.TelescopingExtendedCircuitCell, None)
  110. circhandler.circuit_descs.append(cell.snip)
  111. # Are we done building the circuit?
  112. logging.warning("we may need another circhandler structure for snips")
  113. if len(circhandler.circuit_descs) == 3:
  114. # Yes!
  115. return
  116. nexthopidx = self.channelmgr.relaypicker.pick_weighted_relay_index()
  117. # Construct the VanillaExtendCircuitCell
  118. ntor = relay.NTor(self.channelmgr.perfstats)
  119. ntor_request = ntor.request()
  120. circextendmsg = relay.TelescopingExtendCircuitCell(
  121. nexthopidx, ntor_request)
  122. # Set up the reply handler
  123. circhandler.replace_celltype_handler(
  124. relay.TelescopingExtendedCircuitCell,
  125. TelescopingExtendedHandler(self.channelmgr, ntor))
  126. # Send the cell
  127. circhandler.send_cell(circextendmsg)
  128. class SinglePassCreatedHandler:
  129. """A handler for SinglePassCreatedCircuitCell cells."""
  130. def __init__(self, channelmgr, ntor, client_key):
  131. self.channelmgr = channelmgr
  132. self.ntor = ntor
  133. self.client_key = client_key
  134. def received_cell(self, circhandler, cell):
  135. # We should only get one relay.SinglePassCreatedCircuitCell per
  136. # circuit
  137. circhandler.replace_celltype_handler(relay.SinglePassCreatedCircuitCell, None)
  138. # The circuit always starts with the guard
  139. circhandler.circuit_descs.append(self.channelmgr.guard)
  140. # Process each layer of the message
  141. blinding_keys = []
  142. while cell is not None:
  143. lasthop = circhandler.circuit_descs[-1]
  144. if type(lasthop) is dirauth.RelayDescriptor:
  145. lasthopd = lasthop.descdict
  146. else:
  147. lasthopd = lasthop.snipdict
  148. onionkey = lasthopd["onionkey"]
  149. idkey = lasthopd["idkey"]
  150. pathselkey = lasthopd["pathselkey"]
  151. if cell.enc is None:
  152. secret = self.ntor.verify(cell.ntor_reply, onionkey, idkey)
  153. enckey = nacl.hash.sha256(secret + b'upstream')
  154. deckey = nacl.hash.sha256(secret + b'downstream')
  155. circhandler.add_crypt_layer(enckey, deckey)
  156. cell = None
  157. else:
  158. secret = self.ntor.verify(cell.ntor_reply, onionkey, \
  159. idkey, b'circuit')
  160. enckey = nacl.hash.sha256(secret + b'upstream')
  161. deckey = nacl.hash.sha256(secret + b'downstream')
  162. createdkey = nacl.hash.sha256(secret + b'created')
  163. circhandler.add_crypt_layer(enckey, deckey)
  164. (snip, vrfout, nextlayer) = cell.enc.decrypt(createdkey)
  165. # Check the signature on the SNIP
  166. dirauth.SNIP.verify(snip, self.channelmgr.consensus,
  167. network.thenetwork.dirauthkeys()[0],
  168. self.channelmgr.perfstats)
  169. # TODO: compute the index, check the VRF, ensure the
  170. # SNIP is the correct one
  171. circhandler.circuit_descs.append(snip)
  172. cell = nextlayer
  173. class ClientChannelManager(relay.ChannelManager):
  174. """The subclass of ChannelManager for clients."""
  175. def __init__(self, myaddr, dirauthaddrs, perfstats):
  176. super().__init__(myaddr, dirauthaddrs, perfstats)
  177. self.guardaddr = None
  178. self.guard = None
  179. def get_consensus_from_fallbackrelay(self):
  180. """Download a fresh consensus from a random fallbackrelay."""
  181. fb = network.thenetwork.getfallbackrelay()
  182. logging.debug("Chose fallback %s", fb)
  183. if network.thenetwork.womode == network.WOMode.VANILLA:
  184. if self.consensus is not None and \
  185. len(self.consensus.consdict['relays']) > 0:
  186. self.send_msg(relay.RelayGetConsensusDiffMsg(), fb.netaddr)
  187. else:
  188. self.send_msg(relay.RelayGetConsensusMsg(), fb.netaddr)
  189. else:
  190. self.send_msg(relay.RelayGetConsensusMsg(), fb.netaddr)
  191. def ensure_guard_vanilla(self):
  192. """Ensure that we have a channel to a guard (Vanilla Onion
  193. Routing version)."""
  194. while True:
  195. if self.guardaddr is None:
  196. # Pick a guard from the consensus
  197. self.guard = self.relaypicker.pick_weighted_relay()
  198. self.guardaddr = self.guard.descdict['addr']
  199. self.test_guard_connection()
  200. if self.guardaddr is not None:
  201. break
  202. logging.debug('chose guard=%s', self.guardaddr)
  203. def test_guard_connection(self):
  204. # Connect to the guard
  205. try:
  206. self.get_channel_to(self.guardaddr)
  207. except network.NetNoServer:
  208. # Our guard is gone
  209. self.guardaddr = None
  210. self.guard = None
  211. def ensure_guard_walking_onions(self):
  212. """Ensure we have a channel to a guard (Walking Onions version).
  213. For the first implementation, we assume an out-of-band mechanism
  214. that just simply hands us a guard; we don't count the number of
  215. operations or bandwidth as this operation in practice occurs
  216. infrequently."""
  217. while True:
  218. if self.guardaddr is None:
  219. #randomly-sample a guard
  220. logging.warning("Unimplemented! guard should be selected from any relays.")
  221. self.guard = self.relaypicker.pick_weighted_relay()
  222. # here, we have a SNIP instead of a relay descriptor
  223. self.guardaddr = self.guard.snipdict['addr']
  224. self.test_guard_connection()
  225. if self.guardaddr is not None:
  226. break
  227. # Ensure we have the current descriptor for the guard
  228. # Note that self.guard may be a RelayDescriptor or a SNIP,
  229. # depending on how we got it
  230. if type(self.guard) is dirauth.RelayDescriptor:
  231. guardepoch = self.guard.descdict["epoch"]
  232. else:
  233. guardepoch = self.guard.snipdict["epoch"]
  234. if guardepoch != network.thenetwork.getepoch():
  235. guardchannel = self.get_channel_to(self.guardaddr)
  236. guardchannel.send_msg(relay.RelayGetDescMsg())
  237. logging.debug('chose guard=%s', self.guardaddr)
  238. def ensure_guard(self):
  239. """Ensure that we have a channel to a guard."""
  240. if network.thenetwork.womode == network.WOMode.VANILLA:
  241. self.ensure_guard_vanilla()
  242. return
  243. # At this point, we are either in Telescoping or Single-Pass mode
  244. self.ensure_guard_walking_onions()
  245. def new_circuit_vanilla(self):
  246. """Create a new circuit from this client. (Vanilla Onion Routing
  247. version)"""
  248. # Get our channel to the guard
  249. guardchannel = self.get_channel_to(self.guardaddr)
  250. # Allocate a new circuit id on it
  251. circid, circhandler = guardchannel.new_circuit()
  252. # Construct the VanillaCreateCircuitMsg
  253. ntor = relay.NTor(self.perfstats)
  254. ntor_request = ntor.request()
  255. circcreatemsg = relay.VanillaCreateCircuitMsg(circid, ntor_request)
  256. # Set up the reply handler
  257. circhandler.replace_celltype_handler(
  258. relay.VanillaCreatedCircuitCell,
  259. VanillaCreatedExtendedHandler(self, ntor, self.guard))
  260. # Send the message
  261. guardchannel.send_msg(circcreatemsg)
  262. return circhandler
  263. def new_circuit_telescoping(self):
  264. """Create a new circuit from this client (Telescoping Walking Onions
  265. version). If an error occurs and the circuit is deleted from the guard
  266. channel, return None, otherwise, return the circuit handler."""
  267. # Get our channel to the guard
  268. guardchannel = self.get_channel_to(self.guardaddr)
  269. # Allocate a new circuit id on it
  270. circid, circhandler = guardchannel.new_circuit()
  271. # Construct the TelescopingCreateCircuitMsg
  272. ntor = relay.NTor(self.perfstats)
  273. ntor_request = ntor.request()
  274. circcreatemsg = relay.TelescopingCreateCircuitMsg(circid, ntor_request)
  275. # Set up the reply handler
  276. circhandler.replace_celltype_handler(
  277. relay.TelescopingCreatedCircuitCell,
  278. TelescopingCreatedHandler(self, ntor))
  279. # Send the message
  280. guardchannel.send_msg(circcreatemsg)
  281. # Check to make sure the circuit is open before sending it- if there
  282. # was an error when establishing it, the circuit could already be
  283. # closed.
  284. if not guardchannel.is_circuit_open(circid):
  285. logging.debug("Circuit was already closed, not sending bytes. circid: " + str(circid))
  286. return None
  287. # In Telescoping Walking Onions, it should never happen that the
  288. # guard and exit are the same node, as the
  289. # TelescopingExtendedHandler takes care to not pick an index for
  290. # the exit that matches the guard's range. So this test should
  291. # never trigger. In Single-Pass Walking Onions, however, the
  292. # equivalent test is needed here (but should just log a debug,
  293. # not an error, since the client cannot control the index value
  294. # selected for the exit.
  295. guard = circhandler.circuit_descs[0]
  296. if type(guard) is dirauth.RelayDescriptor:
  297. guardd = guard.descdict
  298. else:
  299. guardd = guard.snipdict
  300. if guardd["addr"] == circhandler.circuit_descs[2].snipdict["addr"]:
  301. logging.error("CIRCUIT IN A LOOP")
  302. circhandler.close()
  303. circhandler = None
  304. return circhandler
  305. def new_circuit_singlepass(self):
  306. """Create a new circuit from this client (Single-Pass Walking Onions
  307. version). If an error occurs and the circuit is deleted from the guard
  308. channel, return None, otherwise, return the circuit handler."""
  309. # Get our channel to the guard
  310. guardchannel = self.get_channel_to(self.guardaddr)
  311. # Allocate a new circuit id on it
  312. circid, circhandler = guardchannel.new_circuit()
  313. # first, create the path-selection key used for Sphinx
  314. client_pathsel_key = nacl.public.PrivateKey.generate()
  315. self.perfstats.keygens += 1
  316. # Construct the SinglePassCreateCircuitMsg
  317. ntor = relay.NTor(self.perfstats)
  318. ntor_request = ntor.request()
  319. circcreatemsg = relay.SinglePassCreateCircuitMsg(circid, ntor_request,
  320. client_pathsel_key.public_key)
  321. # Set up the reply handler
  322. circhandler.replace_celltype_handler(
  323. relay.SinglePassCreatedCircuitCell,
  324. SinglePassCreatedHandler(self, ntor, client_pathsel_key))
  325. # Send the message
  326. guardchannel.send_msg(circcreatemsg)
  327. # Check to make sure the circuit is open before sending it- if there
  328. # was an error when establishing it, the circuit could already be
  329. # closed.
  330. if not guardchannel.is_circuit_open(circid):
  331. logging.debug("Circuit was already closed, not sending bytes. circid: " + str(circid))
  332. return None
  333. # In Single-Pass Walking Onions, we need to check whether the
  334. # circuit got into a loop (guard equals exit); each node will
  335. # refuse to extend to itself, so this is the only possible loop
  336. # in a circuit of length 3
  337. guard = circhandler.circuit_descs[0]
  338. if type(guard) is dirauth.RelayDescriptor:
  339. guardd = guard.descdict
  340. else:
  341. guardd = guard.snipdict
  342. if guardd["addr"] == circhandler.circuit_descs[2].snipdict["addr"]:
  343. logging.debug("circuit in a loop")
  344. circhandler.close()
  345. circhandler = None
  346. return circhandler
  347. def new_circuit(self):
  348. """Create a new circuit from this client."""
  349. circhandler = None
  350. # If an error occured, circhandler will still be None, so we should
  351. # try again.
  352. while circhandler is None:
  353. if network.thenetwork.womode == network.WOMode.VANILLA:
  354. circhandler = self.new_circuit_vanilla()
  355. elif network.thenetwork.womode == network.WOMode.TELESCOPING:
  356. circhandler = self.new_circuit_telescoping()
  357. elif network.thenetwork.womode == network.WOMode.SINGLEPASS:
  358. circhandler = self.new_circuit_singlepass()
  359. return circhandler
  360. def received_msg(self, msg, peeraddr, channel):
  361. """Callback when a NetMsg not specific to a circuit is
  362. received."""
  363. logging.debug("Client %s received msg %s from %s" % (self.myaddr, msg, peeraddr))
  364. if isinstance(msg, relay.RelayConsensusMsg) or \
  365. isinstance(msg, relay.RelayConsensusDiffMsg):
  366. self.relaypicker = dirauth.Consensus.verify(msg.consensus,
  367. network.thenetwork.dirauthkeys(), self.perfstats)
  368. self.consensus = msg.consensus
  369. elif isinstance(msg, relay.RelayDescMsg):
  370. dirauth.RelayDescriptor.verify(msg.desc, self.perfstats)
  371. self.guard = msg.desc
  372. else:
  373. return super().received_msg(msg, peeraddr, channel)
  374. def received_cell(self, circid, cell, peeraddr, channel):
  375. """Callback with a circuit-specific cell is received."""
  376. logging.debug("Client %s received cell on circ %d: %s from %s" % (self.myaddr, circid, cell, peeraddr))
  377. if isinstance(msg, relay.CloseCell):
  378. logging.debug("Log: Client received close cell; closing circuit")
  379. # TODO close cell
  380. return super().received_cell(circid, cell, peeraddr, channel)
  381. class Client:
  382. """A class representing a Tor client."""
  383. def __init__(self, dirauthaddrs):
  384. # Get a network address for client-side use only (do not bind it
  385. # to the network)
  386. self.netaddr = network.NetAddr()
  387. self.perfstats = network.PerfStats(network.EntType.CLIENT)
  388. self.perfstats.name = "Client at %s" % self.netaddr
  389. self.perfstats.is_bootstrapping = True
  390. self.channelmgr = ClientChannelManager(self.netaddr, dirauthaddrs,
  391. self.perfstats)
  392. # Register for epoch tick notifications
  393. network.thenetwork.wantepochticks(self, True)
  394. def terminate(self):
  395. """Quit this client."""
  396. # Stop listening for epoch ticks
  397. network.thenetwork.wantepochticks(self, False)
  398. # Close relay connections
  399. self.channelmgr.terminate()
  400. def get_consensus(self):
  401. """Fetch a new consensus."""
  402. # We're going to want a new consensus from our guard. In order
  403. # to get that, we'll need a channel to our guard. In order to
  404. # get that, we'll need a guard address. In order to get that,
  405. # we'll need a consensus (uh, oh; in that case, fetch the
  406. # consensus from a fallback relay).
  407. guardaddr = self.channelmgr.guardaddr
  408. guardchannel = None
  409. if guardaddr is not None:
  410. try:
  411. guardchannel = self.channelmgr.get_channel_to(guardaddr)
  412. except network.NetNoServer:
  413. guardaddr = None
  414. if guardchannel is None:
  415. logging.debug("In bootstrapping mode")
  416. self.channelmgr.get_consensus_from_fallbackrelay()
  417. logging.debug('client consensus=%s', self.channelmgr.consensus)
  418. return
  419. if network.thenetwork.womode == network.WOMode.VANILLA:
  420. if self.channelmgr.consensus is not None and len(self.channelmgr.consensus.consdict['relays']) > 0:
  421. guardchannel.send_msg(relay.RelayGetConsensusDiffMsg())
  422. logging.debug('got consensus diff, client consensus=%s', self.channelmgr.consensus)
  423. return
  424. # At this point, we are in one of the following scenarios:
  425. # 1. This is a walking onions protocol, and the client fetches the
  426. # complete consensus each epoch
  427. # 2. This is Vanilla Onion Routing and the client doesn't have a
  428. # consensus and needs to bootstrap it.
  429. guardchannel.send_msg(relay.RelayGetConsensusMsg())
  430. logging.debug('client consensus=%s', self.channelmgr.consensus)
  431. def newepoch(self, epoch):
  432. """Callback that fires at the start of each epoch"""
  433. # We'll need a new consensus
  434. self.get_consensus()
  435. # If we don't have a guard, pick one and make a channel to it
  436. self.channelmgr.ensure_guard()
  437. if __name__ == '__main__':
  438. perfstats = network.PerfStats(network.EntType.NONE)
  439. totsent = 0
  440. totrecv = 0
  441. dirasent = 0
  442. dirarecv = 0
  443. relaysent = 0
  444. relayrecv = 0
  445. clisent = 0
  446. clirecv = 0
  447. if len(sys.argv) < 3:
  448. print("Must pass in network mode and snip auth mode!")
  449. print("Network options are vanilla, telescoping, or single-pass.")
  450. print("SNIP auth options are merkle or threshold.")
  451. sys.exit(0)
  452. logging.basicConfig(level=logging.DEBUG)
  453. network_mode = network.WOMode.string_to_type(sys.argv[1])
  454. if network_mode == -1:
  455. print("Not a valid network mode: " + network_mode)
  456. sys.exit(0)
  457. snipauth_mode = network.SNIPAuthMode.string_to_type(sys.argv[2])
  458. if network_mode == -1:
  459. print("Not a valid SNIP authentication mode: " + snipauth_mode)
  460. sys.exit(0)
  461. # Initialize the (non-cryptographic) random seed
  462. random.seed(1)
  463. if network_mode == network.WOMode.VANILLA:
  464. network.thenetwork.set_wo_style(network.WOMode.VANILLA,
  465. network.SNIPAuthMode.NONE)
  466. elif network_mode == network.WOMode.TELESCOPING:
  467. if snipauth_mode == network.SNIPAuthMode.MERKLE:
  468. network.thenetwork.set_wo_style(network.WOMode.TELESCOPING,
  469. network.SNIPAuthMode.MERKLE)
  470. else:
  471. network.thenetwork.set_wo_style(network.WOMode.TELESCOPING,
  472. network.SNIPAuthMode.THRESHSIG)
  473. elif network_mode == network.WOMode.SINGLEPASS:
  474. if snipauth_mode == network.SNIPAuthMode.MERKLE:
  475. network.thenetwork.set_wo_style(network.WOMode.SINGLEPASS,
  476. network.SNIPAuthMode.MERKLE)
  477. else:
  478. network.thenetwork.set_wo_style(network.WOMode.SINGLEPASS,
  479. network.SNIPAuthMode.THRESHSIG)
  480. else:
  481. sys.exit("Received unsupported network mode, exiting.")
  482. # Start some dirauths
  483. numdirauths = 9
  484. dirauthaddrs = []
  485. dirauths = []
  486. for i in range(numdirauths):
  487. dira = dirauth.DirAuth(i, numdirauths)
  488. dirauths.append(dira)
  489. dirauthaddrs.append(dira.netaddr)
  490. # Start some relays
  491. numrelays = 100
  492. relays = []
  493. for i in range(numrelays):
  494. # Relay bandwidths (at least the ones fast enough to get used)
  495. # in the live Tor network (as of Dec 2019) are well approximated
  496. # by (200000-(200000-25000)/3*log10(x)) where x is a
  497. # uniform integer in [1,2500]
  498. x = random.randint(1,2500)
  499. bw = int(200000-(200000-25000)/3*math.log10(x))
  500. relays.append(relay.Relay(dirauthaddrs, bw, 0))
  501. # The fallback relays are a hardcoded list of about 5% of the
  502. # relays, used by clients for bootstrapping
  503. numfallbackrelays = int(numrelays * 0.05) + 1
  504. fallbackrelays = random.sample(relays, numfallbackrelays)
  505. for r in fallbackrelays:
  506. r.set_is_fallbackrelay()
  507. network.thenetwork.setfallbackrelays(fallbackrelays)
  508. # Tick the epoch
  509. network.thenetwork.nextepoch()
  510. dirauth.Consensus.verify(dirauth.DirAuth.consensus, network.thenetwork.dirauthkeys(), perfstats)
  511. print('ticked; epoch=', network.thenetwork.getepoch())
  512. relays[3].channelmgr.send_msg(relay.RelayRandomHopMsg(30), relays[5].netaddr)
  513. # See what channels exist and do a consistency check
  514. for r in relays:
  515. print("%s: %s" % (r.netaddr, [ str(k) for k in r.channelmgr.channels.keys()]))
  516. raddr = r.netaddr
  517. for ad, ch in r.channelmgr.channels.items():
  518. if ch.peer.channelmgr.myaddr != ad:
  519. print('address mismatch:', raddr, ad, ch.peer.channelmgr.myaddr)
  520. if ch.peer.channelmgr.channels[raddr].peer is not ch:
  521. print('asymmetry:', raddr, ad, ch, ch.peer.channelmgr.channels[raddr].peer)
  522. # Start some clients
  523. numclients = 1
  524. clients = []
  525. for i in range(numclients):
  526. clients.append(Client(dirauthaddrs))
  527. # Tick the epoch
  528. network.thenetwork.nextepoch()
  529. # See what channels exist and do a consistency check
  530. for c in clients:
  531. print("%s: %s" % (c.netaddr, [ str(k) for k in c.channelmgr.channels.keys()]))
  532. caddr = c.netaddr
  533. for ad, ch in c.channelmgr.channels.items():
  534. if ch.peer.channelmgr.myaddr != ad:
  535. print('address mismatch:', caddr, ad, ch.peer.channelmgr.myaddr)
  536. if ch.peer.channelmgr.channels[caddr].peer is not ch:
  537. print('asymmetry:', caddr, ad, ch, ch.peer.channelmgr.channels[caddr].peer)
  538. # Pick a bunch of bw-weighted random relays and look at the
  539. # distribution
  540. for i in range(100):
  541. r = relays[0].channelmgr.relaypicker.pick_weighted_relay()
  542. if network.thenetwork.womode == network.WOMode.VANILLA:
  543. print("relay",r.descdict["addr"])
  544. else:
  545. print("relay",r.snipdict["addr"])
  546. relays[3].terminate()
  547. relaysent += relays[3].perfstats.bytes_sent
  548. relayrecv += relays[3].perfstats.bytes_received
  549. del relays[3]
  550. # Tick the epoch
  551. network.thenetwork.nextepoch()
  552. circs = []
  553. for i in range(20):
  554. circ = clients[0].channelmgr.new_circuit()
  555. if circ is None:
  556. sys.exit("ERR: Client unable to create circuits")
  557. circs.append(circ)
  558. circ.send_cell(relay.StringCell("hello world circuit %d" % i))
  559. # Tick the epoch
  560. network.thenetwork.nextepoch()
  561. # See what channels exist and do a consistency check
  562. for r in relays:
  563. print("%s: %s" % (r.netaddr, [ str(k) + str([ck for ck in r.channelmgr.channels[k].circuithandlers.keys()]) for k in r.channelmgr.channels.keys()]))
  564. raddr = r.netaddr
  565. for ad, ch in r.channelmgr.channels.items():
  566. if ch.peer.channelmgr.myaddr != ad:
  567. print('address mismatch:', raddr, ad, ch.peer.channelmgr.myaddr)
  568. if ch.peer.channelmgr.channels[raddr].peer is not ch:
  569. print('asymmetry:', raddr, ad, ch, ch.peer.channelmgr.channels[raddr].peer)
  570. # See what channels exist and do a consistency check
  571. for c in clients:
  572. print("%s: %s" % (c.netaddr, [ str(k) + str([ck for ck in c.channelmgr.channels[k].circuithandlers.keys()]) for k in c.channelmgr.channels.keys()]))
  573. caddr = c.netaddr
  574. for ad, ch in c.channelmgr.channels.items():
  575. if ch.peer.channelmgr.myaddr != ad:
  576. print('address mismatch:', caddr, ad, ch.peer.channelmgr.myaddr)
  577. if ch.peer.channelmgr.channels[caddr].peer is not ch:
  578. print('asymmetry:', caddr, ad, ch, ch.peer.channelmgr.channels[caddr].peer)
  579. if ch.circuithandlers.keys() != \
  580. ch.peer.channelmgr.channels[caddr].circuithandlers.keys():
  581. print('circuit asymmetry:', caddr, ad, ch.peer.channelmgr.myaddr)
  582. for c in circs:
  583. c.close()
  584. for d in dirauths:
  585. print(d.perfstats)
  586. dirasent += d.perfstats.bytes_sent
  587. dirarecv += d.perfstats.bytes_received
  588. print("DirAuths sent=%s recv=%s" % (dirasent, dirarecv))
  589. totsent += dirasent
  590. totrecv += dirarecv
  591. for r in relays:
  592. print(r.perfstats)
  593. relaysent += r.perfstats.bytes_sent
  594. relayrecv += r.perfstats.bytes_received
  595. print("Relays sent=%s recv=%s" % (relaysent, relayrecv))
  596. totsent += relaysent
  597. totrecv += relayrecv
  598. for c in clients:
  599. print(c.perfstats)
  600. clisent += c.perfstats.bytes_sent
  601. clirecv += c.perfstats.bytes_received
  602. print("Client sent=%s recv=%s" % (clisent, clirecv))
  603. totsent += clisent
  604. totrecv += clirecv
  605. print("Total sent=%s recv=%s" % (totsent, totrecv))