relay.py 55 KB

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