relay.py 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921
  1. #!/usr/bin/env python3
  2. import random # For simulation, not cryptography!
  3. import math
  4. import nacl.utils
  5. import nacl.signing
  6. import nacl.public
  7. import nacl.hash
  8. import network
  9. import dirauth
  10. class RelayNetMsg(network.NetMsg):
  11. """The subclass of NetMsg for messages between relays and either
  12. relays or clients."""
  13. class RelayGetConsensusMsg(RelayNetMsg):
  14. """The subclass of RelayNetMsg for fetching the consensus. Sent by
  15. clients to relays."""
  16. class RelayConsensusMsg(RelayNetMsg):
  17. """The subclass of RelayNetMsg for returning the consensus from
  18. relays to clients, in response to RelayGetConsensusMsg."""
  19. def __init__(self, consensus):
  20. self.consensus = consensus
  21. class RelayGetConsensusDiffMsg(RelayNetMsg):
  22. """The subclass of RelayNetMsg for fetching the consensus, if the
  23. requestor already has the previous consensus. Sent by clients to
  24. relays."""
  25. class RelayConsensusDiffMsg(RelayNetMsg):
  26. """The subclass of RelayNetMsg for returning the consensus, if the
  27. requestor already has the previous consensus. We don't _actually_
  28. produce the diff at this time; we just charge fewer bytes for this
  29. message. Sent by relays to clients in response to
  30. RelayGetConsensusDiffMsg."""
  31. def __init__(self, consensus):
  32. self.consensus = consensus
  33. def size(self):
  34. if network.symbolic_byte_counters:
  35. return super().size()
  36. return math.ceil(RelayConsensusMsg(self.consensus).size() \
  37. * network.P_Delta)
  38. class RelayRandomHopMsg(RelayNetMsg):
  39. """A message used for testing, that hops from relay to relay
  40. randomly until its TTL expires."""
  41. def __init__(self, ttl):
  42. self.ttl = ttl
  43. def __str__(self):
  44. return "RandomHop TTL=%d" % self.ttl
  45. class CircuitCellMsg(RelayNetMsg):
  46. """Send a message tagged with a circuit id. This is the container
  47. class for all RelayCell messages."""
  48. def __init__(self, circuitid, cell):
  49. self.circid = circuitid
  50. self.cell = cell
  51. def __str__(self):
  52. return "C%d:%s" % (self.circid, self.cell)
  53. def size(self):
  54. # circuitids are 4 bytes
  55. return 4 + self.cell.size()
  56. class RelayCell(RelayNetMsg):
  57. """All cells (which are sent inside a CircuitCellMsg, and so do not
  58. need their own circuitid) should be a subclass of this class."""
  59. class StringCell(RelayCell):
  60. """Send an arbitrary string as a cell."""
  61. def __init__(self, str):
  62. self.data = str
  63. def __str__(self):
  64. return self.data.__str__()
  65. class CloseCell(RelayCell):
  66. """Close the circuit this cell was sent on. It should be sent
  67. _unencrypted_ (not within an EncryptedCell), and relays that receive
  68. one should forward it along the adjacent circuit, then close both
  69. the circuit it was received on and the adjacent one."""
  70. # It is intentional that VanillaCreateCircuitMsg is a RelayNetMsg and
  71. # not a RelayCell. This is the message that _creates_ the circuit, so
  72. # it can't be sent as a cell _within_ the circuit.
  73. class VanillaCreateCircuitMsg(RelayNetMsg):
  74. """The message for requesting circuit creation in Vanilla Onion
  75. Routing."""
  76. def __init__(self, circid, ntor_request):
  77. self.circid = circid
  78. self.ntor_request = ntor_request
  79. class VanillaCreatedCircuitCell(RelayCell):
  80. """The message for responding to circuit creation in Vanilla Onion
  81. Routing."""
  82. def __init__(self, ntor_reply):
  83. self.ntor_reply = ntor_reply
  84. class VanillaExtendCircuitCell(RelayCell):
  85. """The message for requesting circuit extension in Vanilla Onion
  86. Routing."""
  87. def __init__(self, hopaddr, ntor_request):
  88. self.hopaddr = hopaddr
  89. self.ntor_request = ntor_request
  90. class VanillaExtendedCircuitCell(RelayCell):
  91. """The message for responding to circuit extension in Vanilla Onion
  92. Routing."""
  93. def __init__(self, ntor_reply):
  94. self.ntor_reply = ntor_reply
  95. # It is intentional that TelescopingCreateCircuitMsg is a RelayNetMsg and
  96. # not a RelayCell. This is the message that _creates_ the circuit, so
  97. # it can't be sent as a cell _within_ the circuit.
  98. class TelescopingCreateCircuitMsg(RelayNetMsg):
  99. """The message for requesting circuit creation in Telescoping Onion
  100. Routing."""
  101. def __init__(self, circid, ntor_request):
  102. self.circid = circid
  103. self.ntor_request = ntor_request
  104. class TelescopingCreatedCircuitCell(RelayCell):
  105. """The message for responding to circuit creation in Telescoping Walking
  106. Onions."""
  107. def __init__(self, ntor_reply):
  108. self.ntor_reply = ntor_reply
  109. class TelescopingExtendCircuitCell(RelayCell):
  110. """The message for requesting circuit extension in Telescoping Walking
  111. Onionss."""
  112. def __init__(self, idx, ntor_request):
  113. self.idx = idx
  114. self.ntor_request = ntor_request
  115. class TelescopingExtendedCircuitCell(RelayCell):
  116. """The message for responding to circuit extension in Telescoping Walking
  117. Onions."""
  118. def __init__(self, ntor_reply):
  119. self.ntor_reply = ntor_reply
  120. class EncryptedCell(RelayCell):
  121. """Send a message encrypted with a symmetric key. In this
  122. implementation, the encryption is not really done. A hash of the
  123. key is stored with the message so that it can be checked at
  124. decryption time."""
  125. def __init__(self, key, msg):
  126. self.keyhash = nacl.hash.sha256(key)
  127. self.plaintext = msg
  128. def decrypt(self, key):
  129. keyhash = nacl.hash.sha256(key)
  130. if keyhash != self.keyhash:
  131. raise ValueError("EncryptedCell key mismatch")
  132. return self.plaintext
  133. def size(self):
  134. # Current Tor actually has no overhead for encryption
  135. return self.plaintext.size()
  136. class RelayFallbackTerminationError(Exception):
  137. """An exception raised when someone tries to terminate a fallback
  138. relay."""
  139. class NTor:
  140. """A class implementing the ntor one-way authenticated key agreement
  141. scheme. The details are not exactly the same as either the ntor
  142. paper or Tor's implementation, but it will agree on keys and have
  143. the same number of public key operations."""
  144. def __init__(self, perfstats):
  145. self.perfstats = perfstats
  146. def request(self):
  147. """Create the ntor request message: X = g^x."""
  148. self.client_ephem_key = nacl.public.PrivateKey.generate()
  149. self.perfstats.keygens += 1
  150. return self.client_ephem_key.public_key
  151. @staticmethod
  152. def reply(onion_privkey, idpubkey, client_pubkey, perfstats):
  153. """The server calls this static method to produce the ntor reply
  154. message: (Y = g^y, B = g^b, A = H(M, "verify")) and the shared
  155. secret S = H(M, "secret") for M = (X^y,X^b,ID,B,X,Y)."""
  156. server_ephem_key = nacl.public.PrivateKey.generate()
  157. perfstats.keygens += 1
  158. xykey = nacl.public.Box(server_ephem_key, client_pubkey).shared_key()
  159. xbkey = nacl.public.Box(onion_privkey, client_pubkey).shared_key()
  160. perfstats.dhs += 2
  161. M = xykey + xbkey + \
  162. idpubkey.encode(encoder=nacl.encoding.RawEncoder) + \
  163. onion_privkey.public_key.encode(encoder=nacl.encoding.RawEncoder) + \
  164. server_ephem_key.public_key.encode(encoder=nacl.encoding.RawEncoder)
  165. A = nacl.hash.sha256(M + b'verify', encoder=nacl.encoding.RawEncoder)
  166. S = nacl.hash.sha256(M + b'secret', encoder=nacl.encoding.RawEncoder)
  167. return ((server_ephem_key.public_key, onion_privkey.public_key, A), S)
  168. def verify(self, reply, onion_pubkey, idpubkey):
  169. """The client calls this method to verify the ntor reply
  170. message, passing the onion and id public keys for the server
  171. it's expecting to be talking to . Returns the shared secret on
  172. success, or raises ValueError on failure."""
  173. server_ephem_pubkey, server_onion_pubkey, authtag = reply
  174. if onion_pubkey != server_onion_pubkey:
  175. raise ValueError("NTor onion pubkey mismatch")
  176. xykey = nacl.public.Box(self.client_ephem_key, server_ephem_pubkey).shared_key()
  177. xbkey = nacl.public.Box(self.client_ephem_key, onion_pubkey).shared_key()
  178. self.perfstats.dhs += 2
  179. M = xykey + xbkey + \
  180. idpubkey.encode(encoder=nacl.encoding.RawEncoder) + \
  181. onion_pubkey.encode(encoder=nacl.encoding.RawEncoder) + \
  182. server_ephem_pubkey.encode(encoder=nacl.encoding.RawEncoder)
  183. Acheck = nacl.hash.sha256(M + b'verify', encoder=nacl.encoding.RawEncoder)
  184. S = nacl.hash.sha256(M + b'secret', encoder=nacl.encoding.RawEncoder)
  185. if Acheck != authtag:
  186. raise ValueError("NTor auth mismatch")
  187. return S
  188. class VanillaExtendCircuitHandler:
  189. """A handler for VanillaExtendCircuitCell cells. It allocates a new
  190. circuit id on the Channel to the requested next hop, connects the
  191. existing and new circuits together, and forwards a
  192. VanillaCreateCircuitMsg to the next hop."""
  193. def received_cell(self, circhandler, cell):
  194. # Remove ourselves from handling a second
  195. # VanillaExtendCircuitCell on this circuit
  196. circhandler.replace_celltype_handler(VanillaExtendCircuitCell, None)
  197. # Allocate a new circuit id to the requested next hop
  198. channelmgr = circhandler.channel.channelmgr
  199. nexthopchannel = channelmgr.get_channel_to(cell.hopaddr)
  200. newcircid, newcirchandler = nexthopchannel.new_circuit()
  201. # Connect the existing and new circuits together
  202. circhandler.adjacent_circuit_handler = newcirchandler
  203. newcirchandler.adjacent_circuit_handler = circhandler
  204. # Set up a handler for when the VanillaCreatedCircuitCell comes
  205. # back
  206. newcirchandler.replace_celltype_handler(
  207. VanillaCreatedCircuitCell,
  208. VanillaCreatedRelayHandler())
  209. # Forward a VanillaCreateCircuitMsg to the next hop
  210. nexthopchannel.send_msg(
  211. VanillaCreateCircuitMsg(newcircid, cell.ntor_request))
  212. class TelescopingExtendCircuitHandler:
  213. """A handler for TelescopingExtendCircuitCell cells. It allocates a new
  214. circuit id on the Channel to the requested next hop, connects the
  215. existing and new circuits together, and forwards a
  216. TelescopingCreateCircuitMsg to the next hop."""
  217. def received_cell(self, circhandler, cell):
  218. # Remove ourselves from handling a second
  219. # TelescopingExtendCircuitCell on this circuit
  220. circhandler.replace_celltype_handler(TelescopingExtendCircuitCell, None)
  221. # Allocate a new circuit id to the requested next hop
  222. channelmgr = circhandler.channel.channelmgr
  223. nexthopchannel = channelmgr.get_channel_to(cell.hopaddr)
  224. print("WARNING: Unimplemented! " + cell.hopaddr)
  225. #newcircid, newcirchandler = nexthopchannel.new_circuit()
  226. print("WARNING: Unimplemented! TelescopingExtendCircuitHandler")
  227. class VanillaCreatedRelayHandler:
  228. """Handle a VanillaCreatedCircuitCell received by a _relay_ that
  229. recently received a VanillaExtendCircuitCell from a client, and so
  230. forwarded a VanillaCreateCircuitCell to the next hop."""
  231. def received_cell(self, circhandler, cell):
  232. # Remove ourselves from handling a second
  233. # VanillaCreatedCircuitCell on this circuit
  234. circhandler.replace_celltype_handler(VanillaCreatedCircuitCell, None)
  235. # Just forward a VanillaExtendedCircuitCell back towards the
  236. # client
  237. circhandler.adjacent_circuit_handler.send_cell(
  238. VanillaExtendedCircuitCell(cell.ntor_reply))
  239. class CircuitHandler:
  240. """A class for managing sending and receiving encrypted cells on a
  241. particular circuit."""
  242. class NoCryptLayer:
  243. def encrypt_msg(self, msg): return msg
  244. def decrypt_msg(self, msg): return msg
  245. class CryptLayer:
  246. def __init__(self, enckey, deckey, next_layer):
  247. self.enckey = enckey
  248. self.deckey = deckey
  249. self.next_layer = next_layer
  250. def encrypt_msg(self, msg):
  251. return self.next_layer.encrypt_msg(EncryptedCell(self.enckey, msg))
  252. def decrypt_msg(self, msg):
  253. return self.next_layer.decrypt_msg(msg).decrypt(self.deckey)
  254. def __init__(self, channel, circid):
  255. self.channel = channel
  256. self.circid = circid
  257. # The list of relay descriptors that form the circuit so far
  258. # (client side only)
  259. self.circuit_descs = []
  260. # The dispatch table is indexed by type, and the values are
  261. # objects with received_cell(circhandler, cell) methods.
  262. self.cell_dispatch_table = dict()
  263. # The topmost crypt layer. This is an object with
  264. # encrypt_msg(msg) and decrypt_msg(msg) methods that returns the
  265. # en/decrypted messages respectively. Messages are encrypted
  266. # starting with the last layer that was added (the keys for the
  267. # furthest away relay in the circuit) and are decrypted starting
  268. # with the first layer that was added (the keys for the guard).
  269. self.crypt_layer = self.NoCryptLayer()
  270. # The adjacent CircuitHandler that's connected to this one. If
  271. # we get a cell on one, we forward it to the other (if it's not
  272. # meant for us to handle directly)
  273. self.adjacent_circuit_handler = None
  274. # The function to call when this circuit closes
  275. self.closer = lambda: self.channel.circuithandlers.pop(circid)
  276. def close(self):
  277. """Close the circuit. Sends a CloseCell on the circuit (and its
  278. adjacent circuit, if present) and closes both."""
  279. adjcirchandler = self.adjacent_circuit_handler
  280. self.adjacent_circuit_handler = None
  281. if adjcirchandler is not None:
  282. adjcirchandler.adjacent_circuit_handler = None
  283. self.closer()
  284. self.channel_send_cell(CloseCell())
  285. if adjcirchandler is not None:
  286. adjcirchandler.closer()
  287. adjcirchandler.channel_send_cell(CloseCell())
  288. def send_cell(self, cell):
  289. """Send a cell on this circuit, encrypting as needed."""
  290. self.channel_send_cell(self.crypt_layer.encrypt_msg(cell))
  291. def channel_send_cell(self, cell):
  292. """Send a cell on this circuit directly without encrypting it
  293. first."""
  294. self.channel.send_msg(CircuitCellMsg(self.circid, cell))
  295. def received_cell(self, cell):
  296. """A cell has been received on this circuit. Dispatch it
  297. according to its type."""
  298. if isinstance(cell, EncryptedCell):
  299. cell = self.crypt_layer.decrypt_msg(cell)
  300. print("CircuitHandler: %s received cell %s on circuit %d from %s" % (self.channel.channelmgr.myaddr, cell, self.circid, self.channel.peer.channelmgr.myaddr))
  301. # If it's still encrypted, it's for sure meant for forwarding to
  302. # our adjacent hop, which had better exist.
  303. if isinstance(cell, EncryptedCell):
  304. self.adjacent_circuit_handler.send_cell(cell)
  305. else:
  306. # This is a plaintext cell meant for us. Handle it
  307. # according to the table.
  308. celltype = type(cell)
  309. if celltype in self.cell_dispatch_table:
  310. self.cell_dispatch_table[celltype].received_cell(self, cell)
  311. elif isinstance(cell, StringCell):
  312. # Default handler; just print the message in the cell
  313. print("CircuitHandler: %s received '%s' on circuit %d from %s" \
  314. % (self.channel.channelmgr.myaddr, cell,
  315. self.circid, self.channel.peer.channelmgr.myaddr))
  316. elif isinstance(cell, CloseCell):
  317. # Forward the CloseCell (without encryption) to the
  318. # adjacent circuit, if any, and close both this and the
  319. # adjacent circuit
  320. adjcirchandler = self.adjacent_circuit_handler
  321. self.adjacent_circuit_handler = None
  322. if adjcirchandler is not None:
  323. adjcirchandler.adjacent_circuit_handler = None
  324. self.closer()
  325. if adjcirchandler is not None:
  326. adjcirchandler.closer()
  327. adjcirchandler.channel_send_cell(cell)
  328. else:
  329. # I don't know how to handle this cell?
  330. raise ValueError("CircuitHandler: %s received unknown cell type %s on circuit %d from %s" \
  331. % (self.channel.channelmgr.myaddr, cell,
  332. self.circid, self.channel.peer.channelmgr.myaddr))
  333. def replace_celltype_handler(self, celltype, handler):
  334. """Add an object with a received_cell(circhandler, cell) method
  335. to the cell dispatch table. It replaces anything that's already
  336. there. Passing None as the handler removes the dispatcher for
  337. that cell type."""
  338. if handler is None:
  339. del self.cell_dispatch_table[celltype]
  340. else:
  341. self.cell_dispatch_table[celltype] = handler
  342. def add_crypt_layer(self, enckey, deckey):
  343. """Add a processing layer to this CircuitHandler so that cells
  344. we send will get encrypted with the first given key, and cells
  345. we receive will be decrypted with the other given key."""
  346. current_crypt_layer = self.crypt_layer
  347. self.crypt_layer = self.CryptLayer(enckey, deckey, current_crypt_layer)
  348. class Channel(network.Connection):
  349. """A class representing a channel between a relay and either a
  350. client or a relay, transporting cells from various circuits."""
  351. def __init__(self):
  352. super().__init__()
  353. # The RelayChannelManager managing this Channel
  354. self.channelmgr = None
  355. # The Channel at the other end
  356. self.peer = None
  357. # The function to call when the connection closes
  358. self.closer = lambda: None
  359. # The next circuit id to use on this channel. The party that
  360. # opened the channel uses even numbers; the receiving party uses
  361. # odd numbers.
  362. self.next_circid = None
  363. # A map for CircuitHandlers to use for each open circuit on the
  364. # channel
  365. self.circuithandlers = dict()
  366. def closed(self):
  367. # Close each circuithandler we're managing
  368. while self.circuithandlers:
  369. chitems = iter(self.circuithandlers.items())
  370. circid, circhandler = next(chitems)
  371. print('closing circuit', circid)
  372. circhandler.close()
  373. self.closer()
  374. self.peer = None
  375. def close(self):
  376. peer = self.peer
  377. self.closed()
  378. if peer is not None and peer is not self:
  379. peer.closed()
  380. def new_circuit(self):
  381. """Allocate a new circuit on this channel, returning the new
  382. circuit's id and the new CircuitHandler."""
  383. circid = self.next_circid
  384. self.next_circid += 2
  385. circuithandler = CircuitHandler(self, circid)
  386. self.circuithandlers[circid] = circuithandler
  387. return circid, circuithandler
  388. def new_circuit_with_circid(self, circid):
  389. """Allocate a new circuit on this channel, with the circuit id
  390. received from our peer. Return the new CircuitHandler"""
  391. circuithandler = CircuitHandler(self, circid)
  392. self.circuithandlers[circid] = circuithandler
  393. return circuithandler
  394. def send_cell(self, circid, cell):
  395. """Send the given message on the given circuit, encrypting or
  396. decrypting as needed."""
  397. self.circuithandlers[circid].send_cell(cell)
  398. def send_raw_cell(self, circid, cell):
  399. """Send the given message, tagged for the given circuit id. No
  400. encryption or decryption is done."""
  401. self.send_msg(CircuitCellMsg(self.circid, self.cell))
  402. def send_msg(self, msg):
  403. """Send the given NetMsg on the channel."""
  404. self.channelmgr.perfstats.bytes_sent += msg.size()
  405. self.peer.received(self.channelmgr.myaddr, msg)
  406. def received(self, peeraddr, msg):
  407. """Callback when a message is received from the network."""
  408. print('Channel: %s received %s from %s' % (self.channelmgr.myaddr, msg, peeraddr))
  409. self.channelmgr.perfstats.bytes_received += msg.size()
  410. if isinstance(msg, CircuitCellMsg):
  411. circid, cell = msg.circid, msg.cell
  412. self.circuithandlers[circid].received_cell(cell)
  413. else:
  414. self.channelmgr.received_msg(msg, peeraddr, self)
  415. class ChannelManager:
  416. """The class that manages the channels to other relays and clients.
  417. Relays and clients both use subclasses of this class to both create
  418. on-demand channels to relays, to gracefully handle the closing of
  419. channels, and to handle commands received over the channels."""
  420. def __init__(self, myaddr, dirauthaddrs, perfstats):
  421. # A dictionary of Channels to other hosts, indexed by NetAddr
  422. self.channels = dict()
  423. self.myaddr = myaddr
  424. self.dirauthaddrs = dirauthaddrs
  425. self.consensus = None
  426. self.relaypicker = None
  427. self.perfstats = perfstats
  428. def terminate(self):
  429. """Close all connections we're managing."""
  430. while self.channels:
  431. channelitems = iter(self.channels.items())
  432. addr, channel = next(channelitems)
  433. print('closing channel', addr, channel)
  434. channel.close()
  435. def add_channel(self, channel, peeraddr):
  436. """Add the given channel to the list of channels we are
  437. managing. If we are already managing a channel to the same
  438. peer, close it first."""
  439. if peeraddr in self.channels:
  440. self.channels[peeraddr].close()
  441. channel.channelmgr = self
  442. self.channels[peeraddr] = channel
  443. channel.closer = lambda: self.channels.pop(peeraddr)
  444. def get_channel_to(self, addr):
  445. """Get the Channel connected to the given NetAddr, creating one
  446. if none exists right now."""
  447. if addr in self.channels:
  448. return self.channels[addr]
  449. # Create the new channel
  450. print('getting channel from',self.myaddr,'to',addr)
  451. newchannel = network.thenetwork.connect(self.myaddr, addr,
  452. self.perfstats)
  453. print('got channel from',self.myaddr,'to',addr)
  454. self.channels[addr] = newchannel
  455. newchannel.closer = lambda: self.channels.pop(addr)
  456. newchannel.channelmgr = self
  457. return newchannel
  458. def received_msg(self, msg, peeraddr, channel):
  459. """Callback when a NetMsg not specific to a circuit is
  460. received."""
  461. print("ChannelManager: Node %s received msg %s from %s" % (self.myaddr, msg, peeraddr))
  462. def received_cell(self, circid, cell, peeraddr, channel):
  463. """Callback with a circuit-specific cell is received."""
  464. print("ChannelManager: Node %s received cell on circ %d: %s from %s" % (self.myaddr, circid, cell, peeraddr))
  465. def send_msg(self, msg, peeraddr):
  466. """Send a message to the peer with the given address."""
  467. channel = self.get_channel_to(peeraddr)
  468. channel.send_msg(msg)
  469. def send_cell(self, circid, cell, peeraddr):
  470. """Send a cell on the given circuit to the peer with the given
  471. address."""
  472. channel = self.get_channel_to(peeraddr)
  473. channel.send_cell(circid, cell)
  474. class RelayChannelManager(ChannelManager):
  475. """The subclass of ChannelManager for relays."""
  476. def __init__(self, myaddr, dirauthaddrs, onionprivkey, idpubkey, perfstats):
  477. super().__init__(myaddr, dirauthaddrs, perfstats)
  478. self.onionkey = onionprivkey
  479. self.idpubkey = idpubkey
  480. if network.thenetwork.womode != network.WOMode.VANILLA:
  481. self.endive = None
  482. def get_consensus(self):
  483. """Download a fresh consensus (and ENDIVE if using Walking
  484. Onions) from a random dirauth."""
  485. a = random.choice(self.dirauthaddrs)
  486. c = network.thenetwork.connect(self, a, self.perfstats)
  487. if network.thenetwork.womode == network.WOMode.VANILLA:
  488. if self.consensus is not None and \
  489. len(self.consensus.consdict['relays']) > 0:
  490. self.consensus = c.getconsensusdiff()
  491. else:
  492. self.consensus = c.getconsensus()
  493. self.relaypicker = dirauth.Consensus.verify(self.consensus,
  494. network.thenetwork.dirauthkeys(), self.perfstats)
  495. else:
  496. self.consensus = c.getconsensus()
  497. if self.endive is not None and \
  498. len(self.endive.enddict['snips']) > 0:
  499. self.endive = c.getendivediff()
  500. else:
  501. self.endive = c.getendive()
  502. self.relaypicker = dirauth.ENDIVE.verify(self.endive,
  503. self.consensus, network.thenetwork.dirauthkeys(),
  504. self.perfstats)
  505. c.close()
  506. def received_msg(self, msg, peeraddr, channel):
  507. """Callback when a NetMsg not specific to a circuit is
  508. received."""
  509. print("RelayChannelManager: Node %s received msg %s from %s" % (self.myaddr, msg, peeraddr))
  510. if isinstance(msg, RelayRandomHopMsg):
  511. if msg.ttl > 0:
  512. # Pick a random next hop from the consensus
  513. nexthop = self.relaypicker.pick_weighted_relay()
  514. if network.thenetwork.womode == network.WOMode.VANILLA:
  515. nextaddr = nexthop.descdict["addr"]
  516. else:
  517. nextaddr = nexthop.snipdict["addr"]
  518. self.send_msg(RelayRandomHopMsg(msg.ttl-1), nextaddr)
  519. elif isinstance(msg, RelayGetConsensusMsg):
  520. self.send_msg(RelayConsensusMsg(self.consensus), peeraddr)
  521. elif isinstance(msg, RelayGetConsensusDiffMsg):
  522. self.send_msg(RelayConsensusDiffMsg(self.consensus), peeraddr)
  523. elif isinstance(msg, VanillaCreateCircuitMsg):
  524. # A new circuit has arrived
  525. circhandler = channel.new_circuit_with_circid(msg.circid)
  526. # Create the ntor reply
  527. reply, secret = NTor.reply(self.onionkey, self.idpubkey,
  528. msg.ntor_request, self.perfstats)
  529. # Set up the circuit to use the shared secret
  530. enckey = nacl.hash.sha256(secret + b'downstream')
  531. deckey = nacl.hash.sha256(secret + b'upstream')
  532. circhandler.add_crypt_layer(enckey, deckey)
  533. # Add a handler for if an Extend Cell arrives (there should
  534. # be at most one on this circuit).
  535. circhandler.replace_celltype_handler(
  536. VanillaExtendCircuitCell, VanillaExtendCircuitHandler())
  537. # Send the ntor reply
  538. self.send_msg(CircuitCellMsg(msg.circid,
  539. VanillaCreatedCircuitCell(reply)), peeraddr)
  540. elif isinstance(msg, TelescopingCreateCircuitMsg):
  541. print("LOG: Received TelescopingCreateCircuitMsg circuit message, handling.")
  542. # A new circuit has arrived
  543. circhandler = channel.new_circuit_with_circid(msg.circid)
  544. # Create the ntor reply
  545. reply, secret = NTor.reply(self.onionkey, self.idpubkey,
  546. msg.ntor_request, self.perfstats)
  547. # Set up the circuit to use the shared secret
  548. enckey = nacl.hash.sha256(secret + b'downstream')
  549. deckey = nacl.hash.sha256(secret + b'upstream')
  550. circhandler.add_crypt_layer(enckey, deckey)
  551. # Add a handler for if an Extend Cell arrives (there should
  552. # be at most one on this circuit).
  553. circhandler.replace_celltype_handler(
  554. TelescopingExtendCircuitCell, TelescopingExtendCircuitHandler())
  555. print("LOG: Sending TelescopingCreatedCircuitMsg circuit message")
  556. # Send the ntor reply
  557. self.send_msg(CircuitCellMsg(msg.circid,
  558. TelescopingCreatedCircuitCell(reply)), peeraddr)
  559. else:
  560. return super().received_msg(msg, peeraddr, channel)
  561. def received_cell(self, circid, cell, peeraddr, channel):
  562. """Callback with a circuit-specific cell is received."""
  563. print("RelayChannelManager: Node %s received cell on circ %d: %s from %s" % (self.myaddr, circid, cell, peeraddr))
  564. return super().received_cell(circid, cell, peeraddr, channel)
  565. class Relay(network.Server):
  566. """The class representing an onion relay."""
  567. def __init__(self, dirauthaddrs, bw, flags):
  568. # Gather performance statistics
  569. self.perfstats = network.PerfStats(network.EntType.RELAY)
  570. self.perfstats.is_bootstrapping = True
  571. # Create the identity and onion keys
  572. self.idkey = nacl.signing.SigningKey.generate()
  573. self.onionkey = nacl.public.PrivateKey.generate()
  574. self.perfstats.keygens += 2
  575. self.name = self.idkey.verify_key.encode(encoder=nacl.encoding.HexEncoder).decode("ascii")
  576. # Bind to the network to get a network address
  577. self.netaddr = network.thenetwork.bind(self)
  578. self.perfstats.name = "Relay at %s" % self.netaddr
  579. # Our bandwidth and flags
  580. self.bw = bw
  581. self.flags = flags
  582. # Register for epoch change notification
  583. network.thenetwork.wantepochticks(self, True, end=True)
  584. network.thenetwork.wantepochticks(self, True)
  585. # Create the RelayChannelManager connection manager
  586. self.channelmgr = RelayChannelManager(self.netaddr, dirauthaddrs,
  587. self.onionkey, self.idkey.verify_key, self.perfstats)
  588. # Initially, we're not a fallback relay
  589. self.is_fallbackrelay = False
  590. self.uploaddesc()
  591. def terminate(self):
  592. """Stop this relay."""
  593. if self.is_fallbackrelay:
  594. # Fallback relays must not (for now) terminate
  595. raise RelayFallbackTerminationError(self)
  596. # Stop listening for epoch ticks
  597. network.thenetwork.wantepochticks(self, False, end=True)
  598. network.thenetwork.wantepochticks(self, False)
  599. # Tell the dirauths we're going away
  600. self.uploaddesc(False)
  601. # Close connections to other relays
  602. self.channelmgr.terminate()
  603. # Stop listening to our own bound port
  604. self.close()
  605. def set_is_fallbackrelay(self, isfallback = True):
  606. """Set this relay to be a fallback relay (or unset if passed
  607. False)."""
  608. self.is_fallbackrelay = isfallback
  609. def epoch_ending(self, epoch):
  610. # Download the new consensus, which will have been created
  611. # already since the dirauths' epoch_ending callbacks happened
  612. # before the relays'.
  613. self.channelmgr.get_consensus()
  614. def newepoch(self, epoch):
  615. self.uploaddesc()
  616. def uploaddesc(self, upload=True):
  617. # Upload the descriptor for the epoch to come, or delete a
  618. # previous upload if upload=False
  619. descdict = dict();
  620. descdict["epoch"] = network.thenetwork.getepoch() + 1
  621. descdict["idkey"] = self.idkey.verify_key
  622. descdict["onionkey"] = self.onionkey.public_key
  623. descdict["addr"] = self.netaddr
  624. descdict["bw"] = self.bw
  625. descdict["flags"] = self.flags
  626. desc = dirauth.RelayDescriptor(descdict)
  627. desc.sign(self.idkey, self.perfstats)
  628. dirauth.RelayDescriptor.verify(desc, self.perfstats)
  629. if upload:
  630. descmsg = dirauth.DirAuthUploadDescMsg(desc)
  631. else:
  632. # Note that this relies on signatures being deterministic;
  633. # otherwise we'd need to save the descriptor we uploaded
  634. # before so we could tell the airauths to delete the exact
  635. # one
  636. descmsg = dirauth.DirAuthDelDescMsg(desc)
  637. # Upload them
  638. for a in self.channelmgr.dirauthaddrs:
  639. c = network.thenetwork.connect(self, a, self.perfstats)
  640. c.sendmsg(descmsg)
  641. c.close()
  642. def connected(self, peer):
  643. """Callback invoked when someone (client or relay) connects to
  644. us. Create a pair of linked Channels and return the peer half
  645. to the peer."""
  646. # Create the linked pair
  647. if peer is self.netaddr:
  648. # A self-loop? We'll allow it.
  649. peerchannel = Channel()
  650. peerchannel.peer = peerchannel
  651. peerchannel.next_circid = 2
  652. return peerchannel
  653. peerchannel = Channel()
  654. ourchannel = Channel()
  655. peerchannel.peer = ourchannel
  656. peerchannel.next_circid = 2
  657. ourchannel.peer = peerchannel
  658. ourchannel.next_circid = 1
  659. # Add our channel to the RelayChannelManager
  660. self.channelmgr.add_channel(ourchannel, peer)
  661. return peerchannel
  662. if __name__ == '__main__':
  663. perfstats = network.PerfStats(network.EntType.NONE)
  664. network.thenetwork.set_wo_style(network.WOMode.TELESCOPING,
  665. network.SNIPAuthMode.THRESHSIG)
  666. # Start some dirauths
  667. numdirauths = 9
  668. dirauthaddrs = []
  669. for i in range(numdirauths):
  670. dira = dirauth.DirAuth(i, numdirauths)
  671. dirauthaddrs.append(dira.netaddr)
  672. # Start some relays
  673. numrelays = 10
  674. relays = []
  675. for i in range(numrelays):
  676. # Relay bandwidths (at least the ones fast enough to get used)
  677. # in the live Tor network (as of Dec 2019) are well approximated
  678. # by (200000-(200000-25000)/3*log10(x)) where x is a
  679. # uniform integer in [1,2500]
  680. x = random.randint(1,2500)
  681. bw = int(200000-(200000-25000)/3*math.log10(x))
  682. relays.append(Relay(dirauthaddrs, bw, 0))
  683. # The fallback relays are a hardcoded list of about 5% of the
  684. # relays, used by clients for bootstrapping
  685. numfallbackrelays = int(numrelays * 0.05) + 1
  686. fallbackrelays = random.sample(relays, numfallbackrelays)
  687. for r in fallbackrelays:
  688. r.set_is_fallbackrelay()
  689. network.thenetwork.setfallbackrelays(fallbackrelays)
  690. # Tick the epoch
  691. network.thenetwork.nextepoch()
  692. print(dirauth.DirAuth.consensus)
  693. print(dirauth.DirAuth.endive)
  694. if network.thenetwork.womode == network.WOMode.VANILLA:
  695. relaypicker = dirauth.Consensus.verify(dirauth.DirAuth.consensus,
  696. network.thenetwork.dirauthkeys(), perfstats)
  697. else:
  698. relaypicker = dirauth.ENDIVE.verify(dirauth.DirAuth.endive,
  699. dirauth.DirAuth.consensus,
  700. network.thenetwork.dirauthkeys(), perfstats)
  701. for s in dirauth.DirAuth.endive.enddict['snips']:
  702. dirauth.SNIP.verify(s, dirauth.DirAuth.consensus,
  703. network.thenetwork.dirauthkeys()[0], perfstats)
  704. print('ticked; epoch=', network.thenetwork.getepoch())
  705. relays[3].channelmgr.send_msg(RelayRandomHopMsg(30), relays[5].netaddr)
  706. # See what channels exist and do a consistency check
  707. for r in relays:
  708. print("%s: %s" % (r.netaddr, [ str(k) for k in r.channelmgr.channels.keys()]))
  709. raddr = r.netaddr
  710. for ad, ch in r.channelmgr.channels.items():
  711. if ch.peer.channelmgr.myaddr != ad:
  712. print('address mismatch:', raddr, ad, ch.peer.channelmgr.myaddr)
  713. if ch.peer.channelmgr.channels[raddr].peer is not ch:
  714. print('asymmetry:', raddr, ad, ch, ch.peer.channelmgr.channels[raddr].peer)
  715. # Stop some relays
  716. relays[3].terminate()
  717. del relays[3]
  718. relays[5].terminate()
  719. del relays[5]
  720. relays[7].terminate()
  721. del relays[7]
  722. # Tick the epoch
  723. network.thenetwork.nextepoch()
  724. print(dirauth.DirAuth.consensus)
  725. print(dirauth.DirAuth.endive)
  726. # See what channels exist and do a consistency check
  727. for r in relays:
  728. print("%s: %s" % (r.netaddr, [ str(k) for k in r.channelmgr.channels.keys()]))
  729. raddr = r.netaddr
  730. for ad, ch in r.channelmgr.channels.items():
  731. if ch.peer.channelmgr.myaddr != ad:
  732. print('address mismatch:', raddr, ad, ch.peer.channelmgr.myaddr)
  733. if ch.peer.channelmgr.channels[raddr].peer is not ch:
  734. print('asymmetry:', raddr, ad, ch, ch.peer.channelmgr.channels[raddr].peer)
  735. channel = relays[3].channelmgr.get_channel_to(relays[5].netaddr)
  736. circid, circhandler = channel.new_circuit()
  737. peerchannel = relays[5].channelmgr.get_channel_to(relays[3].netaddr)
  738. peerchannel.new_circuit_with_circid(circid)
  739. relays[3].channelmgr.send_cell(circid, StringCell("test"), relays[5].netaddr)
  740. idpubkey = relays[1].idkey.verify_key
  741. onionpubkey = relays[1].onionkey.public_key
  742. nt = NTor(perfstats)
  743. req = nt.request()
  744. R, S = NTor.reply(relays[1].onionkey, idpubkey, req, perfstats)
  745. S2 = nt.verify(R, onionpubkey, idpubkey)
  746. print(S == S2)