hstox 0.0.1 → 0.0.2
raw patch · 55 files changed
+6973/−3988 lines, 55 filesdep +MonadRandomdep +clockdep +data-msgpack-typesdep ~data-msgpackdep ~networkdep ~network-msgpack-rpcnew-component:exe:tox-refsut
Dependencies added: MonadRandom, clock, data-msgpack-types, entropy, lens-family, mtl, random, semigroups
Dependency ranges changed: data-msgpack, network, network-msgpack-rpc
Files
- LICENSE +675/−0
- LICENSE.md +0/−12
- hstox.cabal +50/−11
- src/testsuite/Network/Tox/Crypto/BoxSpec.hs +1/−1
- src/testsuite/Network/Tox/Crypto/KeyPairSpec.hs +1/−1
- src/testsuite/Network/Tox/Crypto/KeySpec.hs +11/−11
- src/testsuite/Network/Tox/Crypto/NonceSpec.hs +1/−1
- src/testsuite/Network/Tox/DHT/ClientListSpec.hs +74/−0
- src/testsuite/Network/Tox/DHT/DhtRequestPacketSpec.hs +16/−0
- src/testsuite/Network/Tox/DHT/DhtStateSpec.hs +51/−30
- src/testsuite/Network/Tox/DHT/DistanceSpec.lhs +30/−8
- src/testsuite/Network/Tox/DHT/KBucketsSpec.hs +51/−26
- src/testsuite/Network/Tox/DHT/OperationSpec.hs +89/−0
- src/testsuite/Network/Tox/DHT/PendingRepliesSpec.hs +37/−0
- src/tox-refsut.hs +7/−0
- src/tox-spectest.hs +20/−9
- src/tox/Network/Tox.lhs +3601/−3439
- src/tox/Network/Tox/Application/GroupChats.lhs +388/−0
- src/tox/Network/Tox/Binary.hs +8/−3
- src/tox/Network/Tox/Crypto/Box.lhs +4/−3
- src/tox/Network/Tox/Crypto/CombinedKey.lhs +4/−3
- src/tox/Network/Tox/Crypto/Key.lhs +2/−2
- src/tox/Network/Tox/Crypto/KeyPair.lhs +3/−2
- src/tox/Network/Tox/Crypto/Keyed.hs +48/−0
- src/tox/Network/Tox/Crypto/KeyedT.hs +53/−0
- src/tox/Network/Tox/Crypto/Nonce.lhs +2/−1
- src/tox/Network/Tox/DHT.lhs +18/−127
- src/tox/Network/Tox/DHT/ClientList.lhs +156/−0
- src/tox/Network/Tox/DHT/ClientNode.lhs +38/−0
- src/tox/Network/Tox/DHT/DhtPacket.lhs +42/−29
- src/tox/Network/Tox/DHT/DhtRequestPacket.lhs +70/−0
- src/tox/Network/Tox/DHT/DhtState.lhs +205/−99
- src/tox/Network/Tox/DHT/Distance.lhs +21/−12
- src/tox/Network/Tox/DHT/KBuckets.lhs +82/−110
- src/tox/Network/Tox/DHT/NodeList.lhs +75/−0
- src/tox/Network/Tox/DHT/NodesRequest.lhs +1/−1
- src/tox/Network/Tox/DHT/NodesResponse.lhs +4/−4
- src/tox/Network/Tox/DHT/Operation.lhs +590/−0
- src/tox/Network/Tox/DHT/PendingReplies.lhs +45/−0
- src/tox/Network/Tox/DHT/PingPacket.lhs +1/−1
- src/tox/Network/Tox/DHT/RpcPacket.lhs +6/−7
- src/tox/Network/Tox/DHT/Stamped.hs +54/−0
- src/tox/Network/Tox/Network/MonadRandomBytes.hs +105/−0
- src/tox/Network/Tox/Network/Networked.hs +61/−0
- src/tox/Network/Tox/NodeInfo/HostAddress.lhs +1/−1
- src/tox/Network/Tox/NodeInfo/NodeInfo.lhs +4/−4
- src/tox/Network/Tox/NodeInfo/SocketAddress.lhs +1/−2
- src/tox/Network/Tox/NodeInfo/TransportProtocol.lhs +1/−1
- src/tox/Network/Tox/Protocol/Packet.lhs +0/−1
- src/tox/Network/Tox/Protocol/PacketKind.lhs +46/−21
- src/tox/Network/Tox/Testing.lhs +1/−1
- src/tox/Network/Tox/Time.hs +54/−0
- src/tox/Network/Tox/Timed.hs +31/−0
- src/tox/Network/Tox/TimedT.hs +29/−0
- test/testsuite.hs +4/−4
+ LICENSE view
@@ -0,0 +1,675 @@+ GNU GENERAL PUBLIC LICENSE+ Version 3, 29 June 2007++ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>+ Everyone is permitted to copy and distribute verbatim copies+ of this license document, but changing it is not allowed.++ Preamble++ The GNU General Public License is a free, copyleft license for+software and other kinds of works.++ The licenses for most software and other practical works are designed+to take away your freedom to share and change the works. By contrast,+the GNU General Public License is intended to guarantee your freedom to+share and change all versions of a program--to make sure it remains free+software for all its users. We, the Free Software Foundation, use the+GNU General Public License for most of our software; it applies also to+any other work released this way by its authors. You can apply it to+your programs, too.++ When we speak of free software, we are referring to freedom, not+price. Our General Public Licenses are designed to make sure that you+have the freedom to distribute copies of free software (and charge for+them if you wish), that you receive source code or can get it if you+want it, that you can change the software or use pieces of it in new+free programs, and that you know you can do these things.++ To protect your rights, we need to prevent others from denying you+these rights or asking you to surrender the rights. Therefore, you have+certain responsibilities if you distribute copies of the software, or if+you modify it: responsibilities to respect the freedom of others.++ For example, if you distribute copies of such a program, whether+gratis or for a fee, you must pass on to the recipients the same+freedoms that you received. You must make sure that they, too, receive+or can get the source code. And you must show them these terms so they+know their rights.++ Developers that use the GNU GPL protect your rights with two steps:+(1) assert copyright on the software, and (2) offer you this License+giving you legal permission to copy, distribute and/or modify it.++ For the developers' and authors' protection, the GPL clearly explains+that there is no warranty for this free software. For both users' and+authors' sake, the GPL requires that modified versions be marked as+changed, so that their problems will not be attributed erroneously to+authors of previous versions.++ Some devices are designed to deny users access to install or run+modified versions of the software inside them, although the manufacturer+can do so. This is fundamentally incompatible with the aim of+protecting users' freedom to change the software. The systematic+pattern of such abuse occurs in the area of products for individuals to+use, which is precisely where it is most unacceptable. Therefore, we+have designed this version of the GPL to prohibit the practice for those+products. If such problems arise substantially in other domains, we+stand ready to extend this provision to those domains in future versions+of the GPL, as needed to protect the freedom of users.++ Finally, every program is threatened constantly by software patents.+States should not allow patents to restrict development and use of+software on general-purpose computers, but in those that do, we wish to+avoid the special danger that patents applied to a free program could+make it effectively proprietary. To prevent this, the GPL assures that+patents cannot be used to render the program non-free.++ The precise terms and conditions for copying, distribution and+modification follow.++ TERMS AND CONDITIONS++ 0. Definitions.++ "This License" refers to version 3 of the GNU General Public License.++ "Copyright" also means copyright-like laws that apply to other kinds of+works, such as semiconductor masks.++ "The Program" refers to any copyrightable work licensed under this+License. Each licensee is addressed as "you". "Licensees" and+"recipients" may be individuals or organizations.++ To "modify" a work means to copy from or adapt all or part of the work+in a fashion requiring copyright permission, other than the making of an+exact copy. The resulting work is called a "modified version" of the+earlier work or a work "based on" the earlier work.++ A "covered work" means either the unmodified Program or a work based+on the Program.++ To "propagate" a work means to do anything with it that, without+permission, would make you directly or secondarily liable for+infringement under applicable copyright law, except executing it on a+computer or modifying a private copy. Propagation includes copying,+distribution (with or without modification), making available to the+public, and in some countries other activities as well.++ To "convey" a work means any kind of propagation that enables other+parties to make or receive copies. Mere interaction with a user through+a computer network, with no transfer of a copy, is not conveying.++ An interactive user interface displays "Appropriate Legal Notices"+to the extent that it includes a convenient and prominently visible+feature that (1) displays an appropriate copyright notice, and (2)+tells the user that there is no warranty for the work (except to the+extent that warranties are provided), that licensees may convey the+work under this License, and how to view a copy of this License. If+the interface presents a list of user commands or options, such as a+menu, a prominent item in the list meets this criterion.++ 1. Source Code.++ The "source code" for a work means the preferred form of the work+for making modifications to it. "Object code" means any non-source+form of a work.++ A "Standard Interface" means an interface that either is an official+standard defined by a recognized standards body, or, in the case of+interfaces specified for a particular programming language, one that+is widely used among developers working in that language.++ The "System Libraries" of an executable work include anything, other+than the work as a whole, that (a) is included in the normal form of+packaging a Major Component, but which is not part of that Major+Component, and (b) serves only to enable use of the work with that+Major Component, or to implement a Standard Interface for which an+implementation is available to the public in source code form. A+"Major Component", in this context, means a major essential component+(kernel, window system, and so on) of the specific operating system+(if any) on which the executable work runs, or a compiler used to+produce the work, or an object code interpreter used to run it.++ The "Corresponding Source" for a work in object code form means all+the source code needed to generate, install, and (for an executable+work) run the object code and to modify the work, including scripts to+control those activities. However, it does not include the work's+System Libraries, or general-purpose tools or generally available free+programs which are used unmodified in performing those activities but+which are not part of the work. For example, Corresponding Source+includes interface definition files associated with source files for+the work, and the source code for shared libraries and dynamically+linked subprograms that the work is specifically designed to require,+such as by intimate data communication or control flow between those+subprograms and other parts of the work.++ The Corresponding Source need not include anything that users+can regenerate automatically from other parts of the Corresponding+Source.++ The Corresponding Source for a work in source code form is that+same work.++ 2. Basic Permissions.++ All rights granted under this License are granted for the term of+copyright on the Program, and are irrevocable provided the stated+conditions are met. This License explicitly affirms your unlimited+permission to run the unmodified Program. The output from running a+covered work is covered by this License only if the output, given its+content, constitutes a covered work. This License acknowledges your+rights of fair use or other equivalent, as provided by copyright law.++ You may make, run and propagate covered works that you do not+convey, without conditions so long as your license otherwise remains+in force. You may convey covered works to others for the sole purpose+of having them make modifications exclusively for you, or provide you+with facilities for running those works, provided that you comply with+the terms of this License in conveying all material for which you do+not control copyright. Those thus making or running the covered works+for you must do so exclusively on your behalf, under your direction+and control, on terms that prohibit them from making any copies of+your copyrighted material outside their relationship with you.++ Conveying under any other circumstances is permitted solely under+the conditions stated below. Sublicensing is not allowed; section 10+makes it unnecessary.++ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.++ No covered work shall be deemed part of an effective technological+measure under any applicable law fulfilling obligations under article+11 of the WIPO copyright treaty adopted on 20 December 1996, or+similar laws prohibiting or restricting circumvention of such+measures.++ When you convey a covered work, you waive any legal power to forbid+circumvention of technological measures to the extent such circumvention+is effected by exercising rights under this License with respect to+the covered work, and you disclaim any intention to limit operation or+modification of the work as a means of enforcing, against the work's+users, your or third parties' legal rights to forbid circumvention of+technological measures.++ 4. Conveying Verbatim Copies.++ You may convey verbatim copies of the Program's source code as you+receive it, in any medium, provided that you conspicuously and+appropriately publish on each copy an appropriate copyright notice;+keep intact all notices stating that this License and any+non-permissive terms added in accord with section 7 apply to the code;+keep intact all notices of the absence of any warranty; and give all+recipients a copy of this License along with the Program.++ You may charge any price or no price for each copy that you convey,+and you may offer support or warranty protection for a fee.++ 5. Conveying Modified Source Versions.++ You may convey a work based on the Program, or the modifications to+produce it from the Program, in the form of source code under the+terms of section 4, provided that you also meet all of these conditions:++ a) The work must carry prominent notices stating that you modified+ it, and giving a relevant date.++ b) The work must carry prominent notices stating that it is+ released under this License and any conditions added under section+ 7. This requirement modifies the requirement in section 4 to+ "keep intact all notices".++ c) You must license the entire work, as a whole, under this+ License to anyone who comes into possession of a copy. This+ License will therefore apply, along with any applicable section 7+ additional terms, to the whole of the work, and all its parts,+ regardless of how they are packaged. This License gives no+ permission to license the work in any other way, but it does not+ invalidate such permission if you have separately received it.++ d) If the work has interactive user interfaces, each must display+ Appropriate Legal Notices; however, if the Program has interactive+ interfaces that do not display Appropriate Legal Notices, your+ work need not make them do so.++ A compilation of a covered work with other separate and independent+works, which are not by their nature extensions of the covered work,+and which are not combined with it such as to form a larger program,+in or on a volume of a storage or distribution medium, is called an+"aggregate" if the compilation and its resulting copyright are not+used to limit the access or legal rights of the compilation's users+beyond what the individual works permit. Inclusion of a covered work+in an aggregate does not cause this License to apply to the other+parts of the aggregate.++ 6. Conveying Non-Source Forms.++ You may convey a covered work in object code form under the terms+of sections 4 and 5, provided that you also convey the+machine-readable Corresponding Source under the terms of this License,+in one of these ways:++ a) Convey the object code in, or embodied in, a physical product+ (including a physical distribution medium), accompanied by the+ Corresponding Source fixed on a durable physical medium+ customarily used for software interchange.++ b) Convey the object code in, or embodied in, a physical product+ (including a physical distribution medium), accompanied by a+ written offer, valid for at least three years and valid for as+ long as you offer spare parts or customer support for that product+ model, to give anyone who possesses the object code either (1) a+ copy of the Corresponding Source for all the software in the+ product that is covered by this License, on a durable physical+ medium customarily used for software interchange, for a price no+ more than your reasonable cost of physically performing this+ conveying of source, or (2) access to copy the+ Corresponding Source from a network server at no charge.++ c) Convey individual copies of the object code with a copy of the+ written offer to provide the Corresponding Source. This+ alternative is allowed only occasionally and noncommercially, and+ only if you received the object code with such an offer, in accord+ with subsection 6b.++ d) Convey the object code by offering access from a designated+ place (gratis or for a charge), and offer equivalent access to the+ Corresponding Source in the same way through the same place at no+ further charge. You need not require recipients to copy the+ Corresponding Source along with the object code. If the place to+ copy the object code is a network server, the Corresponding Source+ may be on a different server (operated by you or a third party)+ that supports equivalent copying facilities, provided you maintain+ clear directions next to the object code saying where to find the+ Corresponding Source. Regardless of what server hosts the+ Corresponding Source, you remain obligated to ensure that it is+ available for as long as needed to satisfy these requirements.++ e) Convey the object code using peer-to-peer transmission, provided+ you inform other peers where the object code and Corresponding+ Source of the work are being offered to the general public at no+ charge under subsection 6d.++ A separable portion of the object code, whose source code is excluded+from the Corresponding Source as a System Library, need not be+included in conveying the object code work.++ A "User Product" is either (1) a "consumer product", which means any+tangible personal property which is normally used for personal, family,+or household purposes, or (2) anything designed or sold for incorporation+into a dwelling. In determining whether a product is a consumer product,+doubtful cases shall be resolved in favor of coverage. For a particular+product received by a particular user, "normally used" refers to a+typical or common use of that class of product, regardless of the status+of the particular user or of the way in which the particular user+actually uses, or expects or is expected to use, the product. A product+is a consumer product regardless of whether the product has substantial+commercial, industrial or non-consumer uses, unless such uses represent+the only significant mode of use of the product.++ "Installation Information" for a User Product means any methods,+procedures, authorization keys, or other information required to install+and execute modified versions of a covered work in that User Product from+a modified version of its Corresponding Source. The information must+suffice to ensure that the continued functioning of the modified object+code is in no case prevented or interfered with solely because+modification has been made.++ If you convey an object code work under this section in, or with, or+specifically for use in, a User Product, and the conveying occurs as+part of a transaction in which the right of possession and use of the+User Product is transferred to the recipient in perpetuity or for a+fixed term (regardless of how the transaction is characterized), the+Corresponding Source conveyed under this section must be accompanied+by the Installation Information. But this requirement does not apply+if neither you nor any third party retains the ability to install+modified object code on the User Product (for example, the work has+been installed in ROM).++ The requirement to provide Installation Information does not include a+requirement to continue to provide support service, warranty, or updates+for a work that has been modified or installed by the recipient, or for+the User Product in which it has been modified or installed. Access to a+network may be denied when the modification itself materially and+adversely affects the operation of the network or violates the rules and+protocols for communication across the network.++ Corresponding Source conveyed, and Installation Information provided,+in accord with this section must be in a format that is publicly+documented (and with an implementation available to the public in+source code form), and must require no special password or key for+unpacking, reading or copying.++ 7. Additional Terms.++ "Additional permissions" are terms that supplement the terms of this+License by making exceptions from one or more of its conditions.+Additional permissions that are applicable to the entire Program shall+be treated as though they were included in this License, to the extent+that they are valid under applicable law. If additional permissions+apply only to part of the Program, that part may be used separately+under those permissions, but the entire Program remains governed by+this License without regard to the additional permissions.++ When you convey a copy of a covered work, you may at your option+remove any additional permissions from that copy, or from any part of+it. (Additional permissions may be written to require their own+removal in certain cases when you modify the work.) You may place+additional permissions on material, added by you to a covered work,+for which you have or can give appropriate copyright permission.++ Notwithstanding any other provision of this License, for material you+add to a covered work, you may (if authorized by the copyright holders of+that material) supplement the terms of this License with terms:++ a) Disclaiming warranty or limiting liability differently from the+ terms of sections 15 and 16 of this License; or++ b) Requiring preservation of specified reasonable legal notices or+ author attributions in that material or in the Appropriate Legal+ Notices displayed by works containing it; or++ c) Prohibiting misrepresentation of the origin of that material, or+ requiring that modified versions of such material be marked in+ reasonable ways as different from the original version; or++ d) Limiting the use for publicity purposes of names of licensors or+ authors of the material; or++ e) Declining to grant rights under trademark law for use of some+ trade names, trademarks, or service marks; or++ f) Requiring indemnification of licensors and authors of that+ material by anyone who conveys the material (or modified versions of+ it) with contractual assumptions of liability to the recipient, for+ any liability that these contractual assumptions directly impose on+ those licensors and authors.++ All other non-permissive additional terms are considered "further+restrictions" within the meaning of section 10. If the Program as you+received it, or any part of it, contains a notice stating that it is+governed by this License along with a term that is a further+restriction, you may remove that term. If a license document contains+a further restriction but permits relicensing or conveying under this+License, you may add to a covered work material governed by the terms+of that license document, provided that the further restriction does+not survive such relicensing or conveying.++ If you add terms to a covered work in accord with this section, you+must place, in the relevant source files, a statement of the+additional terms that apply to those files, or a notice indicating+where to find the applicable terms.++ Additional terms, permissive or non-permissive, may be stated in the+form of a separately written license, or stated as exceptions;+the above requirements apply either way.++ 8. Termination.++ You may not propagate or modify a covered work except as expressly+provided under this License. Any attempt otherwise to propagate or+modify it is void, and will automatically terminate your rights under+this License (including any patent licenses granted under the third+paragraph of section 11).++ However, if you cease all violation of this License, then your+license from a particular copyright holder is reinstated (a)+provisionally, unless and until the copyright holder explicitly and+finally terminates your license, and (b) permanently, if the copyright+holder fails to notify you of the violation by some reasonable means+prior to 60 days after the cessation.++ Moreover, your license from a particular copyright holder is+reinstated permanently if the copyright holder notifies you of the+violation by some reasonable means, this is the first time you have+received notice of violation of this License (for any work) from that+copyright holder, and you cure the violation prior to 30 days after+your receipt of the notice.++ Termination of your rights under this section does not terminate the+licenses of parties who have received copies or rights from you under+this License. If your rights have been terminated and not permanently+reinstated, you do not qualify to receive new licenses for the same+material under section 10.++ 9. Acceptance Not Required for Having Copies.++ You are not required to accept this License in order to receive or+run a copy of the Program. Ancillary propagation of a covered work+occurring solely as a consequence of using peer-to-peer transmission+to receive a copy likewise does not require acceptance. However,+nothing other than this License grants you permission to propagate or+modify any covered work. These actions infringe copyright if you do+not accept this License. Therefore, by modifying or propagating a+covered work, you indicate your acceptance of this License to do so.++ 10. Automatic Licensing of Downstream Recipients.++ Each time you convey a covered work, the recipient automatically+receives a license from the original licensors, to run, modify and+propagate that work, subject to this License. You are not responsible+for enforcing compliance by third parties with this License.++ An "entity transaction" is a transaction transferring control of an+organization, or substantially all assets of one, or subdividing an+organization, or merging organizations. If propagation of a covered+work results from an entity transaction, each party to that+transaction who receives a copy of the work also receives whatever+licenses to the work the party's predecessor in interest had or could+give under the previous paragraph, plus a right to possession of the+Corresponding Source of the work from the predecessor in interest, if+the predecessor has it or can get it with reasonable efforts.++ You may not impose any further restrictions on the exercise of the+rights granted or affirmed under this License. For example, you may+not impose a license fee, royalty, or other charge for exercise of+rights granted under this License, and you may not initiate litigation+(including a cross-claim or counterclaim in a lawsuit) alleging that+any patent claim is infringed by making, using, selling, offering for+sale, or importing the Program or any portion of it.++ 11. Patents.++ A "contributor" is a copyright holder who authorizes use under this+License of the Program or a work on which the Program is based. The+work thus licensed is called the contributor's "contributor version".++ A contributor's "essential patent claims" are all patent claims+owned or controlled by the contributor, whether already acquired or+hereafter acquired, that would be infringed by some manner, permitted+by this License, of making, using, or selling its contributor version,+but do not include claims that would be infringed only as a+consequence of further modification of the contributor version. For+purposes of this definition, "control" includes the right to grant+patent sublicenses in a manner consistent with the requirements of+this License.++ Each contributor grants you a non-exclusive, worldwide, royalty-free+patent license under the contributor's essential patent claims, to+make, use, sell, offer for sale, import and otherwise run, modify and+propagate the contents of its contributor version.++ In the following three paragraphs, a "patent license" is any express+agreement or commitment, however denominated, not to enforce a patent+(such as an express permission to practice a patent or covenant not to+sue for patent infringement). To "grant" such a patent license to a+party means to make such an agreement or commitment not to enforce a+patent against the party.++ If you convey a covered work, knowingly relying on a patent license,+and the Corresponding Source of the work is not available for anyone+to copy, free of charge and under the terms of this License, through a+publicly available network server or other readily accessible means,+then you must either (1) cause the Corresponding Source to be so+available, or (2) arrange to deprive yourself of the benefit of the+patent license for this particular work, or (3) arrange, in a manner+consistent with the requirements of this License, to extend the patent+license to downstream recipients. "Knowingly relying" means you have+actual knowledge that, but for the patent license, your conveying the+covered work in a country, or your recipient's use of the covered work+in a country, would infringe one or more identifiable patents in that+country that you have reason to believe are valid.++ If, pursuant to or in connection with a single transaction or+arrangement, you convey, or propagate by procuring conveyance of, a+covered work, and grant a patent license to some of the parties+receiving the covered work authorizing them to use, propagate, modify+or convey a specific copy of the covered work, then the patent license+you grant is automatically extended to all recipients of the covered+work and works based on it.++ A patent license is "discriminatory" if it does not include within+the scope of its coverage, prohibits the exercise of, or is+conditioned on the non-exercise of one or more of the rights that are+specifically granted under this License. You may not convey a covered+work if you are a party to an arrangement with a third party that is+in the business of distributing software, under which you make payment+to the third party based on the extent of your activity of conveying+the work, and under which the third party grants, to any of the+parties who would receive the covered work from you, a discriminatory+patent license (a) in connection with copies of the covered work+conveyed by you (or copies made from those copies), or (b) primarily+for and in connection with specific products or compilations that+contain the covered work, unless you entered into that arrangement,+or that patent license was granted, prior to 28 March 2007.++ Nothing in this License shall be construed as excluding or limiting+any implied license or other defenses to infringement that may+otherwise be available to you under applicable patent law.++ 12. No Surrender of Others' Freedom.++ If conditions are imposed on you (whether by court order, agreement or+otherwise) that contradict the conditions of this License, they do not+excuse you from the conditions of this License. If you cannot convey a+covered work so as to satisfy simultaneously your obligations under this+License and any other pertinent obligations, then as a consequence you may+not convey it at all. For example, if you agree to terms that obligate you+to collect a royalty for further conveying from those to whom you convey+the Program, the only way you could satisfy both those terms and this+License would be to refrain entirely from conveying the Program.++ 13. Use with the GNU Affero General Public License.++ Notwithstanding any other provision of this License, you have+permission to link or combine any covered work with a work licensed+under version 3 of the GNU Affero General Public License into a single+combined work, and to convey the resulting work. The terms of this+License will continue to apply to the part which is the covered work,+but the special requirements of the GNU Affero General Public License,+section 13, concerning interaction through a network will apply to the+combination as such.++ 14. Revised Versions of this License.++ The Free Software Foundation may publish revised and/or new versions of+the GNU General Public License from time to time. Such new versions will+be similar in spirit to the present version, but may differ in detail to+address new problems or concerns.++ Each version is given a distinguishing version number. If the+Program specifies that a certain numbered version of the GNU General+Public License "or any later version" applies to it, you have the+option of following the terms and conditions either of that numbered+version or of any later version published by the Free Software+Foundation. If the Program does not specify a version number of the+GNU General Public License, you may choose any version ever published+by the Free Software Foundation.++ If the Program specifies that a proxy can decide which future+versions of the GNU General Public License can be used, that proxy's+public statement of acceptance of a version permanently authorizes you+to choose that version for the Program.++ Later license versions may give you additional or different+permissions. However, no additional obligations are imposed on any+author or copyright holder as a result of your choosing to follow a+later version.++ 15. Disclaimer of Warranty.++ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.++ 16. Limitation of Liability.++ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF+SUCH DAMAGES.++ 17. Interpretation of Sections 15 and 16.++ If the disclaimer of warranty and limitation of liability provided+above cannot be given local legal effect according to their terms,+reviewing courts shall apply local law that most closely approximates+an absolute waiver of all civil liability in connection with the+Program, unless a warranty or assumption of liability accompanies a+copy of the Program in return for a fee.++ END OF TERMS AND CONDITIONS++ How to Apply These Terms to Your New Programs++ If you develop a new program, and you want it to be of the greatest+possible use to the public, the best way to achieve this is to make it+free software which everyone can redistribute and change under these terms.++ To do so, attach the following notices to the program. It is safest+to attach them to the start of each source file to most effectively+state the exclusion of warranty; and each file should have at least+the "copyright" line and a pointer to where the full notice is found.++ <one line to give the program's name and a brief idea of what it does.>+ Copyright (C) <year> <name of author>++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.++Also add information on how to contact you by electronic and paper mail.++ If the program does terminal interaction, make it output a short+notice like this when it starts in an interactive mode:++ <program> Copyright (C) <year> <name of author>+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.+ This is free software, and you are welcome to redistribute it+ under certain conditions; type `show c' for details.++The hypothetical commands `show w' and `show c' should show the appropriate+parts of the General Public License. Of course, your program's commands+might be different; for a GUI interface, you would use an "about box".++ You should also get your employer (if you work as a programmer) or school,+if any, to sign a "copyright disclaimer" for the program, if necessary.+For more information on this, and how to apply and follow the GNU GPL, see+<http://www.gnu.org/licenses/>.++ The GNU General Public License does not permit incorporating your program+into proprietary programs. If your program is a subroutine library, you+may consider it more useful to permit linking proprietary applications with+the library. If this is what you want to do, use the GNU Lesser General+Public License instead of this License. But first, please read+<http://www.gnu.org/philosophy/why-not-lgpl.html>.+
− LICENSE.md
@@ -1,12 +0,0 @@-The software is licensed under [AGPLv3](licenses/gnu-agpl-v3.0.md).--The documentation is the Markdown text extracted from the Software. The parts-of the extracted documentation that were originally part of the-[Tox-Docs](https://github.com/Tox-Docs/Text/commit/8f77b8a7c935871eea48cc5abeef26dfa42a108a)-repository are licensed under [MIT](licenses/mit.md). The parts that were not-originally part of that repository are licensed under AGPLv3.--The copyright of all contributions is owned by their respective contributors.-The TokTok project will not accept, request, or perform copyright assignments.--Contact Iphigenia Df <iphydf@gmail.com> for license inquiries.
hstox.cabal view
@@ -1,20 +1,20 @@ name: hstox synopsis: A Tox protocol implementation in Haskell-version: 0.0.1+version: 0.0.2 cabal-version: >= 1.10-license: AGPL-3-license-file: LICENSE.md+license: GPL-3+license-file: LICENSE build-type: Simple author: iphy maintainer: iphy-copyright: © 2016 iphy-homepage: http://hstox.github.io+copyright: © 2016-2018 iphy+homepage: https://toktok.github.io category: Network description: A Tox protocol implementation in Haskell source-repository head type: git- location: https://github.com/iphydf/hstox+ location: https://github.com/TokTok/hs-toxcore flag library-only description: Build only library, no executables or tests.@@ -34,37 +34,55 @@ , binary , binary-bits , bytestring+ , clock >= 0.3 , containers- , data-msgpack+ , data-msgpack >= 0.0.12+ , data-msgpack-types >= 0.0.2+ , entropy , integer-gmp , iproute- -- network-2.6.2.1 is the last version that can be cross-compiled, so we install- -- it explicitly here.- , network <= 2.6.2.1- , network-msgpack-rpc >= 0.0.3+ , lens-family+ , MonadRandom+ , mtl+ , network+ , network-msgpack-rpc >= 0.0.5 , saltine+ , semigroups+ , random , tagged , text , transformers exposed-modules: Network.Tox+ Network.Tox.Application.GroupChats Network.Tox.Binary Network.Tox.Crypto Network.Tox.Crypto.Box Network.Tox.Crypto.CombinedKey Network.Tox.Crypto.Key+ Network.Tox.Crypto.Keyed+ Network.Tox.Crypto.KeyedT Network.Tox.Crypto.KeyPair Network.Tox.Crypto.Nonce Network.Tox.DHT+ Network.Tox.DHT.ClientList+ Network.Tox.DHT.ClientNode Network.Tox.DHT.DhtPacket+ Network.Tox.DHT.DhtRequestPacket Network.Tox.DHT.DhtState Network.Tox.DHT.Distance Network.Tox.DHT.KBuckets+ Network.Tox.DHT.NodeList Network.Tox.DHT.NodesRequest Network.Tox.DHT.NodesResponse+ Network.Tox.DHT.Operation+ Network.Tox.DHT.PendingReplies Network.Tox.DHT.PingPacket Network.Tox.DHT.RpcPacket+ Network.Tox.DHT.Stamped Network.Tox.Encoding+ Network.Tox.Network.Networked+ Network.Tox.Network.MonadRandomBytes Network.Tox.NodeInfo Network.Tox.NodeInfo.HostAddress Network.Tox.NodeInfo.NodeInfo@@ -75,6 +93,9 @@ Network.Tox.Protocol.Packet Network.Tox.Protocol.PacketKind Network.Tox.Testing+ Network.Tox.Time+ Network.Tox.Timed+ Network.Tox.TimedT if !flag(library-only) hs-source-dirs: src/testsuite@@ -87,12 +108,16 @@ Network.Tox.Crypto.KeySpec Network.Tox.Crypto.NonceSpec Network.Tox.CryptoSpec+ Network.Tox.DHT.ClientListSpec Network.Tox.DHT.DhtPacketSpec+ Network.Tox.DHT.DhtRequestPacketSpec Network.Tox.DHT.DhtStateSpec Network.Tox.DHT.DistanceSpec Network.Tox.DHT.KBucketsSpec Network.Tox.DHT.NodesRequestSpec Network.Tox.DHT.NodesResponseSpec+ Network.Tox.DHT.OperationSpec+ Network.Tox.DHT.PendingRepliesSpec Network.Tox.DHT.PingPacketSpec Network.Tox.DHT.RpcPacketSpec Network.Tox.DHTSpec@@ -109,6 +134,20 @@ Network.Tox.RPCTest exposed-modules: ToxTestSuite++executable tox-refsut+ default-language: Haskell2010+ hs-source-dirs:+ src+ ghc-options:+ -Wall+ -fno-warn-unused-imports+ build-depends:+ base < 5+ , hstox+ main-is: tox-refsut.hs+ if flag(library-only)+ buildable: False executable tox-spectest default-language: Haskell2010
src/testsuite/Network/Tox/Crypto/BoxSpec.hs view
@@ -5,7 +5,7 @@ import Control.Monad.IO.Class (liftIO) import qualified Data.ByteString as ByteString-import qualified Data.MessagePack.Result as R+import qualified Data.MessagePack.Types.Result as R import Data.Proxy (Proxy (..)) import Network.MessagePack.Rpc (rpc) import Network.Tox.RPCTest (equiv3, equivProp, equivProp3,
src/testsuite/Network/Tox/Crypto/KeyPairSpec.hs view
@@ -4,7 +4,7 @@ import Control.Monad.IO.Class (liftIO) import qualified Crypto.Saltine.Class as Sodium (encode) import Data.Proxy (Proxy (..))-import Network.MessagePack.Rpc (rpc, rpc)+import Network.MessagePack.Rpc (rpc) import Network.Tox.RPCTest (equivProp1, runTest) import Test.Hspec import Test.QuickCheck
src/testsuite/Network/Tox/Crypto/KeySpec.hs view
@@ -5,18 +5,18 @@ import Test.Hspec import Test.QuickCheck -import qualified Crypto.Saltine.Class as Sodium-import Data.Binary (Binary)-import Data.ByteString (ByteString)-import qualified Data.ByteString as ByteString-import qualified Data.MessagePack.Result as R-import Data.Proxy (Proxy (..))-import Data.Typeable (Typeable)-import qualified Network.Tox.Binary as Binary-import Network.Tox.Crypto.Key (Key (..))-import qualified Network.Tox.Crypto.Key as Key+import qualified Crypto.Saltine.Class as Sodium+import Data.Binary (Binary)+import Data.ByteString (ByteString)+import qualified Data.ByteString as ByteString+import qualified Data.MessagePack.Types.Result as R+import Data.Proxy (Proxy (..))+import Data.Typeable (Typeable)+import qualified Network.Tox.Binary as Binary+import Network.Tox.Crypto.Key (Key (..))+import qualified Network.Tox.Crypto.Key as Key import Network.Tox.EncodingSpec-import qualified Text.Read as Read+import qualified Text.Read as Read readMaybe :: String -> Maybe Key.PublicKey
src/testsuite/Network/Tox/Crypto/NonceSpec.hs view
@@ -2,7 +2,7 @@ module Network.Tox.Crypto.NonceSpec where import Control.Monad.IO.Class (liftIO)-import Network.MessagePack.Rpc (rpc, rpc)+import Network.MessagePack.Rpc (rpc) import Network.Tox.RPCTest (equivProp1, runTest) import Test.Hspec import Test.QuickCheck
+ src/testsuite/Network/Tox/DHT/ClientListSpec.hs view
@@ -0,0 +1,74 @@+{-# LANGUAGE Trustworthy #-}+module Network.Tox.DHT.ClientListSpec where++import Test.Hspec+import Test.QuickCheck++import Control.Monad (unless, when)+import Data.List (sort, sortBy)+import qualified Data.Map as Map+import Data.Ord (comparing)+import Data.Proxy (Proxy (..))+import Network.Tox.Crypto.Key (PublicKey)+import Network.Tox.DHT.ClientList (ClientList)+import qualified Network.Tox.DHT.ClientList as ClientList+import qualified Network.Tox.DHT.Distance as Distance+import Network.Tox.EncodingSpec+import qualified Network.Tox.NodeInfo.NodeInfo as NodeInfo+++spec :: Spec+spec = do+ readShowSpec (Proxy :: Proxy ClientList)++ it "has no more than maxSize elements" $+ property $ \clientList ->+ Map.size (ClientList.nodes clientList) `shouldSatisfy` (<= ClientList.maxSize clientList)++ it "removing a node twice has no effect" $+ property $ \baseKey time nodeInfo size ->+ let+ empty = ClientList.empty baseKey+ afterAdd = ClientList.addNode time nodeInfo $ empty size+ afterRemove0 = ClientList.removeNode (NodeInfo.publicKey nodeInfo) afterAdd+ afterRemove1 = ClientList.removeNode (NodeInfo.publicKey nodeInfo) afterRemove0+ in+ afterRemove0 `shouldBe` afterRemove1++ it "adding a node twice has no effect" $+ property $ \baseKey time nodeInfo size ->+ let+ empty = ClientList.empty baseKey+ afterAdd0 = ClientList.addNode time nodeInfo $ empty size+ afterAdd1 = ClientList.addNode time nodeInfo afterAdd0+ in+ afterAdd0 `shouldBe` afterAdd1++ it "adding a non-viable node has no effect" $+ property $ \clientList time nodeInfo ->+ let+ viable = ClientList.viable nodeInfo clientList+ afterAdd = ClientList.addNode time nodeInfo clientList+ in+ unless viable $ afterAdd `shouldBe` clientList++ describe "addNode" $+ it "keeps the k nodes closest to the base key" $+ property $ \clientList time nodeInfo ->+ let+ allNodes = (nodeInfo:) $ ClientList.nodeInfos clientList+ keptNodes = ClientList.nodeInfos $ ClientList.addNode time nodeInfo clientList+ nodeDistance node = Distance.xorDistance (ClientList.baseKey clientList) (NodeInfo.publicKey node)+ sortNodes = sortBy $ comparing nodeDistance+ in+ take (ClientList.maxSize clientList) (sortNodes allNodes) `shouldBe` sortNodes keptNodes++ describe "foldNodes" $+ it "iterates over nodes in order of distance from the base key" $+ property $ \clientList ->+ let+ nodes = reverse $ ClientList.foldNodes (flip (:)) [] clientList+ nodeDistance node = Distance.xorDistance (ClientList.baseKey clientList) (NodeInfo.publicKey node)+ sortNodes = sortBy (comparing nodeDistance)+ in+ nodes `shouldBe` sortNodes nodes
+ src/testsuite/Network/Tox/DHT/DhtRequestPacketSpec.hs view
@@ -0,0 +1,16 @@+{-# LANGUAGE Trustworthy #-}+module Network.Tox.DHT.DhtRequestPacketSpec where++import Test.Hspec+import Test.QuickCheck++import Data.Proxy (Proxy (..))+import Network.Tox.DHT.DhtRequestPacket (DhtRequestPacket (..))+import Network.Tox.EncodingSpec+++spec :: Spec+spec = do+ rpcSpec (Proxy :: Proxy DhtRequestPacket)+ binarySpec (Proxy :: Proxy DhtRequestPacket)+ readShowSpec (Proxy :: Proxy DhtRequestPacket)
src/testsuite/Network/Tox/DHT/DhtStateSpec.hs view
@@ -4,12 +4,16 @@ import Test.Hspec import Test.QuickCheck -import Control.Monad (unless)+import Control.Monad (unless, when)+import Data.List (nub, sort) import Data.Proxy (Proxy (..))+import qualified Data.Set as Set import qualified Network.Tox.Crypto.KeyPair as KeyPair import Network.Tox.DHT.DhtState (DhtState) import qualified Network.Tox.DHT.DhtState as DhtState+import qualified Network.Tox.DHT.Distance as Distance+import qualified Network.Tox.DHT.NodeList as NodeList import Network.Tox.EncodingSpec import qualified Network.Tox.NodeInfo.NodeInfo as NodeInfo @@ -19,10 +23,10 @@ readShowSpec (Proxy :: Proxy DhtState) it "the state can never contain itself" $- property $ \keyPair nodeInfo ->+ property $ \keyPair time nodeInfo -> let- dhtState = DhtState.empty keyPair- afterAdd = DhtState.addNode+ dhtState = DhtState.empty time keyPair+ afterAdd = DhtState.addNode time nodeInfo { NodeInfo.publicKey = KeyPair.publicKey keyPair } dhtState in@@ -30,19 +34,19 @@ describe "adding a node that was not yet contained" $ do it "should result in a different state" $- property $ \keyPair nodeInfo ->+ property $ \keyPair time nodeInfo -> let- dhtState = DhtState.empty keyPair- afterAdd = DhtState.addNode nodeInfo dhtState+ dhtState = DhtState.empty time keyPair+ afterAdd = DhtState.addNode time nodeInfo dhtState in unless (DhtState.containsNode (NodeInfo.publicKey nodeInfo) dhtState) $ afterAdd `shouldNotBe` dhtState it "and removing it yields the same state" $- property $ \keyPair nodeInfo ->+ property $ \keyPair time nodeInfo -> let- dhtState = DhtState.empty keyPair- afterAdd = DhtState.addNode nodeInfo dhtState+ dhtState = DhtState.empty time keyPair+ afterAdd = DhtState.addNode time nodeInfo dhtState afterRemove = DhtState.removeNode (NodeInfo.publicKey nodeInfo) afterAdd in unless (DhtState.containsNode (NodeInfo.publicKey nodeInfo) dhtState) $@@ -50,49 +54,66 @@ describe "adding a node" $ it "and adding it again does not change the state twice" $- property $ \keyPair nodeInfo ->+ property $ \keyPair time nodeInfo -> let- dhtState = DhtState.empty keyPair- afterAdd1 = DhtState.addNode nodeInfo dhtState- afterAdd2 = DhtState.addNode nodeInfo afterAdd1+ dhtState = DhtState.empty time keyPair+ afterAdd1 = DhtState.addNode time nodeInfo dhtState+ afterAdd2 = DhtState.addNode time nodeInfo afterAdd1 in afterAdd1 `shouldBe` afterAdd2 describe "adding a search node" $ do it "should result in a different state" $- property $ \keyPair publicKey ->+ property $ \keyPair time publicKey -> let- dhtState = DhtState.empty keyPair- afterAdd = DhtState.addSearchKey publicKey dhtState+ dhtState = DhtState.empty time keyPair+ afterAdd = DhtState.addSearchKey time publicKey dhtState in afterAdd `shouldNotBe` dhtState it "and removing it yields the same state" $- property $ \keyPair publicKey ->+ property $ \keyPair time publicKey -> let- dhtState = DhtState.empty keyPair- afterAdd = DhtState.addSearchKey publicKey dhtState+ dhtState = DhtState.empty time keyPair+ afterAdd = DhtState.addSearchKey time publicKey dhtState afterRemove = DhtState.removeSearchKey publicKey afterAdd in afterRemove `shouldBe` dhtState it "and adding it again does not change the state twice" $- property $ \keyPair publicKey ->+ property $ \keyPair time publicKey -> let- dhtState = DhtState.empty keyPair- afterAdd1 = DhtState.addSearchKey publicKey dhtState- afterAdd2 = DhtState.addSearchKey publicKey afterAdd1+ dhtState = DhtState.empty time keyPair+ afterAdd1 = DhtState.addSearchKey time publicKey dhtState+ afterAdd2 = DhtState.addSearchKey time publicKey afterAdd1 in afterAdd1 `shouldBe` afterAdd2 - it "and adding a node info for it will not add it to the search entry's k-buckets" $- property $ \keyPair nodeInfo ->+ it "and adding a node info for it will not add it to the search entry's Client List" $+ property $ \keyPair time nodeInfo -> let- dhtState = DhtState.empty keyPair- afterAddSearchKey = DhtState.addSearchKey+ dhtState = DhtState.empty time keyPair+ afterAddSearchKey = DhtState.addSearchKey time (NodeInfo.publicKey nodeInfo) dhtState in- DhtState.size (DhtState.addNode nodeInfo afterAddSearchKey)+ DhtState.size (DhtState.addNode time nodeInfo afterAddSearchKey) `shouldBe`- DhtState.size (DhtState.addNode nodeInfo dhtState)+ DhtState.size (DhtState.addNode time nodeInfo dhtState)++ describe "takeClosestNodesTo" $ do+ it "returns the requested number of nodes if there are enough nodes in the state" $+ property $ \dhtState n publicKey -> n >= 0 ==>+ let+ taken = DhtState.takeClosestNodesTo n publicKey dhtState+ nodes = NodeList.foldNodes (flip Set.insert) Set.empty dhtState+ in+ when (Set.size nodes >= n) $ length taken `shouldBe` n++ it "returns distinct nodes sorted by distance from the given key" $+ property $ \dhtState n publicKey -> n >= 0 ==>+ let+ taken = DhtState.takeClosestNodesTo n publicKey dhtState+ dists = map (Distance.xorDistance publicKey . NodeInfo.publicKey) taken+ in+ dists `shouldBe` (nub . sort) dists
src/testsuite/Network/Tox/DHT/DistanceSpec.lhs view
@@ -14,18 +14,36 @@ \end{code} -The XOR metric \texttt{d} satisfies the required conditions:+XOR is a valid metric, i.e. it satisfies the required conditions: \begin{enumerate}- \item Non-negativity \texttt{d(x, y) >= 0}: Since public keys are Crypto- Numbers, which are by definition positive, their XOR is necessarily- positive.- \item Identity of indiscernibles \texttt{d(x, y) == 0} iff \texttt{x == y}:- The XOR of two integers is zero iff they are equal.- \item Symmetry \texttt{d(x, y) == d(y, x)}: XOR is a symmetric operation.- \item Subadditivity \texttt{d(x, z) <= d(x, y) + d(y, z)}: TODO.+ \item Non-negativity \texttt{distance(x, y) >= 0}: Since public keys are+ Crypto Numbers, which are by definition non-negative, their XOR is necessarily+ non-negative.+ \item Identity of indiscernibles \texttt{distance(x, y) == 0} iff \texttt{x ==+ y}: The XOR of two integers is zero iff they are equal.+ \item Symmetry \texttt{distance(x, y) == distance(y, x)}: XOR is a symmetric+ operation.+ \item Subadditivity \texttt{distance(x, z) <= distance(x, y) + distance(y,+ z)}: follows from associativity, since \texttt{x XOR z = x XOR (y XOR y) XOR+ z = distance(x, y) XOR distance(y, z)} which is not greater than+ \texttt{distance(x, y) + distance(y, z)}. \end{enumerate} +In addition, XOR has other useful properties:++\begin{itemize}+ \item Unidirectionality: given the key \texttt{x} and the distance \texttt{d}+ there exist one and only one key \texttt{y} such that \texttt{distance(x,+ y) = d}.++ The implication is that repeated lookups are likely to pass along the same+ way and thus caching makes sense.++ Source:+ \href{http://pdos.csail.mit.edu/~petar/papers/maymounkov-kademlia-lncs.pdf}{maymounkov-kademlia}+\end{itemize}+ \begin{code} metricSpec :: ( Eq a, Arbitrary a, Show a@@ -144,4 +162,8 @@ in log2 (xorDistance k k) `shouldBe` Nothing + describe "rebaseDistance" $+ it "should satisfy: rebaseDistance a b (xorDistance a c) == xorDistance b c" $+ property $ \a b c ->+ rebaseDistance a b (xorDistance a c) `shouldBe` xorDistance b c \end{code}
src/testsuite/Network/Tox/DHT/KBucketsSpec.hs view
@@ -1,18 +1,25 @@-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE Trustworthy #-} module Network.Tox.DHT.KBucketsSpec where import Test.Hspec import Test.QuickCheck -import Control.Monad (when)+import Control.Monad (unless, when)+import Data.List (sort, sortBy) import qualified Data.Map as Map+import Data.Ord (comparing) import Data.Proxy (Proxy (..)) import Network.Tox.Crypto.Key (PublicKey)+import Network.Tox.DHT.ClientList (ClientList)+import qualified Network.Tox.DHT.ClientList as ClientList import qualified Network.Tox.DHT.Distance as Distance import Network.Tox.DHT.KBuckets (KBuckets) import qualified Network.Tox.DHT.KBuckets as KBuckets+import qualified Network.Tox.DHT.NodeList as NodeList import Network.Tox.EncodingSpec+import Network.Tox.NodeInfo.NodeInfo (NodeInfo) import qualified Network.Tox.NodeInfo.NodeInfo as NodeInfo @@ -21,64 +28,73 @@ read $ "\"" ++ map (const '0') [0 .. pos - 1] ++ digit : map (const '0') [pos .. 63] ++ "\"" -getAllBuckets :: KBuckets -> [[KBuckets.KBucketEntry]]+getAllBuckets :: KBuckets -> [[NodeInfo]] getAllBuckets kBuckets =- map (Map.elems . KBuckets.bucketNodes) (Map.elems (KBuckets.buckets kBuckets))+ map ClientList.nodeInfos (Map.elems (KBuckets.buckets kBuckets)) spec :: Spec spec = do readShowSpec (Proxy :: Proxy KBuckets) - it "has no buckets with more than bucketSize elements" $- property $ \kBuckets ->- case map length $ getAllBuckets kBuckets of- [] -> return ()- sizes -> maximum sizes `shouldSatisfy` (<= KBuckets.bucketSize kBuckets)- it "does not accept adding a NodeInfo with the baseKey as publicKey" $- property $ \kBuckets nodeInfo ->- KBuckets.addNode nodeInfo { NodeInfo.publicKey = KBuckets.baseKey kBuckets } kBuckets+ property $ \kBuckets time nodeInfo ->+ KBuckets.addNode time nodeInfo { NodeInfo.publicKey = KBuckets.baseKey kBuckets } kBuckets `shouldBe` kBuckets it "adding a node to an empty k-buckets always succeeds if baseKey <> nodeKey" $- property $ \baseKey nodeInfo ->+ property $ \baseKey time nodeInfo -> let empty = KBuckets.empty baseKey- kBuckets = KBuckets.addNode nodeInfo empty+ kBuckets = KBuckets.addNode time nodeInfo empty in if baseKey == NodeInfo.publicKey nodeInfo then kBuckets `shouldBe` empty else kBuckets `shouldNotBe` empty it "removing a node twice has no effect" $- property $ \baseKey nodeInfo ->+ property $ \baseKey time nodeInfo -> let empty = KBuckets.empty baseKey- afterAdd = KBuckets.addNode nodeInfo empty+ afterAdd = KBuckets.addNode time nodeInfo empty afterRemove0 = KBuckets.removeNode (NodeInfo.publicKey nodeInfo) afterAdd afterRemove1 = KBuckets.removeNode (NodeInfo.publicKey nodeInfo) afterRemove0 in afterRemove0 `shouldBe` afterRemove1 it "adding a node twice has no effect" $- property $ \baseKey nodeInfo ->+ property $ \baseKey time nodeInfo -> let empty = KBuckets.empty baseKey- afterAdd0 = KBuckets.addNode nodeInfo empty- afterAdd1 = KBuckets.addNode nodeInfo afterAdd0+ afterAdd0 = KBuckets.addNode time nodeInfo empty+ afterAdd1 = KBuckets.addNode time nodeInfo afterAdd0 in afterAdd0 `shouldBe` afterAdd1 - describe "KBucketEntry" $ do- it "contains the same base key as the enclosing KBuckets" $- property $ \kBuckets ->- all (KBuckets.baseKey kBuckets ==) $ concatMap (map KBuckets.entryBaseKey) $ getAllBuckets kBuckets+ it "adding a non-viable node has no effect" $+ property $ \(kBuckets::KBuckets) time nodeInfo ->+ let+ viable = KBuckets.viable nodeInfo kBuckets+ afterAdd = KBuckets.addNode time nodeInfo kBuckets+ in+ unless viable $ afterAdd `shouldBe` kBuckets - it "never contains a NodeInfo with the public key equal to the base key" $+ it "never contains a NodeInfo with the public key equal to the base key" $+ property $ \kBuckets ->+ notElem (KBuckets.baseKey kBuckets) $ concatMap (map NodeInfo.publicKey) $ getAllBuckets kBuckets++ describe "each bucket list" $ do+ it "has maximum size bucketSize" $ property $ \kBuckets ->- notElem (KBuckets.baseKey kBuckets) $ concatMap (map $ NodeInfo.publicKey . KBuckets.entryNode) $ getAllBuckets kBuckets+ mapM_+ (`shouldSatisfy` (== KBuckets.bucketSize kBuckets) . ClientList.maxSize)+ . Map.elems $ KBuckets.buckets kBuckets+ it "has base key baseKey" $+ property $ \kBuckets ->+ mapM_+ (`shouldSatisfy` (== KBuckets.baseKey kBuckets) . ClientList.baseKey)+ . Map.elems $ KBuckets.buckets kBuckets describe "bucketIndex" $ do it "returns an integer between 0 and 255 for any two non-equal keys" $@@ -111,3 +127,12 @@ outputs = Nothing : map Just [0 .. 255] in map (KBuckets.bucketIndex zeroKey) inputs `shouldBe` outputs++ describe "foldNodes" $+ it "iterates over nodes in order of distance from the base key" $+ property $ \kBuckets ->+ let+ nodes = reverse $ NodeList.foldNodes (flip (:)) [] kBuckets+ nodeDistance node = Distance.xorDistance (KBuckets.baseKey kBuckets) (NodeInfo.publicKey node)+ in+ nodes `shouldBe` sortBy (comparing nodeDistance) nodes
+ src/testsuite/Network/Tox/DHT/OperationSpec.hs view
@@ -0,0 +1,89 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE Trustworthy #-}+module Network.Tox.DHT.OperationSpec where++import Test.Hspec+import Test.QuickCheck++import Control.Monad (mzero, when)+import Control.Monad.Writer (execWriterT)+import qualified Data.Map as Map+import Data.Proxy (Proxy (..))++import Network.Tox.Crypto.Key (PublicKey)+import qualified Network.Tox.Crypto.KeyPair as KeyPair+import Network.Tox.DHT.DhtState (DhtState)+import qualified Network.Tox.DHT.DhtState as DhtState+import qualified Network.Tox.DHT.Operation as Operation+import Network.Tox.EncodingSpec+import Network.Tox.NodeInfo.NodeInfo (NodeInfo)+import qualified Network.Tox.NodeInfo.NodeInfo as NodeInfo+import qualified Network.Tox.Time as Time++spec :: Spec+spec = do+ describe "a newly initialised DHT node" $ do+ it "contains no nodes" $+ property $ \time seed ->+ DhtState.size (Operation.initTestDhtState seed time) `shouldBe` 0++ it "has a search list containing initRandomSearches search entries" $+ property $ \time seed ->+ (Map.size . DhtState.dhtSearchList $ Operation.initTestDhtState seed time)+ `shouldBe` Operation.initRandomSearches++ describe "periodic nodes requests" $+ it "are not generated for an empty DHT State" $+ property $ \keyPair time time' seed ->+ let+ dhtState = DhtState.empty time keyPair+ requests = Operation.evalTestDhtNode seed time' dhtState . execWriterT $+ Operation.randomRequests >> Operation.checkNodes+ in+ requests `shouldBe` []++ describe "randomRequests" $ do+ it "generates a single Nodes Request to a node in the close list after randomRequestPeriod" $+ property $ \keyPair time (nodeInfos::[NodeInfo]) seed ->+ let+ dhtState = DhtState.empty time keyPair+ afterAdd = foldr (DhtState.addNode time) dhtState nodeInfos+ time' = time Time.+ Operation.randomRequestPeriod+ randomRequests = Operation.evalTestDhtNode seed time' afterAdd+ . execWriterT $ Operation.randomRequests+ in+ case randomRequests of+ [] -> DhtState.size dhtState `shouldBe` 0+ Operation.RequestInfo nodeInfo publicKey : rs -> do+ rs `shouldSatisfy` null+ nodeInfo `shouldSatisfy` (`elem` nodeInfos)+ publicKey `shouldBe` KeyPair.publicKey (DhtState.dhtKeyPair dhtState)++ it "generates a Nodes Request to a node in a new search list after randomRequestPeriod" $+ property $ \time publicKey dhtState (nodeInfos::[NodeInfo]) seed ->+ let+ afterSearch = DhtState.addSearchKey time publicKey dhtState+ afterAdd = foldr (DhtState.addNode time) afterSearch nodeInfos+ nodeAddedToSearch = not $ all ((== publicKey) . NodeInfo.publicKey) nodeInfos+ time' = time Time.+ Operation.randomRequestPeriod+ randomRequests = Operation.evalTestDhtNode seed time' afterAdd+ . execWriterT $ Operation.randomRequests++ requestIsForSearch (Operation.RequestInfo nodeInfo publicKey') =+ publicKey == publicKey' && nodeInfo `elem` nodeInfos &&+ NodeInfo.publicKey nodeInfo /= publicKey+ in+ when nodeAddedToSearch $+ randomRequests `shouldSatisfy` not . all (not . requestIsForSearch)++ describe "checkNodes" $+ it "generates a Nodes Request to a newly added node after checkPeriod" $+ property $ \time dhtState nodeInfo seed ->+ let+ viable = DhtState.viable nodeInfo dhtState+ afterAdd = DhtState.addNode time nodeInfo dhtState+ time' = time Time.+ Operation.checkPeriod+ checks = Operation.evalTestDhtNode seed time' afterAdd+ . execWriterT $ Operation.checkNodes+ in+ when viable $ map Operation.requestTo checks `shouldSatisfy` (nodeInfo `elem`)
+ src/testsuite/Network/Tox/DHT/PendingRepliesSpec.hs view
@@ -0,0 +1,37 @@+{-# LANGUAGE Trustworthy #-}+module Network.Tox.DHT.PendingRepliesSpec where++import Test.Hspec+import Test.QuickCheck++import Network.Tox.DHT.PendingReplies (PendingReplies)+import Network.Tox.DHT.PendingReplies as PendingReplies+import Network.Tox.DHT.Stamped (Stamped)+import qualified Network.Tox.DHT.Stamped as Stamped++spec :: Spec+spec = do+ it "Accepts a response with the same RequestID iff sent since the cutoff" $+ property $ \time time' node requestID ->+ let+ expecting = PendingReplies.expectReply time node requestID Stamped.empty+ in+ fst (PendingReplies.checkExpectedReply time' node requestID expecting)+ `shouldBe` time' <= time++ it "Rejects a response with a different requestID" $+ property $ \time node requestID requestID' ->+ let+ expecting = PendingReplies.expectReply time node requestID Stamped.empty+ in+ fst (PendingReplies.checkExpectedReply time node requestID' expecting)+ `shouldBe` requestID == requestID'++ it "Doesn't accept the same response twice" $+ property $ \time node requestID ->+ let+ expecting = PendingReplies.expectReply time node requestID Stamped.empty+ accepted = snd $ PendingReplies.checkExpectedReply time node requestID expecting+ in+ fst (PendingReplies.checkExpectedReply time node requestID accepted)+ `shouldBe` False
+ src/tox-refsut.hs view
@@ -0,0 +1,7 @@+module Main (main) where++import Network.Tox.Testing (serve)+++main :: IO ()+main = serve
src/tox-spectest.hs view
@@ -15,15 +15,26 @@ sut : rest -> return (sut, rest) -main :: IO ()-main = do- (sut, args) <- getSutAndArgs+withSut :: String -> IO () -> IO ()+withSut "" action = action+withSut sut action = do -- Start a SUT (System Under Test) process that will listen on port 1234. (_, _, _, sutProc) <- createProcess $ proc sut []- -- 100ms delay to give the SUT time to set up its socket before we try to- -- build connections in the test runner.- threadDelay $ 100 * 1000- -- TestSuite (the test runner) makes connections to port 1234 to communicate- -- with the SUT.- withArgs (["--print-cpu-time", "--color"] ++ args) ToxTestSuite.main+ -- 2 seconds delay to give the SUT time to set up its socket before we try+ -- to build connections in the test runner. This is high, because on Travis,+ -- the SUT can take quite some time to become ready to accept connections.+ -- Ideally, we would probe the SUT every 100ms or so until it starts+ -- accepting RPCs, but that's something we should consider putting into the+ -- RPC library itself.+ threadDelay $ 2 * 1000 * 1000+ action terminateProcess sutProc+++main :: IO ()+main = do+ (sut, args) <- getSutAndArgs+ withSut sut $+ -- TestSuite (the test runner) makes connections to port 1234 to communicate+ -- with the SUT.+ withArgs (["--print-cpu-time", "--color"] ++ args) ToxTestSuite.main
src/tox/Network/Tox.lhs view
@@ -10,3445 +10,3607 @@ give enough guidance to permit a complete and correct implementation of the protocol. -All data types are defined before their first use, and their binary protocol-representation is given. The protocol representations are normative and must-be implemented exactly as specified. For some types, human-readable-representations are suggested. An implementation may choose to provide no such-representation or a different one. The implementation is free to choose any-in-memory representation of the specified types, as long as they can be encoded-to and decoded from the specified protocol representation.--Binary formats are specified in tables with length, type, and content-descriptions. If applicable, specific enumeration types are used, so types may-be self-explanatory in some cases. The length can be either a fixed number in-bytes (e.g. \texttt{32}), a number in bits (e.g. \texttt{7} bit), a choice of-lengths (e.g. \texttt{4 | 16}), or an inclusive range (e.g. \texttt{[0,-100]}). Open ranges are denoted \texttt{[n,]} to mean a minimum length of-\texttt{n} with no specified maximum length.--\section{Integers}--The protocol uses four bounded unsigned integer types. Bounded means they have-a upper bound beyond which incrementing is not defined. The integer types-support modular arithmetic, so overflow wraps around to zero. Unsigned means-their lower bound is 0. Signed integer types are not used. The binary-encoding of all integer types is a fixed-width byte sequence with the integer-encoded in \href{https://en.wikipedia.org/wiki/Endianness}{Big Endian} unless-stated otherwise.--\begin{tabular}{l|l|l|l}- Type name & C type & Length & Upper bound \\- \hline- Word8 & \texttt{uint8_t} & 1 & 255 (0xff) \\- Word16 & \texttt{uint16_t} & 2 & 65535 (0xffff) \\- Word32 & \texttt{uint32_t} & 4 & 4294967295 (0xffffffff) \\- Word64 & \texttt{uint64_t} & 8 & 18446744073709551615 (0xffffffffffffffff) \\-\end{tabular}--\section{Strings}--A String is a data structure used for human readable text. Strings are-sequences of glyphs. A glyph consists of one non-zero-width unicode code point-and zero or more zero-width unicode code points. The human-readable-representation of a String starts and ends with a quotation mark (\texttt{"})-and contains all human-readable glyphs verbatim. Control characters are-represented in an isomorphic human-readable way. I.e. every control character-has exactly one human-readable representation, and a mapping exists from the-human-readable representation to the control character. Therefore, the use of-Unicode Control Characters (U+240x) is not permitted without additional marker.--\input{src/tox/Network/Tox/Crypto.lhs}-\input{src/tox/Network/Tox/NodeInfo.lhs}-\input{src/tox/Network/Tox/Protocol.lhs}-\input{src/tox/Network/Tox/DHT.lhs}--\chapter{LAN discovery}--LAN discovery is a way to discover Tox peers that are on a local network. If-two Tox friends are on a local network, the most efficient way for them to-communicate together is to use the local network. If a Tox client is opened on-a local network in which another Tox client exists then good behavior would be-to bootstrap to the network using the Tox client on the local network. This is-what LAN discovery aims to accomplish.--LAN discovery works by sending a UDP packet through the toxcore UDP socket to-the interface broadcast address on IPv4, the global broadcast address-(255.255.255.255) and the multicast address on IPv6 (FF02::1) on the default-Tox UDP port (33445).--The LAN Discovery packet:--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (33) \\- \texttt{32} & DHT public key \\-\end{tabular}--LAN Discovery packets contain the DHT public key of the sender. When a LAN-Discovery packet is received, a DHT get nodes packet will be sent to the sender-of the packet. This means that the DHT instance will bootstrap itself to every-peer from which it receives one of these packet. Through this mechanism, Tox-clients will bootstrap themselves automatically from other Tox clients running-on the local network.--Toxcore sends these packets every 10 seconds to keep delays low. The packets-could be sent up to every 60 seconds but this would make peer finding over the-network 6 times slower.--LAN discovery enables two friends on a local network to find each other as the-DHT prioritizes LAN addresses over non LAN addresses for DHT peers. Sending a-get node request/bootstrapping from a peer successfully should also add them to-the list of DHT peers if we are searching for them. The peer must not be-immediately added if a LAN discovery packet with a DHT public key that we are-searching for is received as there is no cryptographic proof that this packet-is legitimate and not maliciously crafted. This means that a DHT get node or-ping packet must be sent, and a valid response must be received, before we can-say that this peer has been found.--LAN discovery is how Tox handles and makes everything work well on LAN.--\chapter{Messenger}--Messenger is the module at the top of all the other modules. It sits on top of-\texttt{friend_connection} in the hierarchy of toxcore.--Messenger takes care of sending and receiving messages using the connection-provided by \texttt{friend_connection}. The module provides a way for friends-to connect and makes it usable as an instant messenger. For example, Messenger-lets users set a nickname and status message which it then transmits to friends-when they are online. It also allows users to send messages to friends and-builds an instant messenging system on top of the lower level-\texttt{friend_connection} module.--Messenger offers two methods to add a friend. The first way is to add a friend-with only their long term public key, this is used when a friend needs to be-added but for some reason a friend request should not be sent. The friend-should only be added. This method is most commonly used to accept friend-requests but could also be used in other ways. If two friends add each other-using this function they will connect to each other. Adding a friend using-this method just adds the friend to \texttt{friend_connection} and creates a-new friend entry in Messenger for the friend.--The Tox ID is used to identify peers so that they can be added as friends in-Tox. In order to add a friend, a Tox user must have the friend's Tox ID.The-Tox ID contains the long term public key of the peer (32 bytes) followed by the-4 byte nospam (see: \texttt{friend_requests}) value and a 2 byte XOR checksum.-The method of sending the Tox ID to others is up to the user and the client but-the recommended way is to encode it in hexadecimal format and have the user-manually send it to the friend using another program.--Tox ID:--\begin{figure}-\includegraphics{img/tox-id.png}-\caption{Tox ID}-\end{figure}--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{32} & long term public key \\- \texttt{4} & nospam \\- \texttt{2} & checksum \\-\end{tabular}--The checksum is calculated by XORing the first two bytes of the ID with the-next two bytes, then the next two bytes until all the 36 bytes have been XORed-together. The result is then appended to the end to form the Tox ID.--The user must make sure the Tox ID is not intercepted and replaced in transit-by a different Tox ID, which would mean the friend would connect to a malicious-person instead of the user, though taking reasonable precautions as this is-outside the scope of Tox. Tox assumes that the user has ensured that they are-using the correct Tox ID, belonging to the intended person, to add a friend.--The second method to add a friend is by using their Tox ID and a message to be-sent in a friend request. This way of adding friends will try to send a friend-request, with the set message, to the peer whose Tox ID was added. The method-is similar to the first one, except that a friend request is crafted and sent-to the other peer.--When a friend connection associated to a Messenger friend goes online, a ONLINE-packet will be sent to them. Friends are only set as online if an ONLINE-packet is received.--As soon as a friend goes online, Messenger will stop sending friend requests to-that friend, if it was sending them, as they are redundant for this friend.--Friends will be set as offline if either the friend connection associated to-them goes offline or if an OFFLINE packet is received from the friend.--Messenger packets are sent to the friend using the online friend connection to-the friend.--Should Messenger need to check whether any of the non lossy packets in the-following list were received by the friend, for example to implement receipts-for text messages, \texttt{net_crypto} can be used. The \texttt{net_crypto}-packet number, used to send the packets, should be noted and then-\texttt{net_crypto} checked later to see if the bottom of the send array is-after this packet number. If it is, then the friend has received them. Note-that \texttt{net_crypto} packet numbers could overflow after a long time, so-checks should happen within 2**32 \texttt{net_crypto} packets sent with the-same friend connection.--Message receipts for action messages and normal text messages are implemented-by adding the \texttt{net_crypto} packet number of each message, along with the-receipt number, to the top of a linked list that each friend has as they are-sent. Every Messenger loop, the entries are read from the bottom and entries-are removed and passed to the client until an entry that refers to a packet not-yet received by the other is reached, when this happens it stops.--List of Messenger packets:--\section{\texttt{ONLINE}}--length: 1 byte--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x18) \\-\end{tabular}--Sent to a friend when a connection is established to tell them to mark us as-online in their friends list. This packet and the OFFLINE packet are necessary-as \texttt{friend_connections} can be established with non-friends who are part-of a groupchat. The two packets are used to differentiate between these peers,-connected to the user through groupchats, and actual friends who ought to be-marked as online in the friendlist.--On receiving this packet, Messenger will show the peer as being online.--\section{\texttt{OFFLINE}}--length: 1 byte--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x19) \\-\end{tabular}--Sent to a friend when deleting the friend. Prevents a deleted friend from-seeing us as online if we are connected to them because of a group chat.--On receiving this packet, Messenger will show this peer as offline.--\section{\texttt{NICKNAME}}--length: 1 byte to 129 bytes.--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x30) \\- \texttt{[0, 128]} & Nickname as a UTF8 byte string \\-\end{tabular}--Used to send the nickname of the peer to others. This packet should be sent-every time to each friend every time they come online and each time the-nickname is changed.--\section{\texttt{STATUSMESSAGE}}--length: 1 byte to 1008 bytes.--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x31) \\- \texttt{[0, 1007]} & Status message as a UTF8 byte string \\-\end{tabular}--Used to send the status message of the peer to others. This packet should be-sent every time to each friend every time they come online and each time the-status message is changed.--\section{\texttt{USERSTATUS}}--length: 2 bytes--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x32) \\- \texttt{1} & \texttt{uint8_t} status (0 = online, 1 = away, 2 = busy) \\-\end{tabular}--Used to send the user status of the peer to others. This packet should be sent-every time to each friend every time they come online and each time the user-status is changed.--\section{\texttt{TYPING}}--length: 2 bytes--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x33) \\- \texttt{1} & \texttt{uint8_t} typing status (0 = not typing, 1 = typing) \\-\end{tabular}--Used to tell a friend whether the user is currently typing or not.--\section{\texttt{MESSAGE}}--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x40) \\- \texttt{[0, 1372]} & Message as a UTF8 byte string \\-\end{tabular}--Used to send a normal text message to the friend.--\section{\texttt{ACTION}}--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x41) \\- \texttt{[0, 1372]} & Action message as a UTF8 byte string \\-\end{tabular}--Used to send an action message (like an IRC action) to the friend.--\section{\texttt{MSI}}--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x45) \\- \texttt{?} & data \\-\end{tabular}--Reserved for Tox AV usage.--\section{File Transfer Related Packets}--\subsection{\texttt{FILE_SENDREQUEST}}--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x50) \\- \texttt{1} & \texttt{uint8_t} file number \\- \texttt{4} & \texttt{uint32_t} file type \\- \texttt{8} & \texttt{uint64_t} file size \\- \texttt{32} & file id (32 bytes) \\- \texttt{[0, 255]} & filename as a UTF8 byte string \\-\end{tabular}--Note that file type and file size are sent in big endian/network byte format.--\subsection{\texttt{FILE_CONTROL}}--length: 4 bytes if \texttt{control_type} isn't seek. 8 bytes if-\texttt{control_type} is seek.--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x51) \\- \texttt{1} & \texttt{uint8_t} \texttt{send_receive} \\- \texttt{1} & \texttt{uint8_t} file number \\- \texttt{1} & \texttt{uint8_t} \texttt{control_type} \\- \texttt{8} & \texttt{uint64_t} seek parameter \\-\end{tabular}--\texttt{send_receive} is 0 if the control targets a file being sent (by the-peer sending the file control), and 1 if it targets a file being received.--\texttt{control_type} can be one of: 0 = accept, 1 = pause, 2 = kill, 3 = seek.--The seek parameter is only included when \texttt{control_type} is seek (3).--Note that if it is included the seek parameter will be sent in big-endian/network byte format.--\subsection{\texttt{FILE_DATA}}--length: 2 to 1373 bytes.--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x52) \\- \texttt{1} & \texttt{uint8_t} file number \\- \texttt{[0, 1371]} & file data piece \\-\end{tabular}--Files are transferred in Tox using File transfers.--To initiate a file transfer, the friend creates and sends a-\texttt{FILE_SENDREQUEST} packet to the friend it wants to initiate a file-transfer to.--The first part of the \texttt{FILE_SENDREQUEST} packet is the file number. The-file number is the number used to identify this file transfer. As the file-number is represented by a 1 byte number, the maximum amount of concurrent-files Tox can send to a friend is 256. 256 file transfers per friend is enough-that clients can use tricks like queueing files if there are more files needing-to be sent.--256 outgoing files per friend means that there is a maximum of 512 concurrent-file transfers, between two users, if both incoming and outgoing file transfers-are counted together.--As file numbers are used to identify the file transfer, the Tox instance must-make sure to use a file number that isn't used for another outgoing file-transfer to that same friend when creating a new outgoing file transfer. File-numbers are chosen by the file sender and stay unchanged for the entire-duration of the file transfer. The file number is used by both-\texttt{FILE_CONTROL} and \texttt{FILE_DATA} packets to identify which file-transfer these packets are for.--The second part of the file transfer request is the file type. This is simply-a number that identifies the type of file. for example, tox.h defines the file-type 0 as being a normal file and type 1 as being an avatar meaning the Tox-client should use that file as an avatar. The file type does not effect in any-way how the file is transfered or the behavior of the file transfer. It is set-by the Tox client that creates the file transfers and send to the friend-untouched.--The file size indicates the total size of the file that will be transfered. A-file size of \texttt{UINT64_MAX} (maximum value in a \texttt{uint64_t}) means-that the size of the file is undetermined or unknown. For example if someone-wanted to use Tox file transfers to stream data they would set the file size to-\texttt{UINT64_MAX}. A file size of 0 is valid and behaves exactly like a-normal file transfer.--The file id is 32 bytes that can be used to uniquely identify the file-transfer. For example, avatar transfers use it as the hash of the avatar so-that the receiver can check if they already have the avatar for a friend which-saves bandwidth. It is also used to identify broken file transfers across-toxcore restarts (for more info see the file transfer section of tox.h). The-file transfer implementation does not care about what the file id is, as it is-only used by things above it.--The last part of the file transfer is the optional file name which is used to-tell the receiver the name of the file.--When a \texttt{FILE_SENDREQUEST} packet is received, the implementation-validates and sends the info to the Tox client which decides whether they-should accept the file transfer or not.--To refuse or cancel a file transfer, they will send a \texttt{FILE_CONTROL}-packet with \texttt{control_type} 2 (kill).--\texttt{FILE_CONTROL} packets are used to control the file transfer.-\texttt{FILE_CONTROL} packets are used to accept/unpause, pause, kill/cancel-and seek file transfers. The \texttt{control_type} parameter denotes what the-file control packet does.--The \texttt{send_receive} and file number are used to identify a specific file-transfer. Since file numbers for outgoing and incoming files are not related-to each other, the \texttt{send_receive} parameter is used to identify if the-file number belongs to files being sent or files being received. If-\texttt{send_receive} is 0, the file number corresponds to a file being sent by-the user sending the file control packet. If \texttt{send_receive} is 1, it-corresponds to a file being received by the user sending the file control-packet.--\texttt{control_type} indicates the purpose of the \texttt{FILE_CONTROL}-packet. \texttt{control_type} of 0 means that the \texttt{FILE_CONTROL} packet-is used to tell the friend that the file transfer is accepted or that we are-unpausing a previously paused (by us) file transfer. \texttt{control_type} of-1 is used to tell the other to pause the file transfer.--If one party pauses a file transfer, that party must be the one to unpause it.-Should both sides pause a file transfer, both sides must unpause it before the-file can be resumed. For example, if the sender pauses the file transfer, the-receiver must not be able to unpause it. To unpause a file transfer,-\texttt{control_type} 0 is used. Files can only be paused when they are in-progress and have been accepted.--\texttt{control_type} 2 is used to kill, cancel or refuse a file transfer.-When a \texttt{FILE_CONTROL} is received, the targeted file transfer is-considered dead, will immediately be wiped and its file number can be reused.-The peer sending the \texttt{FILE_CONTROL} must also wipe the targeted file-transfer from their side. This control type can be used by both sides of the-transfer at any time.--\texttt{control_type} 3, the seek control type is used to tell the sender of-the file to start sending from a different index in the file than 0. It can-only be used right after receiving a \texttt{FILE_SENDREQUEST} packet and-before accepting the file by sending a \texttt{FILE_CONTROL} with-\texttt{control_type} 0. When this \texttt{control_type} is used, an extra 8-byte number in big endian format is appended to the \texttt{FILE_CONTROL} that-is not present with other control types. This number indicates the index in-bytes from the beginning of the file at which the file sender should start-sending the file. The goal of this control type is to ensure that files can be-resumed across core restarts. Tox clients can know if they have received a-part of a file by using the file id and then using this packet to tell the-other side to start sending from the last received byte. If the seek position-is bigger or equal to the size of the file, the seek packet is invalid and the-one receiving it will discard it.--To accept a file Tox will therefore send a seek packet, if it is needed, and-then send a \texttt{FILE_CONTROL} packet with \texttt{control_type} 0 (accept)-to tell the file sender that the file was accepted.--Once the file transfer is accepted, the file sender will start sending file-data in sequential chunks from the beginning of the file (or the position from-the \texttt{FILE_CONTROL} seek packet if one was received).--File data is sent using \texttt{FILE_DATA} packets. The file number-corresponds to the file transfer that the file chunks belong to. The receiver-assumes that the file transfer is over as soon as a chunk with the file data-size not equal to the maximum size (1371 bytes) is received. This is how the-sender tells the receiver that the file transfer is complete in file transfers-where the size of the file is unknown (set to \texttt{UINT64_MAX}). The-receiver also assumes that if the amount of received data equals to the file-size received in the \texttt{FILE_SENDREQUEST}, the file sending is finished-and has been successfully received. Immediately after this occurs, the-receiver frees up the file number so that a new incoming file transfer can use-that file number. The implementation should discard any extra data received-which is larger than the file size received at the beginning.--In 0 filesize file transfers, the sender will send one \texttt{FILE_DATA}-packet with a file data size of 0.--The sender will know if the receiver has received the file successfully by-checking if the friend has received the last \texttt{FILE_DATA} packet sent-(containing the last chunk of the file). \texttt{Net_crypto} can be used to-check whether packets sent through it have been received by storing the packet-number of the sent packet and verifying later in \texttt{net_crypto} to see-whether it was received or not. As soon as \texttt{net_crypto} says the other-received the packet, the file transfer is considered successful, wiped and the-file number can be reused to send new files.--\texttt{FILE_DATA} packets should be sent as fast as the \texttt{net_crypto}-connection can handle it respecting its congestion control.--If the friend goes offline, all file transfers are cleared in toxcore. This-makes it simpler for toxcore as it does not have to deal with resuming file-transfers. It also makes it simpler for clients as the method for resuming-file transfers remains the same, even if the client is restarted or toxcore-loses the connection to the friend because of a bad internet connection.--\section{Group Chat Related Packets}--\begin{tabular}{l|l}- Packet ID & Packet Name \\- \hline- 0x60 & \texttt{INVITE_GROUPCHAT} \\- 0x61 & \texttt{ONLINE_PACKET} \\- 0x62 & \texttt{DIRECT_GROUPCHAT} \\- 0x63 & \texttt{MESSAGE_GROUPCHAT} \\- 0xC7 & \texttt{LOSSY_GROUPCHAT} \\-\end{tabular}--Messenger also takes care of saving the friends list and other friend-information so that it's possible to close and start toxcore while keeping all-your friends, your long term key and the information necessary to reconnect to-the network.--Important information messenger stores includes: the long term private key, our-current nospam value, our friends' public keys and any friend requests the user-is currently sending. The network DHT nodes, TCP relays and some onion nodes-are stored to aid reconnection.--In addition to this, a lot of optional data can be stored such as the usernames-of friends, our current username, status messages of friends, our status-message, etc... can be stored. The exact format of the toxcore save is-explained later.--The TCP server is run from the toxcore messenger module if the client has-enabled it. TCP server is usually run independently as part of the bootstrap-node package but it can be enabled in clients. If it is enabled in toxcore,-Messenger will add the running TCP server to the TCP relay.--Messenger is the module that transforms code that can connect to friends based-on public key into a real instant messenger.--\chapter{TCP client}--\texttt{TCP client} is the client for the TCP server. It establishes and keeps-a connection to the TCP server open.--All the packet formats are explained in detail in \texttt{TCP server} so this-section will only cover \texttt{TCP client} specific details which are not-covered in the \texttt{TCP server} documentation.--TCP clients can choose to connect to TCP servers through a proxy. Most common-types of proxies (SOCKS, HTTP) work by establishing a connection through a-proxy using the protocol of that specific type of proxy. After the connection-through that proxy to a TCP server is established, the socket behaves from the-point of view of the application exactly like a TCP socket that connects-directly to a TCP server instance. This means supporting proxies is easy.--\texttt{TCP client} first establishes a TCP connection, either through a proxy-or directly to a TCP server. It uses the DHT public key as its long term key-when connecting to the TCP server.--It establishes a secure connection to the TCP server. After establishing a-connection to the TCP server, and when the handshake response has been received-from the TCP server, the toxcore implementation immediately sends a ping-packet. Ideally the first packets sent would be routing request packets but-this solution aids code simplicity and allows the server to confirm the-connection.--Ping packets, like all other data packets, are sent as encrypted packets.--Ping packets are sent by the toxcore TCP client every 30 seconds with a timeout-of 10 seconds, the same interval and timeout as toxcore TCP server ping-packets. They are the same because they accomplish the same thing.--\texttt{TCP client} must have a mechanism to make sure important packets-(routing requests, disconnection notifications, ping packets, ping response-packets) don't get dropped because the TCP socket is full. Should this happen,-the TCP client must save these packets and prioritize sending them, in order,-when the TCP socket on the server becomes available for writing again.-\texttt{TCP client} must also take into account that packets might be bigger-than the number of bytes it can currently write to the socket. In this case,-it must save the bytes of the packet that it didn't write to the socket and-write them to the socket as soon as the socket allows so that the connection-does not get broken. It must also assume that it may receive only part of an-encrypted packet. If this occurs it must save the part of the packet it has-received and wait for the rest of the packet to arrive before handling it.--\texttt{TCP client} can be used to open up a route to friends who are connected-to the TCP server. This is done by sending a routing request to the TCP server-with the DHT public key of the friend. This tells the server to register a-\texttt{connection_id} to the DHT public key sent in the packet. The server-will then respond with a routing response packet. If the connection was-accepted, the \texttt{TCP client} will store the \texttt{connection id} for-this connection. The \texttt{TCP client} will make sure that routing response-packets are responses to a routing packet that it sent by storing that it sent-a routing packet to that public key and checking the response against it. This-prevents the possibility of a bad TCP server exploiting the client.--The \texttt{TCP client} will handle connection notifications and disconnection-notifications by alerting the module using it that the connection to the peer-is up or down.--\texttt{TCP client} will send a disconnection notification to kill a connection-to a friend. It must send a disconnection notification packet regardless of-whether the peer was online or offline so that the TCP server will unregister-the connection.--Data to friends can be sent through the TCP relay using OOB (out of band)-packets and connected connections. To send an OOB packet, the DHT public key-of the friend must be known. OOB packets are sent in blind and there is no way-to query the TCP relay to see if the friend is connected before sending one.-OOB packets should be sent when the connection to the friend via the TCP relay-isn't in an connected state but it is known that the friend is connected to-that relay. If the friend is connected via the TCP relay, then normal data-packets must be sent as they are smaller than OOB packets.--OOB recv and data packets must be handled and passed to the module using it.--\chapter{TCP connections}--\texttt{TCP_connections} takes care of handling multiple TCP client instances-to establish a reliable connection via TCP relays to a friend. Connecting to a-friend with only one relay would not be very reliable, so-\texttt{TCP_connections} provides the level of abstraction needed to manage-multiple relays. For example, it ensures that if a relay goes down, the-connection to the peer will not be impacted. This is done by connecting to the-other peer with more than one relay.--\texttt{TCP_connections} is above \href{#tcp-client}{\texttt{TCP client}} and-below \texttt{net_crypto}.--A TCP connection in \texttt{TCP_connections} is defined as a connection to a-peer though one or more TCP relays. To connect to another peer with-\texttt{TCP_connections}, a connection in \texttt{TCP_connections} to the peer-with DHT public key X will be created. Some TCP relays which we know the peer-is connected to will then be associated with that peer. If the peer isn't-connected directly yet, these relays will be the ones that the peer has sent to-us via the onion module. The peer will also send some relays it is directly-connected to once a connection is established, however, this is done by another-module.--\texttt{TCP_connections} has a list of all relays it is connected to. It tries-to keep the number of relays it is connected to as small as possible in order-to minimize load on relays and lower bandwidth usage for the client. The-desired number of TCP relay connections per peer is set to 3 in toxcore with-the maximum number set to 6. The reason for these numbers is that 1 would mean-no backup relays and 2 would mean only 1 backup. To be sure that the-connection is reliable 3 seems to be a reasonable lower bound. The maximum-number of 6 is the maximum number of relays that can be tied to each peer. If-2 peers are connected each to the same 6+ relays and they both need to be-connected to that amount of relays because of other friends this is where this-maximum comes into play. There is no reason why this number is 6 but in-toxcore it has to be at least double than the desired number (3) because the-code assumes this.--If necessary, \texttt{TCP_connections} will connect to TCP relays to use them-to send onion packets. This is only done if there is no UDP connection to the-network. When there is a UDP connection, packets are sent with UDP only-because sending them with TCP relays can be less reliable. It is also-important that we are connected at all times to some relays as these relays-will be used by TCP only peers to initiate a connection to us.--In toxcore, each client is connected to 3 relays even if there are no TCP peers-and the onion is not needed. It might be optimal to only connect to these-relays when toxcore is initializing as this is the only time when peers will-connect to us via TCP relays we are connected to. Due to how the onion works,-after the initialization phase, where each peer is searched in the onion and-then if they are found the info required to connect back (DHT pk, TCP relays)-is sent to them, there should be no more peers connecting to us via TCP relays.-This may be a way to further reduce load on TCP relays, however, more research-is needed before it is implemented.--\texttt{TCP_connections} picks one relay and uses only it for sending data to-the other peer. The reason for not picking a random connected relay for each-packet is that it severely deteriorates the quality of the link between two-peers and makes performance of lossy video and audio transmissions really poor.-For this reason, one relay is picked and used to send all data. If for any-reason no more data can be sent through that relay, the next relay is used.-This may happen if the TCP socket is full and so the relay should not-necessarily be dropped if this occurs. Relays are only dropped if they time-out or if they become useless (if the relay is one too many or is no longer-being used to relay data to any peers).--\texttt{TCP_connections} in toxcore also contains a mechanism to make-connections go to sleep. TCP connections to other peers may be put to sleep if-the connection to the peer establishes itself with UDP after the connection is-established with TCP. UDP is the method preferred by \texttt{net_crypto} to-communicate with other peers. In order to keep track of the relays which were-used to connect with the other peer in case the UDP connection fails, they are-saved by \texttt{TCP_connections} when the connection is put to sleep. Any-relays which were only used by this redundant connection are saved then-disconnected from. If the connection is awakened, the relays are reconnected-to and the connection is reestablished. Putting a connection to sleep is the-same as saving all the relays used by the connection and removing the-connection. Awakening the connection is the same as creating a new connection-with the same parameters and restoring all the relays.--A method to detect potentially dysfunctional relays that try to disrupt the-network by lying that they are connecting to a peer when they are not or that-maliciously drop all packets should be considered. Toxcore doesn't currently-implement such a system and adding one requires more research and likely also-requires extending the protocol.--When TCP connections connects to a relay it will create a new-\href{#tcp-client}{\texttt{TCP_client}} instance for that relay. At any time-if the \texttt{TCP_client} instance reports that it has disconnected, the TCP-relay will be dropped. Once the TCP relay reports that it is connected,-\texttt{TCP_connections} will find all the connections that are associated to-the relay and announce to the relay that it wants to connect to each of them-with routing requests. If the relay reports that the peer for a connection is-online, the connection number and relay will be used to send data in that-connection with data packets. If the peer isn't reported as online but the-relay is associated to a connection, TCP OOB (out of band) packets will be used-to send data instead of data packets. TCP OOB packets are used in this case-since the relay most likely has the peer connected but it has not sent a-routing request to connect to us.--\texttt{TCP_connections} is used as the bridge between individual-\texttt{TCP_client} instances and \texttt{net_crypto}, or the bridge between-individual connections and something that requires an interface that looks like-one connection.--\chapter{TCP server}--The TCP server in tox has the goal of acting like a TCP relay between clients-who cannot connect directly to each other or who for some reason are limited to-using the TCP protocol to connect to each other. \texttt{TCP_server} is-typically run only on actual server machines but any Tox client could host one-as the api to run one is exposed through the tox.h api.--To connect to a hosted TCP server toxcore uses the TCP client module.--The TCP server implementation in toxcore can currently either work on epoll on-linux or using unoptimized but portable socket polling.--TCP connections between the TCP client and the server are encrypted to prevent-an outsider from knowing information like who is connecting to who just be-looking at someones connection to a TCP server. This is useful when someone-connects though something like Tor for example. It also prevents someone from-injecting data in the stream and makes it so we can assume that any data-received was not tampered with and is exactly what was sent by the client.--When a client first connects to a TCP server he opens up a TCP connection to-the ip and port the TCP server is listening on. Once the connection is-established he then sends a handshake packet, the server then responds with his-own and a secure connection is established. The connection is then said to be-unconfirmed and the client must then send some encrypted data to the server-before the server can mark the connection as confirmed. The reason it works-like this is to prevent a type of attack where a peer would send a handshake-packet and then time out right away. To prevent this the server must wait a-few seconds for a sign that the client received his handshake packet before-confirming the connection. The both can then communicate with each other using-the encrypted connection.--The TCP server essentially acts as just a relay between 2 peers. When a TCP-client connects to the server he tells the server which clients he wants the-server to connect him to. The server will only let two clients connect to each-other if both have indicated to the server that they want to connect to each-other. This is to prevent non friends from checking if someone is connected to-a TCP server. The TCP server supports sending packets blindly through it to-clients with a client with public key X (OOB packets) however the TCP server-does not give any feedback or anything to say if the packet arrived or not and-as such it is only useful to send data to friends who may not know that we are-connected to the current TCP server while we know they are. This occurs when-one peer discovers the TCP relay and DHT public key of the other peer before-the other peer discovers its DHT public key. In that case OOB packets would be-used until the other peer knows that the peer is connected to the relay and-establishes a connection through it.--In order to make toxcore work on TCP only the TCP server supports relaying-onion packets from TCP clients and sending any responses from them to TCP-clients.--To establish a secure connection with a TCP server send the following 128 bytes-of data or handshake packet to the server:--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{32} & DHT public key of client \\- \texttt{24} & Nonce for the encrypted data \\- \texttt{72} & Payload (plus MAC) \\-\end{tabular}--Payload is encrypted with the DHT private key of the client and public key of-the server and the nonce:--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{32} & Public key \\- \texttt{24} & Base nonce \\-\end{tabular}--The base nonce is the one TCP client wants the TCP server to use to encrypt the-packets sent to the TCP client.--The first 32 bytes are the public key (DHT public key) that the TCP client is-announcing itself to the server with. The next 24 bytes are a nonce which the-TCP client uses along with the secret key associated with the public key in the-first 32 bytes of the packet to encrypt the rest of this 'packet'. The-encrypted part of this packet contains a temporary public key that will be used-for encryption during the connection and will be discarded after. It also-contains a base nonce which will be used later for encrypting packets sent to-the TCP client.--If the server decrypts successfully the encrypted data in the handshake packet-and responds with the following handshake response of length 96 bytes:--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{24} & Nonce for the encrypted data \\- \texttt{72} & Payload (plus MAC) \\-\end{tabular}--Payload is encrypted with the private key of the server and the DHT public key-of the client and the nonce:--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{32} & Public key \\- \texttt{24} & Base nonce \\-\end{tabular}--The base nonce is the one the TCP server wants the TCP client to use to encrypt-the packets sent to the TCP server.--The client already knows the long term public key of the server so it is-omitted in the response, instead only a nonce is present in the unencrypted-part. The encrypted part of the response has the same elements as the-encrypted part of the request: a temporary public key tied to this connection-and a base nonce which will be used later when decrypting packets received from-the TCP client both unique for the connection.--In toxcore the base nonce is generated randomly like all the other nonces, it-must be randomly generated to prevent nonce reuse. For example if the nonce-used was 0 for both sides since both sides use the same keys to encrypt packets-they send to each other, two packets would be encrypted with the same nonce.-These packets could then be possibly replayed back to the sender which would-cause issues. A similar mechanism is used in \texttt{net_crypto}.--After this the client will know the connection temporary public key and base-nonce of the server and the server will know the connection base nonce and-temporary public key of the client.--The client will then send an encrypted packet to the server, the contents of-the packet do not matter and it must be handled normally by the server (ex: if-it was a ping send a pong response. The first packet must be any valid-encrypted data packet), the only thing that does matter is that the packet was-encrypted correctly by the client because it means that the client has-correctly received the handshake response the server sent to it and that the-handshake the client sent to the server really came from the client and not-from an attacker replaying packets. The server must prevent resource consuming-attacks by timing out clients if they do not send any encrypted packets so the-server to prove to the server that the connection was established correctly.--Toxcore does not have a timeout for clients, instead it stores connecting-clients in large circular lists and times them out if their entry in the list-gets replaced by a newer connection. The reasoning behind this is that it-prevents TCP flood attacks from having a negative impact on the currently-connected nodes. There are however much better ways to do this and the only-reason toxcore does it this way is because writing it was very simple. When-connections are confirmed they are moved somewhere else.--When the server confirms the connection he must look in the list of connected-peers to see if he is already connected to a client with the same announced-public key. If this is the case the server must kill the previous connection-because this means that the client previously timed out and is reconnecting.-Because of Toxcore design it is very unlikely to happen that two legitimate-different peers will have the same public key so this is the correct behavior.--Encrypted data packets look like this to outsiders:--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{2} & \texttt{uint16_t} length of data \\- variable & encrypted data \\-\end{tabular}--In a TCP stream they would look like:-\texttt{[[length][data]][[length][data]][[length][data]]...}.--Both the client and server use the following (temp public and private (client-and server) connection keys) which are each generated for the connection and-then sent to the other in the handshake and sent to the other. They are then-used like the next diagram shows to generate a shared key which is equal on-both sides.--\begin{verbatim}-Client: Server:-generate_shared_key( generate_shared_key(-[temp connection public key of server], [temp connection public key of client],-[temp connection private key of client]) [temp connection private key of server])-= =-[shared key] [shared key]-\end{verbatim}--The generated shared key is equal on both sides and is used to encrypt and-decrypt the encrypted data packets.--each encrypted data packet sent to the client will be encrypted with the shared-key and with a nonce equal to: (client base nonce + number of packets sent so-for the first packet it is (starting at 0) nonce + 0, the second is nonce + 1-and so on. Note that nonces like all other numbers sent over the network in-toxcore are numbers in big endian format so when increasing them by 1 the least-significant byte is the last one)--each packet received from the client will be decrypted with the shared key and-with a nonce equal to: (server base nonce + number of packets sent so for the-first packet it is (starting at 0) nonce + 0, the second is nonce + 1 and so-on. Note that nonces like all other numbers sent over the network in toxcore-are numbers in big endian format so when increasing them by 1 the least-significant byte is the last one)--Encrypted data packets have a hard maximum size of 2 + 2048 bytes in the-toxcore TCP server implementation, 2048 bytes is big enough to make sure that-all toxcore packets can go through and leaves some extra space just in case the-protocol needs to be changed in the future. The 2 bytes represents the size of-the data length and the 2048 bytes the max size of the encrypted part. This-means the maximum size is 2050 bytes. In current toxcore, the largest-encrypted data packets sent will be of size 2 + 1417 which is 1419 total.--The logic behind the format of the handshake is that we:--\begin{enumerate}-\item need to prove to the server that we own the private key related to the public- key we are announcing ourselves with.-\item need to establish a secure connection that has perfect forward secrecy-\item prevent any replay, impersonation or other attacks-\end{enumerate}--How it accomplishes each of those points:--\begin{enumerate}- \item If the client does not own the private key related to the public key they- will not be able to create the handshake packet.- \item Temporary session keys generated by the client and server in the encrypted- part of the handshake packets are used to encrypt/decrypt packets during the- session.- \item The following attacks are prevented:- \begin{itemize}- \item Attacker modifies any byte of the handshake packets: Decryption fail, no- attacks possible.- \item Attacker captures the handshake packet from the client and replays it- later to the server: Attacker will never get the server to confirm the- connection (no effect).- \item Attacker captures a server response and sends it to the client next time- they try to connect to the server: Client will never confirm the- connection. (See: \texttt{TCP_client})- \item Attacker tries to impersonate a server: They won't be able to decrypt the- handshake and won't be able to respond.- \item Attacker tries to impersonate a client: Server won't be able to decrypt- the handshake.- \end{itemize}-\end{enumerate}--The logic behind the format of the encrypted packets is that:--\begin{enumerate}- \item TCP is a stream protocol, we need packets.- \item Any attacks must be prevented-\end{enumerate}--How it accomplishes each of those points:--\begin{enumerate}- \item 2 bytes before each packet of encrypted data denote the length. We assume a- functioning TCP will deliver bytes in order which makes it work. If the TCP- doesn't it most likely means it is under attack and for that see the next- point.- \item The following attacks are prevented:- \begin{itemize}- \item Modifying the length bytes will either make the connection time out- and/or decryption fail.- \item Modifying any encrypted bytes will make decryption fail.- \item Injecting any bytes will make decryption fail.- \item Trying to re order the packets will make decryption fail because of the- ordered nonce.- \item Removing any packets from the stream will make decryption fail because of- the ordered nonce.- \end{itemize}-\end{enumerate}--\section{Encrypted payload types}--The folowing represents the various types of data that can be sent inside-encrypted data packets.--\subsection{Routing request (0x00)}--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x00) \\- \texttt{32} & Public key \\-\end{tabular}--\subsection{Routing request response (0x01)}--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x01) \\- \texttt{1} & \texttt{uint8_t} rpid \\- \texttt{32} & Public key \\-\end{tabular}--rpid is invalid \texttt{connection_id} (0) if refused, \texttt{connection_id} if accepted.--\subsection{Connect notification (0x02)}--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x02) \\- \texttt{1} & \texttt{uint8_t} \texttt{connection_id} of connection that got connected \\-\end{tabular}--\subsection{Disconnect notification (0x03)}--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x03) \\- \texttt{1} & \texttt{uint8_t} \texttt{connection_id} of connection that got disconnected \\-\end{tabular}--\subsection{Ping packet (0x04)}--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x04) \\- \texttt{8} & \texttt{uint64_t} \texttt{ping_id} (0 is invalid) \\-\end{tabular}--\subsection{Ping response (pong) (0x05)}--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x05) \\- \texttt{8} & \texttt{uint64_t} \texttt{ping_id} (0 is invalid) \\-\end{tabular}--\subsection{OOB send (0x06)}--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x06) \\- \texttt{32} & Destination public key \\- variable & Data \\-\end{tabular}--\subsection{OOB recv (0x07)}--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x07) \\- \texttt{32} & Sender public key \\- variable & Data \\-\end{tabular}--\subsection{Onion packet (0x08)}--Same format as initial onion packet but packet id is 0x08 instead of 0x80.--\subsection{Onion packet response (0x09)}--Same format as onion packet but packet id is 0x09 instead of 0x8e.--\subsection{Data (0x10 and up)}--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} packet id \\- \texttt{1} & \texttt{uint8_t} connection id \\- variable & data \\-\end{tabular}--The TCP server is set up in a way to minimize waste while relaying the many-packets that might go between two tox peers hence clients must create-connections to other clients on the relay. The connection number is a-\texttt{uint8_t} and must be equal or greater to 16 in order to be valid.-Because a \texttt{uint8_t} has a maximum value of 256 it means that the maximum-number of different connections to other clients that each connection can have-is 240. The reason valid \texttt{connection_ids} are bigger than 16 is because-they are the first byte of data packets. Currently only number 0 to 9 are-taken however we keep a few extras in case we need to extend the protocol-without breaking it completely.--Routing request (Sent by client to server): Send a routing request to the-server that we want to connect to peer with public key where the public key is-the public the peer announced themselves as. The server must respond to this-with a Routing response.--Routing response (Sent by server to client): The response to the routing-request, tell the client if the routing request succeeded (valid-\texttt{connection_id}) and if it did, tell them the id of the connection-(\texttt{connection_id}). The public key sent in the routing request is also-sent in the response so that the client can send many requests at the same time-to the server without having code to track which response belongs to which-public key.--The only reason a routing request should fail is if the connection has reached-the maximum number of simultaneous connections. In case the routing request-fails the public key in the response will be the public key in the failed-request.--Connect notification (Sent by server to client): Tell the client that-\texttt{connection_id} is now connected meaning the other is online and data-can be sent using this \texttt{connection_id}.--Disconnect notification (Sent by client to server): Sent when client wants the-server to forget about the connection related to the \texttt{connection_id} in-the notification. Server must remove this connection and must be able to reuse-the \texttt{connection_id} for another connection. If the connection was-connected the server must send a disconnect notification to the other client.-The other client must think that this client has simply disconnected from the-TCP server.--Disconnect notification (Sent by server to client): Sent by the server to the-client to tell them that the connection with \texttt{connection_id} that was-connected is now disconnect. It is sent either when the other client of the-connection disconnect or when they tell the server to kill the connection (see-above).--Ping and Pong packets (can be sent by both client and server, both will-respond): ping packets are used to know if the other side of the connection is-still live. TCP when established doesn't have any sane timeouts (1 week isn't-sane) so we are obliged to have our own way to check if the other side is still-live. Ping ids can be anything except 0, this is because of how toxcore sets-the variable storing the \texttt{ping_id} that was sent to 0 when it receives a-pong response which means 0 is invalid.--The server should send ping packets every X seconds (toxcore-\texttt{TCP_server} sends them every 30 seconds and times out the peer if it-doesn't get a response in 10). The server should respond immediately to ping-packets with pong packets.--The server should respond to ping packets with pong packets with the same-\texttt{ping_id} as was in the ping packet. The server should check that each-pong packet contains the same \texttt{ping_id} as was in the ping, if not the-pong packet must be ignored.--OOB send (Sent by client to server): If a peer with private key equal to the-key they announced themselves with is connected, the data in the OOB send-packet will be sent to that peer as an OOB recv packet. If no such peer is-connected, the packet is discarded. The toxcore \texttt{TCP_server}-implementation has a hard maximum OOB data length of 1024. 1024 was picked-because it is big enough for the \texttt{net_crypto} packets related to the-handshake and is large enough that any changes to the protocol would not-require breaking TCP server. It is however not large enough for the biggest-\texttt{net_crypto} packets sent with an established \texttt{net_crypto}-connection to prevent sending those via OOB packets.--OOB recv (Sent by server to client): OOB recv are sent with the announced-public key of the peer that sent the OOB send packet and the exact data.--OOB packets can be used just like normal data packets however the extra size-makes sending data only through them less efficient than data packets.--Data: Data packets can only be sent and received if the corresponding-\texttt{connection_id} is connection (a Connect notification has been received-from it) if the server receives a Data packet for a non connected or existent-connection it will discard it.--Why did I use different packet ids for all packets when some are only sent by-the client and some only by the server? It's less confusing.--\chapter{Friend connection}--\texttt{friend_connection} is the module that sits on top of the DHT, onion and-\texttt{net_crypto} modules and takes care of linking the 3 together.--Friends in \texttt{friend_connection} are represented by their real public key.-When a friend is added in \texttt{friend_connection}, an onion search entry is-created for that friend. This means that the onion module will start looking-for this friend and send that friend their DHT public key, and the TCP relays-it is connected to, in case a connection is only possible with TCP.--Once the onion returns the DHT public key of the peer, the DHT public key is-saved, added to the DHT friends list and a new \texttt{net_crypto} connection-is created. Any TCP relays returned by the onion for this friend are passed to-the \texttt{net_crypto} connection.--If the DHT establishes a direct UDP connection with the friend,-\texttt{friend_connection} will pass the IP/port of the friend to-\texttt{net_crypto} and also save it to be used to reconnect to the friend if-they disconnect.--If \texttt{net_crypto} finds that the friend has a different DHT public key,-which can happen if the friend restarted their client, \texttt{net_crypto} will-pass the new DHT public key to the onion module and will remove the DHT entry-for the old DHT public key and replace it with the new one. The current-\texttt{net_crypto} connection will also be killed and a new one with the-correct DHT public key will be created.--When the \texttt{net_crypto} connection for a friend goes online,-\texttt{friend_connection} will tell the onion module that the friend is online-so that it can stop spending resources looking for the friend. When the friend-connection goes offline, \texttt{friend_connection} will tell the onion module-so that it can start looking for the friend again.--There are 2 types of data packets sent to friends with the \texttt{net_crypto}-connection handled at the level of \texttt{friend_connection}, Alive packets-and TCP relay packets. Alive packets are packets with the packet id or first-byte of data (only byte in this packet) being 16. They are used in order to-check if the other friend is still online. \texttt{net_crypto} does not have-any timeout when the connection is established so timeouts are caught using-this packet. In toxcore, this packet is sent every 8 seconds. If none of-these packets are received for 32 seconds, the connection is timed out and-killed. These numbers seem to cause the least issues and 32 seconds is not too-long so that, if a friend times out, toxcore won't falsely see them online for-too long. Usually when a friend goes offline they have time to send a-disconnect packet in the \texttt{net_crypto} connection which makes them appear-offline almost instantly.--The timeout for when to stop retrying to connect to a friend by creating new-\texttt{net_crypto} connections when the old one times out in toxcore is the-same as the timeout for DHT peers (122 seconds). However, it is calculated-from the last time a DHT public key was received for the friend or time the-friend's \texttt{net_crypto} connection went offline after being online. The-highest time is used to calculate when the timeout is. \texttt{net_crypto}-connections will be recreated (if the connection fails) until this timeout.--\texttt{friend_connection} sends a list of 3 relays (the same number as the-target number of TCP relay connections in \texttt{TCP_connections}) to each-connected friend every 5 minutes in toxcore. Immediately before sending the-relays, they are associated to the current \texttt{net_crypto->TCP_connections}-connection. This facilitates connecting the two friends together using the-relays as the friend who receives the packet will associate the sent relays to-the \texttt{net_crypto} connection they received it from. When both sides do-this they will be able to connect to each other using the relays. The packet-id or first byte of the packet of share relay packets is 0x11. This is then-followed by some TCP relays stored in packed node format.--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x11) \\- variable & TCP relays in packed node format (see DHT) \\-\end{tabular}--If local IPs are received as part of the packet, the local IP will be replaced-with the IP of the peer that sent the relay. This is because we assume this is-the best way to attempt to connect to the TCP relay. If the peer that sent the-relay is using a local IP, then the sent local IP should be used to connect to-the relay.--For all other data packets, are passed by \texttt{friend_connection} up to the-upper Messenger module. It also separates lossy and lossless packets from-\texttt{net_crypto}.--Friend connection takes care of establishing the connection to the friend and-gives the upper messenger layer a simple interface to receive and send-messages, add and remove friends and know if a friend is connected (online) or-not connected (offline).--\chapter{Friend requests}--When a Tox user adds someone with Tox, toxcore will try sending a friend-request to that person. A friend request contains the long term public key of-the sender, a nospam number and a message.--Transmitting the long term public key is the primary goal of the friend request-as it is what the peer needs to find and establish a connection to the sender.-The long term public key is what the receiver adds to his friends list if he-accepts the friend request.--The nospam is a number used to prevent someone from spamming the network with-valid friend requests. It makes sure that the only people who have seen the-Tox ID of a peer are capable of sending them a friend request. The nospam is-one of the components of the Tox ID.--The nospam is a number or a list of numbers set by the peer, only received-friend requests that contain a nospam that was set by the peer are sent to the-client to be accepted or refused by the user. The nospam prevents random peers-in the network from sending friend requests to non friends. The nospam is not-long enough to be secure meaning an extremely resilient attacker could manage-to send a spam friend request to someone. 4 bytes is large enough to prevent-spam from random peers in the network. The nospam could also allow Tox users-to issue different Tox IDs and even change Tox IDs if someone finds a Tox ID-and decides to send it hundreds of spam friend requests. Changing the nospam-would stop the incoming wave of spam friend requests without any negative-effects to the users friends list. For example if users would have to change-their public key to prevent them from receiving friend requests it would mean-they would have to essentially abandon all their current friends as friends are-tied to the public key. The nospam is not used at all once the friends have-each other added which means changing it won't have any negative effects.--Friend request:--\begin{verbatim}-[uint32_t nospam][Message (UTF8) 1 to ONION_CLIENT_MAX_DATA_SIZE bytes]-\end{verbatim}--Friend request packet when sent as an onion data packet:--\begin{verbatim}-[uint8_t (32)][Friend request]-\end{verbatim}--Friend request packet when sent as a \texttt{net_crypto} data packet (If we are-directly connected to the peer because of a group chat but are not friends with-them):--\begin{verbatim}-[uint8_t (18)][Friend request]-\end{verbatim}--When a friend is added to toxcore with their Tox ID and a message, the friend-is added in \texttt{friend_connection} and then toxcore tries to send friend-requests.--When sending a friend request, toxcore will check if the peer which a friend-request is being sent to is already connected to using a \texttt{net_crypto}-connection which can happen if both are in the same group chat. If this is the-case the friend request will be sent as a \texttt{net_crypto} packet using that-connection. If not, it will be sent as an onion data packet.--Onion data packets contain the real public key of the sender and if a-\texttt{net_crypto} connection is established it means the peer knows our real-public key. This is why the friend request does not need to contain the real-public key of the peer.--Friend requests are sent with exponentially increasing interval of 2 seconds, 4-seconds, 8 seconds, etc... in toxcore. This is so friend requests get resent-but eventually get resent in intervals that are so big that they essentially-expire. The sender has no way of knowing if a peer refuses a friend requests-which is why friend requests need to expire in some way. Note that the-interval is the minimum timeout, if toxcore cannot send that friend request it-will try again until it manages to send it. One reason for not being able to-send the friend request would be that the onion has not found the friend in the-onion and so cannot send an onion data packet to them.--Received friend requests are passed to the client, the client is expected to-show the message from the friend request to the user and ask the user if they-want to accept the friend request or not. Friend requests are accepted by-adding the peer sending the friend request as a friend and refused by simply-ignoring it.--Friend requests are sent multiple times meaning that in order to prevent the-same friend request from being sent to the client multiple times toxcore keeps-a list of the last real public keys it received friend requests from and-discards any received friend requests that are from a real public key that is-in that list. In toxcore this list is a simple circular list. There are many-ways this could be improved and made more efficient as a circular list isn't-very efficient however it has worked well in toxcore so far.--Friend requests from public keys that are already added to the friends list-should also be discarded.--\chapter{Group}--Group chats in Tox work by temporarily adding some peers (up to 4) present in-the group chat as temporary \texttt{friend_connection} friends, that are-deleted when the group chat is exited.--Each peer in the group chat is identified by their real long term public key-however peers transmit their DHT public keys to each other via the group chat-in order to speed up the connection by making it unnecessary for the peers to-find each others DHT public keys with the onion which would happen if they-would have added themselves as normal friends.--The upside of using \texttt{friend_connection} is that group chats do not have-to deal with things like hole punching, peers only on TCP or other low level-networking things. The downside however is that every single peer knows each-others real long term public key and DHT public key which means these group-chats should only be used between friends.--To connect to each other, two peers must have the other added to their list of-friend connections. This is not a problem if the group chat has an equal or-smaller number of participants than 5 as each of the 5 peers will have the 4-others added to their list of friend connections. When there are more peers-there must be a way to ensure that peers will manage to connect to other-groupchat peers.--Since the maximum number of peers per groupchat that will be connected to with-friend connections is 4, if all peers in the groupchat are arranged in a-perfect circle and each peer connects to the 2 peers that are the closest to-the right of them and the 2 peers that are closest to the left of them, the-peers should form a well connected circle of peers.--Group chats in toxcore do this by subtracting the real long term public key of-the peer with all the others in the group (our PK - other peer PK) and finding-the two peers for which the result of this operation is the smallest. The-operation is then inversed (other peer PK - our PK) and this operation is done-again with all the public keys of the peers in the group. The 2 peers for-which the result is again the smallest are picked.--This gives 4 peers that are then added as a friend connection and associated to-the group. If every peer in the group does this, they will form a circle of-perfectly connected peers.--Once the peers are connected to each other in a circle they relay each others-messages. Every time a peer leaves the group or a new peer joins each member-of the chat will recalculate the peers they should connect to.--To join a group chat the peer must first be invited to it by their friend. To-make a groupchat the peer will first create a groupchat and then invite people-to this group chat. Once their friends are in the group chat they can invite-their other friends to the chat and so on.--To create a group chat the peer will generate a random 32 byte id that will be-used to uniquely identify this group chat. 32 bytes is enough so that when-randomly generated with a secure random number generator every groupchat ever-created will have a different id. The goal of this 32 byte id is so that peers-have a way of identifying each group chat so that they can prevent themselves-from joining a groupchat twice for example.--The groupchat will also have an unsigned 1 byte type. This type indicates what-kind of groupchat the groupchat is, the current types are:--0: text-1: audio--Text groupchats are text only while audio indicates that the groupchat supports-sending audio to it as well as text.--The groupchat will also be identified by a unique unsigned 2 byte integer which-in toxcore corresponds to the index of the groupchat in the array it is being-stored in. Every groupchat in the current instance must have a different-number. This number is used by groupchat peers that are directly connected to-us to tell us which packets are for which groupchat. This is why every-groupchat packet contains a groupchat number as part of them. Putting a 32-byte groupchat id in each packet would increase bandwidth waste by a lot which-is the reason why groupchat numbers are used instead.--Using the group number as the index of the array used to store the groupchat-instances is recommended because this kind of access is usually most efficient-and it ensures that each groupchat has a unique group number.--When creating a new groupchat, the peer will add themselves as a groupchat peer-with a peer number of 0 and their own long term public key and DHT public key.--Invite packets:--Invite packet:--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x60) \\- \texttt{1} & \texttt{uint8_t} (0x00) \\- \texttt{2} & \texttt{uint16_t} group number \\- \texttt{33} & Group chat identifier \\-\end{tabular}--A group chat identifier consists of a 1-byte type and a 32-byte ID-concatenated.--Response packet--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x60) \\- \texttt{1} & \texttt{uint8_t} (0x01) \\- \texttt{2} & \texttt{uint16_t} group number (local) \\- \texttt{2} & \texttt{uint16_t} group number to join \\- \texttt{33} & Group chat identifier \\-\end{tabular}--To invite a friend to a group chat, an invite packet is sent to the friend.-These packets are sent using Messenger (if you look at the Messenger packet id-section, all the groupchat packet ids are in there). Note that all numbers-like all other numbers sent using Tox packets are sent in big endian format.--The group chat number is as explained above, the number used to uniquely-identify the groupchat instance from all the other groupchat instances the peer-has. It is sent in the invite packet because it is needed by the friend in-order to send back groupchat related packets.--What follows is the 1 byte type with the 32 byte groupchat id appended to it.--To refuse the invite, the friend receiving it will simply ignore and discard-it.--To accept the invite, the friend will create their own groupchat instance with-the 32 byte groupchat id and 1 byte type sent in the request and send a invite-response packet back. The friend will also add the one who sent the invite as-a temporary invited groupchat connection.--The first group number in the response packet is the group number of the-groupchat the invited friend just created. The second group number is the-group chat number that was sent in the invite request. What follows is the 1-byte type and 32 byte groupchat id that were sent in the invite request.--When a peer receives an invite response packet they will check if the group id-sent back corresponds to the group id of the groupchat with the group number-also sent back. If everything is ok, a new peer number will be generated for-the peer that sent the invite response packet. Then the peer with their-generated peer number, their long term public key and DHT public key will be-added to the peer list of the groupchat. A new peer packet will also be sent-to tell everyone in the group chat about the new peer. The peer will also be-added as a temporary invited groupchat connection.--Peer numbers are used to uniquely identify each peer in the group chat. They-are used in groupchat message packets so that peers receiving them can know who-or which groupchat peer sent them. As groupchat packets are relayed, they must-contain something that is used by others to identify the sender. Since putting-a 32 byte public key in each packet would be wasteful a 2 byte peer number is-instead used. Each peer in the groupchat has a unique peer number. Toxcore-generates each peer number randomly but makes sure newly generated peer numbers-are not equal to current ones already used by other peers in the group chat.-If two peers join the groupchat from two different endpoints there is a small-possibility that both will be given the same peer number however this-possibility is low enough in practice that is is not an issue.--Temporary invited groupchat connections are groupchat connections to the-groupchat inviter used by groupchat peers to bootstrap themselves the the-groupchat. They are the same thing as connections to groupchat peers via-friend connections except that they are discarded after the peer is fully-connected to the group chat.--Peer online packet:--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x61) \\- \texttt{2} & \texttt{uint16_t} group number (local) \\- \texttt{33} & Group chat identifier \\-\end{tabular}--Peer leave packet:--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x62) \\- \texttt{2} & \texttt{uint16_t} group number (local) \\- \texttt{1} & \texttt{uint8_t} (0x01) \\-\end{tabular}--For a groupchat connection to work, both peers in the groupchat must be-attempting to connect directly to each other.--Groupchat connections are established when both peers who want to connect to-each other either create a new friend connection to connect to each other or-reuse an exiting friend connection that connects them together (if they are-friends or already are connected together because of another group chat).--As soon as the connection to the other peer is opened, a peer online packet is-sent to the peer. The goal of the online packet is to tell the peer that we-want to establish the groupchat connection with them and tell them the-groupchat number of our groupchat instance. The peer online packet contains-the group number and the group type and 32 byte groupchat id. The group number-is the group number the peer has for the group with the group id sent in the-packet.--When both sides send a online packet to the other peer, a connection is-established.--When an online packet is received, the group number to communicate with the-group is saved. If the connection to the peer is already established (an-online packet has been already received) then the packet is dropped. If there-is no group connection to that peer being established, the packet is dropped.-If this is the first group connection to that group we establish, a peer query-packet is sent. This is so we can get the list of peers from the group.--The peer leave packet is sent to the peer right before killing a group-connection. It is only used to tell the other side that the connection is dead-if the friend connection is used for other uses than the group chat (another-group chat, for a connection to a friend). If not, then the other peer will-see the friend connection go offline which will prompt them to stop using it-and kill the group connection tied to it.--Peer query packet:--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x62) \\- \texttt{2} & \texttt{uint16_t} group number \\- \texttt{1} & \texttt{uint8_t} (0x08) \\-\end{tabular}--Peer response packet:--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x62) \\- \texttt{2} & \texttt{uint16_t} group number \\- \texttt{1} & \texttt{uint8_t} (0x09) \\- variable & Repeated times number of peers: Peer info \\-\end{tabular}--The Peer info structure is as follows:--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{2} & \texttt{uint16_t} peer number \\- \texttt{32} & Long term public key \\- \texttt{32} & DHT public key \\- \texttt{1} & \texttt{uint8_t} Name length \\- \texttt{[0, 255]} & Name \\-\end{tabular}--Title response packet:--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x62) \\- \texttt{2} & \texttt{uint16_t} group number \\- \texttt{1} & \texttt{uint8_t} (0x0a) \\- variable & Title \\-\end{tabular}--Message packets:--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x63) \\- \texttt{2} & \texttt{uint16_t} group number \\- \texttt{2} & \texttt{uint16_t} peer number \\- \texttt{4} & \texttt{uint32_t} message number \\- \texttt{1} & \texttt{uint8_t} with a value representing id of message \\- variable & Data \\-\end{tabular}--Lossy Message packets:--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0xc7) \\- \texttt{2} & \texttt{uint16_t} group number \\- \texttt{2} & \texttt{uint16_t} peer number \\- \texttt{4} & \texttt{uint16_t} message number \\- \texttt{1} & \texttt{uint8_t} with a value representing id of message \\- variable & Data \\-\end{tabular}--If a peer query packet is received, the receiver takes his list of peers and-creates a peer response packet which is then sent to the other peer. If there-are too many peers in the group chat and the peer response packet would be-larger than the maximum size of friend connection packets (1373 bytes), more-than one peer response packet is sent back. A Title response packet is also-sent back. This is how the peer that joins a group chat finds out the list of-peers in the group chat and the title of the group chat right after joining.--Peer response packets are straightforward and contain the information for each-peer (peer number, real public key, DHT public key, name) appended to each-other. The title response is also straight forward.--Both the maximum length of groupchat peer names and the groupchat title is 128-bytes. This is the same maximum length as names in all of toxcore.--When a peer receives the peer response packet(s), they will add each of the-received peers to their groupchat peer list, find the 4 closest peers to them-and create groupchat connections to them as was explained previously.--To find their peer number, the peer will find themselves in the list of-received peers and use the peer number assigned to them as their own.--Message packets are used to send messages to all peers in the groupchat. To-send a message packet, a peer will first take their peer number and the message-they want to send. Each message packet sent will have a message number that is-equal to the last message number sent + 1. Like all other numbers (group chat-number, peer number) in the packet, the message number in the packet will be in-big endian format. When a Message packet is received, the peer receiving it-will take the message number in the packet and see if it is bigger than the one-it has saved for the peer with peer number. If this is the first Message-packet being received for this peer then this check is omitted. The message-number is used to know if a Message packet was already received and relayed to-prevent packets from looping around the groupchat. If the message number check-says that the packet was already received, then the packet is discarded. If it-was not already received, a Message packet with the message is sent (relayed)-to all current group connections (normal groupchat connections + temporary-invited groupchat connections) except the one that it was received from. The-only thing that should change in the Message packet as it is relayed is the-group number.--\section{Message ids}--\subsection{ping (0x00)}--Sent approximately every 60 seconds by every peer. Contains no data.--\subsection{\texttt{new_peer} (0x10)}--Tell everyone about a new peer in the chat.--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{2} & \texttt{uint16_t} Peer number \\- \texttt{32} & Long term public key \\- \texttt{32} & DHT public key \\-\end{tabular}--\subsection{\texttt{kill_peer} (0x11)}--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{2} & \texttt{uint16_t} Peer number \\-\end{tabular}--\subsection{Name change (0x30)}--\begin{tabular}{l|l}- Length & Contents \\- \hline- variable & Name (namelen) \\-\end{tabular}--\subsection{Groupchat title change (0x31)}--\begin{tabular}{l|l}- Length & Contents \\- \hline- variable & Title (titlelen) \\-\end{tabular}--\subsection{Chat message (0x40)}--\begin{tabular}{l|l}- Length & Contents \\- \hline- variable & Message (messagelen) \\-\end{tabular}--\subsection{Action (/me) (0x41)}--\begin{tabular}{l|l}- Length & Contents \\- \hline- variable & Message (messagelen) \\-\end{tabular}--Ping messages must be sent every 60 seconds by every peer. This is how other-peers know that the peers are still alive.--When a new peer joins, the peer which invited the joining peer will send a new-peer message to warn everyone that there is a new peer in the chat. When a new-peer message is received, the peer in the packet must be added to the peer-list.--Kill peer messages are used to indicate that a peer has quit the group chat.-It is sent by the one quitting the group chat right before they quit it.--name change messages are used to change or set the name of the peer sending it.-They are also sent by a joining peer right after receiving the list of peers in-order to tell others what their name is.--title change packets are used to change the title of the group chat and can be-sent by anyone in the group chat.--Chat and action messages are used by the group chat peers to send messages to-others in the group chat.--Lossy message packets are used to send audio packets to others in audio group-chats. Lossy packets work the same way as normal relayed groupchat messages in-that they are relayed to everyone in the group chat until everyone has them.--Some differences with them though is that first of all the message number is a-2 byte integer. If I were to improve the groupchats protocol I would make the-message number for normal message packets 2 bytes. 1 byte means only 256-packets can be received at the same time. With the delays in groupchats and-256 packets corresponding to less than a high quality video frame it would not-work. This is why 2 bytes was chosen.--Note that this message number like all other numbers in the packet are in big-endian format.--When receiving a lossy packet the peer will first check if it was already-received. If it wasn't, the packet will be added to the list of received-packets and then the packet will be passed to its handler and then sent to the-2 closest connected groupchat peers that are not the sender. The reason for it-to be 2 instead of 4 (well 3 if we are not the original sender) for normal-message packets is that it reduces bandwidth usage without lowering the quality-of the received audio stream via lossy packets. Message packets also are sent-relatively rarely, enough so that changing it to 2 would have a minimal impact-in bandwidth usage.--To check if a packet was received, the last up to 65536 received packet numbers-are stored, current groups store the last 256 packet numbers however that is-because it is currently audio only. If video was added meaning a much higher-number of packets would be sent, this number would be increased. If the packet-number is in this list then it was received.--This is how groupchats in Tox work.--\chapter{Net crypto}--The Tox transport protocol is what Tox uses to establish and send data securely-to friends and provides encryption, ordered delivery, and perfect forward-secrecy. It is a UDP protocol but it is also used when 2 friends connect over-TCP relays.--The reason the protocol for connections to friends over TCP relays and direct-UDP is the same is for simplicity and so the connection can switch between both-without the peers needing to disconnect and reconnect. For example two Tox-friends might first connect over TCP and a few seconds later switch to UDP when-a direct UDP connection becomes possible. The opening up of the UDP route or-'hole punching' is done by the DHT module and the opening up of a relayed TCP-connection is done by the \texttt{TCP_connection} module. The Tox transport-protocol has the job of connecting two peers (tox friends) safely once a route-or communications link between both is found. Direct UDP is preferred over TCP-because it is direct and isn't limited by possibly congested TCP relays. Also,-a peer can only connect to another using the Tox transport protocol if they-know the real public key and DHT public key of the peer they want to connect-to. However, both the DHT and TCP connection modules require this information-in order to find and open the route to the peer which means we assume this-information is known by toxcore and has been passed to \texttt{net_crypto} when-the \texttt{net_crypto} connection was created.--Because this protocol has to work over UDP it must account for possible packet-loss, packets arriving in the wrong order and has to implement some kind of-congestion control. This is implemented above the level at which the packets-are encrypted. This prevents a malicious TCP relay from disrupting the-connection by modifying the packets that go through it. The packet loss-prevention makes it work very well on TCP relays that we assume may go down at-any time as the connection will stay strong even if there is need to switch to-another TCP relay which will cause some packet loss.--Before sending the actual handshake packet the peer must obtain a cookie. This-cookie step serves as a way for the receiving peer to confirm that the peer-initiating the connection can receive the responses in order to prevent certain-types of DoS attacks.--The peer receiving a cookie request packet must not allocate any resources to-the connection. They will simply respond to the packet with a cookie response-packet containing the cookie that the requesting peer must then use in the-handshake to initiate the actual connection.--The cookie response must be sent back using the exact same link the cookie-request packet was sent from. The reason for this is that if it is sent back-using another link, the other link might not work and the peer will not be-expecting responses from another link. For example, if a request is sent from-UDP with ip port X, it must be sent back by UDP to ip port X. If it was-received from a TCP OOB packet it must be sent back by a TCP OOB packet via the-same relay with the destination being the peer who sent the request. If it was-received from an established TCP relay connection it must be sent back via that-same exact connection.--When a cookie request is received, the peer must not use the information in the-request packet for anything, he must not store it, he must only create a cookie-and cookie response from it, then send the created cookie response packet and-forget them. The reason for this is to prevent possible attacks. For example-if a peer would allocate long term memory for each cookie request packet-received then a simple packet flood would be enough to achieve an effective-denial of service attack by making the program run out of memory.--cookie request packet (145 bytes):--\begin{verbatim}-[uint8_t 24]-[Sender's DHT Public key (32 bytes)]-[Random nonce (24 bytes)]-[Encrypted message containing:- [Sender's real public key (32 bytes)]- [padding (32 bytes)]- [uint64_t echo id (must be sent back untouched in cookie response)]-]-\end{verbatim}--Encrypted message is encrypted with sender's DHT private key, receiver's DHT-public key and the nonce.--The packet id for cookie request packets is 24. The request contain the DHT-public key of the sender which is the key used (The DHT private key) (along-with the DHT public key of the receiver) to encrypt the encrypted part of the-cookie packet and a nonce also used to encrypt the encrypted part of the-packet. Padding is used to maintain backwards-compatibility with previous-versions of the protocol. The echo id in the cookie request must be sent back-untouched in the cookie response. This echo id is how the peer sending the-request can be sure that the response received was a response to the packet-that he sent.--The reason for sending the DHT public key and real public key in the cookie-request is that both are contained in the cookie sent back in the response.--Toxcore currently sends 1 cookie request packet every second 8 times before it-kills the connection if there are no responses.--cookie response packet (161 bytes):--\begin{verbatim}-[uint8_t 25]-[Random nonce (24 bytes)]-[Encrypted message containing:- [Cookie]- [uint64_t echo id (that was sent in the request)]-]-\end{verbatim}--Encrypted message is encrypted with the exact same symmetric key as the cookie-request packet it responds to but with a different nonce.--The packet id for cookie request packets is 25. The response contains a nonce-and an encrypted part encrypted with the nonce. The encrypted part is-encrypted with the same key used to decrypt the encrypted part of the request-meaning the expensive shared key generation needs to be called only once in-order to handle and respond to a cookie request packet with a cookie response.--The Cookie (see below) and the echo id that was sent in the request are the-contents of the encrypted part.--The Cookie should be (112 bytes):--\begin{verbatim}-[nonce]-[encrypted data:- [uint64_t time]- [Sender's real public key (32 bytes)]- [Sender's DHT public key (32 bytes)]-]-\end{verbatim}--The cookie is a 112 byte piece of data that is created and sent to the-requester as part of the cookie response packet. A peer who wants to connect-to another must obtain a cookie packet from the peer they are trying to connect-to. The only way to send a valid handshake packet to another peer is to first-obtain a cookie from them.--The cookie contains information that will both prove to the receiver of the-handshake that the peer has received a cookie response and contains encrypted-info that tell the receiver of the handshake packet enough info to both decrypt-and validate the handshake packet and accept the connection.--When toxcore is started it generates a symmetric encryption key that it uses to-encrypt and decrypt all cookie packets (using NaCl authenticated encryption-exactly like encryption everywhere else in toxcore). Only the instance of-toxcore that create the packets knows the encryption key meaning any cookie it-successfully decrypts and validates were created by it.--The time variable in the cookie is used to prevent cookie packets that are too-old from being used. Toxcore has a time out of 15 seconds for cookie packets.-If a cookie packet is used more than 15 seconds after it is created toxcore-will see it as invalid.--When responding to a cookie request packet the sender's real public key is the-known key sent by the peer in the encrypted part of the cookie request packet-and the senders DHT public key is the key used to encrypt the encrypted part of-the cookie request packet.--When generating a cookie to put inside the encrypted part of the handshake: One-of the requirements to connect successfully to someone else is that we know-their DHT public key and their real long term public key meaning there is-enough information to construct the cookie.--Handshake packet:--\begin{verbatim}-[uint8_t 26]-[Cookie]-[nonce (24 bytes)]-[Encrypted message containing:- [24 bytes base nonce]- [session public key of the peer (32 bytes)]- [sha512 hash of the entire Cookie sitting outside the encrypted part]- [Other Cookie (used by the other to respond to the handshake packet)]-]-\end{verbatim}--The packet id for handshake packets is 26. The cookie is a cookie obtained by-sending a cookie request packet to the peer and getting a cookie response-packet with a cookie in it. It may also be obtained in the handshake packet by-a peer receiving a handshake packet (Other Cookie).--The nonce is a nonce used to encrypt the encrypted part of the handshake-packet. The encrypted part of the handshake packet is encrypted with the long-term keys of both peers. This is to prevent impersonation.--Inside the encrypted part of the handshake packet there is a 'base nonce' and a-session public key. The 'base nonce' is a nonce that the other should use to-encrypt each data packet, adding + 1 to it for each data packet sent. (first-packet is 'base nonce' + 0, next is 'base nonce' + 1, etc. Note that for-mathematical operations the nonce is considered to be a 24 byte number in big-endian format). The session key is the temporary connection public key that-the peer has generated for this connection and it sending to the other. This-session key is used so that the connection has perfect forward secrecy. It is-important to save the private key counterpart of the session public key sent in-the handshake, the public key received by the other and both the received and-sent base nonces as they are used to encrypt/decrypt the data packets.--The hash of the cookie in the encrypted part is used to make sure that an-attacker has not taken an older valid handshake packet and then replaced the-cookie packet inside with a newer one which would be bad as they could replay-it and might be able to make a mess.--The 'Other Cookie' is a valid cookie that we put in the handshake so that the-other can respond with a valid handshake without having to make a cookie-request to obtain one.--The handshake packet is sent by both sides of the connection. If a peer-receives a handshake it will check if the cookie is valid, if the encrypted-section decrypts and validates, if the cookie hash is valid, if long term-public key belongs to a known friend. If all these are true then the-connection is considered 'Accepted' but not 'Confirmed'.--If there is no existing connection to the peer identified by the long term-public key to set to 'Accepted', one will be created with that status. If a-connection to such peer with a not yet 'Accepted' status to exists, this-connection is set to accepted. If a connection with a 'Confirmed' status-exists for this peer, the handshake packet will be ignored and discarded (The-reason for discarding it is that we do not want slightly late handshake packets-to kill the connection) except if the DHT public key in the cookie contained in-the handshake packet is different from the known DHT public key of the peer.-If this happens the connection will be immediately killed because it means it-is no longer valid and a new connection will be created immediately with the-'Accepted' status.--Sometimes toxcore might receive the DHT public key of the peer first with a-handshake packet so it is important that this case is handled and that the-implementation passes the DHT public key to the other modules (DHT,-\texttt{TCP_connection}) because this does happen.--Handshake packets must be created only once during the connection but must be-sent in intervals until we are sure the other received them. This happens when-a valid encrypted data packet is received and decrypted.--The states of a connection:--\begin{enumerate}- \item Not accepted: Send handshake packets.- \item Accepted: A handshake packet has been received from the other peer but no- encrypted encrypted packets: continue (or start) sending handshake packets- because the peer can't know if the other has received them.- \item Confirmed: A valid encrypted packet has been received from the other peer:- Connection is fully established: stop sending handshake packets.-\end{enumerate}--Toxcore sends handshake packets every second 8 times and times out the-connection if the connection does not get confirmed (no encrypted packet is-received) within this time.--Perfect handshake scenario:--\begin{verbatim}-Peer 1 Peer 2-Cookie request ->- <- Cookie response-Handshake packet ->- * accepts connection- <- Handshake packet-*accepts connection-Encrypted packet -> <- Encrypted packet-*confirms connection *confirms connection- Connection successful.-Encrypted packets -> <- Encrypted packets--More realistic handshake scenario:-Peer 1 Peer 2-Cookie request -> *packet lost*-Cookie request ->- <- Cookie response- *Peer 2 randomly starts new connection to peer 1- <- Cookie request-Cookie response ->-Handshake packet -> <- Handshake packet-*accepts connection * accepts connection-Encrypted packet -> <- Encrypted packet-*confirms connection *confirms connection- Connection successful.-Encrypted packets -> <- Encrypted packets-\end{verbatim}--The reason why the handshake is like this is because of certain design-requirements:--\begin{enumerate}- \item The handshake must not leak the long term public keys of the peers to a- possible attacker who would be looking at the packets but each peer must know- for sure that they are connecting to the right peer and not an impostor.- \item A connection must be able of being established if only one of the peers has- the information necessary to initiate a connection (DHT public key of the- peer and a link to the peer).- \item If both peers initiate a connection to each other at the same time the- connection must succeed without issues.- \item There must be perfect forward secrecy.- \item Must be resistant to any possible attacks.-\end{enumerate}--Due to how it is designed only one connection is possible at a time between 2-peers.--Encrypted packets:--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x1b) \\- \texttt{2} & \texttt{uint16_t} The last 2 bytes of the nonce used to encrypt this \\- variable & Payload \\-\end{tabular}--The payload is encrypted with the session key and 'base nonce' set by the-receiver in their handshake + packet number (starting at 0, big endian math).--The packet id for encrypted packets is 27. Encrypted packets are the packets-used to send data to the other peer in the connection. Since these packets can-be sent over UDP the implementation must assume that they can arrive out of-order or even not arrive at all.--To get the key used to encrypt/decrypt each packet in the connection a peer-takes the session public key received in the handshake and the private key-counterpart of the key it sent it the handshake and generates a shared key from-it. This shared key will be identical for both peers. It is important to note-that connection keys must be wiped when the connection is killed.--To create an encrypted packet to be sent to the other peer, the data is-encrypted with the shared key for this connection and the base nonce that the-other peer sent in the handshake packet with the total number of encrypted-packets sent in the connection added to it ('base nonce' + 0 for the first-encrypted data packet sent, 'base nonce' + 1 for the second, etc. Note that-the nonce is treated as a big endian number for mathematical operations like-additions). The 2 byte (\texttt{uint16_t}) number at the beginning of the-encrypted packet is the last 2 bytes of this 24 byte nonce.--To decrypt a received encrypted packet, the nonce the packet was encrypted with-is calculated using the base nonce that the peer sent to the other and the 2-byte number at the beginning of the packet. First we assume that packets will-most likely arrive out of order and that some will be lost but that packet loss-and out of orderness will never be enough to make the 2 byte number need an-extra byte. The packet is decrypted using the shared key for the connection-and the calculated nonce.--Toxcore uses the following method to calculate the nonce for each packet:--\begin{enumerate}- \item \texttt{diff} = (2 byte number on the packet) - (last 2 bytes of the current saved- base nonce) NOTE: treat the 3 variables as 16 bit unsigned ints, the result- is expected to sometimes roll over.- \item copy \texttt{saved_base_nonce} to \texttt{temp_nonce}.- \item \texttt{temp_nonce = temp_nonce + diff}. \texttt{temp_nonce} is the correct nonce that- can be used to decrypt the packet.- \item \texttt{DATA_NUM_THRESHOLD} = (1/3 of the maximum number that can be stored in an- unsigned 2 bit integer)- \item if decryption succeeds and \texttt{diff > (DATA_NUM_THRESHOLD * 2)} then:- \begin{itemize}- \item \texttt{saved_base_nonce = saved_base_nonce + DATA_NUM_THRESHOLD}- \end{itemize}-\end{enumerate}--First it takes the difference between the 2 byte number on the packet and the-last. Because the 3 values are unsigned 16 bit ints and rollover is part of-the math something like diff = (10 - 65536) means diff is equal to 11.--Then it copies the saved base nonce to a temp nonce buffer.--Then it adds diff to the nonce (the nonce is in big endian format).--After if decryption was successful it checks if diff was bigger than 2/3 of the-value that can be contained in a 16 bit unsigned int and increases the saved-base nonce by 1/3 of the maximum value if it succeeded.--This is only one of many ways that the nonce for each encrypted packet can be-calculated.--Encrypted packets that cannot be decrypted are simply dropped.--The reason for exchanging base nonces is because since the key for encrypting-packets is the same for received and sent packets there must be a cryptographic-way to make it impossible for someone to do an attack where they would replay-packets back to the sender and the sender would think that those packets came-from the other peer.--Data in the encrypted packets:--\begin{verbatim}-[our recvbuffers buffer_start, (highest packet number handled + 1), (big endian)]-[uint32_t packet number if lossless, sendbuffer buffer_end if lossy, (big endian)]-[data]-\end{verbatim}--Encrypted packets may be lossy or lossless. Lossy packets are simply encrypted-packets that are sent to the other. If they are lost, arrive in the wrong-order or even if an attacker duplicates them (be sure to take this into account-for anything that uses lossy packets) they will simply be decrypted as they-arrive and passed upwards to what should handle them depending on the data id.--Lossless packets are packets containing data that will be delivered in order by-the implementation of the protocol. In this protocol, the receiver tells the-sender which packet numbers he has received and which he has not and the sender-must resend any packets that are dropped. Any attempt at doubling packets will-cause all (except the first received) to be ignored.--Each lossless packet contains both a 4 byte number indicating the highest-packet number received and processed and a 4 byte packet number which is the-packet number of the data in the packet.--In lossy packets, the layout is the same except that instead of a packet-number, the second 4 byte number represents the packet number of a lossless-packet if one were sent right after. This number is used by the receiver to-know if any packets have been lost. (for example if it receives 4 packets with-numbers (0, 1, 2, 5) and then later a lossy packet with this second number as:-8 it knows that packets: 3, 4, 6, 7 have been lost and will request them)--How the reliability is achieved:--First it is important to say that packet numbers do roll over, the next number-after 0xFFFFFFFF (maximum value in 4 bytes) is 0. Hence all the mathematical-operations dealing with packet numbers are assumed to be done only on unsigned-32 bit integer unless said otherwise. For example 0 - 0xFFFFFFFF would equal-to 1 because of the rollover.--When sending a lossless packet, the packet is created with its packet number-being the number of the last lossless packet created + 1 (starting at 0). The-packet numbers are used for both reliability and in ordered delivery and so-must be sequential.--The packet is then stored along with its packet number in order for the peer to-be able to send it again if the receiver does not receive it. Packets are only-removed from storage when the receiver confirms they have received them.--The receiver receives packets and stores them along with their packet number.-When a receiver receives a packet he stores the packet along with its packet-number in an array. If there is already a packet with that number in the-buffer, the packet is dropped. If the packet number is smaller than the last-packet number that was processed, the packet is dropped. A processed packet-means it was removed from the buffer and passed upwards to the relevant module.--Assuming a new connection, the sender sends 5 lossless packets to the receiver:-0, 1, 2, 3, 4 are the packet numbers sent and the receiver receives: 3, 2, 0, 2-in that order.--The receiver will save the packets and discards the second packet with the-number 2, he has: 0, 2, 3 in his buffer. He will pass the first packet to the-relevant module and remove it from the array but since packet number 1 is-missing he will stop there. Contents of the buffer are now: 2, 3. The-receiver knows packet number 1 is missing and will request it from the sender-by using a packet request packet:--data ids:--\begin{tabular}{l|l}- ID & Data \\- \hline- 0 & padding (skipped until we hit a non zero (data id) byte) \\- 1 & packet request packet (lossy packet) \\- 2 & connection kill packet (lossy packet) \\- ... & ... \\- 16+ & reserved for Messenger usage (lossless packets) \\- 192+ & reserved for Messenger usage (lossy packets) \\- 255 & reserved for Messenger usage (lossless packet) \\-\end{tabular}--Connection kill packets tell the other that the connection is over.--Packet numbers are the first byte of data in the packet.--packet request packet:--\begin{verbatim}-[uint8_t (1)][uint8_t num][uint8_t num][uint8_t num]...[uint8_t num]-\end{verbatim}--Packet request packets are used by one side of the connection to request-packets from the other. To create a full packet request packet, the one-requesting the packet takes the last packet number that was processed (sent to-the relevant module and removed from the array (0 in the example above)).-Subtract the number of the first missing packet from that number (1 - 0) = 1.-Which means the full packet to request packet number 1 will look like:--\begin{verbatim}-[uint32_t 1]-[uint32_t 0]-[uint8_t 1][uint8_t 1]-\end{verbatim}--If packet number 4 was being requested as well, take the difference between the-packet number and the last packet number being requested (4 - 1) = 3. So the-packet will look like:--\begin{verbatim}-[uint32_t 1]-[uint32_t 0]-[uint8_t 1][uint8_t 1][uint8_t 3]-\end{verbatim}--But what if the number is greater than 255? Let's say the peer needs to request-packets 3, 6, 1024, the packet will look like:--\begin{verbatim}-[uint32_t 1]-[uint32_t 2]-[uint8_t 1][uint8_t 3][uint8_t 3][uint8_t 0][uint8_t 0][uint8_t 0][uint8_t 253]-\end{verbatim}--Each 0 in the packet represents adding 255 until a non 0 byte is reached which-is then added and the resulting requested number is what is left.--This request is designed to be small when requesting packets in real network-conditions where the requested packet numbers will be close to each other.-Putting each requested 4 byte packet number would be very simple but would make-the request packets unnecessarily large which is why the packets look like-this.--When a request packet is received, it will be decoded and all packets in-between the requested packets will be assumed to be successfully received by-the other.--Packet request packets are sent at least every 1 second in toxcore and more-when packets are being received.--The current formula used is (note that this formula is likely sub-optimal):--\begin{verbatim}-REQUEST_PACKETS_COMPARE_CONSTANT = 50.0 double request_packet_interval =-(REQUEST_PACKETS_COMPARE_CONSTANT /-(((double)num_packets_array(&conn->recv_array) + 1.0) / (conn->packet_recv_rate-+ 1.0)));-\end{verbatim}--\texttt{num_packets_array(&conn->recv_array)} returns the difference between-the highest packet number received and the last one handled. In the toxcore-code it refers to the total size of the current array (with the holes which are-the placeholders for not yet received packets that are known to be missing).--\texttt{conn->packet_recv_rate} is the number of data packets successfully-received per second.--This formula was created with the logic that the higher the 'delay' in packets-(\texttt{num_packets_array(&conn->recv_array)}) vs the speed of packets-received, the more request packets should be sent.--Requested packets are resent every time they can be resent as in they will obey-the congestion control and not bypass it. They are resent once, subsequent-request packets will be used to know if the packet was received or if it should-be resent.--The ping or rtt (round trip time) between two peers can be calculated by saving-the time each packet was sent and taking the difference between the time the-latest packet confirmed received by a request packet was sent and the time the-request packet was received. The rtt can be calculated for every request-packet. The lowest one (for all packets) will be the closest to the real ping.--This ping or rtt can be used to know if a request packet that requests a packet-we just sent should be resent right away or we should wait or not for the next-one (to know if the other side actually had time to receive the packet).--The congestion control algorithm has the goal of guessing how many packets can-be sent through the link every second before none can be sent through anymore.-How it works is basically to send packets faster and faster until none can go-through the link and then stop sending them faster than that.--Currently the congestion control uses the following formula in toxcore however-that is probably not the best way to do it.--The current formula is to take the difference between the current size of the-send queue and the size of the send queue 1.2 seconds ago, take the total-number of packets sent in the last 1.2 seconds and subtract the previous number-from it.--Then divide this number by 1.2 to get a packet speed per second. If this speed-is lower than the minimum send rate of 8 packets per second, set it to 8.--A congestion event can be defined as an event when the number of requested-packets exceeds the number of packets the congestion control says can be sent-during this frame. If a congestion event occurred during the last 2 seconds,-the packet send rate of the connection is set to the send rate previously-calculated, if not it is set to that send rate times 1.25 in order to increase-the speed.--Like I said this isn't perfect and a better solution can likely be found or the-numbers tweaked.--To fix the possible issue where it would be impossible to send very low-bandwidth data like text messages when sending high bandwidth data like files-it is possible to make priority packets ignore the congestion control-completely by placing them into the send packet queue and sending them even if-the congestion control says not to. This is used in toxcore for all non file-transfer packets to prevent file transfers from preventing normal message-packets from being sent.--\chapter{network.txt}--The network module is the lowest file in toxcore that everything else depends-on. This module is basically a UDP socket wrapper, serves as the sorting-ground for packets received by the socket, initializes and uninitializes the-socket. It also contains many socket, networking related and some other-functions like a monotonic time function used by other toxcore modules.--Things of note in this module are the maximum UDP packet size define-(\texttt{MAX_UDP_PACKET_SIZE}) which sets the maximum UDP packet size toxcore-can send and receive. The list of all UDP packet ids: \texttt{NET_PACKET_*}.-UDP packet ids are the value of the first byte of each UDP packet and is how-each packet gets sorted to the right module that can handle it.-\texttt{networking_registerhandler()} is used by higher level modules in order-to tell the network object which packets to send to which module via a-callback.--It also contains datastructures used for ip addresses in toxcore. IP4 and IP6-are the datastructures for ipv4 and ipv6 addresses, IP is the datastructure for-storing either (the family can be set to \texttt{AF_INET} (ipv4) or-\texttt{AF_INET6} (ipv6). It can be set to another value like-\texttt{TCP_ONION_FAMILY}, \texttt{TCP_INET}, \texttt{TCP_INET6} or-\texttt{TCP_FAMILY} which are invalid values in the network modules but valid-values in some other module and denote a special type of ip) and-\texttt{IP_Port} stores an IP datastructure with a port.--Since the network module interacts directly with the underlying operating-system with its socket functions it has code to make it work on windows, linux,-etc... unlike most modules that sit at a higher level.--The network module currently uses the polling method to read from the UDP-socket. The \texttt{networking_poll()} function is called to read all the-packets from the socket and pass them to the callbacks set using the-\texttt{networking_registerhandler()} function. The reason it uses polling is-simply because it was easier to write it that way, another method would be-better here.--The goal of this module is to provide an easy interface to a UDP socket and-other networking related functions.--\chapter{Onion}--The goal of the onion module in Tox is to prevent peers that are not friends-from finding out the temporary DHT public key from a known long term public key-of the peer and to prevent peers from discovering the long term public key of-peers when only the temporary DHT key is known.--It makes sure only friends of a peer can find it and connect to it and-indirectly makes sure non friends cannot find the ip address of the peer when-knowing the Tox address of the friend.--The only way to prevent peers in the network from associating the temporary DHT-public key with the long term public key is to not broadcast the long term key-and only give others in the network that are not friends the DHT public key.--The onion lets peers send their friends, whose real public key they know as it-is part of the Tox ID, their DHT public key so that the friends can then find-and connect to them without other peers being able to identify the real public-keys of peers.--So how does the onion work?--The onion works by enabling peers to announce their real public key to peers by-going through the onion path. It is like a DHT but through onion paths. In-fact it uses the DHT in order for peers to be able to find the peers with ids-closest to their public key by going through onion paths.--In order to announce its real public key anonymously to the Tox network while-using the onion, a peer first picks 3 random nodes that it knows (they can be-from anywhere: the DHT, connected TCP relays or nodes found while finding peers-with the onion). The nodes should be picked in a way that makes them unlikely-to be operated by the same person perhaps by looking at the ip addresses and-looking if they are in the same subnet or other ways. More research is needed-to make sure nodes are picked in the safest way possible.--The reason for 3 nodes is that 3 hops is what they use in Tor and other-anonymous onion based networks.--These nodes are referred to as nodes A, B and C. Note that if a peer cannot-communicate via UDP, its first peer will be one of the TCP relays it is-connected to, which will be used to send its onion packet to the network.--TCP relays can only be node A or the first peer in the chain as the TCP relay-is essentially acting as a gateway to the network. The data sent to the TCP-Client module to be sent as a TCP onion packet by the module is different from-the one sent directly via UDP. This is because it doesn't need to be encrypted-(the connection to the TCP relay server is already encrypted).--First I will explain how communicating via onion packets work.--Note: nonce is a 24 byte nonce. The nested nonces are all the same as the-outer nonce.--Onion packet (request):--Initial (TCP) data sent as the data of a onion packet through the TCP client-module:--\begin{itemize}- \item \texttt{IP_Port} of node B- \item A random public key PK1- \item Encrypted with the secret key SK1 and the public key of Node B and the nonce:- \begin{itemize}- \item \texttt{IP_Port} of node C- \item A random public key PK2- \item Encrypted with the secret key SK2 and the public key of Node C and the nonce:- \begin{itemize}- \item \texttt{IP_Port} of node D- \item Data to send to Node D- \end{itemize}- \end{itemize}-\end{itemize}--Initial (UDP) (sent from us to node A):--\begin{itemize}- \item \texttt{uint8_t} (0x80) packet id- \item Nonce- \item Our temporary DHT public key- \item Encrypted with our temporary DHT secret key and the public key of Node A and- the nonce:- \begin{itemize}- \item \texttt{IP_Port} of node B- \item A random public key PK1- \item Encrypted with the secret key SK1 and the public key of Node B and the nonce:- \begin{itemize}- \item \texttt{IP_Port} of node C- \item A random public key PK2- \item Encrypted with the secret key SK2 and the public key of Node C and the- nonce:- \begin{itemize}- \item \texttt{IP_Port} of node D- \item Data to send to Node D- \end{itemize}- \end{itemize}- \end{itemize}-\end{itemize}--(sent from node A to node B):--\begin{itemize}- \item \texttt{uint8_t} (0x81) packet id- \item Nonce- \item A random public key PK1- \item Encrypted with the secret key SK1 and the public key of Node B and the nonce:- \begin{itemize}- \item \texttt{IP_Port} of node C- \item A random public key PK2- \item Encrypted with the secret key SK2 and the public key of Node C and the nonce:- \begin{itemize}- \item \texttt{IP_Port} of node D- \item Data to send to Node D- \end{itemize}- \end{itemize}- \item Nonce- \item Encrypted with temporary symmetric key of Node A and the nonce:- \begin{itemize}- \item \texttt{IP_Port} (of us)- \end{itemize}-\end{itemize}--(sent from node B to node C):--\begin{itemize}- \item \texttt{uint8_t} (0x82) packet id- \item Nonce- \item A random public key PK1- \item Encrypted with the secret key SK1 and the public key of Node C and the nonce:- \begin{itemize}- \item \texttt{IP_Port} of node D- \item Data to send to Node D- \end{itemize}- \item Nonce- \item Encrypted with temporary symmetric key of Node B and the nonce:- \begin{itemize}- \item \texttt{IP_Port} (of Node A)- \item Nonce- \item Encrypted with temporary symmetric key of Node A and the nonce:- \begin{itemize}- \item \texttt{IP_Port} (of us)- \end{itemize}- \end{itemize}-\end{itemize}--(sent from node C to node D):--\begin{itemize}- \item Data to send to Node D- \item Nonce- \item Encrypted with temporary symmetric key of Node C and the nonce:- \begin{itemize}- \item \texttt{IP_Port} (of Node B)- \item Nonce- \item Encrypted with temporary symmetric key of Node B and the nonce:- \begin{itemize}- \item \texttt{IP_Port} (of Node A)- \item Nonce- \item Encrypted with temporary symmetric key of Node A and the nonce:- \begin{itemize}- \item \texttt{IP_Port} (of us)- \end{itemize}- \end{itemize}- \end{itemize}-\end{itemize}--Onion packet (response):--initial (sent from node D to node C):--\begin{itemize}- \item \texttt{uint8_t} (0x8c) packet id- \item Nonce- \item Encrypted with the temporary symmetric key of Node C and the nonce:- \begin{itemize}- \item \texttt{IP_Port} (of Node B)- \item Nonce- \item Encrypted with the temporary symmetric key of Node B and the nonce:- \begin{itemize}- \item \texttt{IP_Port} (of Node A)- \item Nonce- \item Encrypted with the temporary symmetric key of Node A and the nonce:- \begin{itemize}- \item \texttt{IP_Port} (of us)- \end{itemize}- \end{itemize}- \end{itemize}- \item Data to send back-\end{itemize}--(sent from node C to node B):--\begin{itemize}- \item \texttt{uint8_t} (0x8d) packet id- \item Nonce- \item Encrypted with the temporary symmetric key of Node B and the nonce:- \begin{itemize}- \item \texttt{IP_Port} (of Node A)- \item Nonce- \item Encrypted with the temporary symmetric key of Node A and the nonce:- \begin{itemize}- \item \texttt{IP_Port} (of us)- \end{itemize}- \end{itemize}- \item Data to send back-\end{itemize}--(sent from node B to node A):--\begin{itemize}- \item \texttt{uint8_t} (0x8e) packet id- \item Nonce- \item Encrypted with the temporary symmetric key of Node A and the nonce:- \begin{itemize}- \item \texttt{IP_Port} (of us)- \end{itemize}- \item Data to send back-\end{itemize}--(sent from node A to us):--\begin{itemize}- \item Data to send back-\end{itemize}--Each packet is encrypted multiple times so that only node A will be able to-receive and decrypt the first packet and know where to send it to, node B will-only be able to receive that decrypted packet, decrypt it again and know where-to send it and so on. You will also notice a piece of encrypted data (the-sendback) at the end of the packet that grows larger and larger at every layer-with the IP of the previous node in it. This is how the node receiving the end-data (Node D) will be able to send data back.--When a peer receives an onion packet, they will decrypt it, encrypt the-coordinates (IP/port) of the source along with the already existing encrypted-data (if it exists) with a symmetric key known only by the peer and only-refreshed every hour (in toxcore) as a security measure to force expire paths.--Here's a diagram how it works:--\begin{verbatim}-peer- -> [onion1[onion2[onion3[data]]]] -> Node A- -> [onion2[onion3[data]]][sendbackA] -> Node B- -> [onion3[data]][sendbackB[sendbackA]] -> Node C- -> [data][SendbackC[sendbackB[sendbackA]]]-> Node D (end)-\end{verbatim}--\begin{verbatim}-Node D- -> [SendbackC[sendbackB[sendbackA]]][response] -> Node C- -> [sendbackB[sendbackA]][response] -> Node B- -> [sendbackA][response] -> Node A- -> [response] -> peer-\end{verbatim}--The random public keys in the onion packets are temporary public keys generated-for and used for that onion path only. This is done in order to make it-difficult for others to link different paths together. Each encrypted layer-must have a different public key. This is the reason why there are multiple-keys in the packet definintions above.--The nonce is used to encrypt all the layers of encryption. This 24 byte nonce-should be randomly generated. If it isn't randomly generated and has a-relation to nonces used for other paths it could be possible to tie different-onion paths together.--The \texttt{IP_Port} is an ip and port in packed format:--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{TOX_AF_INET} (2) for IPv4 or \texttt{TOX_AF_INET6} (10) for IPv6 \\- \texttt{4 | 16} & IP address (4 bytes if IPv4, 16 if IPv6) \\- \texttt{12 | 0} & Zeroes \\- \texttt{2} & \texttt{uint16_t} Port \\-\end{tabular}--If IPv4 the format is padded with 12 bytes of zeroes so that both IPv4 and IPv6-have the same stored size.--The \texttt{IP_Port} will always end up being of size 19 bytes. This is to-make it hard to know if an ipv4 or ipv6 ip is in the packet just by looking at-the size. The 12 bytes of zeros when ipv4 must be set to 0 and not left-uninitialized as some info may be leaked this way if it stays uninitialized.-All numbers here are in big endian format.--The \texttt{IP_Port} in the sendback data can be in any format as long as the-length is 19 bytes because only the one who writes it can decrypt it and read-it, however, using the previous format is recommended because of code reuse.-The nonce in the sendback data must be a 24 byte nonce.--Each onion layers has a different packed id that identifies it so that an-implementation knows exactly how to handle them. Note that any data being sent-back must be encrypted, appear random and not leak information in any way as-all the nodes in the path will see it.--If anything is wrong with the received onion packets (decryption fails) the-implementation should drop them.--The implementation should have code for each different type of packet that-handles it, adds (or decrypts) a sendback and sends it to the next peer in the-path. There are a lot of packets but an implementation should be very-straightforward.--Note that if the first node in the path is a TCP relay, the TCP relay must put-an identifier (instead of an IP/Port) in the sendback so that it knows that any-response should be sent to the appropriate peer connected to the TCP relay.--This explained how to create onion packets and how they are sent back. Next is-what is actually sent and received on top of these onion packets or paths.--Note: nonce is a 24 byte nonce.--announce request packet:--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x83) \\- \texttt{24} & Nonce \\- \texttt{32} & A public key (real or temporary) \\- \texttt{?} & Payload \\-\end{tabular}--The public key is our real long term public key if we want to announce-ourselves, a temporary one if we are searching for friends.--The payload is encrypted with the secret key part of the sent public key, the-public key of Node D and the nonce, and contains:--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{32} & Ping ID \\- \texttt{32} & Public key we are searching for \\- \texttt{32} & Public key that we want those sending back data packets to use \\- \texttt{8} & Data to send back in response \\-\end{tabular}--If the ping id is zero, respond with a announce response packet.--If the ping id matches the one the node sent in the announce response and the-public key matches the one being searched for, add the part used to send data-to our list. If the list is full make it replace the furthest entry.--data to route request packet:--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x85) \\- \texttt{32} & Public key of destination node \\- \texttt{24} & Nonce \\- \texttt{32} & Temporary just generated public key \\- variable & Payload \\-\end{tabular}--The payload is encrypted with that temporary secret key and the nonce and the-public key from the announce response packet of the destination node. If Node-D contains the ret data for the node, it sends the stuff in this packet as a-data to route response packet to the right node.--The data in the previous packet is in format:--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{32} & Real public key of sender \\- variable & Payload \\-\end{tabular}--The payload is encrypted with real secret key of the sender, the nonce in the-data packet and the real public key of the receiver:--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} id \\- variable & Data (optional) \\-\end{tabular}--Data sent to us:--announce response packet:--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x84) \\- \texttt{8} & Data to send back in response \\- \texttt{24} & Nonce \\- variable & Payload \\-\end{tabular}--The payload is encrypted with the DHT secret key of Node D, the public key in-the request and the nonce:--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} \texttt{is_stored} \\- \texttt{32} & Ping ID or Public Key \\- variable & Maximum of 4 nodes in packed node format (see DHT) \\-\end{tabular}--The packet contains a ping ID if \texttt{is_stored} is 0 or 2, or the public-key that must be used to send data packets if \texttt{is_stored} is 1.--If the \texttt{is_stored} is not 0, it means the information to reach the-public key we are searching for is stored on this node. \texttt{is_stored} is-2 as a response to a peer trying to announce himself to tell the peer that he-is currently announced successfully.--data to route response packet:--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x86) \\- \texttt{24} & Nonce \\- \texttt{32} & Temporary just generated public key \\- variable & Payload \\-\end{tabular}--The payload is encrypted with that temporary secret key, the nonce and the-public key from the announce response packet of the destination node.--There are 2 types of request packets and 2 'response' packets to go with them.-The announce request is used to announce ourselves to a node and announce-response packet is used by the node to respond to this packet. The data to-route request packet is a packet used to send packets through the node to-another peer that has announced itself and that we have found. The data to-route response packet is what the node transforms this packet into.--To announce ourselves to the network we must first find, using announce-packets, the peers with the DHT public key closest to our real public key. We-must then announce ourselves to these peers. Friends will then be able to send-messages to us using data to route packets by sending them to these peers. To-find the peers we have announced ourselves to, our friends will find the peers-closest to our real public key and ask them if they know us. They will then be-able to use the peers that know us to send us some messages that will contain-their DHT public key (which we need to know to connect directly to them), TCP-relays that they are connected to (so we can connect to them with these relays-if we need to) and some DHT peers they are connected to (so we can find them-faster in the DHT).--Announce request packets are the same packets used slightly differently if we-are announcing ourselves or searching for peers that know one of our friends.--If we are announcing ourselves we must put our real long term public key in the-packet and encrypt it with our long term private key. This is so the peer we-are announcing ourselves to can be sure that we actually own that public key.-If we are looking for peers we use a temporary public key used only for packets-looking for that peer in order to leak as little information as possible. The-\texttt{ping_id} is a 32 byte number which is sent to us in the announce-response and we must send back to the peer in another announce request. This-is done in order to prevent people from easily announcing themselves many times-as they have to prove they can respond to packets from the peer before the peer-will let them announce themselves. This \texttt{ping_id} is set to 0 when none-is known.--The public key we are searching for is set to our long term public key when-announcing ourselves and set to the long term public key of the friend we are-searching for if we are looking for peers.--When announcing ourselves, the public key we want others to use to send us data-back is set to a temporary public key and we use the private key part of this-key to decrypt packet routing data sent to us. This public key is to prevent-peers from saving old data to route packets from previous sessions and be able-to replay them in future Tox sessions. This key is set to zero when searching-for peers.--The sendback data is an 8 byte number that will be sent back in the announce-packet response. Its goal is to be used to learn which announce request packet-the response is responding to, and hence its location in the unencrypted part-of the response. This is needed in toxcore to find and check info about the-packet in order to decrypt it and handle it correctly. Toxcore uses it as an-index to its special \texttt{ping_array}.--Why don't we use different packets instead of having one announce packet-request and one response that does everything? It makes it a lot more difficult-for possible attackers to know if we are merely announcing ourselves or if we-are looking for friends as the packets for both look the same and are the same-size.--The unencrypted part of an announce response packet contains the sendback data,-which was sent in the request this packet is responding to and a 24 byte random-nonce used to encrypt the encrypted part.--The \texttt{is_stored} number is set to either 0, 1 or 2. 0 means that the-public key that was being searched in the request isn't stored or known by this-peer. 1 means that it is and 2 means that we are announced successfully at-that node. Both 1 and 2 are needed so that when clients are restarted it is-possible to reannounce without waiting for the timeout of the previous-announce. This would not otherwise be possible as a client would receive-response 1 without a \texttt{ping_id} which is needed in order to reannounce-successfully.--When the \texttt{is_stored} number is 0 or 2, the next 32 bytes is a-\texttt{ping_id}. When \texttt{is_stored} is 1 it corresponds to a public key-(the send back data public key set by the friend in their announce request)-that must be used to encrypt and send data to the friend.--Then there is an optional maximum 4 nodes, in DHT packed nodes format (see-DHT), attached to the response which denote the 4 DHT peers with the DHT public-keys closest to the searched public key in the announce request known by the-peer (see DHT). To find these peers, toxcore uses the same function as is used-to find peers for get node DHT responses. Peers wanting to announce themselves-or searching for peers that 'know' their friends will recursively query closer-and closer peers until they find the closest possible and then either announce-themselves to them or just ping them every once in a while to know if their-friend can be contacted. Note that the distance function used for this is the-same as the Tox DHT.--Data to route request packets are packets used to send data directly to another-peer via a node that knows that peer. The public key is the public key of the-final destination where we want the packet to be sent (the real public key of-our friend). The nonce is a 24 byte random nonce and the public key is a-random temporary public key used to encrypt the data in the packet and, if-possible, only to send packets to this friend (we want to leak as little info-to the network as possible so we use temp public keys as we don't want a peer-to see the same public keys and be able to link things together). The data is-encrypted data that we want to send to the peer with the public key.--The route response packets are just the last elements (nonce, public key,-encrypted data) of the data to route request packet copied into a new packet-and sent to the appropriate destination.--To handle onion announce packets, toxcore first receives an announce packet and-decrypts it.--Toxcore generates \texttt{ping_id}s by taking a 32 byte sha hash of the current-time, some secret bytes generated when the instance is created, the current-time divided by a 20 second timeout, the public key of the requester and the-source ip/port that the packet was received from. Since the ip/port that the-packet was received from is in the \texttt{ping_id}, the announce packets being-sent with a ping id must be sent using the same path as the packet that we-received the \texttt{ping_id} from or announcing will fail.--The reason for this 20 second timeout in toxcore is that it gives a reasonable-time (20 to 40 seconds) for a peer to announce himself while taking in count-all the possible delays with some extra seconds.--Toxcore generates 2 different ping ids, the first is generated with the current-time (divided by 20) and the second with the current time + 20 (divided by 20).-The two ping ids are then compared to the ping ids in the received packets.-The reason for doing this is that storing every ping id received might be-expensive and leave us vulnerable to a DoS attack, this method makes sure that-the other cannot generate \texttt{ping_id}s and must ask for them. The reason-for the 2 \texttt{ping_id}s is that we want to make sure that the timeout is at-least 20 seconds and cannot be 0.--If one of the two ping ids is equal to the public key used to encrypt the-announce packet (the pk the peer is announcing himself as), the sendback data-public key and the sendback data are stored in the datastructure used to store-announced peers. If the implementation has a limit to how many announced-entries it can store, it should only store the entries closest (determined by-the DHT distance function) to its DHT public key. If the entry is already-there, the information will simply be updated with the new one and the timeout-will be reset for that entry.--Toxcore has a timeout of 300 seconds for announce entries after which they are-removed which is long enough to make sure the entries don't expire prematurely-but not long enough for peers to stay announced for extended amounts of time-after they go offline.--Toxcore will then copy the 4 DHT nodes closest to the public key being searched-to a new packet (the response).--Toxcore will look if the public key being searched is in the datastructure. If-it isn't it will copy the first generated \texttt{ping_id} (the one generated-with the current time) to the response, set the \texttt{is_stored} number to 0-and send the packet back.--If the public key is in the datastructure, it will check whether the public key-that was used to encrypt the announce packet is equal to the announced public-key, if it isn't then it means that the peer is searching for a peer and that-we know it. This means the \texttt{is_stored} is set to 1 and the sending back-data public key in the announce entry is copied to the packet.--If it (key used to encrypt the announce packet) is equal (to the announced-public key which is also the 'public key we are searching for' in the announce-packet) meaning the peer is announcing itself and an entry for it exists, the-sending back data public key is checked to see if it equals the one it the-packet. If it is not equal it means that it is outdated, probably because the-announcing peer's toxcore instance was restarted and so their-\texttt{is_stored} is set to 0, if it is equal it means the peer is announced-correctly so the \texttt{is_stored} is set to 2. The first generated-\texttt{ping_id} is then copied to the packet.--Once the packet is contructed a random 24 byte nonce is generated, the packet-is encrypted (the shared key used to decrypt the request can be saved and used-to encrypt the response to save an expensive key derivation operation), the-data to send back is copied to the unencrypted part and the packet is sent back-as a onion response packet.--In order to announce itself using onion announce packets toxcore first takes-DHT peers, picks random ones and builds onion paths with them by saving 3-nodes, calling it a path, generating some keypairs for encrypting the onion-packets and using them to send onion packets. If the peer is only connected-with TCP, the initial nodes will be bootstrap nodes and connected TCP relays-(for the first peer in the path). Once the peer is connected to the onion he-can fill up his list of known peers with peers sent in announce responses if-needed.--Onion paths have different timeouts depending on whether the path is confirmed-or unconfirmed. Unconfirmed paths (paths that core has never received any-responses from) have a timeout of 4 seconds with 2 tries before they are deemed-non working. This is because, due to network conditions, there may be a large-number of newly created paths that do not work and so trying them a lot would-make finding a working path take much longer. The timeout for a confirmed path-(from which a response was received) is 10 seconds with 4 tries without a-response. A confirmed path has a maximum lifetime of 1200 seconds to make-possible deanonimization attacks more difficult.--Toxcore saves a maximum of 12 paths: 6 paths are reserved for announcing-ourselves and 6 others are used to search for friends. This may not be the-safest way (some nodes may be able to associate friends together) however it is-much more performant than having different paths for each friend. The main-benefit is that the announcing and searching are done with different paths,-which makes it difficult to know that peer with real public key X is friends-with Y and Z. More research is needed to find the best way to do this. At-first toxcore did have different paths for each friend, however, that meant-that each friend path was almost never used (and checked). When using a low-amount of paths for searching there is less resources needed to find good-paths. 6 paths are used because 4 was too low and caused some performance-issues because it took longer to find some good paths at the beginning because-only 4 could be tried at a time. A too high number meanwhile would mean each-path is used (and tested) less. The reason why the numbers are the same for-both types of paths is for code simplification purposes.--To search/announce itself to peers, toxcore keeps the 8 closest peers to each-key it is searching (or announcing itself to). To populate these it starts by-sending announce requests to random peers for all the public keys it is-searching for. It then recursively searches closer and closer peers (DHT-distance function) until it no longer finds any. It is important to make sure-it is not too aggressive at searching the peers as some might no longer be-online but peers might still send announce responses with their information.-Toxcore keeps lists of last pinged nodes for each key searched so as not to-ping dead nodes too aggressively.--Toxcore decides if it will send an announce packet to one of the 4 peers in the-announce response by checking if the peer would be stored as one of the stored-8 closest peers if it responded; if it would not be it doesn't send a announce-request, if it would be it sends one.--Peers are only put in the 8 closest peers array if they respond to an announce-request. If the peers fail to respond to 3 announce requests they are deemed-timed out and removed.--The reason for the number of peers being 8 is that a lower number might make-searching for and announcing too unreliable and a higher number too-bandwidth/resource intensive.--Toxcore uses \texttt{ping_array} (see \texttt{ping_array}) for the 8 byte-sendback data in announce packets to store information that it will need to-handle the response (key to decrypt it, why was it sent? (to announce ourselves-or to search? For what key? and some other info)). For security purposes it-checks to make sure the packet was received from the right ip/port and checks-if the key in the unencrypted part of the packet is the right public key.--For peers we are announcing ourselves to, if we are not announced to them-toxcore tries every 3 seconds to announce ourselves to them until they return-that we have announced ourselves to, then toxcore sends an announce request-packet every 15 seconds to see if we are still announced and re announce-ourselves at the same time. The timeout of 15 seconds means a \texttt{ping_id}-received in the last packet will not have had time to expire (20 second minimum-timeout) before it is resent 15 seconds later. Toxcore sends every announce-packet with the \texttt{ping_id} previously received from that peer with the-same path (if possible).--For friends this is slightly different. It is important to start searching for-friends after we are fully announced. Assuming a perfect network, we would-only need to do a search for friend public keys only when first starting the-instance (or going offline and back online) as peers starting up after us would-be able to find us immediately just by searching for us. If we start searching-for friends after we are announced we prevent a scenario where 2 friends start-their clients at the same time but are enable to find each other right away-because they start searching for each other while they have not announced-themselves.--For this reason, after the peer is announced successfully for 17 seconds,-announce packets are sent aggressively every 3 seconds to each known close peer-(in the list of 8 peers) to search aggressively for peers that know the peer we-are searching for.--There are other ways this could be done and which would still work but, if-making your own implementation, keep in mind that these are likely not the most-optimized way to do things.--If we find peers (more than 1) that know a friend we will send them an onion-data packet with our DHT public key, up to 2 TCP relays we are connected to and-2 DHT peers close to us to help the friend connect back to us.--Onion data packets are packets sent as the data of data to route packets.--Onion data packets:--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{32} & Long term public key of sender \\- variable & Payload \\-\end{tabular}--The payload is encrypted with long term private key of the sender, the long-term public key of the receiver and the nonce used in the data to route request-packet used to send this onion data packet (shaves off 24 bytes).--DHT public key packet:--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x9c) \\- \texttt{8} & \texttt{uint64_t} \texttt{no_replay} \\- \texttt{32} & Our DHT public key \\- \texttt{[39, 204]} & Maximum of 4 nodes in packed format \\-\end{tabular}--The packet will only be accepted if the \texttt{no_replay} number is greater-than the \texttt{no_replay} number in the last packet received.--The nodes sent in this packet have TCP so that the friend can connect to us.--Why another round of encryption? We have to prove to the receiver that we own-the long term public key we say we own when sending them our DHT public key.-Friend requests are also sent using onion data packets but their exact format-is explained in Messenger.--The \texttt{no_replay} number is protection if someone tries to replay an older-packet and should be set to an always increasing number. It is 8 bytes so you-should set a high resolution monotonic time as the value.--We send this packet every 30 seconds if there is more than one peer (in the 8)-that says they our friend is announced on them. This packet can also be sent-through the DHT module as a DHT request packet (see DHT) if we know the DHT-public key of the friend and are looking for them in the DHT but have not-connected to them yet. 30 second is a reasonable timeout to not flood the-network with too many packets while making sure the other will eventually-receive the packet. Since packets are sent through every peer that knows the-friend, resending it right away without waiting has a high likelihood of-failure as the chances of packet loss happening to all (up to to 8) packets-sent is low.--When sent as a DHT request packet (this is the data sent in the DHT request-packet):--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x9c) \\- \texttt{32} & Long term public key of sender \\- \texttt{24} & Nonce \\- variable & Encrypted payload \\-\end{tabular}--The payload is encrypted with long term private key of sender, the long term-public key of receiver and the nonce, and contains the DHT public key packet.--When sent as a DHT request packet the DHT public key packet is (before being-sent as the data of a DHT request packet) encrypted with the long term keys of-both the sender and receiver and put in that format. This is done for the same-reason as the double encryption of the onion data packet.--Toxcore tries to resend this packet through the DHT every 20 seconds. 20-seconds is a reasonable resend rate which isn't too aggressive.--Toxcore has a DHT request packet handler that passes received DHT public key-packets from the DHT module to this module.--If we receive a DHT public key packet, we will first check if the DHT packet is-from a friend, if it is not from a friend, it will be discarded. The-\texttt{no_replay} will then be checked to see if it is good and no packet with-a lower one was received during the session. The DHT key, the TCP nodes in the-packed nodes and the DHT nodes in the packed nodes will be passed to their-relevant modules. The fact that we have the DHT public key of a friend means-this module has achieved its goal.--If a friend is online and connected to us, the onion will stop all of its-actions for that friend. If the peer goes offline it will restart searching-for the friend as if toxcore was just started.--If toxcore goes offline (no onion traffic for 20 seconds) toxcore will-aggressively reannounce itself and search for friends as if it was just-started.--\chapter{Ping array}--Ping array is an array used in toxcore to store data for pings. It enables the-storage of arbitrary data that can then be retrieved later by passing the 8-byte ping id that was returned when the data was stored. It also frees data-from pings that are older than a ping expiring delay set when initializing the-array.--Ping arrays are initialized with a size and a timeout parameter. The size-parameter denotes the maximum number of entries in the array and the timeout-denotes the number of seconds to keep an entry in the array. Timeout and size-must be bigger than 0.--Adding an entry to the ping array will make it return an 8 byte number that can-be used as the ping number of a ping packet. This number is generated by first-generating a random 8 byte number (toxcore uses the cryptographic secure random-number generator), dividing then multiplying it by the total size of the array-and then adding the index of the element that was added. This generates a-random looking number that will return the index of the element that was added-to the array. This number is also stored along with the added data and the-current time (to check for timeouts). Data is added to the array in a cyclical-manner (0, 1, 2, 3... (array size - 1), 0, 1, ...). If the array is full, the-oldest element is overwritten.--To get data from the ping array, the ping number is passed to the function to-get the data from the array. The modulo of the ping number with the total size-of the array will return the index at which the data is. If there is no data-stored at this index, the function returns an error. The ping number is then-checked against the ping number stored for this element, if it is not equal the-function returns an error. If the array element has timed out, the function-returns an error. If all the checks succeed the function returns the exact-data that was stored and it is removed from the array.--Ping array is used in many places in toxcore to efficiently keep track of sent-packets.--\chapter{State Format}--The reference Tox implementation uses a custom binary format to save the state-of a Tox client between restarts. This format is far from perfect and will be-replaced eventually. For the sake of maintaining compatibility down the road,-it is documented here.--The binary encoding of all integer types in the state format is a fixed-width-byte sequence with the integer encoded in Little Endian unless stated otherwise.--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{4} & Zeroes \\- \texttt{4} & \texttt{uint32_t} (0x15ED1B1F) \\- \texttt{?} & List of sections \\-\end{tabular}--\section{Sections}--The core of the state format consists of a list of sections. Every section has-its type and length specified at the beginning. In some cases, a section only-contains one item and thus takes up the entire length of the section. This is-denoted with '?'.--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{4} & \texttt{uint32_t} Length of this section \\- \texttt{2} & \texttt{uint16_t} Section type \\- \texttt{2} & \texttt{uint16_t} (0x01CE) \\- \texttt{?} & Section \\-\end{tabular}--Section types:--\begin{tabular}{l|l}- Name & Value \\- \hline- NospamKeys & 0x01 \\- DHT & 0x02 \\- Friends & 0x03 \\- Name & 0x04 \\- StatusMessage & 0x05 \\- Status & 0x06 \\- TcpRelays & 0x0A \\- PathNodes & 0x0B \\- EOF & 0xFF \\-\end{tabular}--Not every section listed above is required to be present in order to restore-from a state file. Only NospamKeys is required.--\subsection{Nospam and Keys (0x01)}--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{4} & \texttt{uint32_t} Nospam \\- \texttt{32} & Long term public key \\- \texttt{32} & Long term secret key \\-\end{tabular}--\subsection{DHT (0x02)}--This section contains a list of DHT-related sections.--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{4} & \texttt{uint32_t} (0x159000D) \\- \texttt{?} & List of DHT sections \\-\end{tabular}--\subsubsection{DHT Sections}--Every DHT section has the following structure:--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{4} & \texttt{uint32_t} Length of this section \\- \texttt{2} & \texttt{uint16_t} DHT section type \\- \texttt{2} & \texttt{uint16_t} (0x11CE) \\- \texttt{?} & DHT section \\-\end{tabular}--DHT section types:--\begin{tabular}{l|l}- Name & Value \\- \hline- Nodes & 0x04 \\-\end{tabular}--\paragraph{Nodes (0x04)}--This section contains a list of nodes. These nodes are used to quickly reconnect-to the DHT after a Tox client is restarted.--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{?} & List of nodes \\-\end{tabular}--The structure of a node is the same as \texttt{Node Info}. Note: this means-that the integers stored in these nodes are stored in Big Endian as well.--\subsection{Friends (0x03)}--This section contains a list of friends. A friend can either be a peer we've-sent a friend request to or a peer we've accepted a friend request from.--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{?} & List of friends \\-\end{tabular}--Friend:--Some of the integers in this structure are stored in Big Endian. This is-denoted with "(BE)".--Unfortunately, toxcore copies the friend structure directly from memory to the-state file. This makes the state format platform dependent because the way a-structure is laid out in memory differs across platforms and compilers. A-common layout of this structure in memory (GCC on x86 and x86\_64) is described-below and should be accounted for both when serializing and deserializing the-state file.--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} Status \\- \texttt{32} & Long term public key \\- \texttt{1024} & Friend request message as a UTF-8 encoded string \\- \texttt{2} & \texttt{uint16_t} Size of the friend request message (BE) \\- \texttt{128} & Name as a UTF-8 encoded string \\- \texttt{2} & \texttt{uint16_t} Size of the name (BE) \\- \texttt{1007} & Status message as a UTF-8 encoded string \\- \texttt{2} & \texttt{uint16_t} Size of the status message (BE) \\- \texttt{1} & \texttt{uint8_t} User status (see also: \texttt{USERSTATUS}) \\- \texttt{3} & PADDING \\- \texttt{4} & \texttt{uint32_t} Nospam (only used for sending a friend request) \\- \texttt{8} & \texttt{uint64_t} Last seen time \\-\end{tabular}--Status can be one of:--\begin{tabular}{l|l}- Status & Meaning \\- \hline- 0 & Not a friend \\- 1 & Friend added \\- 2 & Friend request sent \\- 3 & Confirmed friend \\- 4 & Friend online \\-\end{tabular}--\subsection{Name (0x04)}--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{?} & Name as a UTF-8 encoded string \\-\end{tabular}--\subsection{Status Message (0x05)}--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{?} & Status message as a UTF-8 encoded string \\-\end{tabular}--\subsection{Status (0x06)}--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} User status (see also: \texttt{USERSTATUS}) \\+\section{Objectives}++This section provides an overview of goals and non-goals of Tox. It provides+the reader with:++\begin{itemize}+ \item a basic understanding of what problems Tox intends to solve;+ \item a means to validate whether those problems are indeed solved by the+ protocol as specified;+ \item the ability to make better tradeoffs and decisions in their own+ reimplementation of the protocol.+\end{itemize}++\subsection{Goals}++\begin{itemize}++ \item \textbf{Authentication:} Tox aims to provide authenticated+ communication. This means that during a communication session, both parties+ can be sure of the other party's identity. Users are identified by their+ public key. The initial key exchange is currently not in scope for the Tox+ protocol. In the future, Tox may provide a means for initial authentication+ using a challenge/response or shared secret based exchange.++ If the secret key is compromised, the user's identity is compromised, and an+ attacker can impersonate that user. When this happens, the user must create+ a new identity with a new public key.++ \item \textbf{End-to-end encryption:} The Tox protocol establishes end-to-end+ encrypted communication links. Shared keys are deterministically derived+ using a Diffie-Hellman-like method, so keys are never transferred over the+ network.++ \item \textbf{Forward secrecy}: Session keys are re-negotiated when the peer+ connection is established.++ \item \textbf{Privacy}: When Tox establishes a communication link, it aims to+ avoid leaking to any third party the identities of the parties involved+ (i.e. their public keys).++ Furthermore, it aims to avoid allowing third parties to determine the IP+ address of a given user.++ \item \textbf{Resilience:}+ \begin{itemize}+ \item Independence of infrastructure: Tox avoids relying on servers as+ much as possible. Communications are not transmitted via or stored on+ central servers. Joining a Tox network requires connecting to a+ well-known node called a bootstrap node. Anyone can run a bootstrap+ node, and users need not put any trust in them.+ \item Tox tries to establish communication paths in difficult network+ situations. This includes connecting to peers behind a NAT or firewall.+ Various techniques help achieve this, such as UDP hole-punching, UPnP,+ NAT-PMP, other untrusted nodes acting as relays, and DNS tunnels.+ \item Resistance to basic denial of service attacks: short timeouts make+ the network dynamic and resilient against poisoning attempts.+ \end{itemize}++ \item \textbf{Minimum configuration:} Tox aims to be nearly zero-conf.+ User-friendliness is an important aspect to security. Tox aims to make+ security easy to achieve for average users.+\end{itemize}++\subsection{Non-goals}++\begin{itemize}+ \item \textbf{Anonymity} is not in scope for the Tox protocol itself, but it+ provides an easy way to integrate with software providing anonymity, such as+ Tor.++ By default, Tox tries to establish direct connections between peers; as a+ consequence, each is aware of the other's IP address, and third parties+ may be able to determine that a connection has been established between+ those IP addresses. One of the reasons for making direct connections is that+ relaying real-time multimedia conversations over anonymity networks is not+ feasible with the current network infrastructure.+\end{itemize}++\section{Threat model}++TODO(iphydf): Define one.++\section{Data types}++All data types are defined before their first use, and their binary protocol+representation is given. The protocol representations are normative and must+be implemented exactly as specified. For some types, human-readable+representations are suggested. An implementation may choose to provide no such+representation or a different one. The implementation is free to choose any+in-memory representation of the specified types.++Binary formats are specified in tables with length, type, and content+descriptions. If applicable, specific enumeration types are used, so types may+be self-explanatory in some cases. The length can be either a fixed number in+bytes (e.g. \texttt{32}), a number in bits (e.g. \texttt{7} bit), a choice of+lengths (e.g. \texttt{4 $|$ 16}), or an inclusive range (e.g. \texttt{[0,+100]}). Open ranges are denoted \texttt{[n,]} to mean a minimum length of+\texttt{n} with no specified maximum length.++\section{Integers}++The protocol uses four bounded unsigned integer types. Bounded means they have+an upper bound beyond which incrementing is not defined. The integer types+support modular arithmetic, so overflow wraps around to zero. Unsigned means+their lower bound is 0. Signed integer types are not used. The binary+encoding of all integer types is a fixed-width byte sequence with the integer+encoded in \href{https://en.wikipedia.org/wiki/Endianness}{Big Endian} unless+stated otherwise.++\begin{tabular}{l|l|l|l}+ Type name & C type & Length & Upper bound \\+ \hline+ Word8 & \texttt{uint8\_t} & 1 & 255 (0xff) \\+ Word16 & \texttt{uint16\_t} & 2 & 65535 (0xffff) \\+ Word32 & \texttt{uint32\_t} & 4 & 4294967295 (0xffffffff) \\+ Word64 & \texttt{uint64\_t} & 8 & 18446744073709551615 (0xffffffffffffffff) \\+\end{tabular}++\section{Strings}++A String is a data structure used for human readable text. Strings are+sequences of glyphs. A glyph consists of one non-zero-width unicode code point+and zero or more zero-width unicode code points. The human-readable+representation of a String starts and ends with a quotation mark (\texttt{"})+and contains all human-readable glyphs verbatim. Control characters are+represented in an isomorphic human-readable way. I.e. every control character+has exactly one human-readable representation, and a mapping exists from the+human-readable representation to the control character. Therefore, the use of+Unicode Control Characters (U+240x) is not permitted without additional marker.++\input{src/tox/Network/Tox/Crypto.lhs}+\input{src/tox/Network/Tox/NodeInfo.lhs}+\input{src/tox/Network/Tox/Protocol.lhs}+\input{src/tox/Network/Tox/DHT.lhs}++\chapter{LAN discovery}++LAN discovery is a way to discover Tox peers that are on a local network. If+two Tox friends are on a local network, the most efficient way for them to+communicate together is to use the local network. If a Tox client is opened on+a local network in which another Tox client exists then good behavior would be+to bootstrap to the network using the Tox client on the local network. This is+what LAN discovery aims to accomplish.++LAN discovery works by sending a UDP packet through the toxcore UDP socket to+the interface broadcast address on IPv4, the global broadcast address+(255.255.255.255) and the multicast address on IPv6 (FF02::1) on the default+Tox UDP port (33445).++The LAN Discovery packet:++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (33) \\+ \texttt{32} & DHT public key \\+\end{tabular}++LAN Discovery packets contain the DHT public key of the sender. When a LAN+Discovery packet is received, a DHT get nodes packet will be sent to the sender+of the packet. This means that the DHT instance will bootstrap itself to every+peer from which it receives one of these packets. Through this mechanism, Tox+clients will bootstrap themselves automatically from other Tox clients running+on the local network.++When enabled, toxcore sends these packets every 10 seconds to keep delays low.+The packets could be sent up to every 60 seconds but this would make peer+finding over the network 6 times slower.++LAN discovery enables two friends on a local network to find each other as the+DHT prioritizes LAN addresses over non LAN addresses for DHT peers. Sending a+get node request/bootstrapping from a peer successfully should also add them to+the list of DHT peers if we are searching for them. The peer must not be+immediately added if a LAN discovery packet with a DHT public key that we are+searching for is received as there is no cryptographic proof that this packet+is legitimate and not maliciously crafted. This means that a DHT get node or+ping packet must be sent, and a valid response must be received, before we can+say that this peer has been found.++LAN discovery is how Tox handles and makes everything work well on LAN.++\chapter{Messenger}++Messenger is the module at the top of all the other modules. It sits on top of+\texttt{friend\_connection} in the hierarchy of toxcore.++Messenger takes care of sending and receiving messages using the connection+provided by \texttt{friend\_connection}. The module provides a way for friends+to connect and makes it usable as an instant messenger. For example, Messenger+lets users set a nickname and status message which it then transmits to friends+when they are online. It also allows users to send messages to friends and+builds an instant messenging system on top of the lower level+\texttt{friend\_connection} module.++Messenger offers two methods to add a friend. The first way is to add a friend+with only their long term public key, this is used when a friend needs to be+added but for some reason a friend request should not be sent. The friend+should only be added. This method is most commonly used to accept friend+requests but could also be used in other ways. If two friends add each other+using this function they will connect to each other. Adding a friend using+this method just adds the friend to \texttt{friend\_connection} and creates a+new friend entry in Messenger for the friend.++The Tox ID is used to identify peers so that they can be added as friends in+Tox. In order to add a friend, a Tox user must have the friend's Tox ID. The+Tox ID contains the long term public key of the peer (32 bytes) followed by the+4 byte nospam (see: \texttt{friend\_requests}) value and a 2 byte XOR checksum.+The method of sending the Tox ID to others is up to the user and the client but+the recommended way is to encode it in hexadecimal format and have the user+manually send it to the friend using another program.++Tox ID:++\begin{figure}+\includegraphics{res/images/tox-id.png}+\caption{Tox ID}+\end{figure}++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{32} & long term public key \\+ \texttt{4} & nospam \\+ \texttt{2} & checksum \\+\end{tabular}++The checksum is calculated by XORing the first two bytes of the ID with the+next two bytes, then the next two bytes until all the 36 bytes have been XORed+together. The result is then appended to the end to form the Tox ID.++The user must make sure the Tox ID is not intercepted and replaced in transit+by a different Tox ID, which would mean the friend would connect to a malicious+person instead of the user, though taking reasonable precautions as this is+outside the scope of Tox. Tox assumes that the user has ensured that they are+using the correct Tox ID, belonging to the intended person, to add a friend.++The second method to add a friend is by using their Tox ID and a message to be+sent in a friend request. This way of adding friends will try to send a friend+request, with the set message, to the peer whose Tox ID was added. The method+is similar to the first one, except that a friend request is crafted and sent+to the other peer.++When a friend connection associated to a Messenger friend goes online, a ONLINE+packet will be sent to them. Friends are only set as online if an ONLINE+packet is received.++As soon as a friend goes online, Messenger will stop sending friend requests to+that friend, if it was sending them, as they are redundant for this friend.++Friends will be set as offline if either the friend connection associated to+them goes offline or if an OFFLINE packet is received from the friend.++Messenger packets are sent to the friend using the online friend connection to+the friend.++Should Messenger need to check whether any of the non lossy packets in the+following list were received by the friend, for example to implement receipts+for text messages, \texttt{net\_crypto} can be used. The \texttt{net\_crypto}+packet number, used to send the packets, should be noted and then+\texttt{net\_crypto} checked later to see if the bottom of the send array is+after this packet number. If it is, then the friend has received them. Note+that \texttt{net\_crypto} packet numbers could overflow after a long time, so+checks should happen within 2**32 \texttt{net\_crypto} packets sent with the+same friend connection.++Message receipts for action messages and normal text messages are implemented+by adding the \texttt{net\_crypto} packet number of each message, along with the+receipt number, to the top of a linked list that each friend has as they are+sent. Every Messenger loop, the entries are read from the bottom and entries+are removed and passed to the client until an entry that refers to a packet not+yet received by the other is reached, when this happens it stops.++List of Messenger packets:++\section{\texttt{ONLINE}}++length: 1 byte++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x18) \\+\end{tabular}++Sent to a friend when a connection is established to tell them to mark us as+online in their friends list. This packet and the OFFLINE packet are necessary+as \texttt{friend\_connections} can be established with non-friends who are part+of a groupchat. The two packets are used to differentiate between these peers,+connected to the user through groupchats, and actual friends who ought to be+marked as online in the friendlist.++On receiving this packet, Messenger will show the peer as being online.++\section{\texttt{OFFLINE}}++length: 1 byte++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x19) \\+\end{tabular}++Sent to a friend when deleting the friend. Prevents a deleted friend from+seeing us as online if we are connected to them because of a group chat.++On receiving this packet, Messenger will show this peer as offline.++\section{\texttt{NICKNAME}}++length: 1 byte to 129 bytes.++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x30) \\+ \texttt{[0, 128]} & Nickname as a UTF8 byte string \\+\end{tabular}++Used to send the nickname of the peer to others. This packet should be sent+every time to each friend every time they come online and each time the+nickname is changed.++\section{\texttt{STATUSMESSAGE}}++length: 1 byte to 1008 bytes.++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x31) \\+ \texttt{[0, 1007]} & Status message as a UTF8 byte string \\+\end{tabular}++Used to send the status message of the peer to others. This packet should be+sent every time to each friend every time they come online and each time the+status message is changed.++\section{\texttt{USERSTATUS}}++length: 2 bytes++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x32) \\+ \texttt{1} & \texttt{uint8\_t} status (0 = online, 1 = away, 2 = busy) \\+\end{tabular}++Used to send the user status of the peer to others. This packet should be sent+every time to each friend every time they come online and each time the user+status is changed.++\section{\texttt{TYPING}}++length: 2 bytes++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x33) \\+ \texttt{1} & \texttt{uint8\_t} typing status (0 = not typing, 1 = typing) \\+\end{tabular}++Used to tell a friend whether the user is currently typing or not.++\section{\texttt{MESSAGE}}++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x40) \\+ \texttt{[0, 1372]} & Message as a UTF8 byte string \\+\end{tabular}++Used to send a normal text message to the friend.++\section{\texttt{ACTION}}++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x41) \\+ \texttt{[0, 1372]} & Action message as a UTF8 byte string \\+\end{tabular}++Used to send an action message (like an IRC action) to the friend.++\section{\texttt{MSI}}++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x45) \\+ \texttt{?} & data \\+\end{tabular}++Reserved for Tox AV usage.++\section{File Transfer Related Packets}++\subsection{\texttt{FILE\_SENDREQUEST}}++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x50) \\+ \texttt{1} & \texttt{uint8\_t} file number \\+ \texttt{4} & \texttt{uint32\_t} file type \\+ \texttt{8} & \texttt{uint64\_t} file size \\+ \texttt{32} & file id (32 bytes) \\+ \texttt{[0, 255]} & filename as a UTF8 byte string \\+\end{tabular}++Note that file type and file size are sent in big endian/network byte format.++\subsection{\texttt{FILE\_CONTROL}}++length: 4 bytes if \texttt{control\_type} isn't seek. 8 bytes if+\texttt{control\_type} is seek.++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x51) \\+ \texttt{1} & \texttt{uint8\_t} \texttt{send\_receive} \\+ \texttt{1} & \texttt{uint8\_t} file number \\+ \texttt{1} & \texttt{uint8\_t} \texttt{control\_type} \\+ \texttt{8} & \texttt{uint64\_t} seek parameter \\+\end{tabular}++\texttt{send\_receive} is 0 if the control targets a file being sent (by the+peer sending the file control), and 1 if it targets a file being received.++\texttt{control\_type} can be one of: 0 = accept, 1 = pause, 2 = kill, 3 = seek.++The seek parameter is only included when \texttt{control\_type} is seek (3).++Note that if it is included the seek parameter will be sent in big+endian/network byte format.++\subsection{\texttt{FILE\_DATA}}++length: 2 to 1373 bytes.++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x52) \\+ \texttt{1} & \texttt{uint8\_t} file number \\+ \texttt{[0, 1371]} & file data piece \\+\end{tabular}++Files are transferred in Tox using File transfers.++To initiate a file transfer, the friend creates and sends a+\texttt{FILE\_SENDREQUEST} packet to the friend it wants to initiate a file+transfer to.++The first part of the \texttt{FILE\_SENDREQUEST} packet is the file number. The+file number is the number used to identify this file transfer. As the file+number is represented by a 1 byte number, the maximum amount of concurrent+files Tox can send to a friend is 256. 256 file transfers per friend is enough+that clients can use tricks like queueing files if there are more files needing+to be sent.++256 outgoing files per friend means that there is a maximum of 512 concurrent+file transfers, between two users, if both incoming and outgoing file transfers+are counted together.++As file numbers are used to identify the file transfer, the Tox instance must+make sure to use a file number that isn't used for another outgoing file+transfer to that same friend when creating a new outgoing file transfer. File+numbers are chosen by the file sender and stay unchanged for the entire+duration of the file transfer. The file number is used by both+\texttt{FILE\_CONTROL} and \texttt{FILE\_DATA} packets to identify which file+transfer these packets are for.++The second part of the file transfer request is the file type. This is simply+a number that identifies the type of file. for example, tox.h defines the file+type 0 as being a normal file and type 1 as being an avatar meaning the Tox+client should use that file as an avatar. The file type does not effect in any+way how the file is transfered or the behavior of the file transfer. It is set+by the Tox client that creates the file transfers and send to the friend+untouched.++The file size indicates the total size of the file that will be transfered. A+file size of \texttt{UINT64\_MAX} (maximum value in a \texttt{uint64\_t}) means+that the size of the file is undetermined or unknown. For example if someone+wanted to use Tox file transfers to stream data they would set the file size to+\texttt{UINT64\_MAX}. A file size of 0 is valid and behaves exactly like a+normal file transfer.++The file id is 32 bytes that can be used to uniquely identify the file+transfer. For example, avatar transfers use it as the hash of the avatar so+that the receiver can check if they already have the avatar for a friend which+saves bandwidth. It is also used to identify broken file transfers across+toxcore restarts (for more info see the file transfer section of tox.h). The+file transfer implementation does not care about what the file id is, as it is+only used by things above it.++The last part of the file transfer is the optional file name which is used to+tell the receiver the name of the file.++When a \texttt{FILE\_SENDREQUEST} packet is received, the implementation+validates and sends the info to the Tox client which decides whether they+should accept the file transfer or not.++To refuse or cancel a file transfer, they will send a \texttt{FILE\_CONTROL}+packet with \texttt{control\_type} 2 (kill).++\texttt{FILE\_CONTROL} packets are used to control the file transfer.+\texttt{FILE\_CONTROL} packets are used to accept/unpause, pause, kill/cancel+and seek file transfers. The \texttt{control\_type} parameter denotes what the+file control packet does.++The \texttt{send\_receive} and file number are used to identify a specific file+transfer. Since file numbers for outgoing and incoming files are not related+to each other, the \texttt{send\_receive} parameter is used to identify if the+file number belongs to files being sent or files being received. If+\texttt{send\_receive} is 0, the file number corresponds to a file being sent by+the user sending the file control packet. If \texttt{send\_receive} is 1, it+corresponds to a file being received by the user sending the file control+packet.++\texttt{control\_type} indicates the purpose of the \texttt{FILE\_CONTROL}+packet. \texttt{control\_type} of 0 means that the \texttt{FILE\_CONTROL} packet+is used to tell the friend that the file transfer is accepted or that we are+unpausing a previously paused (by us) file transfer. \texttt{control\_type} of+1 is used to tell the other to pause the file transfer.++If one party pauses a file transfer, that party must be the one to unpause it.+Should both sides pause a file transfer, both sides must unpause it before the+file can be resumed. For example, if the sender pauses the file transfer, the+receiver must not be able to unpause it. To unpause a file transfer,+\texttt{control\_type} 0 is used. Files can only be paused when they are in+progress and have been accepted.++\texttt{control\_type} 2 is used to kill, cancel or refuse a file transfer.+When a \texttt{FILE\_CONTROL} is received, the targeted file transfer is+considered dead, will immediately be wiped and its file number can be reused.+The peer sending the \texttt{FILE\_CONTROL} must also wipe the targeted file+transfer from their side. This control type can be used by both sides of the+transfer at any time.++\texttt{control\_type} 3, the seek control type is used to tell the sender of+the file to start sending from a different index in the file than 0. It can+only be used right after receiving a \texttt{FILE\_SENDREQUEST} packet and+before accepting the file by sending a \texttt{FILE\_CONTROL} with+\texttt{control\_type} 0. When this \texttt{control\_type} is used, an extra 8+byte number in big endian format is appended to the \texttt{FILE\_CONTROL} that+is not present with other control types. This number indicates the index in+bytes from the beginning of the file at which the file sender should start+sending the file. The goal of this control type is to ensure that files can be+resumed across core restarts. Tox clients can know if they have received a+part of a file by using the file id and then using this packet to tell the+other side to start sending from the last received byte. If the seek position+is bigger or equal to the size of the file, the seek packet is invalid and the+one receiving it will discard it.++To accept a file Tox will therefore send a seek packet, if it is needed, and+then send a \texttt{FILE\_CONTROL} packet with \texttt{control\_type} 0 (accept)+to tell the file sender that the file was accepted.++Once the file transfer is accepted, the file sender will start sending file+data in sequential chunks from the beginning of the file (or the position from+the \texttt{FILE\_CONTROL} seek packet if one was received).++File data is sent using \texttt{FILE\_DATA} packets. The file number+corresponds to the file transfer that the file chunks belong to. The receiver+assumes that the file transfer is over as soon as a chunk with the file data+size not equal to the maximum size (1371 bytes) is received. This is how the+sender tells the receiver that the file transfer is complete in file transfers+where the size of the file is unknown (set to \texttt{UINT64\_MAX}). The+receiver also assumes that if the amount of received data equals to the file+size received in the \texttt{FILE\_SENDREQUEST}, the file sending is finished+and has been successfully received. Immediately after this occurs, the+receiver frees up the file number so that a new incoming file transfer can use+that file number. The implementation should discard any extra data received+which is larger than the file size received at the beginning.++In 0 filesize file transfers, the sender will send one \texttt{FILE\_DATA}+packet with a file data size of 0.++The sender will know if the receiver has received the file successfully by+checking if the friend has received the last \texttt{FILE\_DATA} packet sent+(containing the last chunk of the file). \texttt{net\_crypto} can be used to+check whether packets sent through it have been received by storing the packet+number of the sent packet and verifying later in \texttt{net\_crypto} to see+whether it was received or not. As soon as \texttt{net\_crypto} says the other+received the packet, the file transfer is considered successful, wiped and the+file number can be reused to send new files.++\texttt{FILE\_DATA} packets should be sent as fast as the \texttt{net\_crypto}+connection can handle it respecting its congestion control.++If the friend goes offline, all file transfers are cleared in toxcore. This+makes it simpler for toxcore as it does not have to deal with resuming file+transfers. It also makes it simpler for clients as the method for resuming+file transfers remains the same, even if the client is restarted or toxcore+loses the connection to the friend because of a bad internet connection.++\section{Group Chat Related Packets}++\begin{tabular}{l|l}+ Packet ID & Packet Name \\+ \hline+ 0x60 & \texttt{INVITE\_GROUPCHAT} \\+ 0x61 & \texttt{ONLINE\_PACKET} \\+ 0x62 & \texttt{DIRECT\_GROUPCHAT} \\+ 0x63 & \texttt{MESSAGE\_GROUPCHAT} \\+ 0xC7 & \texttt{LOSSY\_GROUPCHAT} \\+\end{tabular}++Messenger also takes care of saving the friends list and other friend+information so that it's possible to close and start toxcore while keeping all+your friends, your long term key and the information necessary to reconnect to+the network.++Important information messenger stores includes: the long term private key, our+current nospam value, our friends' public keys and any friend requests the user+is currently sending. The network DHT nodes, TCP relays and some onion nodes+are stored to aid reconnection.++In addition to this, a lot of optional data can be stored such as the usernames+of friends, our current username, status messages of friends, our status+message, etc... can be stored. The exact format of the toxcore save is+explained later.++The TCP server is run from the toxcore messenger module if the client has+enabled it. TCP server is usually run independently as part of the bootstrap+node package but it can be enabled in clients. If it is enabled in toxcore,+Messenger will add the running TCP server to the TCP relay.++Messenger is the module that transforms code that can connect to friends based+on public key into a real instant messenger.++\chapter{TCP client}++\texttt{TCP client} is the client for the TCP server. It establishes and keeps+a connection to the TCP server open.++All the packet formats are explained in detail in \texttt{TCP server} so this+section will only cover \texttt{TCP client} specific details which are not+covered in the \texttt{TCP server} documentation.++TCP clients can choose to connect to TCP servers through a proxy. Most common+types of proxies (SOCKS, HTTP) work by establishing a connection through a+proxy using the protocol of that specific type of proxy. After the connection+through that proxy to a TCP server is established, the socket behaves from the+point of view of the application exactly like a TCP socket that connects+directly to a TCP server instance. This means supporting proxies is easy.++\texttt{TCP client} first establishes a TCP connection, either through a proxy+or directly to a TCP server. It uses the DHT public key as its long term key+when connecting to the TCP server.++It establishes a secure connection to the TCP server. After establishing a+connection to the TCP server, and when the handshake response has been received+from the TCP server, the toxcore implementation immediately sends a ping+packet. Ideally the first packets sent would be routing request packets but+this solution aids code simplicity and allows the server to confirm the+connection.++Ping packets, like all other data packets, are sent as encrypted packets.++Ping packets are sent by the toxcore TCP client every 30 seconds with a timeout+of 10 seconds, the same interval and timeout as toxcore TCP server ping+packets. They are the same because they accomplish the same thing.++\texttt{TCP client} must have a mechanism to make sure important packets+(routing requests, disconnection notifications, ping packets, ping response+packets) don't get dropped because the TCP socket is full. Should this happen,+the TCP client must save these packets and prioritize sending them, in order,+when the TCP socket on the server becomes available for writing again.+\texttt{TCP client} must also take into account that packets might be bigger+than the number of bytes it can currently write to the socket. In this case,+it must save the bytes of the packet that it didn't write to the socket and+write them to the socket as soon as the socket allows so that the connection+does not get broken. It must also assume that it may receive only part of an+encrypted packet. If this occurs it must save the part of the packet it has+received and wait for the rest of the packet to arrive before handling it.++\texttt{TCP client} can be used to open up a route to friends who are connected+to the TCP server. This is done by sending a routing request to the TCP server+with the DHT public key of the friend. This tells the server to register a+\texttt{connection\_id} to the DHT public key sent in the packet. The server+will then respond with a routing response packet. If the connection was+accepted, the \texttt{TCP client} will store the \texttt{connection id} for+this connection. The \texttt{TCP client} will make sure that routing response+packets are responses to a routing packet that it sent by storing that it sent+a routing packet to that public key and checking the response against it. This+prevents the possibility of a bad TCP server exploiting the client.++The \texttt{TCP client} will handle connection notifications and disconnection+notifications by alerting the module using it that the connection to the peer+is up or down.++\texttt{TCP client} will send a disconnection notification to kill a connection+to a friend. It must send a disconnection notification packet regardless of+whether the peer was online or offline so that the TCP server will unregister+the connection.++Data to friends can be sent through the TCP relay using OOB (out of band)+packets and connected connections. To send an OOB packet, the DHT public key+of the friend must be known. OOB packets are sent in blind and there is no way+to query the TCP relay to see if the friend is connected before sending one.+OOB packets should be sent when the connection to the friend via the TCP relay+isn't in an connected state but it is known that the friend is connected to+that relay. If the friend is connected via the TCP relay, then normal data+packets must be sent as they are smaller than OOB packets.++OOB recv and data packets must be handled and passed to the module using it.++\chapter{TCP connections}++\texttt{TCP\_connections} takes care of handling multiple TCP client instances+to establish a reliable connection via TCP relays to a friend. Connecting to a+friend with only one relay would not be very reliable, so+\texttt{TCP\_connections} provides the level of abstraction needed to manage+multiple relays. For example, it ensures that if a relay goes down, the+connection to the peer will not be impacted. This is done by connecting to the+other peer with more than one relay.++\texttt{TCP\_connections} is above \href{#tcp-client}{\texttt{TCP client}} and+below \texttt{net\_crypto}.++A TCP connection in \texttt{TCP\_connections} is defined as a connection to a+peer though one or more TCP relays. To connect to another peer with+\texttt{TCP\_connections}, a connection in \texttt{TCP\_connections} to the peer+with DHT public key X will be created. Some TCP relays which we know the peer+is connected to will then be associated with that peer. If the peer isn't+connected directly yet, these relays will be the ones that the peer has sent to+us via the onion module. The peer will also send some relays it is directly+connected to once a connection is established, however, this is done by another+module.++\texttt{TCP\_connections} has a list of all relays it is connected to. It tries+to keep the number of relays it is connected to as small as possible in order+to minimize load on relays and lower bandwidth usage for the client. The+desired number of TCP relay connections per peer is set to 3 in toxcore with+the maximum number set to 6. The reason for these numbers is that 1 would mean+no backup relays and 2 would mean only 1 backup. To be sure that the+connection is reliable 3 seems to be a reasonable lower bound. The maximum+number of 6 is the maximum number of relays that can be tied to each peer. If+2 peers are connected each to the same 6+ relays and they both need to be+connected to that amount of relays because of other friends this is where this+maximum comes into play. There is no reason why this number is 6 but in+toxcore it has to be at least double than the desired number (3) because the+code assumes this.++If necessary, \texttt{TCP\_connections} will connect to TCP relays to use them+to send onion packets. This is only done if there is no UDP connection to the+network. When there is a UDP connection, packets are sent with UDP only+because sending them with TCP relays can be less reliable. It is also+important that we are connected at all times to some relays as these relays+will be used by TCP only peers to initiate a connection to us.++In toxcore, each client is connected to 3 relays even if there are no TCP peers+and the onion is not needed. It might be optimal to only connect to these+relays when toxcore is initializing as this is the only time when peers will+connect to us via TCP relays we are connected to. Due to how the onion works,+after the initialization phase, where each peer is searched in the onion and+then if they are found the info required to connect back (DHT pk, TCP relays)+is sent to them, there should be no more peers connecting to us via TCP relays.+This may be a way to further reduce load on TCP relays, however, more research+is needed before it is implemented.++\texttt{TCP\_connections} picks one relay and uses only it for sending data to+the other peer. The reason for not picking a random connected relay for each+packet is that it severely deteriorates the quality of the link between two+peers and makes performance of lossy video and audio transmissions really poor.+For this reason, one relay is picked and used to send all data. If for any+reason no more data can be sent through that relay, the next relay is used.+This may happen if the TCP socket is full and so the relay should not+necessarily be dropped if this occurs. Relays are only dropped if they time+out or if they become useless (if the relay is one too many or is no longer+being used to relay data to any peers).++\texttt{TCP\_connections} in toxcore also contains a mechanism to make+connections go to sleep. TCP connections to other peers may be put to sleep if+the connection to the peer establishes itself with UDP after the connection is+established with TCP. UDP is the method preferred by \texttt{net\_crypto} to+communicate with other peers. In order to keep track of the relays which were+used to connect with the other peer in case the UDP connection fails, they are+saved by \texttt{TCP\_connections} when the connection is put to sleep. Any+relays which were only used by this redundant connection are saved then+disconnected from. If the connection is awakened, the relays are reconnected+to and the connection is reestablished. Putting a connection to sleep is the+same as saving all the relays used by the connection and removing the+connection. Awakening the connection is the same as creating a new connection+with the same parameters and restoring all the relays.++A method to detect potentially dysfunctional relays that try to disrupt the+network by lying that they are connecting to a peer when they are not or that+maliciously drop all packets should be considered. Toxcore doesn't currently+implement such a system and adding one requires more research and likely also+requires extending the protocol.++When TCP connections connects to a relay it will create a new+\href{#tcp-client}{\texttt{TCP\_client}} instance for that relay. At any time+if the \texttt{TCP\_client} instance reports that it has disconnected, the TCP+relay will be dropped. Once the TCP relay reports that it is connected,+\texttt{TCP\_connections} will find all the connections that are associated to+the relay and announce to the relay that it wants to connect to each of them+with routing requests. If the relay reports that the peer for a connection is+online, the connection number and relay will be used to send data in that+connection with data packets. If the peer isn't reported as online but the+relay is associated to a connection, TCP OOB (out of band) packets will be used+to send data instead of data packets. TCP OOB packets are used in this case+since the relay most likely has the peer connected but it has not sent a+routing request to connect to us.++\texttt{TCP\_connections} is used as the bridge between individual+\texttt{TCP\_client} instances and \texttt{net\_crypto}, or the bridge between+individual connections and something that requires an interface that looks like+one connection.++\chapter{TCP server}++The TCP server in tox has the goal of acting like a TCP relay between clients+who cannot connect directly to each other or who for some reason are limited to+using the TCP protocol to connect to each other. \texttt{TCP\_server} is+typically run only on actual server machines but any Tox client could host one+as the api to run one is exposed through the tox.h api.++To connect to a hosted TCP server toxcore uses the TCP client module.++The TCP server implementation in toxcore can currently either work on epoll on+linux or using unoptimized but portable socket polling.++TCP connections between the TCP client and the server are encrypted to prevent+an outsider from knowing information like who is connecting to whom just be+looking at someones connection to a TCP server. This is useful when someone+connects though something like Tor for example. It also prevents someone from+injecting data in the stream and makes it so we can assume that any data+received was not tampered with and is exactly what was sent by the client.++When a client first connects to a TCP server he opens up a TCP connection to+the ip and port the TCP server is listening on. Once the connection is+established he then sends a handshake packet, the server then responds with his+own and a secure connection is established. The connection is then said to be+unconfirmed and the client must then send some encrypted data to the server+before the server can mark the connection as confirmed. The reason it works+like this is to prevent a type of attack where a peer would send a handshake+packet and then time out right away. To prevent this the server must wait a+few seconds for a sign that the client received his handshake packet before+confirming the connection. The both can then communicate with each other using+the encrypted connection.++The TCP server essentially acts as just a relay between 2 peers. When a TCP+client connects to the server he tells the server which clients he wants the+server to connect him to. The server will only let two clients connect to each+other if both have indicated to the server that they want to connect to each+other. This is to prevent non friends from checking if someone is connected to+a TCP server. The TCP server supports sending packets blindly through it to+clients with a client with public key X (OOB packets) however the TCP server+does not give any feedback or anything to say if the packet arrived or not and+as such it is only useful to send data to friends who may not know that we are+connected to the current TCP server while we know they are. This occurs when+one peer discovers the TCP relay and DHT public key of the other peer before+the other peer discovers its DHT public key. In that case OOB packets would be+used until the other peer knows that the peer is connected to the relay and+establishes a connection through it.++In order to make toxcore work on TCP only the TCP server supports relaying+onion packets from TCP clients and sending any responses from them to TCP+clients.++To establish a secure connection with a TCP server send the following 128 bytes+of data or handshake packet to the server:++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{32} & DHT public key of client \\+ \texttt{24} & Nonce for the encrypted data \\+ \texttt{72} & Payload (plus MAC) \\+\end{tabular}++Payload is encrypted with the DHT private key of the client and public key of+the server and the nonce:++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{32} & Public key \\+ \texttt{24} & Base nonce \\+\end{tabular}++The base nonce is the one TCP client wants the TCP server to use to encrypt the+packets sent to the TCP client.++The first 32 bytes are the public key (DHT public key) that the TCP client is+announcing itself to the server with. The next 24 bytes are a nonce which the+TCP client uses along with the secret key associated with the public key in the+first 32 bytes of the packet to encrypt the rest of this 'packet'. The+encrypted part of this packet contains a temporary public key that will be used+for encryption during the connection and will be discarded after. It also+contains a base nonce which will be used later for encrypting packets sent to+the TCP client.++If the server decrypts successfully the encrypted data in the handshake packet+and responds with the following handshake response of length 96 bytes:++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{24} & Nonce for the encrypted data \\+ \texttt{72} & Payload (plus MAC) \\+\end{tabular}++Payload is encrypted with the private key of the server and the DHT public key+of the client and the nonce:++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{32} & Public key \\+ \texttt{24} & Base nonce \\+\end{tabular}++The base nonce is the one the TCP server wants the TCP client to use to encrypt+the packets sent to the TCP server.++The client already knows the long term public key of the server so it is+omitted in the response, instead only a nonce is present in the unencrypted+part. The encrypted part of the response has the same elements as the+encrypted part of the request: a temporary public key tied to this connection+and a base nonce which will be used later when decrypting packets received from+the TCP client both unique for the connection.++In toxcore the base nonce is generated randomly like all the other nonces, it+must be randomly generated to prevent nonce reuse. For example if the nonce+used was 0 for both sides since both sides use the same keys to encrypt packets+they send to each other, two packets would be encrypted with the same nonce.+These packets could then be possibly replayed back to the sender which would+cause issues. A similar mechanism is used in \texttt{net\_crypto}.++After this the client will know the connection temporary public key and base+nonce of the server and the server will know the connection base nonce and+temporary public key of the client.++The client will then send an encrypted packet to the server, the contents of+the packet do not matter and it must be handled normally by the server (ex: if+it was a ping send a pong response. The first packet must be any valid+encrypted data packet), the only thing that does matter is that the packet was+encrypted correctly by the client because it means that the client has+correctly received the handshake response the server sent to it and that the+handshake the client sent to the server really came from the client and not+from an attacker replaying packets. The server must prevent resource consuming+attacks by timing out clients if they do not send any encrypted packets so the+server to prove to the server that the connection was established correctly.++Toxcore does not have a timeout for clients, instead it stores connecting+clients in large circular lists and times them out if their entry in the list+gets replaced by a newer connection. The reasoning behind this is that it+prevents TCP flood attacks from having a negative impact on the currently+connected nodes. There are however much better ways to do this and the only+reason toxcore does it this way is because writing it was very simple. When+connections are confirmed they are moved somewhere else.++When the server confirms the connection he must look in the list of connected+peers to see if he is already connected to a client with the same announced+public key. If this is the case the server must kill the previous connection+because this means that the client previously timed out and is reconnecting.+Because of Toxcore design it is very unlikely to happen that two legitimate+different peers will have the same public key so this is the correct behavior.++Encrypted data packets look like this to outsiders:++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{2} & \texttt{uint16\_t} length of data \\+ variable & encrypted data \\+\end{tabular}++In a TCP stream they would look like:+\texttt{[[length][data]][[length][data]][[length][data]]...}.++Both the client and server use the following (temp public and private (client+and server) connection keys) which are each generated for the connection and+then sent to the other in the handshake and sent to the other. They are then+used like the next diagram shows to generate a shared key which is equal on+both sides.++\begin{verbatim}+Client: Server:+generate_shared_key( generate_shared_key(+[temp connection public key of server], [temp connection public key of client],+[temp connection private key of client]) [temp connection private key of server])+= =+[shared key] [shared key]+\end{verbatim}++The generated shared key is equal on both sides and is used to encrypt and+decrypt the encrypted data packets.++each encrypted data packet sent to the client will be encrypted with the shared+key and with a nonce equal to: (client base nonce + number of packets sent so+for the first packet it is (starting at 0) nonce + 0, the second is nonce + 1+and so on. Note that nonces like all other numbers sent over the network in+toxcore are numbers in big endian format so when increasing them by 1 the least+significant byte is the last one)++each packet received from the client will be decrypted with the shared key and+with a nonce equal to: (server base nonce + number of packets sent so for the+first packet it is (starting at 0) nonce + 0, the second is nonce + 1 and so+on. Note that nonces like all other numbers sent over the network in toxcore+are numbers in big endian format so when increasing them by 1 the least+significant byte is the last one)++Encrypted data packets have a hard maximum size of 2 + 2048 bytes in the+toxcore TCP server implementation, 2048 bytes is big enough to make sure that+all toxcore packets can go through and leaves some extra space just in case the+protocol needs to be changed in the future. The 2 bytes represents the size of+the data length and the 2048 bytes the max size of the encrypted part. This+means the maximum size is 2050 bytes. In current toxcore, the largest+encrypted data packets sent will be of size 2 + 1417 which is 1419 total.++The logic behind the format of the handshake is that we:++\begin{enumerate}+\item need to prove to the server that we own the private key related to the public+ key we are announcing ourselves with.+\item need to establish a secure connection that has perfect forward secrecy+\item prevent any replay, impersonation or other attacks+\end{enumerate}++How it accomplishes each of those points:++\begin{enumerate}+ \item If the client does not own the private key related to the public key they+ will not be able to create the handshake packet.+ \item Temporary session keys generated by the client and server in the encrypted+ part of the handshake packets are used to encrypt/decrypt packets during the+ session.+ \item The following attacks are prevented:+ \begin{itemize}+ \item Attacker modifies any byte of the handshake packets: Decryption fail, no+ attacks possible.+ \item Attacker captures the handshake packet from the client and replays it+ later to the server: Attacker will never get the server to confirm the+ connection (no effect).+ \item Attacker captures a server response and sends it to the client next time+ they try to connect to the server: Client will never confirm the+ connection. (See: \texttt{TCP\_client})+ \item Attacker tries to impersonate a server: They won't be able to decrypt the+ handshake and won't be able to respond.+ \item Attacker tries to impersonate a client: Server won't be able to decrypt+ the handshake.+ \end{itemize}+\end{enumerate}++The logic behind the format of the encrypted packets is that:++\begin{enumerate}+ \item TCP is a stream protocol, we need packets.+ \item Any attacks must be prevented+\end{enumerate}++How it accomplishes each of those points:++\begin{enumerate}+ \item 2 bytes before each packet of encrypted data denote the length. We assume a+ functioning TCP will deliver bytes in order which makes it work. If the TCP+ doesn't it most likely means it is under attack and for that see the next+ point.+ \item The following attacks are prevented:+ \begin{itemize}+ \item Modifying the length bytes will either make the connection time out+ and/or decryption fail.+ \item Modifying any encrypted bytes will make decryption fail.+ \item Injecting any bytes will make decryption fail.+ \item Trying to re order the packets will make decryption fail because of the+ ordered nonce.+ \item Removing any packets from the stream will make decryption fail because of+ the ordered nonce.+ \end{itemize}+\end{enumerate}++\section{Encrypted payload types}++The folowing represents the various types of data that can be sent inside+encrypted data packets.++\subsection{Routing request (0x00)}++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x00) \\+ \texttt{32} & Public key \\+\end{tabular}++\subsection{Routing request response (0x01)}++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x01) \\+ \texttt{1} & \texttt{uint8\_t} rpid \\+ \texttt{32} & Public key \\+\end{tabular}++rpid is invalid \texttt{connection\_id} (0) if refused, \texttt{connection\_id} if accepted.++\subsection{Connect notification (0x02)}++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x02) \\+ \texttt{1} & \texttt{uint8\_t} \texttt{connection\_id} of connection that got connected \\+\end{tabular}++\subsection{Disconnect notification (0x03)}++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x03) \\+ \texttt{1} & \texttt{uint8\_t} \texttt{connection\_id} of connection that got disconnected \\+\end{tabular}++\subsection{Ping packet (0x04)}++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x04) \\+ \texttt{8} & \texttt{uint64\_t} \texttt{ping\_id} (0 is invalid) \\+\end{tabular}++\subsection{Ping response (pong) (0x05)}++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x05) \\+ \texttt{8} & \texttt{uint64\_t} \texttt{ping\_id} (0 is invalid) \\+\end{tabular}++\subsection{OOB send (0x06)}++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x06) \\+ \texttt{32} & Destination public key \\+ variable & Data \\+\end{tabular}++\subsection{OOB recv (0x07)}++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x07) \\+ \texttt{32} & Sender public key \\+ variable & Data \\+\end{tabular}++\subsection{Onion packet (0x08)}++Same format as initial onion packet but packet id is 0x08 instead of 0x80.++\subsection{Onion packet response (0x09)}++Same format as onion packet but packet id is 0x09 instead of 0x8e.++\subsection{Data (0x10 and up)}++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} packet id \\+ \texttt{1} & \texttt{uint8\_t} connection id \\+ variable & data \\+\end{tabular}++The TCP server is set up in a way to minimize waste while relaying the many+packets that might go between two tox peers hence clients must create+connections to other clients on the relay. The connection number is a+\texttt{uint8\_t} and must be equal or greater to 16 in order to be valid.+Because a \texttt{uint8\_t} has a maximum value of 256 it means that the maximum+number of different connections to other clients that each connection can have+is 240. The reason valid \texttt{connection\_ids} are bigger than 16 is because+they are the first byte of data packets. Currently only number 0 to 9 are+taken however we keep a few extras in case we need to extend the protocol+without breaking it completely.++Routing request (Sent by client to server): Send a routing request to the+server that we want to connect to peer with public key where the public key is+the public the peer announced themselves as. The server must respond to this+with a Routing response.++Routing response (Sent by server to client): The response to the routing+request, tell the client if the routing request succeeded (valid+\texttt{connection\_id}) and if it did, tell them the id of the connection+(\texttt{connection\_id}). The public key sent in the routing request is also+sent in the response so that the client can send many requests at the same time+to the server without having code to track which response belongs to which+public key.++The only reason a routing request should fail is if the connection has reached+the maximum number of simultaneous connections. In case the routing request+fails the public key in the response will be the public key in the failed+request.++Connect notification (Sent by server to client): Tell the client that+\texttt{connection\_id} is now connected meaning the other is online and data+can be sent using this \texttt{connection\_id}.++Disconnect notification (Sent by client to server): Sent when client wants the+server to forget about the connection related to the \texttt{connection\_id} in+the notification. Server must remove this connection and must be able to reuse+the \texttt{connection\_id} for another connection. If the connection was+connected the server must send a disconnect notification to the other client.+The other client must think that this client has simply disconnected from the+TCP server.++Disconnect notification (Sent by server to client): Sent by the server to the+client to tell them that the connection with \texttt{connection\_id} that was+connected is now disconnected. It is sent either when the other client of the+connection disconnect or when they tell the server to kill the connection (see+above).++Ping and Pong packets (can be sent by both client and server, both will+respond): ping packets are used to know if the other side of the connection is+still live. TCP when established doesn't have any sane timeouts (1 week isn't+sane) so we are obliged to have our own way to check if the other side is still+live. Ping ids can be anything except 0, this is because of how toxcore sets+the variable storing the \texttt{ping\_id} that was sent to 0 when it receives a+pong response which means 0 is invalid.++The server should send ping packets every X seconds (toxcore+\texttt{TCP\_server} sends them every 30 seconds and times out the peer if it+doesn't get a response in 10). The server should respond immediately to ping+packets with pong packets.++The server should respond to ping packets with pong packets with the same+\texttt{ping\_id} as was in the ping packet. The server should check that each+pong packet contains the same \texttt{ping\_id} as was in the ping, if not the+pong packet must be ignored.++OOB send (Sent by client to server): If a peer with private key equal to the+key they announced themselves with is connected, the data in the OOB send+packet will be sent to that peer as an OOB recv packet. If no such peer is+connected, the packet is discarded. The toxcore \texttt{TCP\_server}+implementation has a hard maximum OOB data length of 1024. 1024 was picked+because it is big enough for the \texttt{net\_crypto} packets related to the+handshake and is large enough that any changes to the protocol would not+require breaking TCP server. It is however not large enough for the biggest+\texttt{net\_crypto} packets sent with an established \texttt{net\_crypto}+connection to prevent sending those via OOB packets.++OOB recv (Sent by server to client): OOB recv are sent with the announced+public key of the peer that sent the OOB send packet and the exact data.++OOB packets can be used just like normal data packets however the extra size+makes sending data only through them less efficient than data packets.++Data: Data packets can only be sent and received if the corresponding+\texttt{connection\_id} is connection (a Connect notification has been received+from it) if the server receives a Data packet for a non connected or existent+connection it will discard it.++Why did I use different packet ids for all packets when some are only sent by+the client and some only by the server? It's less confusing.++\chapter{Friend connection}++\texttt{friend\_connection} is the module that sits on top of the DHT, onion and+\texttt{net\_crypto} modules and takes care of linking the 3 together.++Friends in \texttt{friend\_connection} are represented by their real public key.+When a friend is added in \texttt{friend\_connection}, an onion search entry is+created for that friend. This means that the onion module will start looking+for this friend and send that friend their DHT public key, and the TCP relays+it is connected to, in case a connection is only possible with TCP.++Once the onion returns the DHT public key of the peer, the DHT public key is+saved, added to the DHT friends list and a new \texttt{net\_crypto} connection+is created. Any TCP relays returned by the onion for this friend are passed to+the \texttt{net\_crypto} connection.++If the DHT establishes a direct UDP connection with the friend,+\texttt{friend\_connection} will pass the IP/port of the friend to+\texttt{net\_crypto} and also save it to be used to reconnect to the friend if+they disconnect.++If \texttt{net\_crypto} finds that the friend has a different DHT public key,+which can happen if the friend restarted their client, \texttt{net\_crypto} will+pass the new DHT public key to the onion module and will remove the DHT entry+for the old DHT public key and replace it with the new one. The current+\texttt{net\_crypto} connection will also be killed and a new one with the+correct DHT public key will be created.++When the \texttt{net\_crypto} connection for a friend goes online,+\texttt{friend\_connection} will tell the onion module that the friend is online+so that it can stop spending resources looking for the friend. When the friend+connection goes offline, \texttt{friend\_connection} will tell the onion module+so that it can start looking for the friend again.++There are 2 types of data packets sent to friends with the \texttt{net\_crypto}+connection handled at the level of \texttt{friend\_connection}, Alive packets+and TCP relay packets. Alive packets are packets with the packet id or first+byte of data (only byte in this packet) being 16. They are used in order to+check if the other friend is still online. \texttt{net\_crypto} does not have+any timeout when the connection is established so timeouts are caught using+this packet. In toxcore, this packet is sent every 8 seconds. If none of+these packets are received for 32 seconds, the connection is timed out and+killed. These numbers seem to cause the least issues and 32 seconds is not too+long so that, if a friend times out, toxcore won't falsely see them online for+too long. Usually when a friend goes offline they have time to send a+disconnect packet in the \texttt{net\_crypto} connection which makes them appear+offline almost instantly.++The timeout for when to stop retrying to connect to a friend by creating new+\texttt{net\_crypto} connections when the old one times out in toxcore is the+same as the timeout for DHT peers (122 seconds). However, it is calculated+from the last time a DHT public key was received for the friend or time the+friend's \texttt{net\_crypto} connection went offline after being online. The+highest time is used to calculate when the timeout is. \texttt{net\_crypto}+connections will be recreated (if the connection fails) until this timeout.++\texttt{friend\_connection} sends a list of 3 relays (the same number as the+target number of TCP relay connections in \texttt{TCP\_connections}) to each+connected friend every 5 minutes in toxcore. Immediately before sending the+relays, they are associated to the current \texttt{net\_crypto->TCP\_connections}+connection. This facilitates connecting the two friends together using the+relays as the friend who receives the packet will associate the sent relays to+the \texttt{net\_crypto} connection they received it from. When both sides do+this they will be able to connect to each other using the relays. The packet+id or first byte of the packet of share relay packets is 0x11. This is then+followed by some TCP relays stored in packed node format.++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x11) \\+ variable & TCP relays in packed node format (see DHT) \\+\end{tabular}++If local IPs are received as part of the packet, the local IP will be replaced+with the IP of the peer that sent the relay. This is because we assume this is+the best way to attempt to connect to the TCP relay. If the peer that sent the+relay is using a local IP, then the sent local IP should be used to connect to+the relay.++For all other data packets, are passed by \texttt{friend\_connection} up to the+upper Messenger module. It also separates lossy and lossless packets from+\texttt{net\_crypto}.++Friend connection takes care of establishing the connection to the friend and+gives the upper messenger layer a simple interface to receive and send+messages, add and remove friends and know if a friend is connected (online) or+not connected (offline).++\chapter{Friend requests}++When a Tox user adds someone with Tox, toxcore will try sending a friend+request to that person. A friend request contains the long term public key of+the sender, a nospam number and a message.++Transmitting the long term public key is the primary goal of the friend request+as it is what the peer needs to find and establish a connection to the sender.+The long term public key is what the receiver adds to his friends list if he+accepts the friend request.++The nospam is a number used to prevent someone from spamming the network with+valid friend requests. It makes sure that the only people who have seen the+Tox ID of a peer are capable of sending them a friend request. The nospam is+one of the components of the Tox ID.++The nospam is a number or a list of numbers set by the peer, only received+friend requests that contain a nospam that was set by the peer are sent to the+client to be accepted or refused by the user. The nospam prevents random peers+in the network from sending friend requests to non friends. The nospam is not+long enough to be secure meaning an extremely resilient attacker could manage+to send a spam friend request to someone. 4 bytes is large enough to prevent+spam from random peers in the network. The nospam could also allow Tox users+to issue different Tox IDs and even change Tox IDs if someone finds a Tox ID+and decides to send it hundreds of spam friend requests. Changing the nospam+would stop the incoming wave of spam friend requests without any negative+effects to the users friends list. For example if users would have to change+their public key to prevent them from receiving friend requests it would mean+they would have to essentially abandon all their current friends as friends are+tied to the public key. The nospam is not used at all once the friends have+each other added which means changing it won't have any negative effects.++Friend request:++\begin{verbatim}+[uint32_t nospam][Message (UTF8) 1 to ONION_CLIENT_MAX_DATA_SIZE bytes]+\end{verbatim}++Friend request packet when sent as an onion data packet:++\begin{verbatim}+[uint8_t (32)][Friend request]+\end{verbatim}++Friend request packet when sent as a \texttt{net\_crypto} data packet (If we are+directly connected to the peer because of a group chat but are not friends with+them):++\begin{verbatim}+[uint8_t (18)][Friend request]+\end{verbatim}++When a friend is added to toxcore with their Tox ID and a message, the friend+is added in \texttt{friend\_connection} and then toxcore tries to send friend+requests.++When sending a friend request, toxcore will check if the peer which a friend+request is being sent to is already connected to using a \texttt{net\_crypto}+connection which can happen if both are in the same group chat. If this is the+case the friend request will be sent as a \texttt{net\_crypto} packet using that+connection. If not, it will be sent as an onion data packet.++Onion data packets contain the real public key of the sender and if a+\texttt{net\_crypto} connection is established it means the peer knows our real+public key. This is why the friend request does not need to contain the real+public key of the peer.++Friend requests are sent with exponentially increasing interval of 2 seconds, 4+seconds, 8 seconds, etc... in toxcore. This is so friend requests get resent+but eventually get resent in intervals that are so big that they essentially+expire. The sender has no way of knowing if a peer refuses a friend requests+which is why friend requests need to expire in some way. Note that the+interval is the minimum timeout, if toxcore cannot send that friend request it+will try again until it manages to send it. One reason for not being able to+send the friend request would be that the onion has not found the friend in the+onion and so cannot send an onion data packet to them.++Received friend requests are passed to the client, the client is expected to+show the message from the friend request to the user and ask the user if they+want to accept the friend request or not. Friend requests are accepted by+adding the peer sending the friend request as a friend and refused by simply+ignoring it.++Friend requests are sent multiple times meaning that in order to prevent the+same friend request from being sent to the client multiple times toxcore keeps+a list of the last real public keys it received friend requests from and+discards any received friend requests that are from a real public key that is+in that list. In toxcore this list is a simple circular list. There are many+ways this could be improved and made more efficient as a circular list isn't+very efficient however it has worked well in toxcore so far.++Friend requests from public keys that are already added to the friends list+should also be discarded.++\chapter{Group}++Group chats in Tox work by temporarily adding some peers present in the group+chat as temporary \texttt{friend\_connection} friends, that are deleted when the+group chat is exited.++Each peer in the group chat is identified by their real long term public key.+Peers also transmit their DHT public keys to each other via the group chat in+order to speed up the connection by making it unnecessary for the peers to find+each other's DHT public keys with the onion, as would happen had they added each+other as normal friends.++The upside of using \texttt{friend\_connection} is that group chats do not have+to deal with things like hole punching, peers only on TCP or other low level+networking things. The downside however is that every single peer knows each+other's real long term public key and DHT public key, meaning that these group+chats should only be used between friends.++Each peer adds a \texttt{friend\_connection} for each of up to 4 other peers in+the group. If the group chat has 5 participants or fewer, each of the peers will+therefore have each of the others added to their list of friend connections, and+a peer wishing to send a message to the group may communicate it directly to the+other peers. When there are more than 5 peers, messages are relayed along friend+connections.++Since the maximum number of peers per groupchat that will be connected to with+friend connections is 4, if the peers in the groupchat are arranged in a circle+and each peer connects to the 2 peers that are closest to the right of them and+the 2 peers that are closest to the left of them, then the peers should form a+well-connected circle of peers.++Group chats in toxcore do this by subtracting the real long term public key of+the peer with all the others in the group (our PK - other peer PK), using+modular arithmetic, and finding the two peers for which the result of this+operation is the smallest. The operation is then inversed (other peer PK - our+PK) and this operation is done again with all the public keys of the peers in+the group. The 2 peers for which the result is again the smallest are picked.++This gives 4 peers that are then added as a friend connection and associated to+the group. If every peer in the group does this, they will form a circle of+perfectly connected peers.++Once the peers are connected to each other in a circle they relay each other's+messages. Every time a peer leaves the group or a new peer joins, each member+of the chat will recalculate the peers they should connect to.++To join a group chat, a peer must first be invited to it by their friend. To+make a groupchat the peer will first create a groupchat and then invite people+to this group chat. Once their friends are in the group chat, those friends can+invite their other friends to the chat, and so on.++To create a group chat, a peer generates a random 32 byte id that is used to+uniquely identify the group chat. 32 bytes is enough so that when randomly+generated with a secure random number generator every groupchat ever created+will have a different id. The goal of this 32 byte id is so that peers have a+way of identifying each group chat, so that they can prevent themselves from+joining a groupchat twice for example.++The groupchat will also have an unsigned 1 byte type. This type indicates what+kind of groupchat the groupchat is. The current types are:++\begin{tabular}{l|l}+ Type number & Type \\+ \hline+ \texttt{0} & text \\+ \texttt{1} & audio \\+\end{tabular}++Text groupchats are text only, while audio indicates that the groupchat supports+sending audio to it as well as text.++The groupchat will also be identified by a unique unsigned 2 byte integer, which+in toxcore corresponds to the index of the groupchat in the array it is being+stored in. Every groupchat in the current instance must have a different+number. This number is used by groupchat peers that are directly connected to+us to tell us which packets are for which groupchat. Every groupchat packet+contains a 2 byte groupchat number. Putting a 32 byte groupchat id in each+packet would increase bandwidth waste by a lot, and this is the reason why+groupchat numbers are used instead.++Using the group number as the index of the array used to store the groupchat+instances is recommended, because this kind of access is usually most efficient+and it ensures that each groupchat has a unique group number.++When creating a new groupchat, the peer will add themselves as a groupchat peer+with a peer number of 0 and their own long term public key and DHT public key.++Invite packets:++Invite packet:++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x60) \\+ \texttt{1} & \texttt{uint8\_t} (0x00) \\+ \texttt{2} & \texttt{uint16\_t} group number \\+ \texttt{33} & Group chat identifier \\+\end{tabular}++Response packet:++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x60) \\+ \texttt{1} & \texttt{uint8\_t} (0x01) \\+ \texttt{2} & \texttt{uint16\_t} group number (local) \\+ \texttt{2} & \texttt{uint16\_t} group number to join \\+ \texttt{33} & Group chat identifier \\+\end{tabular}++A group chat identifier consists of a 1-byte type and a 32-byte id concatenated:++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} type \\+ \texttt{32} & \texttt{uint8\_t} groupchat id \\+\end{tabular}++To invite a friend to a group chat, an invite packet is sent to the friend.+These packets are sent using Messenger (if you look at the Messenger packet id+section, all the groupchat packet ids are in there). Note that all numbers+here, like all numbers sent using Tox packets, are sent in big endian format.++The group chat number is as explained above, the number used to uniquely+identify the groupchat instance from all the other groupchat instances the peer+has. It is sent in the invite packet because it is needed by the friend in+order to send back groupchat related packets.++What follows is the 33 byte group chat identifier.++To refuse the invite, the friend receiving it will simply ignore and discard+it.++To accept the invite, the friend will create their own groupchat instance with+the 1 byte type and 32 byte groupchat id sent in the request, and send an invite+response packet back. The friend will also add the peer who sent the invite as+a groupchat connection, and mark the connection as introducing the friend.++The first group number in the response packet is the group number of the+groupchat the invited friend just created. The second group number is the group+number that was sent in the invite request. What follows is the 33 byte group+chat identifier which was sent in the invite request.++When a peer receives an invite response packet they will check if the group+identifier sent back corresponds to the group identifier of the groupchat with+the group number also sent back. If so, a new peer number will be generated for+the peer that sent the invite response packet. Then the peer with their+generated peer number, their long term public key and their DHT public key will+be added to the peer list of the groupchat. A new peer message packet will also+be sent to tell everyone in the group chat about the new peer. The peer will+also be added as a groupchat connection, and the connection will be marked as+introducing the peer.++Peer numbers are used to uniquely identify each peer in the group chat. They+are used in groupchat message packets so that peers receiving them can know who+or which groupchat peer sent them. As groupchat message packets are relayed,+they must contain something that is used by others to identify the sender. Since+putting a 32 byte public key in each packet would be wasteful, a 2 byte peer+number is used instead. Each peer in the groupchat has a unique peer number.+Toxcore generates each peer number randomly but makes sure newly generated peer+numbers are not equal to current ones already used by other peers in the group+chat. If two peers join the groupchat from two different endpoints there is a+small possibility that both will be given the same peer number, but the+probability of this occurring is low enough in practice that it is not an issue.++Peer online packet:++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x61) \\+ \texttt{2} & \texttt{uint16\_t} group number (local) \\+ \texttt{33} & Group chat identifier \\+\end{tabular}++Peer introduced packet:++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x62) \\+ \texttt{2} & \texttt{uint16\_t} group number (local) \\+ \texttt{1} & \texttt{uint8\_t} (0x01) \\+\end{tabular}++For a groupchat connection to work, both peers in the groupchat must be+attempting to connect directly to each other.++Groupchat connections are established when both peers who want to connect to+each other either create a new friend connection to connect to each other or+reuse an existing friend connection that connects them together (if they are+friends or already are connected together because of another group chat).++As soon as the connection to the other peer is opened, a peer online packet is+sent to the peer. The goal of the online packet is to tell the peer that we+want to establish the groupchat connection with them and tell them the+groupchat number of our groupchat instance. The peer online packet contains+the group number and the 33 byte group chat identifier. The group number is the+group number the peer has for the group with the group id sent in the packet.++When both sides send an online packet to the other peer, a connection is+established.++When an online packet is received from a peer, if the connection to the peer+is already established (an online packet has been already received), or if+there is no group connection to that peer being established, the packet is+dropped. Otherwise, the group number to communicate with the group via the+peer is saved, the connection is considered established, and an online packet+is sent back to the peer. A ping message is sent to the group. If this is the+first group connection to that group we establish, or the connection is marked+as introducing us, we send a peer query packet back to the peer. This is so+we can get the list of peers from the group. If the connection is marked as+introducing the peer, we send a new peer message to the group announcing the+peer, and a name message reannouncing our name.++A groupchat connection can be marked as introducing one or both of the peers it+connects, to indicate that the connection should be maintained until that peer+is well connected to the group. A peer maintains a groupchat connection to a+second peer as long as the second peer is one of the four closest peers in the+groupchat to the first, or the connection is marked as introducing a peer who+still requires the connection. A peer requires a groupchat connection to a+second peer which introduces the first peer until the first peer has more than+4 groupchat connections and receives a message from the second peer via a+different groupchat connection. The first peer then sends a peer introduced+packet to the second peer to indicate that they no longer require the+connection.++Peer query packet:++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x62) \\+ \texttt{2} & \texttt{uint16\_t} group number \\+ \texttt{1} & \texttt{uint8\_t} (0x08) \\+\end{tabular}++Peer response packet:++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x62) \\+ \texttt{2} & \texttt{uint16\_t} group number \\+ \texttt{1} & \texttt{uint8\_t} (0x09) \\+ variable & Repeated times number of peers: Peer info \\+\end{tabular}++The Peer info structure is as follows:++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{2} & \texttt{uint16\_t} peer number \\+ \texttt{32} & Long term public key \\+ \texttt{32} & DHT public key \\+ \texttt{1} & \texttt{uint8\_t} Name length \\+ \texttt{[0, 255]} & Name \\+\end{tabular}++Title response packet:++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x62) \\+ \texttt{2} & \texttt{uint16\_t} group number \\+ \texttt{1} & \texttt{uint8\_t} (0x0a) \\+ variable & Title \\+\end{tabular}++Message packets:++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x63) \\+ \texttt{2} & \texttt{uint16\_t} group number \\+ \texttt{2} & \texttt{uint16\_t} peer number \\+ \texttt{4} & \texttt{uint32\_t} message number \\+ \texttt{1} & \texttt{uint8\_t} with a value representing id of message \\+ variable & Data \\+\end{tabular}++Lossy Message packets:++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0xc7) \\+ \texttt{2} & \texttt{uint16\_t} group number \\+ \texttt{2} & \texttt{uint16\_t} peer number \\+ \texttt{2} & \texttt{uint16\_t} message number \\+ \texttt{1} & \texttt{uint8\_t} with a value representing id of message \\+ variable & Data \\+\end{tabular}++If a peer query packet is received, the receiver takes their list of peers and+creates a peer response packet which is then sent to the other peer. If there+are too many peers in the group chat and the peer response packet would be+larger than the maximum size of friend connection packets (1373 bytes), more+than one peer response packet is sent back. A Title response packet is also+sent back. This is how the peer that joins a group chat finds out the list of+peers in the group chat and the title of the group chat right after joining.++Peer response packets are straightforward and contain the information for each+peer (peer number, real public key, DHT public key, name) appended to each+other. The title response is also straightforward.++Both the maximum length of groupchat peer names and the groupchat title is 128+bytes. This is the same maximum length as names in all of toxcore.++When a peer receives a peer response packet, they will add each of the+received peers to their groupchat peer list, find the 4 closest peers to them+and create groupchat connections to them as was explained previously. The DHT+public key of an already known peer is updated to one given in the response+packet if the peer is frozen, or if it has been frozen since its DHT public+key was last updated.++When a peer receives a title response packet, they update the title for the+groupchat accordingly if the title has not already been set, or if since it+was last set there has been a time at which all peers were frozen.++If the peer does not yet know their own peer number, as is the case if they+have just accepted an invitation, the peer will find themselves in the list of+received peers and use the peer number assigned to them as their own. They are+then able to send messages and invite other peers to the groupchat. They+immediately send a name message to announce their name to the group.++Message packets are used to send messages to all peers in the groupchat. To+send a message packet, a peer will first take their peer number and the message+they want to send. Each message packet sent will have a message number that is+equal to the last message number sent + 1. Like all other numbers (group chat+number, peer number) in the packet, the message number in the packet will be in+big endian format.++When a Message packet is received, the peer receiving it will first check that+the peer number of the sender is in their peer list. If not, the peer ignores+the message but sends a peer query packet to the peer the packet was directly+received from. That peer should have the message sender in their peer list,+and so will send the sender's peer info back in a peer response.++If the sender is in the receiver's peer list, the receiver now checks whether+they have already seen a message with the same sender and message number. This+is achieved by storing the 8 greatest message numbers received from a given+sender peer number. If the message has lesser message number than any of those+8, it is assumed to have been received. If the message has already been+received according to this check, or if it is a name or title message and+another message of the same type from the same sender with a greater message+number has been received, then the packet is discarded. Otherwise, the+message is processed as described below, and a Message packet with the message+is sent (relayed) to all current group connections except the one that it was+received from. The only thing that should change in the Message packet as it+is relayed is the group number.++Lossy message packets are used to send audio packets to others in audio group+chats. Lossy packets work the same way as normal relayed groupchat messages in+that they are relayed to everyone in the group chat until everyone has them, but+there are a few differences. Firstly, the message number is only a 2 byte+integer. When receiving a lossy packet from a peer the receiving peer will first+check if a message with that message number was already received from that peer.+If it wasn't, the packet will be added to the list of received packets and then+the packet will be passed to its handler and then sent to the 2 closest+connected groupchat peers that are not the sender. The reason for it to be 2+instead of 4 (or 3 if we are not the original sender) as for lossless message+packets is that it reduces bandwidth usage without lowering the quality of the+received audio stream via lossy packets, at the cost of reduced robustness+against connections failing. To check if a packet was already received, the last+256 message numbers received from each peer are stored. If video was added+meaning a much higher number of packets would be sent, this number would be+increased. If the packet number is in this list then it was received.++\section{Message ids}++\subsection{ping (0x00)}++Sent approximately every 20 seconds by every peer. Contains no data.++\subsection{\texttt{new\_peer} (0x10)}++Tell everyone about a new peer in the chat.++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{2} & \texttt{uint16\_t} Peer number \\+ \texttt{32} & Long term public key \\+ \texttt{32} & DHT public key \\+\end{tabular}++\subsection{\texttt{kill\_peer} (0x11)}++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{2} & \texttt{uint16\_t} Peer number \\+\end{tabular}++\subsection{Name change (0x30)}++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ variable & Name (namelen) \\+\end{tabular}++\subsection{Groupchat title change (0x31)}++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ variable & Title (titlelen) \\+\end{tabular}++\subsection{Chat message (0x40)}++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ variable & Message (messagelen) \\+\end{tabular}++\subsection{Action (/me) (0x41)}++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ variable & Message (messagelen) \\+\end{tabular}++Ping messages are sent every 20 seconds by every peer. This is how other+peers know that the peers are still alive.++When a new peer joins, the peer which invited the joining peer will send a new+peer message to warn everyone that there is a new peer in the chat. When a new+peer message is received, the peer in the message must be added to the peer+list if it is not there already, and its DHT public key must be set to that+in the message.++Kill peer messages are used to indicate that a peer has quit the group chat.+It is sent by the one quitting the group chat right before they quit it.++Name change messages are used to change or set the name of the peer sending it.+They are also sent by a joining peer right after receiving the list of peers in+order to tell others what their name is.++Title change packets are used to change the title of the group chat and can be+sent by anyone in the group chat.++Chat and action messages are used by the group chat peers to send messages to+others in the group chat.++\section{Timeouts and reconnection}++Groupchat connections may go down, and this may lead to a peer becoming+disconnected from the group or the group otherwise splitting into multiple+connected components. To ensure the group becomes fully connected again once+suitable connections are re-established, peers keep track of peers who are no+longer visible in the group ("frozen" peers), and try to re-integrate them+into the group via any suitable friend connections which may come to be+available. The rejoin packet is used for this.++Rejoin packet:++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x64) \\+ \texttt{33} & Group chat identifier \\+\end{tabular}++A peer in a groupchat is considered to be active when a group message or+rejoin packet is received from it, or a new peer message is received for it.+A peer which remains inactive for 60 seconds is set as frozen; this means it+is removed from the peer list and added to a separate list of frozen peers.+Frozen peers are disregarded for all purposes except those discussed below.++If a frozen peer becomes active, we unfreeze it, meaning that we move it from+the frozen peers list to the peer list, and we send a name message to the+group.++Whenever we make a new friend connection to a peer, we check whether the+public key of the peer is that of any frozen peer. If so, we send a rejoin+packet to the peer along the friend connection, and create a groupchat+connection to the peer, marked as introducing us, and send a peer online+packet to the peer.++If we receive a rejoin packet from a peer along a friend connection, then,+after unfreezing the peer if it was frozen, we update the peer's DHT public+key in the groupchat peer list to the key in the friend connection, and create+a groupchat connection for the peer, marked as introducing the peer, and send+a peer online packet to the peer.++\input{src/tox/Network/Tox/Application/GroupChats.lhs}++\chapter{Net crypto}++The Tox transport protocol is what Tox uses to establish and send data securely+to friends and provides encryption, ordered delivery, and perfect forward+secrecy. It is a UDP protocol but it is also used when 2 friends connect over+TCP relays.++The reason the protocol for connections to friends over TCP relays and direct+UDP is the same is for simplicity and so the connection can switch between both+without the peers needing to disconnect and reconnect. For example two Tox+friends might first connect over TCP and a few seconds later switch to UDP when+a direct UDP connection becomes possible. The opening up of the UDP route or+'hole punching' is done by the DHT module and the opening up of a relayed TCP+connection is done by the \texttt{TCP\_connection} module. The Tox transport+protocol has the job of connecting two peers (tox friends) safely once a route+or communications link between both is found. Direct UDP is preferred over TCP+because it is direct and isn't limited by possibly congested TCP relays. Also,+a peer can only connect to another using the Tox transport protocol if they+know the real public key and DHT public key of the peer they want to connect+to. However, both the DHT and TCP connection modules require this information+in order to find and open the route to the peer which means we assume this+information is known by toxcore and has been passed to \texttt{net\_crypto} when+the \texttt{net\_crypto} connection was created.++Because this protocol has to work over UDP it must account for possible packet+loss, packets arriving in the wrong order and has to implement some kind of+congestion control. This is implemented above the level at which the packets+are encrypted. This prevents a malicious TCP relay from disrupting the+connection by modifying the packets that go through it. The packet loss+prevention makes it work very well on TCP relays that we assume may go down at+any time as the connection will stay strong even if there is need to switch to+another TCP relay which will cause some packet loss.++Before sending the actual handshake packet the peer must obtain a cookie. This+cookie step serves as a way for the receiving peer to confirm that the peer+initiating the connection can receive the responses in order to prevent certain+types of DoS attacks.++The peer receiving a cookie request packet must not allocate any resources to+the connection. They will simply respond to the packet with a cookie response+packet containing the cookie that the requesting peer must then use in the+handshake to initiate the actual connection.++The cookie response must be sent back using the exact same link the cookie+request packet was sent from. The reason for this is that if it is sent back+using another link, the other link might not work and the peer will not be+expecting responses from another link. For example, if a request is sent from+UDP with ip port X, it must be sent back by UDP to ip port X. If it was+received from a TCP OOB packet it must be sent back by a TCP OOB packet via the+same relay with the destination being the peer who sent the request. If it was+received from an established TCP relay connection it must be sent back via that+same exact connection.++When a cookie request is received, the peer must not use the information in the+request packet for anything, he must not store it, he must only create a cookie+and cookie response from it, then send the created cookie response packet and+forget them. The reason for this is to prevent possible attacks. For example+if a peer would allocate long term memory for each cookie request packet+received then a simple packet flood would be enough to achieve an effective+denial of service attack by making the program run out of memory.++cookie request packet (145 bytes):++\begin{verbatim}+[uint8_t 24]+[Sender's DHT Public key (32 bytes)]+[Random nonce (24 bytes)]+[Encrypted message containing:+ [Sender's real public key (32 bytes)]+ [padding (32 bytes)]+ [uint64_t echo id (must be sent back untouched in cookie response)]+]+\end{verbatim}++Encrypted message is encrypted with sender's DHT private key, receiver's DHT+public key and the nonce.++The packet id for cookie request packets is 24. The request contains the DHT+public key of the sender which is the key used (The DHT private key) (along+with the DHT public key of the receiver) to encrypt the encrypted part of the+cookie packet and a nonce also used to encrypt the encrypted part of the+packet. Padding is used to maintain backwards-compatibility with previous+versions of the protocol. The echo id in the cookie request must be sent back+untouched in the cookie response. This echo id is how the peer sending the+request can be sure that the response received was a response to the packet+that he sent.++The reason for sending the DHT public key and real public key in the cookie+request is that both are contained in the cookie sent back in the response.++Toxcore currently sends 1 cookie request packet every second 8 times before it+kills the connection if there are no responses.++cookie response packet (161 bytes):++\begin{verbatim}+[uint8_t 25]+[Random nonce (24 bytes)]+[Encrypted message containing:+ [Cookie]+ [uint64_t echo id (that was sent in the request)]+]+\end{verbatim}++Encrypted message is encrypted with the exact same symmetric key as the cookie+request packet it responds to but with a different nonce.++The packet id for cookie request packets is 25. The response contains a nonce+and an encrypted part encrypted with the nonce. The encrypted part is+encrypted with the same key used to decrypt the encrypted part of the request+meaning the expensive shared key generation needs to be called only once in+order to handle and respond to a cookie request packet with a cookie response.++The Cookie (see below) and the echo id that was sent in the request are the+contents of the encrypted part.++The Cookie should be (112 bytes):++\begin{verbatim}+[nonce]+[encrypted data:+ [uint64_t time]+ [Sender's real public key (32 bytes)]+ [Sender's DHT public key (32 bytes)]+]+\end{verbatim}++The cookie is a 112 byte piece of data that is created and sent to the+requester as part of the cookie response packet. A peer who wants to connect+to another must obtain a cookie packet from the peer they are trying to connect+to. The only way to send a valid handshake packet to another peer is to first+obtain a cookie from them.++The cookie contains information that will both prove to the receiver of the+handshake that the peer has received a cookie response and contains encrypted+info that tell the receiver of the handshake packet enough info to both decrypt+and validate the handshake packet and accept the connection.++When toxcore is started it generates a symmetric encryption key that it uses to+encrypt and decrypt all cookie packets (using NaCl authenticated encryption+exactly like encryption everywhere else in toxcore). Only the instance of+toxcore that create the packets knows the encryption key meaning any cookie it+successfully decrypts and validates were created by it.++The time variable in the cookie is used to prevent cookie packets that are too+old from being used. Toxcore has a time out of 15 seconds for cookie packets.+If a cookie packet is used more than 15 seconds after it is created toxcore+will see it as invalid.++When responding to a cookie request packet the sender's real public key is the+known key sent by the peer in the encrypted part of the cookie request packet+and the senders DHT public key is the key used to encrypt the encrypted part of+the cookie request packet.++When generating a cookie to put inside the encrypted part of the handshake: One+of the requirements to connect successfully to someone else is that we know+their DHT public key and their real long term public key meaning there is+enough information to construct the cookie.++Handshake packet:++\begin{verbatim}+[uint8_t 26]+[Cookie]+[nonce (24 bytes)]+[Encrypted message containing:+ [24 bytes base nonce]+ [session public key of the peer (32 bytes)]+ [sha512 hash of the entire Cookie sitting outside the encrypted part]+ [Other Cookie (used by the other to respond to the handshake packet)]+]+\end{verbatim}++The packet id for handshake packets is 26. The cookie is a cookie obtained by+sending a cookie request packet to the peer and getting a cookie response+packet with a cookie in it. It may also be obtained in the handshake packet by+a peer receiving a handshake packet (Other Cookie).++The nonce is a nonce used to encrypt the encrypted part of the handshake+packet. The encrypted part of the handshake packet is encrypted with the long+term keys of both peers. This is to prevent impersonation.++Inside the encrypted part of the handshake packet there is a 'base nonce' and a+session public key. The 'base nonce' is a nonce that the other should use to+encrypt each data packet, adding + 1 to it for each data packet sent. (first+packet is 'base nonce' + 0, next is 'base nonce' + 1, etc. Note that for+mathematical operations the nonce is considered to be a 24 byte number in big+endian format). The session key is the temporary connection public key that+the peer has generated for this connection and it sending to the other. This+session key is used so that the connection has perfect forward secrecy. It is+important to save the private key counterpart of the session public key sent in+the handshake, the public key received by the other and both the received and+sent base nonces as they are used to encrypt/decrypt the data packets.++The hash of the cookie in the encrypted part is used to make sure that an+attacker has not taken an older valid handshake packet and then replaced the+cookie packet inside with a newer one which would be bad as they could replay+it and might be able to make a mess.++The 'Other Cookie' is a valid cookie that we put in the handshake so that the+other can respond with a valid handshake without having to make a cookie+request to obtain one.++The handshake packet is sent by both sides of the connection. If a peer+receives a handshake it will check if the cookie is valid, if the encrypted+section decrypts and validates, if the cookie hash is valid, if long term+public key belongs to a known friend. If all these are true then the+connection is considered 'Accepted' but not 'Confirmed'.++If there is no existing connection to the peer identified by the long term+public key to set to 'Accepted', one will be created with that status. If a+connection to such peer with a not yet 'Accepted' status to exists, this+connection is set to accepted. If a connection with a 'Confirmed' status+exists for this peer, the handshake packet will be ignored and discarded (The+reason for discarding it is that we do not want slightly late handshake packets+to kill the connection) except if the DHT public key in the cookie contained in+the handshake packet is different from the known DHT public key of the peer.+If this happens the connection will be immediately killed because it means it+is no longer valid and a new connection will be created immediately with the+'Accepted' status.++Sometimes toxcore might receive the DHT public key of the peer first with a+handshake packet so it is important that this case is handled and that the+implementation passes the DHT public key to the other modules (DHT,+\texttt{TCP\_connection}) because this does happen.++Handshake packets must be created only once during the connection but must be+sent in intervals until we are sure the other received them. This happens when+a valid encrypted data packet is received and decrypted.++The states of a connection:++\begin{enumerate}+ \item Not accepted: Send handshake packets.++ \item Accepted: A handshake packet has been received from the other peer but+ no encrypted packets: continue (or start) sending handshake packets because+ the peer can't know if the other has received them.++ \item Confirmed: A valid encrypted packet has been received from the other+ peer: Connection is fully established: stop sending handshake packets.+\end{enumerate}++Toxcore sends handshake packets every second 8 times and times out the+connection if the connection does not get confirmed (no encrypted packet is+received) within this time.++Perfect handshake scenario:++\begin{verbatim}+Peer 1 Peer 2+Cookie request ->+ <- Cookie response+Handshake packet ->+ * accepts connection+ <- Handshake packet+*accepts connection+Encrypted packet -> <- Encrypted packet+*confirms connection *confirms connection+ Connection successful.+Encrypted packets -> <- Encrypted packets++More realistic handshake scenario:+Peer 1 Peer 2+Cookie request -> *packet lost*+Cookie request ->+ <- Cookie response+ *Peer 2 randomly starts new connection to peer 1+ <- Cookie request+Cookie response ->+Handshake packet -> <- Handshake packet+*accepts connection * accepts connection+Encrypted packet -> <- Encrypted packet+*confirms connection *confirms connection+ Connection successful.+Encrypted packets -> <- Encrypted packets+\end{verbatim}++The reason why the handshake is like this is because of certain design+requirements:++\begin{enumerate}+ \item The handshake must not leak the long term public keys of the peers to a+ possible attacker who would be looking at the packets but each peer must know+ for sure that they are connecting to the right peer and not an impostor.+ \item A connection must be able of being established if only one of the peers has+ the information necessary to initiate a connection (DHT public key of the+ peer and a link to the peer).+ \item If both peers initiate a connection to each other at the same time the+ connection must succeed without issues.+ \item There must be perfect forward secrecy.+ \item Must be resistant to any possible attacks.+\end{enumerate}++Due to how it is designed only one connection is possible at a time between 2+peers.++Encrypted packets:++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x1b) \\+ \texttt{2} & \texttt{uint16\_t} The last 2 bytes of the nonce used to encrypt this \\+ variable & Payload \\+\end{tabular}++The payload is encrypted with the session key and 'base nonce' set by the+receiver in their handshake + packet number (starting at 0, big endian math).++The packet id for encrypted packets is 27. Encrypted packets are the packets+used to send data to the other peer in the connection. Since these packets can+be sent over UDP the implementation must assume that they can arrive out of+order or even not arrive at all.++To get the key used to encrypt/decrypt each packet in the connection a peer+takes the session public key received in the handshake and the private key+counterpart of the key it sent it the handshake and generates a shared key from+it. This shared key will be identical for both peers. It is important to note+that connection keys must be wiped when the connection is killed.++To create an encrypted packet to be sent to the other peer, the data is+encrypted with the shared key for this connection and the base nonce that the+other peer sent in the handshake packet with the total number of encrypted+packets sent in the connection added to it ('base nonce' + 0 for the first+encrypted data packet sent, 'base nonce' + 1 for the second, etc. Note that+the nonce is treated as a big endian number for mathematical operations like+additions). The 2 byte (\texttt{uint16\_t}) number at the beginning of the+encrypted packet is the last 2 bytes of this 24 byte nonce.++To decrypt a received encrypted packet, the nonce the packet was encrypted with+is calculated using the base nonce that the peer sent to the other and the 2+byte number at the beginning of the packet. First we assume that packets will+most likely arrive out of order and that some will be lost but that packet loss+and out of orderness will never be enough to make the 2 byte number need an+extra byte. The packet is decrypted using the shared key for the connection+and the calculated nonce.++Toxcore uses the following method to calculate the nonce for each packet:++\begin{enumerate}+ \item \texttt{diff} = (2 byte number on the packet) - (last 2 bytes of the current saved+ base nonce) NOTE: treat the 3 variables as 16 bit unsigned ints, the result+ is expected to sometimes roll over.+ \item copy \texttt{saved\_base\_nonce} to \texttt{temp\_nonce}.+ \item \texttt{temp\_nonce = temp\_nonce + diff}. \texttt{temp\_nonce} is the correct nonce that+ can be used to decrypt the packet.+ \item \texttt{DATA\_NUM\_THRESHOLD} = (1/3 of the maximum number that can be stored in an+ unsigned 2 bit integer)+ \item if decryption succeeds and \texttt{diff > (DATA\_NUM\_THRESHOLD * 2)} then:+ \begin{itemize}+ \item \texttt{saved\_base\_nonce = saved\_base\_nonce + DATA\_NUM\_THRESHOLD}+ \end{itemize}+\end{enumerate}++First it takes the difference between the 2 byte number on the packet and the+last. Because the 3 values are unsigned 16 bit ints and rollover is part of+the math something like diff = (10 - 65536) means diff is equal to 11.++Then it copies the saved base nonce to a temp nonce buffer.++Then it adds diff to the nonce (the nonce is in big endian format).++After if decryption was successful it checks if diff was bigger than 2/3 of the+value that can be contained in a 16 bit unsigned int and increases the saved+base nonce by 1/3 of the maximum value if it succeeded.++This is only one of many ways that the nonce for each encrypted packet can be+calculated.++Encrypted packets that cannot be decrypted are simply dropped.++The reason for exchanging base nonces is because since the key for encrypting+packets is the same for received and sent packets there must be a cryptographic+way to make it impossible for someone to do an attack where they would replay+packets back to the sender and the sender would think that those packets came+from the other peer.++Data in the encrypted packets:++\begin{verbatim}+[our recvbuffers buffer_start, (highest packet number handled + 1), (big endian)]+[uint32_t packet number if lossless, sendbuffer buffer_end if lossy, (big endian)]+[data]+\end{verbatim}++Encrypted packets may be lossy or lossless. Lossy packets are simply encrypted+packets that are sent to the other. If they are lost, arrive in the wrong+order or even if an attacker duplicates them (be sure to take this into account+for anything that uses lossy packets) they will simply be decrypted as they+arrive and passed upwards to what should handle them depending on the data id.++Lossless packets are packets containing data that will be delivered in order by+the implementation of the protocol. In this protocol, the receiver tells the+sender which packet numbers he has received and which he has not and the sender+must resend any packets that are dropped. Any attempt at doubling packets will+cause all (except the first received) to be ignored.++Each lossless packet contains both a 4 byte number indicating the highest+packet number received and processed and a 4 byte packet number which is the+packet number of the data in the packet.++In lossy packets, the layout is the same except that instead of a packet+number, the second 4 byte number represents the packet number of a lossless+packet if one were sent right after. This number is used by the receiver to+know if any packets have been lost. (for example if it receives 4 packets with+numbers (0, 1, 2, 5) and then later a lossy packet with this second number as:+8 it knows that packets: 3, 4, 6, 7 have been lost and will request them)++How the reliability is achieved:++First it is important to say that packet numbers do roll over, the next number+after 0xFFFFFFFF (maximum value in 4 bytes) is 0. Hence, all the mathematical+operations dealing with packet numbers are assumed to be done only on unsigned+32 bit integer unless said otherwise. For example 0 - 0xFFFFFFFF would equal+to 1 because of the rollover.++When sending a lossless packet, the packet is created with its packet number+being the number of the last lossless packet created + 1 (starting at 0). The+packet numbers are used for both reliability and in ordered delivery and so+must be sequential.++The packet is then stored along with its packet number in order for the peer to+be able to send it again if the receiver does not receive it. Packets are only+removed from storage when the receiver confirms they have received them.++The receiver receives packets and stores them along with their packet number.+When a receiver receives a packet he stores the packet along with its packet+number in an array. If there is already a packet with that number in the+buffer, the packet is dropped. If the packet number is smaller than the last+packet number that was processed, the packet is dropped. A processed packet+means it was removed from the buffer and passed upwards to the relevant module.++Assuming a new connection, the sender sends 5 lossless packets to the receiver:+0, 1, 2, 3, 4 are the packet numbers sent and the receiver receives: 3, 2, 0, 2+in that order.++The receiver will save the packets and discards the second packet with the+number 2, he has: 0, 2, 3 in his buffer. He will pass the first packet to the+relevant module and remove it from the array but since packet number 1 is+missing he will stop there. Contents of the buffer are now: 2, 3. The+receiver knows packet number 1 is missing and will request it from the sender+by using a packet request packet:++data ids:++\begin{tabular}{l|l}+ ID & Data \\+ \hline+ 0 & padding (skipped until we hit a non zero (data id) byte) \\+ 1 & packet request packet (lossy packet) \\+ 2 & connection kill packet (lossy packet) \\+ ... & ... \\+ 16+ & reserved for Messenger usage (lossless packets) \\+ 192+ & reserved for Messenger usage (lossy packets) \\+ 255 & reserved for Messenger usage (lossless packet) \\+\end{tabular}++Connection kill packets tell the other that the connection is over.++Packet numbers are the first byte of data in the packet.++packet request packet:++\begin{verbatim}+[uint8_t (1)][uint8_t num][uint8_t num][uint8_t num]...[uint8_t num]+\end{verbatim}++Packet request packets are used by one side of the connection to request+packets from the other. To create a full packet request packet, the one+requesting the packet takes the last packet number that was processed (sent to+the relevant module and removed from the array (0 in the example above)).+Subtract the number of the first missing packet from that number (1 - 0) = 1.+Which means the full packet to request packet number 1 will look like:++\begin{verbatim}+[uint32_t 1]+[uint32_t 0]+[uint8_t 1][uint8_t 1]+\end{verbatim}++If packet number 4 was being requested as well, take the difference between the+packet number and the last packet number being requested (4 - 1) = 3. So the+packet will look like:++\begin{verbatim}+[uint32_t 1]+[uint32_t 0]+[uint8_t 1][uint8_t 1][uint8_t 3]+\end{verbatim}++But what if the number is greater than 255? Let's say the peer needs to request+packets 3, 6, 1024, the packet will look like:++\begin{verbatim}+[uint32_t 1]+[uint32_t 2]+[uint8_t 1][uint8_t 3][uint8_t 3][uint8_t 0][uint8_t 0][uint8_t 0][uint8_t 253]+\end{verbatim}++Each 0 in the packet represents adding 255 until a non 0 byte is reached which+is then added and the resulting requested number is what is left.++This request is designed to be small when requesting packets in real network+conditions where the requested packet numbers will be close to each other.+Putting each requested 4 byte packet number would be very simple but would make+the request packets unnecessarily large which is why the packets look like+this.++When a request packet is received, it will be decoded and all packets in+between the requested packets will be assumed to be successfully received by+the other.++Packet request packets are sent at least every 1 second in toxcore and more+when packets are being received.++The current formula used is (note that this formula is likely sub-optimal):++\begin{verbatim}+REQUEST_PACKETS_COMPARE_CONSTANT = 50.0 double request_packet_interval =+(REQUEST_PACKETS_COMPARE_CONSTANT /+(((double)num_packets_array(&conn->recv_array) + 1.0) / (conn->packet_recv_rate++ 1.0)));+\end{verbatim}++\texttt{num\_packets\_array(&conn->recv\_array)} returns the difference between+the highest packet number received and the last one handled. In the toxcore+code it refers to the total size of the current array (with the holes which are+the placeholders for not yet received packets that are known to be missing).++\texttt{conn->packet\_recv\_rate} is the number of data packets successfully+received per second.++This formula was created with the logic that the higher the 'delay' in packets+(\texttt{num\_packets\_array(&conn->recv\_array)}) vs the speed of packets+received, the more request packets should be sent.++Requested packets are resent every time they can be resent as in they will obey+the congestion control and not bypass it. They are resent once, subsequent+request packets will be used to know if the packet was received or if it should+be resent.++The ping or rtt (round trip time) between two peers can be calculated by saving+the time each packet was sent and taking the difference between the time the+latest packet confirmed received by a request packet was sent and the time the+request packet was received. The rtt can be calculated for every request+packet. The lowest one (for all packets) will be the closest to the real ping.++This ping or rtt can be used to know if a request packet that requests a packet+we just sent should be resent right away or we should wait or not for the next+one (to know if the other side actually had time to receive the packet).++The congestion control algorithm has the goal of guessing how many packets can+be sent through the link every second before none can be sent through anymore.+How it works is basically to send packets faster and faster until none can go+through the link and then stop sending them faster than that.++Currently the congestion control uses the following formula in toxcore however+that is probably not the best way to do it.++The current formula is to take the difference between the current size of the+send queue and the size of the send queue 1.2 seconds ago, take the total+number of packets sent in the last 1.2 seconds and subtract the previous number+from it.++Then divide this number by 1.2 to get a packet speed per second. If this speed+is lower than the minimum send rate of 8 packets per second, set it to 8.++A congestion event can be defined as an event when the number of requested+packets exceeds the number of packets the congestion control says can be sent+during this frame. If a congestion event occurred during the last 2 seconds,+the packet send rate of the connection is set to the send rate previously+calculated, if not it is set to that send rate times 1.25 in order to increase+the speed.++Like I said this isn't perfect and a better solution can likely be found or the+numbers tweaked.++To fix the possible issue where it would be impossible to send very low+bandwidth data like text messages when sending high bandwidth data like files+it is possible to make priority packets ignore the congestion control+completely by placing them into the send packet queue and sending them even if+the congestion control says not to. This is used in toxcore for all non file+transfer packets to prevent file transfers from preventing normal message+packets from being sent.++\chapter{network.txt}++The network module is the lowest file in toxcore that everything else depends+on. This module is basically a UDP socket wrapper, serves as the sorting+ground for packets received by the socket, initializes and uninitializes the+socket. It also contains many socket, networking related and some other+functions like a monotonic time function used by other toxcore modules.++Things of note in this module are the maximum UDP packet size define+(\texttt{MAX\_UDP\_PACKET\_SIZE}) which sets the maximum UDP packet size toxcore+can send and receive. The list of all UDP packet ids: \texttt{NET\_PACKET\_*}.+UDP packet ids are the value of the first byte of each UDP packet and is how+each packet gets sorted to the right module that can handle it.+\texttt{networking\_registerhandler()} is used by higher level modules in order+to tell the network object which packets to send to which module via a+callback.++It also contains datastructures used for ip addresses in toxcore. IP4 and IP6+are the datastructures for ipv4 and ipv6 addresses, IP is the datastructure for+storing either (the family can be set to \texttt{AF\_INET} (ipv4) or+\texttt{AF\_INET6} (ipv6). It can be set to another value like+\texttt{TCP\_ONION\_FAMILY}, \texttt{TCP\_INET}, \texttt{TCP\_INET6} or+\texttt{TCP\_FAMILY} which are invalid values in the network modules but valid+values in some other module and denote a special type of ip) and+\texttt{IP\_Port} stores an IP datastructure with a port.++Since the network module interacts directly with the underlying operating+system with its socket functions it has code to make it work on windows, linux,+etc... unlike most modules that sit at a higher level.++The network module currently uses the polling method to read from the UDP+socket. The \texttt{networking\_poll()} function is called to read all the+packets from the socket and pass them to the callbacks set using the+\texttt{networking\_registerhandler()} function. The reason it uses polling is+simply because it was easier to write it that way, another method would be+better here.++The goal of this module is to provide an easy interface to a UDP socket and+other networking related functions.++\chapter{Onion}++The goal of the onion module in Tox is to prevent peers that are not friends+from finding out the temporary DHT public key from a known long term public key+of the peer and to prevent peers from discovering the long term public key of+peers when only the temporary DHT key is known.++It makes sure only friends of a peer can find it and connect to it and+indirectly makes sure non friends cannot find the ip address of the peer when+knowing the Tox address of the friend.++The only way to prevent peers in the network from associating the temporary DHT+public key with the long term public key is to not broadcast the long term key+and only give others in the network that are not friends the DHT public key.++The onion lets peers send their friends, whose real public key they know as it+is part of the Tox ID, their DHT public key so that the friends can then find+and connect to them without other peers being able to identify the real public+keys of peers.++So how does the onion work?++The onion works by enabling peers to announce their real public key to peers by+going through the onion path. It is like a DHT but through onion paths. In+fact it uses the DHT in order for peers to be able to find the peers with ids+closest to their public key by going through onion paths.++In order to announce its real public key anonymously to the Tox network while+using the onion, a peer first picks 3 random nodes that it knows (they can be+from anywhere: the DHT, connected TCP relays or nodes found while finding peers+with the onion). The nodes should be picked in a way that makes them unlikely+to be operated by the same person perhaps by looking at the ip addresses and+looking if they are in the same subnet or other ways. More research is needed+to make sure nodes are picked in the safest way possible.++The reason for 3 nodes is that 3 hops is what they use in Tor and other+anonymous onion based networks.++These nodes are referred to as nodes A, B and C. Note that if a peer cannot+communicate via UDP, its first peer will be one of the TCP relays it is+connected to, which will be used to send its onion packet to the network.++TCP relays can only be node A or the first peer in the chain as the TCP relay+is essentially acting as a gateway to the network. The data sent to the TCP+Client module to be sent as a TCP onion packet by the module is different from+the one sent directly via UDP. This is because it doesn't need to be encrypted+(the connection to the TCP relay server is already encrypted).++First I will explain how communicating via onion packets work.++Note: nonce is a 24 byte nonce. The nested nonces are all the same as the+outer nonce.++Onion packet (request):++Initial (TCP) data sent as the data of an onion packet through the TCP client+module:++\begin{itemize}+ \item \texttt{IP\_Port} of node B+ \item A random public key PK1+ \item Encrypted with the secret key SK1 and the public key of Node B and the nonce:+ \begin{itemize}+ \item \texttt{IP\_Port} of node C+ \item A random public key PK2+ \item Encrypted with the secret key SK2 and the public key of Node C and the nonce:+ \begin{itemize}+ \item \texttt{IP\_Port} of node D+ \item Data to send to Node D+ \end{itemize}+ \end{itemize}+\end{itemize}++Initial (UDP) (sent from us to node A):++\begin{itemize}+ \item \texttt{uint8\_t} (0x80) packet id+ \item Nonce+ \item Our temporary DHT public key+ \item Encrypted with our temporary DHT secret key and the public key of Node A and+ the nonce:+ \begin{itemize}+ \item \texttt{IP\_Port} of node B+ \item A random public key PK1+ \item Encrypted with the secret key SK1 and the public key of Node B and the nonce:+ \begin{itemize}+ \item \texttt{IP\_Port} of node C+ \item A random public key PK2+ \item Encrypted with the secret key SK2 and the public key of Node C and the+ nonce:+ \begin{itemize}+ \item \texttt{IP\_Port} of node D+ \item Data to send to Node D+ \end{itemize}+ \end{itemize}+ \end{itemize}+\end{itemize}++(sent from node A to node B):++\begin{itemize}+ \item \texttt{uint8\_t} (0x81) packet id+ \item Nonce+ \item A random public key PK1+ \item Encrypted with the secret key SK1 and the public key of Node B and the nonce:+ \begin{itemize}+ \item \texttt{IP\_Port} of node C+ \item A random public key PK2+ \item Encrypted with the secret key SK2 and the public key of Node C and the nonce:+ \begin{itemize}+ \item \texttt{IP\_Port} of node D+ \item Data to send to Node D+ \end{itemize}+ \end{itemize}+ \item Nonce+ \item Encrypted with temporary symmetric key of Node A and the nonce:+ \begin{itemize}+ \item \texttt{IP\_Port} (of us)+ \end{itemize}+\end{itemize}++(sent from node B to node C):++\begin{itemize}+ \item \texttt{uint8\_t} (0x82) packet id+ \item Nonce+ \item A random public key PK1+ \item Encrypted with the secret key SK1 and the public key of Node C and the nonce:+ \begin{itemize}+ \item \texttt{IP\_Port} of node D+ \item Data to send to Node D+ \end{itemize}+ \item Nonce+ \item Encrypted with temporary symmetric key of Node B and the nonce:+ \begin{itemize}+ \item \texttt{IP\_Port} (of Node A)+ \item Nonce+ \item Encrypted with temporary symmetric key of Node A and the nonce:+ \begin{itemize}+ \item \texttt{IP\_Port} (of us)+ \end{itemize}+ \end{itemize}+\end{itemize}++(sent from node C to node D):++\begin{itemize}+ \item Data to send to Node D+ \item Nonce+ \item Encrypted with temporary symmetric key of Node C and the nonce:+ \begin{itemize}+ \item \texttt{IP\_Port} (of Node B)+ \item Nonce+ \item Encrypted with temporary symmetric key of Node B and the nonce:+ \begin{itemize}+ \item \texttt{IP\_Port} (of Node A)+ \item Nonce+ \item Encrypted with temporary symmetric key of Node A and the nonce:+ \begin{itemize}+ \item \texttt{IP\_Port} (of us)+ \end{itemize}+ \end{itemize}+ \end{itemize}+\end{itemize}++Onion packet (response):++initial (sent from node D to node C):++\begin{itemize}+ \item \texttt{uint8\_t} (0x8c) packet id+ \item Nonce+ \item Encrypted with the temporary symmetric key of Node C and the nonce:+ \begin{itemize}+ \item \texttt{IP\_Port} (of Node B)+ \item Nonce+ \item Encrypted with the temporary symmetric key of Node B and the nonce:+ \begin{itemize}+ \item \texttt{IP\_Port} (of Node A)+ \item Nonce+ \item Encrypted with the temporary symmetric key of Node A and the nonce:+ \begin{itemize}+ \item \texttt{IP\_Port} (of us)+ \end{itemize}+ \end{itemize}+ \end{itemize}+ \item Data to send back+\end{itemize}++(sent from node C to node B):++\begin{itemize}+ \item \texttt{uint8\_t} (0x8d) packet id+ \item Nonce+ \item Encrypted with the temporary symmetric key of Node B and the nonce:+ \begin{itemize}+ \item \texttt{IP\_Port} (of Node A)+ \item Nonce+ \item Encrypted with the temporary symmetric key of Node A and the nonce:+ \begin{itemize}+ \item \texttt{IP\_Port} (of us)+ \end{itemize}+ \end{itemize}+ \item Data to send back+\end{itemize}++(sent from node B to node A):++\begin{itemize}+ \item \texttt{uint8\_t} (0x8e) packet id+ \item Nonce+ \item Encrypted with the temporary symmetric key of Node A and the nonce:+ \begin{itemize}+ \item \texttt{IP\_Port} (of us)+ \end{itemize}+ \item Data to send back+\end{itemize}++(sent from node A to us):++\begin{itemize}+ \item Data to send back+\end{itemize}++Each packet is encrypted multiple times so that only node A will be able to+receive and decrypt the first packet and know where to send it to, node B will+only be able to receive that decrypted packet, decrypt it again and know where+to send it and so on. You will also notice a piece of encrypted data (the+sendback) at the end of the packet that grows larger and larger at every layer+with the IP of the previous node in it. This is how the node receiving the end+data (Node D) will be able to send data back.++When a peer receives an onion packet, they will decrypt it, encrypt the+coordinates (IP/port) of the source along with the already existing encrypted+data (if it exists) with a symmetric key known only by the peer and only+refreshed every hour (in toxcore) as a security measure to force expire paths.++Here's a diagram how it works:++\begin{verbatim}+peer+ -> [onion1[onion2[onion3[data]]]] -> Node A+ -> [onion2[onion3[data]]][sendbackA] -> Node B+ -> [onion3[data]][sendbackB[sendbackA]] -> Node C+ -> [data][SendbackC[sendbackB[sendbackA]]]-> Node D (end)+\end{verbatim}++\begin{verbatim}+Node D+ -> [SendbackC[sendbackB[sendbackA]]][response] -> Node C+ -> [sendbackB[sendbackA]][response] -> Node B+ -> [sendbackA][response] -> Node A+ -> [response] -> peer+\end{verbatim}++The random public keys in the onion packets are temporary public keys generated+for and used for that onion path only. This is done in order to make it+difficult for others to link different paths together. Each encrypted layer+must have a different public key. This is the reason why there are multiple+keys in the packet definintions above.++The nonce is used to encrypt all the layers of encryption. This 24 byte nonce+should be randomly generated. If it isn't randomly generated and has a+relation to nonces used for other paths it could be possible to tie different+onion paths together.++The \texttt{IP\_Port} is an ip and port in packed format:++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{TOX\_AF\_INET} (2) for IPv4 or \texttt{TOX\_AF\_INET6} (10) for IPv6 \\+ \texttt{4 $|$ 16} & IP address (4 bytes if IPv4, 16 if IPv6) \\+ \texttt{12 $|$ 0} & Zeroes \\+ \texttt{2} & \texttt{uint16\_t} Port \\+\end{tabular}++If IPv4 the format is padded with 12 bytes of zeroes so that both IPv4 and IPv6+have the same stored size.++The \texttt{IP\_Port} will always end up being of size 19 bytes. This is to+make it hard to know if an ipv4 or ipv6 ip is in the packet just by looking at+the size. The 12 bytes of zeros when ipv4 must be set to 0 and not left+uninitialized as some info may be leaked this way if it stays uninitialized.+All numbers here are in big endian format.++The \texttt{IP\_Port} in the sendback data can be in any format as long as the+length is 19 bytes because only the one who writes it can decrypt it and read+it, however, using the previous format is recommended because of code reuse.+The nonce in the sendback data must be a 24 byte nonce.++Each onion layers has a different packed id that identifies it so that an+implementation knows exactly how to handle them. Note that any data being sent+back must be encrypted, appear random and not leak information in any way as+all the nodes in the path will see it.++If anything is wrong with the received onion packets (decryption fails) the+implementation should drop them.++The implementation should have code for each different type of packet that+handles it, adds (or decrypts) a sendback and sends it to the next peer in the+path. There are a lot of packets but an implementation should be very+straightforward.++Note that if the first node in the path is a TCP relay, the TCP relay must put+an identifier (instead of an IP/Port) in the sendback so that it knows that any+response should be sent to the appropriate peer connected to the TCP relay.++This explained how to create onion packets and how they are sent back. Next is+what is actually sent and received on top of these onion packets or paths.++Note: nonce is a 24 byte nonce.++announce request packet:++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x83) \\+ \texttt{24} & Nonce \\+ \texttt{32} & A public key (real or temporary) \\+ \texttt{?} & Payload \\+\end{tabular}++The public key is our real long term public key if we want to announce+ourselves, a temporary one if we are searching for friends.++The payload is encrypted with the secret key part of the sent public key, the+public key of Node D and the nonce, and contains:++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{32} & Ping ID \\+ \texttt{32} & Public key we are searching for \\+ \texttt{32} & Public key that we want those sending back data packets to use \\+ \texttt{8} & Data to send back in response \\+\end{tabular}++If the ping id is zero, respond with an announce response packet.++If the ping id matches the one the node sent in the announce response and the+public key matches the one being searched for, add the part used to send data+to our list. If the list is full make it replace the furthest entry.++data to route request packet:++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x85) \\+ \texttt{32} & Public key of destination node \\+ \texttt{24} & Nonce \\+ \texttt{32} & Temporary just generated public key \\+ variable & Payload \\+\end{tabular}++The payload is encrypted with that temporary secret key and the nonce and the+public key from the announce response packet of the destination node. If Node+D contains the ret data for the node, it sends the stuff in this packet as a+data to route response packet to the right node.++The data in the previous packet is in format:++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{32} & Real public key of sender \\+ variable & Payload \\+\end{tabular}++The payload is encrypted with real secret key of the sender, the nonce in the+data packet and the real public key of the receiver:++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} id \\+ variable & Data (optional) \\+\end{tabular}++Data sent to us:++announce response packet:++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x84) \\+ \texttt{8} & Data to send back in response \\+ \texttt{24} & Nonce \\+ variable & Payload \\+\end{tabular}++The payload is encrypted with the DHT secret key of Node D, the public key in+the request and the nonce:++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} \texttt{is\_stored} \\+ \texttt{32} & Ping ID or Public Key \\+ variable & Maximum of 4 nodes in packed node format (see DHT) \\+\end{tabular}++The packet contains a ping ID if \texttt{is\_stored} is 0 or 2, or the public+key that must be used to send data packets if \texttt{is\_stored} is 1.++If the \texttt{is\_stored} is not 0, it means the information to reach the+public key we are searching for is stored on this node. \texttt{is\_stored} is+2 as a response to a peer trying to announce himself to tell the peer that he+is currently announced successfully.++data to route response packet:++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x86) \\+ \texttt{24} & Nonce \\+ \texttt{32} & Temporary just generated public key \\+ variable & Payload \\+\end{tabular}++The payload is encrypted with that temporary secret key, the nonce and the+public key from the announce response packet of the destination node.++There are 2 types of request packets and 2 'response' packets to go with them.+The announce request is used to announce ourselves to a node and announce+response packet is used by the node to respond to this packet. The data to+route request packet is a packet used to send packets through the node to+another peer that has announced itself and that we have found. The data to+route response packet is what the node transforms this packet into.++To announce ourselves to the network we must first find, using announce+packets, the peers with the DHT public key closest to our real public key. We+must then announce ourselves to these peers. Friends will then be able to send+messages to us using data to route packets by sending them to these peers. To+find the peers we have announced ourselves to, our friends will find the peers+closest to our real public key and ask them if they know us. They will then be+able to use the peers that know us to send us some messages that will contain+their DHT public key (which we need to know to connect directly to them), TCP+relays that they are connected to (so we can connect to them with these relays+if we need to) and some DHT peers they are connected to (so we can find them+faster in the DHT).++Announce request packets are the same packets used slightly differently if we+are announcing ourselves or searching for peers that know one of our friends.++If we are announcing ourselves we must put our real long term public key in the+packet and encrypt it with our long term private key. This is so the peer we+are announcing ourselves to can be sure that we actually own that public key.+If we are looking for peers we use a temporary public key used only for packets+looking for that peer in order to leak as little information as possible. The+\texttt{ping\_id} is a 32 byte number which is sent to us in the announce+response and we must send back to the peer in another announce request. This+is done in order to prevent people from easily announcing themselves many times+as they have to prove they can respond to packets from the peer before the peer+will let them announce themselves. This \texttt{ping\_id} is set to 0 when none+is known.++The public key we are searching for is set to our long term public key when+announcing ourselves and set to the long term public key of the friend we are+searching for if we are looking for peers.++When announcing ourselves, the public key we want others to use to send us data+back is set to a temporary public key and we use the private key part of this+key to decrypt packet routing data sent to us. This public key is to prevent+peers from saving old data to route packets from previous sessions and be able+to replay them in future Tox sessions. This key is set to zero when searching+for peers.++The sendback data is an 8 byte number that will be sent back in the announce+packet response. Its goal is to be used to learn which announce request packet+the response is responding to, and hence its location in the unencrypted part+of the response. This is needed in toxcore to find and check info about the+packet in order to decrypt it and handle it correctly. Toxcore uses it as an+index to its special \texttt{ping\_array}.++Why don't we use different packets instead of having one announce packet+request and one response that does everything? It makes it a lot more difficult+for possible attackers to know if we are merely announcing ourselves or if we+are looking for friends as the packets for both look the same and are the same+size.++The unencrypted part of an announce response packet contains the sendback data,+which was sent in the request this packet is responding to and a 24 byte random+nonce used to encrypt the encrypted part.++The \texttt{is\_stored} number is set to either 0, 1 or 2. 0 means that the+public key that was being searched in the request isn't stored or known by this+peer. 1 means that it is and 2 means that we are announced successfully at+that node. Both 1 and 2 are needed so that when clients are restarted it is+possible to reannounce without waiting for the timeout of the previous+announce. This would not otherwise be possible as a client would receive+response 1 without a \texttt{ping\_id} which is needed in order to reannounce+successfully.++When the \texttt{is\_stored} number is 0 or 2, the next 32 bytes is a+\texttt{ping\_id}. When \texttt{is\_stored} is 1 it corresponds to a public key+(the send back data public key set by the friend in their announce request)+that must be used to encrypt and send data to the friend.++Then there is an optional maximum 4 nodes, in DHT packed nodes format (see+DHT), attached to the response which denote the 4 DHT peers with the DHT public+keys closest to the searched public key in the announce request known by the+peer (see DHT). To find these peers, toxcore uses the same function as is used+to find peers for get node DHT responses. Peers wanting to announce themselves+or searching for peers that 'know' their friends will recursively query closer+and closer peers until they find the closest possible and then either announce+themselves to them or just ping them every once in a while to know if their+friend can be contacted. Note that the distance function used for this is the+same as the Tox DHT.++Data to route request packets are packets used to send data directly to another+peer via a node that knows that peer. The public key is the public key of the+final destination where we want the packet to be sent (the real public key of+our friend). The nonce is a 24 byte random nonce and the public key is a+random temporary public key used to encrypt the data in the packet and, if+possible, only to send packets to this friend (we want to leak as little info+to the network as possible so we use temp public keys as we don't want a peer+to see the same public keys and be able to link things together). The data is+encrypted data that we want to send to the peer with the public key.++The route response packets are just the last elements (nonce, public key,+encrypted data) of the data to route request packet copied into a new packet+and sent to the appropriate destination.++To handle onion announce packets, toxcore first receives an announce packet and+decrypts it.++Toxcore generates \texttt{ping\_id}s by taking a 32 byte sha hash of the current+time, some secret bytes generated when the instance is created, the current+time divided by a 300 second timeout, the public key of the requester and the+source ip/port that the packet was received from. Since the ip/port that the+packet was received from is in the \texttt{ping\_id}, the announce packets being+sent with a ping id must be sent using the same path as the packet that we+received the \texttt{ping\_id} from or announcing will fail.++The reason for this 300 second timeout in toxcore is that it gives a reasonable+time (300 to 600 seconds) for peers to announce themselves.++Toxcore generates 2 different ping ids, the first is generated with the current+time (divided by 300) and the second with the current time + 300 (divided by 300).+The two ping ids are then compared to the ping ids in the received packets.+The reason for doing this is that storing every ping id received might be+expensive and leave us vulnerable to a DoS attack, this method makes sure that+the other cannot generate \texttt{ping\_id}s and must ask for them. The reason+for the 2 \texttt{ping\_id}s is that we want to make sure that the timeout is at+least 300 seconds and cannot be 0.++If one of the two ping ids is equal to the ping id in the announce request,+the sendback data public key and the sendback data are stored in the+datastructure used to store announced peers. If the implementation has a+limit to how many announced entries it can store, it should only store the+entries closest (determined by the DHT distance function) to its DHT public+key. If the entry is already there, the information will simply be updated+with the new one and the timeout will be reset for that entry.++Toxcore has a timeout of 300 seconds for announce entries after which they are+removed which is long enough to make sure the entries don't expire prematurely+but not long enough for peers to stay announced for extended amounts of time+after they go offline.++Toxcore will then copy the 4 DHT nodes closest to the public key being searched+to a new packet (the response).++Toxcore will look if the public key being searched is in the datastructure. If+it isn't it will copy the second generated \texttt{ping\_id} (the one generated+with the current time plus 300 seconds) to the response, set the+\texttt{is\_stored} number to 0 and send the packet back.++If the public key is in the datastructure, it will check whether the public key+that was used to encrypt the announce packet is equal to the announced public+key, if it isn't then it means that the peer is searching for a peer and that+we know it. This means the \texttt{is\_stored} is set to 1 and the sending back+data public key in the announce entry is copied to the packet.++If it (key used to encrypt the announce packet) is equal (to the announced+public key which is also the 'public key we are searching for' in the announce+packet) meaning the peer is announcing itself and an entry for it exists, the+sending back data public key is checked to see if it equals the one in the+packet. If it is not equal it means that it is outdated, probably because the+announcing peer's toxcore instance was restarted and so their+\texttt{is\_stored} is set to 0, if it is equal it means the peer is announced+correctly so the \texttt{is\_stored} is set to 2. The second generated+\texttt{ping\_id} is then copied to the packet.++Once the packet is contructed a random 24 byte nonce is generated, the packet+is encrypted (the shared key used to decrypt the request can be saved and used+to encrypt the response to save an expensive key derivation operation), the+data to send back is copied to the unencrypted part and the packet is sent back+as an onion response packet.++In order to announce itself using onion announce packets toxcore first takes+DHT peers, picks random ones and builds onion paths with them by saving 3+nodes, calling it a path, generating some keypairs for encrypting the onion+packets and using them to send onion packets. If the peer is only connected+with TCP, the initial nodes will be bootstrap nodes and connected TCP relays+(for the first peer in the path). Once the peer is connected to the onion he+can fill up his list of known peers with peers sent in announce responses if+needed.++Onion paths have different timeouts depending on whether the path is confirmed+or unconfirmed. Unconfirmed paths (paths that core has never received any+responses from) have a timeout of 4 seconds with 2 tries before they are deemed+non working. This is because, due to network conditions, there may be a large+number of newly created paths that do not work and so trying them a lot would+make finding a working path take much longer. The timeout for a confirmed path+(from which a response was received) is 10 seconds with 4 tries without a+response. A confirmed path has a maximum lifetime of 1200 seconds to make+possible deanonimization attacks more difficult.++Toxcore saves a maximum of 12 paths: 6 paths are reserved for announcing+ourselves and 6 others are used to search for friends. This may not be the+safest way (some nodes may be able to associate friends together) however it is+much more performant than having different paths for each friend. The main+benefit is that the announcing and searching are done with different paths,+which makes it difficult to know that peer with real public key X is friends+with Y and Z. More research is needed to find the best way to do this. At+first toxcore did have different paths for each friend, however, that meant+that each friend path was almost never used (and checked). When using a low+amount of paths for searching there is less resources needed to find good+paths. 6 paths are used because 4 was too low and caused some performance+issues because it took longer to find some good paths at the beginning because+only 4 could be tried at a time. A too high number meanwhile would mean each+path is used (and tested) less. The reason why the numbers are the same for+both types of paths is for code simplification purposes.++To search/announce itself to peers, toxcore keeps the 8 closest peers (12 for+announcing) to each key it is searching (or announcing itself to). To+populate these it starts by sending announce requests to random peers for all+the public keys it is searching for. It then recursively searches closer and+closer peers (DHT distance function) until it no longer finds any. It is+important to make sure it is not too aggressive at searching the peers as some+might no longer be online but peers might still send announce responses with+their information. Toxcore keeps lists of last pinged nodes for each key+searched so as not to ping dead nodes too aggressively.++Toxcore decides if it will send an announce packet to one of the 4 peers in the+announce response by checking if the peer would be stored as one of the stored+closest peers if it responded; if it would not be it doesn't send an announce+request, if it would be it sends one.++Peers are only put in the closest peers array if they respond to an announce+request. If the peers fail to respond to 3 announce requests they are deemed+timed out and removed. When sending an announce request to a peer to which we+have been announcing ourselves for at least 90 seconds and which has failed to+respond to the previous 2 requests, toxcore uses a random path for the request.+This reduces the chances that a good node will be removed due to bad paths.++The reason for the numbers of peers being 8 and 12 is that lower numbers might+make searching for and announcing too unreliable and a higher number too+bandwidth/resource intensive.++Toxcore uses \texttt{ping\_array} (see \texttt{ping\_array}) for the 8 byte+sendback data in announce packets to store information that it will need to+handle the response (key to decrypt it, why was it sent? (to announce ourselves+or to search? For what key? and some other info)). For security purposes it+checks to make sure the packet was received from the right ip/port and checks+if the key in the unencrypted part of the packet is the right public key.++For peers we are announcing ourselves to, if we are not announced to them+toxcore tries every 3 seconds to announce ourselves to them until they return+that we have announced ourselves to them, then initially toxcore sends an+announce request packet every 15 seconds to see if we are still announced and+reannounce ourselves at the same time. Toxcore sends every announce packet+with the \texttt{ping\_id} previously received from that peer with the same+path (if possible). Toxcore use a timeout of 120 seconds rather than 15+seconds if we have been announcing to the peer for at least 90 seconds, and+the onion path we are are using for the peer has also been alive for at least+90 seconds, and we have not been waiting for at least 15 seconds for a+response to a request sent to the peer, nor for at least 10 seconds for a+response to a request sent via the path. The timeout of at most 120 seconds+means a \texttt{ping\_id} received in the last packet will not have had time+to expire (300 second minimum timeout) before it is resent 120 seconds later.++For friends this is slightly different. It is important to start searching for+friends after we are fully announced. Assuming a perfect network, we would+only need to do a search for friend public keys only when first starting the+instance (or going offline and back online) as peers starting up after us would+be able to find us immediately just by searching for us. If we start searching+for friends after we are announced we prevent a scenario where 2 friends start+their clients at the same time but are unable to find each other right away+because they start searching for each other while they have not announced+themselves.++For this reason, after the peer is announced successfully, for 17 seconds+announce packets are sent aggressively every 3 seconds to each known close peer+(in the list of 8 peers) to search aggressively for peers that know the peer we+are searching for.++After this, toxcore sends requests once per 15 seconds initially, then+uses linear backoff to increase the interval. In detail, the interval used+when searching for a given friend is at least 15 and at most 2400 seconds, and+within these bounds is calculated as one quarter of the time since we began+searching for the friend, or since the friend was last seen. For this purpose,+a friend is considered to be seen when some peer reports that the friend is+announced, or we receive a DHT Public Key packet from the friend, or we obtain+a new DHT key for them from a group, or a friend connection for the friend+goes offline.++There are other ways this could be done and which would still work but, if+making your own implementation, keep in mind that these are likely not the most+optimized way to do things.++If we find peers (more than 1) that know a friend we will send them an onion+data packet with our DHT public key, up to 2 TCP relays we are connected to and+2 DHT peers close to us to help the friend connect back to us.++Onion data packets are packets sent as the data of data to route packets.++Onion data packets:++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{32} & Long term public key of sender \\+ variable & Payload \\+\end{tabular}++The payload is encrypted with long term private key of the sender, the long+term public key of the receiver and the nonce used in the data to route request+packet used to send this onion data packet (shaves off 24 bytes).++DHT public key packet:++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x9c) \\+ \texttt{8} & \texttt{uint64\_t} \texttt{no\_replay} \\+ \texttt{32} & Our DHT public key \\+ \texttt{[39, 204]} & Maximum of 4 nodes in packed format \\+\end{tabular}++The packet will only be accepted if the \texttt{no\_replay} number is greater+than the \texttt{no\_replay} number in the last packet received.++The nodes sent in the packet comprise 2 TCP relays to which we are+connected (or fewer if there are not 2 available) and a number of DHT nodes+from our Close List, with the total number of nodes sent being at most 4. The+nodes chosen from the Close List are those closest in DHT distance to us. This +allows the friend to find us more easily in the DHT, or to connect to us via a +TCP relay.++Why another round of encryption? We have to prove to the receiver that we own+the long term public key we say we own when sending them our DHT public key.+Friend requests are also sent using onion data packets but their exact format+is explained in Messenger.++The \texttt{no\_replay} number is protection if someone tries to replay an older+packet and should be set to an always increasing number. It is 8 bytes so you+should set a high resolution monotonic time as the value.++We send this packet every 30 seconds if there is more than one peer (in the 8)+that says they our friend is announced on them. This packet can also be sent+through the DHT module as a DHT request packet (see DHT) if we know the DHT+public key of the friend and are looking for them in the DHT but have not+connected to them yet. 30 second is a reasonable timeout to not flood the+network with too many packets while making sure the other will eventually+receive the packet. Since packets are sent through every peer that knows the+friend, resending it right away without waiting has a high likelihood of+failure as the chances of packet loss happening to all (up to to 8) packets+sent is low.++When sent as a DHT request packet (this is the data sent in the DHT request+packet):++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0x9c) \\+ \texttt{32} & Long term public key of sender \\+ \texttt{24} & Nonce \\+ variable & Encrypted payload \\+\end{tabular}++The payload is encrypted with long term private key of sender, the long term+public key of receiver and the nonce, and contains the DHT public key packet.++When sent as a DHT request packet the DHT public key packet is (before being+sent as the data of a DHT request packet) encrypted with the long term keys of+both the sender and receiver and put in that format. This is done for the same+reason as the double encryption of the onion data packet.++Toxcore tries to resend this packet through the DHT every 20 seconds. 20+seconds is a reasonable resend rate which isn't too aggressive.++Toxcore has a DHT request packet handler that passes received DHT public key+packets from the DHT module to this module.++If we receive a DHT public key packet, we will first check if the DHT packet is+from a friend, if it is not from a friend, it will be discarded. The+\texttt{no\_replay} will then be checked to see if it is good and no packet with+a lower one was received during the session. The DHT key, the TCP nodes in the+packed nodes and the DHT nodes in the packed nodes will be passed to their+relevant modules. The fact that we have the DHT public key of a friend means+this module has achieved its goal.++If a friend is online and connected to us, the onion will stop all of its+actions for that friend. If the peer goes offline it will restart searching+for the friend as if toxcore was just started.++If toxcore goes offline (no onion traffic for 75 seconds) toxcore will+aggressively reannounce itself and search for friends as if it was just+started.++\chapter{Ping array}++Ping array is an array used in toxcore to store data for pings. It enables the+storage of arbitrary data that can then be retrieved later by passing the 8+byte ping id that was returned when the data was stored. It also frees data+from pings that are older than a ping expiring delay set when initializing the+array.++Ping arrays are initialized with a size and a timeout parameter. The size+parameter denotes the maximum number of entries in the array and the timeout+denotes the number of seconds to keep an entry in the array. Timeout and size+must be bigger than 0.++Adding an entry to the ping array will make it return an 8 byte number that can+be used as the ping number of a ping packet. This number is generated by first+generating a random 8 byte number (toxcore uses the cryptographic secure random+number generator), dividing then multiplying it by the total size of the array+and then adding the index of the element that was added. This generates a+random looking number that will return the index of the element that was added+to the array. This number is also stored along with the added data and the+current time (to check for timeouts). Data is added to the array in a cyclical+manner (0, 1, 2, 3... (array size - 1), 0, 1, ...). If the array is full, the+oldest element is overwritten.++To get data from the ping array, the ping number is passed to the function to+get the data from the array. The modulo of the ping number with the total size+of the array will return the index at which the data is. If there is no data+stored at this index, the function returns an error. The ping number is then+checked against the ping number stored for this element, if it is not equal the+function returns an error. If the array element has timed out, the function+returns an error. If all the checks succeed the function returns the exact+data that was stored and it is removed from the array.++Ping array is used in many places in toxcore to efficiently keep track of sent+packets.++\chapter{State Format}++The reference Tox implementation uses a custom binary format to save the state+of a Tox client between restarts. This format is far from perfect and will be+replaced eventually. For the sake of maintaining compatibility down the road,+it is documented here.++The binary encoding of all integer types in the state format is a fixed-width+byte sequence with the integer encoded in Little Endian unless stated otherwise.++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{4} & Zeroes \\+ \texttt{4} & \texttt{uint32\_t} (0x15ED1B1F) \\+ \texttt{?} & List of sections \\+\end{tabular}++\section{Sections}++The core of the state format consists of a list of sections. Every section has+its type and length specified at the beginning. In some cases, a section only+contains one item and thus takes up the entire length of the section. This is+denoted with '?'.++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{4} & \texttt{uint32\_t} Length of this section \\+ \texttt{2} & \texttt{uint16\_t} Section type \\+ \texttt{2} & \texttt{uint16\_t} (0x01CE) \\+ \texttt{?} & Section \\+\end{tabular}++Section types:++\begin{tabular}{l|l}+ Name & Value \\+ \hline+ NospamKeys & 0x01 \\+ DHT & 0x02 \\+ Friends & 0x03 \\+ Name & 0x04 \\+ StatusMessage & 0x05 \\+ Status & 0x06 \\+ TcpRelays & 0x0A \\+ PathNodes & 0x0B \\+ EOF & 0xFF \\+\end{tabular}++Not every section listed above is required to be present in order to restore+from a state file. Only NospamKeys is required.++\subsection{Nospam and Keys (0x01)}++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{4} & \texttt{uint32\_t} Nospam \\+ \texttt{32} & Long term public key \\+ \texttt{32} & Long term secret key \\+\end{tabular}++\subsection{DHT (0x02)}++This section contains a list of DHT-related sections.++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{4} & \texttt{uint32\_t} (0x159000D) \\+ \texttt{?} & List of DHT sections \\+\end{tabular}++\subsubsection{DHT Sections}++Every DHT section has the following structure:++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{4} & \texttt{uint32\_t} Length of this section \\+ \texttt{2} & \texttt{uint16\_t} DHT section type \\+ \texttt{2} & \texttt{uint16\_t} (0x11CE) \\+ \texttt{?} & DHT section \\+\end{tabular}++DHT section types:++\begin{tabular}{l|l}+ Name & Value \\+ \hline+ Nodes & 0x04 \\+\end{tabular}++\paragraph{Nodes (0x04)}++This section contains a list of nodes. These nodes are used to quickly reconnect+to the DHT after a Tox client is restarted.++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{?} & List of nodes \\+\end{tabular}++The structure of a node is the same as \texttt{Node Info}. Note: this means+that the integers stored in these nodes are stored in Big Endian as well.++\subsection{Friends (0x03)}++This section contains a list of friends. A friend can either be a peer we've+sent a friend request to or a peer we've accepted a friend request from.++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{?} & List of friends \\+\end{tabular}++Friend:++Some of the integers in this structure are stored in Big Endian. This is+denoted with "(BE)".++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} Status \\+ \texttt{32} & Long term public key \\+ \texttt{1024} & Friend request message as a byte string \\+ \texttt{1} & PADDING \\+ \texttt{2} & \texttt{uint16\_t} Size of the friend request message (BE) \\+ \texttt{128} & Name as a byte string \\+ \texttt{2} & \texttt{uint16\_t} Size of the name (BE) \\+ \texttt{1007} & Status message as a byte string \\+ \texttt{1} & PADDING \\+ \texttt{2} & \texttt{uint16\_t} Size of the status message (BE) \\+ \texttt{1} & \texttt{uint8\_t} User status (see also: \texttt{USERSTATUS}) \\+ \texttt{3} & PADDING \\+ \texttt{4} & \texttt{uint32\_t} Nospam (only used for sending a friend request) \\+ \texttt{8} & \texttt{uint64\_t} Last seen time \\+\end{tabular}++Status can be one of:++\begin{tabular}{l|l}+ Status & Meaning \\+ \hline+ 0 & Not a friend \\+ 1 & Friend added \\+ 2 & Friend request sent \\+ 3 & Confirmed friend \\+ 4 & Friend online \\+\end{tabular}++\subsection{Name (0x04)}++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{?} & Name as a UTF-8 encoded string \\+\end{tabular}++\subsection{Status Message (0x05)}++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{?} & Status message as a UTF-8 encoded string \\+\end{tabular}++\subsection{Status (0x06)}++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} User status (see also: \texttt{USERSTATUS}) \\ \end{tabular} \subsection{Tcp Relays (0x0A)}
+ src/tox/Network/Tox/Application/GroupChats.lhs view
@@ -0,0 +1,388 @@+\chapter{DHT Group Chats}++This document details the groupchat implementation, giving a high level overview+of all the important features and aspects, as well as some important low level+implementation details. This documentation reflects what is currently+implemented at the time of writing; it is not speculative. For detailed API docs+see the groupchats section of the tox.h header file.++\section{Features}++\begin{itemize}+ \item Private messages+ \item Action messages (/me)+ \item Public groups (peers may join via a public key)+ \item Private groups (peers require a friend invite)+ \item Permanence (a group cannot 'die' as long as at least one peer retains+ their group credentials)+ \item Persistence across client restarts+ \item Ability to set peer limits+ \item Moderation (kicking, banning, silencing)+ \item Permanent group names (set on creation)+ \item Topics (may only be set by moderators and the founder)+ \item Password protection+ \item Self-repairing (auto-rejoin on disconnect, group split protection, state+ syncing)+ \item Identity separation from the Tox ID+ \item Ability to ignore peers+ \item Unique nicknames which can be set on a per-group basis+ \item Peer statuses (online, away, busy) which can be set on a per-group basis+ \item Custom parting/exit messages+\end{itemize}++\section{Group roles}++There are four distinct roles which are hierarchical in nature (higher roles+have all the privileges of lower roles).++\begin{itemize}+ \item \textbf{Founder} - The group's creator. May set all other peers roles+ to anything except founder. May also set the group password, toggle the+ privacy state, and set the peer limit.+ \item \textbf{Moderator} - Promoted by the founder. May kick, ban and set+ the user and observer roles for peers below this role. May also set the+ topic.+ \item \textbf{User} - Default non-founder role. May communicate with other+ peers normally.+ \item \textbf{Observer} - Demoted by moderators and the founder. May observe+ the group and ignore peers; may not communicate with other peers or with the+ group.+\end{itemize}++\section{Group types}++Groups can have two types: private and public. The type can be set on creation,+and may also be toggled by the group founder at any point after creation.+(\emph{Note: password protection is completely independent of the group type})++\subsection{Public}++Anyone may join the group using the Chat ID. If the group is public, information+about peers inside the group, including their IP addresses and group public keys+(but not their Tox ID's) is visible to anyone with access to a node storing+their DHT announcement. See the \href{#dht-announcements}{DHT Announcements}+section for details.++\subsection{Private}++The only way to join a private group is by having someone in your friend list+send you an invite. If the group is private, no peer/group information+(mentioned in the Public section) is present in the DHT; the DHT is not used for+any purpose at all. If a public group is set to private, all DHT information+related to the group will expire within a few minutes.++\section{Cryptography}++Groupchats use the+\href{https://en.wikipedia.org/wiki/NaCl_(software)}{NaCl/libsodium cryptography+library} for all cryptography related operations. All group communication is+end-to-end encrypted. Message confidentiality, integrity, and repudability are+guaranteed via+\href{https://en.wikipedia.org/wiki/Authenticated_encryption}{authenticated+encryption}, and \href{https://en.wikipedia.org/wiki/Forward_secrecy}{perfect+forward secrecy} is also provided.++One of the most important security improvements from the old groupchat+implementation is the removal of a message-relay mechanism that uses a+group-wide shared key. Instead, connections are 1-to-1 (a complete graph),+meaning an outbound message is sent once per peer, and encrypted/decrypted using+a key unique to each peer. This prevents MITM attacks that were previously+possible. This additionally ensures that private messages are truly private.++Groups make use of 13 unique keys in total: Two permanent keypairs (encryption+and signature), two group keypairs (encryption and signature), one session+keypair (encryption), one shared symmetric key (encryption), and one temp DHT+keypair (encryption).++The Tox ID/Tox public key is not used for any purpose. As such, neither peers in+a given group nor in the group DHT can be matched with their Tox ID. In other+words, there is no way of identifying a peer aside from their IP address,+nickname, and group public key. (\emph{Note: group nicknames can be different+from the client's main nickname that their friends see}).++\subsection{Permanent keypairs}++When a peer creates or joins a group they generate two permanent keypairs: an+encryption keypair and a signature keypair, both of which are unique to the+group. The two public keys are the only guaranteed way to identify a peer, and+both keypairs will persist for as long as a peer remains in the group (even+across client restarts). If a peer exits the group these keypairs will be lost+forever.++This encryption keypair is not used for any encryption operations except for the+initial handshake when connecting to another peer. For usage details on the+signature key, see the \href{#moderation}{Moderation} section.++\subsection{Session keypair/shared symmetric key}++When two peers establish a connection they each generate a session encryption+keypair and share one another's resulting public key. With their own session+secret key and the other's session public key, they will both generate the same+symmetric encryption key. This symmetric key will be used for all further+encryption operations between them for the current session (i.e. until one of+them disconnects).++The purpose of this extra key exchange is to prevent an adversary from+decrypting messages from previous sessions in event that a secret encryption key+becomes compromised. This is known as forward secrecy.++\subsection{Group keypairs}++The group founder generates two additional permanent keypairs when the group is+created: an encryption keypair, and a signature keypair. The public signature+key is considered the \textbf{Chat ID} and is used as the group's permanent+identifier, allowing other peers to join public groups via the DHT. Every peer+in the group holds a copy of the group's public encryption key along with the+public signature key/Chat ID.++The group secret keys are similar to the permanent keypairs in that they will+persist across client restarts, but will be lost forever if the founder exits+the group. This is particularly important as administration related+functionality will not work without these keys. See the+\href{#founders}{Founders} section for usage details.++\subsection{Temporary DHT keypair}++All group related DHT procedures make use of toxcore's temp DHT keypair. This+keypair is generated when the Tox object is initialized and does not persist+across client restarts. See the \href{#dht-announcements}{DHT Announcements}+section for further details.++\section{Founders}++The peer who creates the group is the group's founder. Founders have a set of+admin privileges, including:++\begin{itemize}+ \item Promoting and demoting moderators+ \item The ability to kick/ban moderators+ \item Setting the peer limit+ \item Setting the group's privacy state+ \item Setting group passwords+\end{itemize}++\subsection{Shared state}++Groups contain a data structure called the \textbf{shared state} which is given+to every peer who joins the group. In this structure resides all data pertaining+to the group that must only be modifiable by the group founder. This includes+things like the group name, the group type, the peer limit, and the password.+Additionally, the shared state holds a copy of the group founder's public+encryption and signature keys, which is how other peers in the group are able to+verify the identity of the group founder.++The shared state is signed by the founder using the group secret signature key.+As the founder is the only peer who holds this secret key, this ensures that the+shared state may be safely shared by untrusted peers, even in the absence of the+founder.++When the founder modifies the shared state, he increments the shared state+version, signs the new shared state data with the group secret signature key,+and broadcasts the new shared state data along with its signature to the entire+group. When a peer receives this broadcast, he uses the group public signature+key to verify that the data was signed with the group secret signature key, and+also verifies that the new version is not older than the current version.++\subsection{Moderation}++The founder has the ability to promote other peers to the moderator role.+Moderators have all the privileges of normal users, and additionally have the+power to kick, ban, and unban, as well as give peers below the moderator role+the roles of user and observer (see the \href{#group-roles}{Group roles}+section). Moderators can also modify the group topic. Moderators have no power+over one another; only the founder can kick, ban, or change the role of a+moderator.++\subsection{Kicks/bans}++When a peer is kicked or banned from the group, his chat instance and all its+associated data will be destroyed. This includes all public and secret keys.+Additionally, the the peer will not receive any notifiactions; it will simply+appear to them as if the group is inactive.++\subsection{Moderator list}++Each peer holds a copy of the \textbf{moderator list}, which is an array of+public signature keys of peers who currently have the moderator role (including+those who are offline). A hash (sha256) of this list called the+\textbf{\verb'mod_list_hash'} is stored in the shared state, which is itself+signed by the founder using the group secret signature key. This allows the+moderator list to be shared between untrusted peers, even in the absence of the+founder, while maintaining moderator verifiability.++When the founder modifies the moderator list, he updates the+\verb'mod_list_hash', increments the shared state version, signs the new shared+state, broadcasts the new shared state data along with its signature to the+entire group, then broadcasts the new moderator list to the entire group. When a+peer receives this moderator list (having already verified the new shared+state), he creates a hash of the new list and verifies that it is identical to+the \verb'mod_list_hash'.++\subsection{Sanctions list}++Each peer holds a copy of the \textbf{sanctions list}. This list holds two+sublists: Banned peers, and peers with the observer role, or the \textbf{ban+list} and the \textbf{observer list} respectively. The ban list contains entries+of peers who have been banned, including their last used nickname, IP+address/port, and a unique ID. The sanctions list contains entries of peers who+have been demoted to the observer role, including just their public encryption+key.++All entries additionally contain a timestamp of the time the entry was made, the+public signature key of the peer who set the sanction, and a signature of the+entry's data, which is signed by the peer who created the entry using their+secret signature key. Individual entries are verified by ensuring that the+entry's public signature key belongs to the founder or is present in the+moderator list, and then verifying that the entry's data was signed by the owner+of that key.++Although each individual entry can be verified, we still need a way to verify+that the list as a whole is complete and identical for every peer, otherwise any+peer would be able to remove entries arbitrarily, or replace the list with an+older version. Therefore each peer holds a copy of the \textbf{sanctions list+credentials}. This is a data structure that holds the version, a hash (sha256)+of all sanctions list entries plus the version, the public signature key of the+last peer to have modified the sanctions list, and a signature of the hash,+which is created by that key.++When a moderator or founder modifies the sanctions list, he will increment the+version, create a new hash, sign the hash+version with his secret signature key,+and replace the old public signature key with his own. He will then broadcast+the new changes (not the entire list) to the entire group along with the new+credentials. When a peer receives this broadcast, he will verify that the new+credentials version is not older than the current version and verify that the+changes were made by a moderator or the founder. If adding an entry, he will+verify that the entry was signed by the signature key of the entry's creator.++When the founder kicks, bans or demotes a moderator, he will first go through+the sanctions list and re-sign each entry made by that moderator with his own+founder key, then re-broadcast the sanctions list to the entire group. This is+necessary to guarantee that all sanctions list entries and its credentials are+signed by a current moderator or the founder at all times.++\textbf{Note:} \emph{The sanctions list is not saved to the Tox save file,+meaning that if the group ever becomes empty, the sanctions list will be reset.+This is in contrast to the shared state and moderator list, which are both saved+and will persist even if the group becomes empty.}++\section{Topics}++Founders and moderators have the ability to set the \textbf{topic}, which is+simply an arbitrary string of characters. The integrity of a topic is maintained+in a similar manner as sanctions entries, using a data structure called+\textbf{\verb'topic_info'}. This is a struct which contains the topic, a+version, and the public key of the peer who set it.++When a peer modifies the topic, they will increment the version, sign the new+topic+version with their secret signature key, replace the public key with their+own, then broadcast the new \verb'topic_info' data along with the signature to+the entire group. When a peer receives this broadcast, they will first check if+the public signature key of the setter either belongs to the founder, or is in+the moderator list. They will then verify the signature using the setter's+public signature key, and finally they will ensure that the version is not older+than the current topic version.++If the moderator who set the current topic is kicked, banned, or demoted, the+founder will re-sign the topic using his own signature key, and rebroadcast it+to the entire group.++\section{State syncing}++Peers send four unsigned 32-bit integers along with their ping packets: Their+peer count\footnote{We use a "real" peer count, which is the number of confirmed+peers in the peerlist (that is, peers who you have successfully handshaked and+exchanged peer info with).}, their shared state version, their sanctions+credentials version, and their topic version. If a peer receives a ping in which+any of these values are greater than their own, this indicates that they may be+out of sync with the rest of the group. In this case they will do one of two+things: If they already have a sync request flagged for this peer, they will+send a sync request. Otherwise they will set the flag and wait until the next+ping arrives (this waiting is to correct for false-positives in the case of high+network latency). The flag is reset after a sync request is sent, or whenever a+ping is received in which all data is in sync.++\section{Group syncing}++In order to prevent entirely separate subgroups with the same Chat ID from being+created, be it due to network issues or a malicious MITM attempt, it's necessary+for groups to periodically search the DHT for announced nodes that match the+group's Chat ID but are not present in the group. In case an unknown node is+found, an attempt will be made to connect with it. If successful, the state sync+mechanism will merge the subgroups shortly.++Since we don't want to spam the DHT with a redundant number of requests that+grows linearly with the size of the group, peers will take turns doing the+search. Peers decide independently if it's their turn to search. Each peer has+the same base timer T, and every interval of T they will do a search with a+probability P which is inversely proportionate to the number of peers N. For+example, if N=1 then P=1.0. If N=4 then P=0.25. If N=100 then P=0.01 and so on.+This guarantees that a given group will do 1 search per T interval on average+regardless of its size, and it also ensures that a full spectrum of the network+is searched. Moreover, because peers act independently rather than in+coordination, malicious peers have little exploit potential (e.g. attempting to+stop the group from searching the DHT).++In addition, peers who join a group via the DHT will attempt to connect to any+nodes that are not in their freshly synced peer list.++\section{DHT Announcements}++Groupchats make use of the Tox DHT network in order to allow for groups that can+be joined by anyone who possesses the Chat ID. As all of the information stored+in or passed through the DHT can be viewed by any of the involved nodes, these+types of groups are considered to be public. Private groups in contrast do not+make use of the DHT for any purpose, and as such require a friend invite in+order to join.++\subsection{Announcement requests}++When peers create or successfully join a public group they send an+\textbf{announcement request}, containing information about the group that+they're announcing and themselves to K of their close DHT nodes. The information+in this request includes the announcer's group public encryption key and IP+address/port, as well as the Chat ID of the group. The DHT attempts to store+this announcement in the node that's closest to the Chat ID (\textbf{closeness}+is calculated by the DHT's close function). DHT nodes can store up to N+announcements each, after which they will replace the oldest announcements+first. See the \href{#redundancy}{Redundancy} section for details on how DDoS+attacks are mitigated.++\subsection{Get nodes requests}++When peers attempt to join a public group using the Chat ID they send a+\textbf{get nodes request}, containing their IP/port, their group public+encryption key, and the Chat ID to K of their close nodes. Those nodes will then+check if any of their announcement entries match the supplied Chat ID. If not,+they will relay the message to K of their own close nodes who will repeat the+process (note that the close function guarantees that each successive relay will+bring us closer to the Chat ID until we either find one of its entries, or have+traversed the entire DHT network).++Once a node finds an entry with the queried Chat ID it will send a \textbf{send+nodes response} to the original node who made the request. The response will+contain at least one entry (possibly more) which will hold the group public+encryption key and the IP address/port of a peer who had previously made an+announcement request for Chat ID. With this information the requester will+automatically initiate the handshake protocol and attempt to join the group.++\subsection{Redundancy}++DHT nodes will send ping requests to all of their announcement entries+periodically in order to ensure that they are still present in the+network/group. When a peer goes offline or leaves a group, they no longer+respond to these ping requests, and the nodes holding their entries will discard+them.++There are scenarios in which an announcement may be dropped from the network,+such as if the sole node holding the entry goes offline, or in the case of DDOS+attack which attempts to push all old entries out of the DHT. In order to ensure+that those announcements are not permanently lost, announcers will periodically+check when they last received a ping request for a given announcement. After a+certain amount of time without receiving a ping request they will assume that+their entry is no longer in the DHT network and re-announce themselves. This+ensures that every peer present in a group has an active announcement in the DHT+at all times, and it also ensures that a group cannot become 'lost'.++\begin{code}+module Network.Tox.Application.GroupChats where+\end{code}
src/tox/Network/Tox/Binary.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE Safe #-} {-# LANGUAGE ScopedTypeVariables #-} module Network.Tox.Binary@@ -7,9 +8,7 @@ , decode, decodeC, decodeS ) where -import Control.Applicative ((<$>))-import Control.Monad ((>=>))-import Data.Binary (Binary, get, put)+import Data.Binary (Binary) import Data.ByteString (ByteString) import Data.MessagePack (MessagePack, fromObject, toObject)@@ -29,6 +28,7 @@ import qualified Network.Tox.Crypto.Key as T import qualified Network.Tox.Crypto.KeyPair as T import qualified Network.Tox.DHT.DhtPacket as T+import qualified Network.Tox.DHT.DhtRequestPacket as T import qualified Network.Tox.DHT.NodesRequest as T import qualified Network.Tox.DHT.NodesResponse as T import qualified Network.Tox.DHT.PingPacket as T@@ -50,6 +50,7 @@ data KnownType = CipherText T.CipherText | DhtPacket T.DhtPacket+ | DhtRequestPacket T.DhtRequestPacket | HostAddress T.HostAddress | Word64 Word64 | Key T.PublicKey@@ -71,6 +72,7 @@ knownTypeToObject = \case CipherText x -> toObject x DhtPacket x -> toObject x+ DhtRequestPacket x -> toObject x HostAddress x -> toObject x Word64 x -> toObject x Key x -> toObject x@@ -92,6 +94,7 @@ knownTypeEncode = \case CipherText x -> encode x DhtPacket x -> encode x+ DhtRequestPacket x -> encode x HostAddress x -> encode x Word64 x -> encode x Key x -> encode x@@ -137,6 +140,7 @@ decodeKnownType = \case "CipherText" -> go CipherText "DhtPacket" -> go DhtPacket+ "DhtRequestPacket" -> go DhtRequestPacket "HostAddress" -> go HostAddress "Word64" -> go Word64 "Key PublicKey" -> go Key@@ -184,6 +188,7 @@ encodeKnownType = \case "CipherText" -> go CipherText "DhtPacket" -> go DhtPacket+ "DhtRequestPacket" -> go DhtRequestPacket "HostAddress" -> go HostAddress "Word64" -> go Word64 "Key PublicKey" -> go Key
src/tox/Network/Tox/Crypto/Box.lhs view
@@ -5,6 +5,7 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE Trustworthy #-} module Network.Tox.Crypto.Box ( PlainText (..)@@ -114,7 +115,7 @@ \end{code} The encryption function takes a Combined Key, a Nonce, and a Plain Text, and-returns a Cipher Text. It uses \texttt{crypto_box_afternm} to perform the+returns a Cipher Text. It uses \texttt{crypto\_box\_afternm} to perform the encryption. The meaning of the sentence "encrypting with a secret key, a public key, and a nonce" is: compute a combined key from the secret key and the public key and then use the encryption function for the transformation.@@ -135,7 +136,7 @@ The decryption function takes a Combined Key, a Nonce, and a Cipher Text, and returns either a Plain Text or an error. It uses-\texttt{crypto_box_open_afternm} from the NaCl library. Since the cipher is+\texttt{crypto\_box\_open\_afternm} from the NaCl library. Since the cipher is symmetric, the encryption function can also perform decryption, but will not perform message authentication, so the implementation must be careful to use the correct functions.@@ -154,7 +155,7 @@ \end{code} -\texttt{crypto_box} uses xsalsa20 symmetric encryption and poly1305+\texttt{crypto\_box} uses xsalsa20 symmetric encryption and poly1305 authentication. The create and handle request functions are the encrypt and decrypt functions
src/tox/Network/Tox/Crypto/CombinedKey.lhs view
@@ -1,7 +1,8 @@ \subsection{Combined Key} \begin{code}-{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE Trustworthy #-} module Network.Tox.Crypto.CombinedKey where import qualified Crypto.Saltine.Core.Box as Sodium (beforeNM)@@ -21,8 +22,8 @@ \end{code} A Combined Key is computed from a Secret Key and a Public Key using the NaCl-function \texttt{crypto_box_beforenm}. Given two Key Pairs KP1 (SK1, PK1) and-KP2 (SK2, PK1), the Combined Key computed from (SK1, PK2) equals the one+function \texttt{crypto\_box\_beforenm}. Given two Key Pairs KP1 (SK1, PK1) and+KP2 (SK2, PK2), the Combined Key computed from (SK1, PK2) equals the one computed from (SK2, PK1). This allows for symmetric encryption, as peers can derive the same shared key from their own secret key and their peer's public key.
src/tox/Network/Tox/Crypto/Key.lhs view
@@ -42,11 +42,11 @@ \end{code} -A Crypto Number is a large fixed size unsigned (positive) integer. Its binary+A Crypto Number is a large fixed size unsigned (non-negative) integer. Its binary encoding is as a Big Endian integer in exactly the encoded byte size. Its human-readable encoding is as a base-16 number encoded as String. The NaCl implementation \href{https://github.com/jedisct1/libsodium}{libsodium} supplies-the functions \texttt{sodium_bin2hex} and \texttt{sodium_hex2bin} to aid in+the functions \texttt{sodium\_bin2hex} and \texttt{sodium\_hex2bin} to aid in implementing the human-readable encoding. The in-memory encoding of these crypto numbers in NaCl already satisfies the binary encoding, so for applications directly using those APIs, binary encoding and decoding is the
src/tox/Network/Tox/Crypto/KeyPair.lhs view
@@ -1,13 +1,13 @@ \subsection{Key Pair} A Key Pair is a pair of Secret Key and Public Key. A new key pair is generated-using the \texttt{crypto_box_keypair} function of the NaCl crypto library. Two+using the \texttt{crypto\_box\_keypair} function of the NaCl crypto library. Two separate calls to the key pair generation function must return distinct key pairs. See the \href{https://nacl.cr.yp.to/box.html}{NaCl documentation} for details. A Public Key can be computed from a Secret Key using the NaCl function-\texttt{crypto_scalarmult_base}, which computes the scalar product of a+\texttt{crypto\_scalarmult\_base}, which computes the scalar product of a standard group element and the Secret Key. See the \href{https://nacl.cr.yp.to/scalarmult.html}{NaCl documentation} for details. @@ -15,6 +15,7 @@ {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE Trustworthy #-} module Network.Tox.Crypto.KeyPair where
+ src/tox/Network/Tox/Crypto/Keyed.hs view
@@ -0,0 +1,48 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE Safe #-}+{-# LANGUAGE UndecidableInstances #-}++-- | Monad class for caching of combined keys+module Network.Tox.Crypto.Keyed where++import Control.Applicative (Applicative, pure, (<*>))+import Control.Monad (Monad)+import Control.Monad.Random (RandT)+import Control.Monad.Reader (ReaderT)+import Control.Monad.RWS (RWST)+import Control.Monad.State (StateT)+import Control.Monad.Trans (lift)+import Control.Monad.Writer (WriterT)+import Data.Monoid (Monoid)++import qualified Network.Tox.Crypto.CombinedKey as CombinedKey+import Network.Tox.Crypto.Key (CombinedKey, PublicKey,+ SecretKey)++class (Monad m, Applicative m) => Keyed m where+ getCombinedKey :: SecretKey -> PublicKey -> m CombinedKey++instance Keyed m => Keyed (ReaderT r m) where+ getCombinedKey = (lift .) . getCombinedKey+instance (Monoid w, Keyed m) => Keyed (WriterT w m) where+ getCombinedKey = (lift .) . getCombinedKey+instance Keyed m => Keyed (StateT s m) where+ getCombinedKey = (lift .) . getCombinedKey+instance (Monoid w, Keyed m) => Keyed (RWST r w s m) where+ getCombinedKey = (lift .) . getCombinedKey+instance Keyed m => Keyed (RandT s m) where+ getCombinedKey = (lift .) . getCombinedKey++-- | trivial instance: the trivial monad, with no caching of keys+newtype NullKeyed a = NullKeyed { runNullKeyed :: a }+instance Functor NullKeyed where+ fmap f (NullKeyed x) = NullKeyed (f x)+instance Applicative NullKeyed where+ pure = NullKeyed+ (NullKeyed f) <*> (NullKeyed x) = NullKeyed (f x)+instance Monad NullKeyed where+ return = NullKeyed+ NullKeyed x >>= f = f x+instance Keyed NullKeyed where+ getCombinedKey = (NullKeyed .) . CombinedKey.precompute
+ src/tox/Network/Tox/Crypto/KeyedT.hs view
@@ -0,0 +1,53 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE UndecidableInstances #-}++module Network.Tox.Crypto.KeyedT where++import Control.Applicative (Applicative, (<$>))+import Control.Monad (Monad)+import Control.Monad.IO.Class (MonadIO)+import Control.Monad.State (MonadState, StateT,+ StateT (..), evalStateT,+ gets, modify, runStateT,+ state)+import Control.Monad.Trans (MonadTrans)+import Control.Monad.Writer (MonadWriter)++import Data.Map (Map)+import qualified Data.Map as Map+import qualified Network.Tox.Crypto.CombinedKey as CombinedKey+import Network.Tox.Crypto.Key (CombinedKey, PublicKey,+ SecretKey)+import Network.Tox.Crypto.Keyed (Keyed (..))+import Network.Tox.Network.MonadRandomBytes (MonadRandomBytes)+import Network.Tox.Network.Networked (Networked)+import Network.Tox.Timed (Timed)++type KeyRing = Map (SecretKey, PublicKey) CombinedKey++-- | caches computations of combined keys. Makes no attempt to delete old keys.+newtype KeyedT m a = KeyedT (StateT KeyRing m a)+ deriving (Monad, Applicative, Functor, MonadWriter w+ , MonadRandomBytes, MonadTrans, MonadIO, Networked, Timed)++runKeyedT :: Monad m => KeyedT m a -> KeyRing -> m (a, KeyRing)+runKeyedT (KeyedT m) = runStateT m++evalKeyedT :: Monad m => KeyedT m a -> KeyRing -> m a+evalKeyedT (KeyedT m) = evalStateT m++instance (MonadState s m, Applicative m) => MonadState s (KeyedT m) where+ state f = KeyedT . StateT $ \s -> flip (,) s <$> state f++instance (Monad m, Applicative m) => Keyed (KeyedT m) where+ getCombinedKey secretKey publicKey =+ let keys = (secretKey, publicKey)+ in KeyedT $ gets (Map.lookup keys) >>= \case+ Nothing ->+ let shared = CombinedKey.precompute secretKey publicKey+ in modify (Map.insert keys shared) >> return shared+ Just shared -> return shared
src/tox/Network/Tox/Crypto/Nonce.lhs view
@@ -14,7 +14,8 @@ together. \begin{code}-{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE Trustworthy #-} module Network.Tox.Crypto.Nonce where import Control.Applicative ((<$>))
src/tox/Network/Tox/DHT.lhs view
@@ -6,15 +6,15 @@ \end{code} The DHT is a self-organizing swarm of all nodes in the Tox network. A node in-the Tox network is also called "Tox node". When we talk about "peers", we mean+the Tox network is also called a "Tox node". When we talk about "peers", we mean any node that is not the local node (the subject). This module takes care of finding the IP and port of nodes and establishing a route to them directly via UDP using \href{#hole-punching}{hole punching} if necessary. The DHT only runs on UDP and so is only used if UDP works. -Every node in the Tox DHT has an ephemeral Key Pair called the DHT Key Pair+Every node in the Tox DHT has an ephemeral Key Pair called the DHT Key Pair, consisting of the DHT Secret Key and the DHT Public Key. The DHT Public Key-acts as the node address. The DHT Key Pair is renewed every time the tox+acts as the node address. The DHT Key Pair is renewed every time the Tox instance is closed or restarted. An implementation may choose to renew the key more often, but doing so will disconnect all peers. @@ -23,6 +23,7 @@ connect directly to them via UDP. \input{src/tox/Network/Tox/DHT/Distance.lhs}+\input{src/tox/Network/Tox/DHT/ClientList.lhs} \input{src/tox/Network/Tox/DHT/KBuckets.lhs} \input{src/tox/Network/Tox/DHT/DhtState.lhs} @@ -51,135 +52,23 @@ The Nodes Service is used to query another DHT node for up to 4 nodes they know that are the closest to a requested node. -\input{src/tox/Network/Tox/DHT/NodesRequest.lhs}-\input{src/tox/Network/Tox/DHT/NodesResponse.lhs}--\subsection{Packed node format}--The DHT Send nodes uses the Packed Node Format.+The DHT Nodes RPC service uses the Packed Node Format. -Only the UDP Protocol (IP Type \texttt{2} and \texttt{10}) are used in the DHT+Only the UDP Protocol (IP Type \texttt{2} and \texttt{10}) is used in the DHT module when sending nodes with the packed node format. This is because the TCP Protocol is used to send TCP relay information and the DHT is UDP only. -This is done to increase the speed at which peers are found. Toxcore also-stores the 8 nodes (Must be the same or smaller than the nodes toxcore stores-for each index in its close list to make sure all the closest peers found will-know the node being searched) closest to each of the public keys in its DHT-friends list (or list of DHT public keys that it actively tries to find and-connect to). Toxcore pings every node in the lists every 60 seconds to see if-they are alive. It does not store itself in either list and does not send any-requests to itself. Nodes can be in more than one list for example if the DHT-public key of the peer is very close to the DHT public key of a friend being-searched. It also sends get node requests to a random node (random makes it-unpredictable, predictability or knowing which node a node will ping next could-make some attacks that disrupt the network more easy as it adds a possible-attack vector) in each of these lists of nodes every 20 seconds, with the-search public key being its public key for the closest node and the public key-being searched for being the ones in the DHT friends list. Nodes are removed-after 122 seconds of no response. Nodes are added to the lists after a valid-ping response or send node packet is received from them. If the node is-already present in the list it is updated if the IP address changed. A node-can only be added to a list if the list is not full or if the nodes DHT public-key is closer than the DHT public key of at least one of the nodes in the list-to the public key being searched with that list. When a node is added to a-full list, it will replace the furthest node.--If the 32 nodes number where increased, it would increase the amount of packets-needed to check if each of them are still alive which would increase the-bandwidth usage but reliability would go up. If the number of nodes were-decreased, reliability would go down along with bandwidth usage. The reason-for this relationship between reliability and number of nodes is that if we-assume that not every node has its UDP ports open or is behind a cone NAT it-means that each of these nodes must be able to store a certain number of nodes-behind restrictive NATs in order for others to be able to find those nodes-behind restrictive NATs. For example if 7/8 nodes were behind restrictive-NATs, using 8 nodes would not be enough because the chances of some of these-nodes being impossible to find in the network would be too high.--If the ping timeouts and delays between pings were higher it would decrease the-bandwidth usage but increase the amount of disconnected nodes that are still-being stored in the lists. Decreasing these delays would do the opposite.--If the 8 nodes closest to each public key were increased to 16 it would-increase the bandwidth usage, might increase hole punching efficiency on-symmetric NATs (more ports to guess from, see Hole punching) and might increase-the reliability. Lowering this number would have the opposite effect.--When receiving a send node packet, toxcore will check if each of the received-nodes could be added to any one of the lists. If the node can, toxcore will-send a ping packet to it, if it cannot it will be ignored.--When receiving a get node packet, toxcore will find the 4 nodes, in its nodes-lists, closest to the public key in the packet and send them in the send node-response.--The timeouts and number of nodes in lists for toxcore where picked by feeling-alone and are probably not the best values. This also applies to the behavior-which is simple and should be improved in order to make the network resist-better to sybil attacks.--\section{DHT Request packets}--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0x20) \\- \texttt{32} & receiver's DHT public key \\- \texttt{32} & sender's DHT public key \\- \texttt{24} & nonce \\- \texttt{?} & encrypted message \\-\end{tabular}--DHT Request packets are packets that can be sent across one DHT node to one-that they know. They are used to send encrypted data to friends that we are-not necessarily connected to directly in the DHT.--A DHT node that receives a DHT request packet will check whether the node with-the receivers public key is their DHT public key and, if it is, they will-decrypt and handle the packet. If it is not they will check whether they know-that DHT public key (if it's in their list of close nodes). If it isn't, they-will drop the packet. If it is they will resend the exact packet to that DHT-node.--The encrypted message is encrypted using the receiver's DHT Public key, the-sender's DHT private key and the nonce (randomly generated 24 bytes).--DHT request packets are used for DHTPK packets (see onion) and NAT ping-packets.--\subsection{NAT ping packets}--Sits inside the DHT request packet.--NAT ping packets are used to see if a friend we are not connected to directly-is online and ready to do the hole punching.--\subsubsection{NAT ping request}--\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0xfe) \\- \texttt{1} & \texttt{uint8_t} (0x00) \\- \texttt{8} & \texttt{uint64_t} random number \\-\end{tabular}--\subsubsection{NAT ping response}+\input{src/tox/Network/Tox/DHT/NodesRequest.lhs}+\input{src/tox/Network/Tox/DHT/NodesResponse.lhs} -\begin{tabular}{l|l}- Length & Contents \\- \hline- \texttt{1} & \texttt{uint8_t} (0xfe) \\- \texttt{1} & \texttt{uint8_t} (0x01) \\- \texttt{8} & \texttt{uint64_t} random number (the same that was received in request) \\-\end{tabular}+\input{src/tox/Network/Tox/DHT/Operation.lhs} -\section{Hole punching}+\section{NATs} -For holepunching we assume that people using Tox are on one of 3 types of NAT:+We assume that peers are either directly accessible or are behind one of 3+types of NAT: -Cone NATs: Assign one whole port to each UDP socket behind the NAT, any packet+Cone NATs: Assign one whole port to each UDP socket behind the NAT; any packet from any IP/port sent to that assigned port from the internet will be forwarded to the socket behind it. @@ -192,6 +81,9 @@ \texttt{'connection'} and will only forward packets from the IP/port of that \texttt{'connection'}. ++\section{Hole punching}+ Holepunching on normal cone NATs is achieved simply through the way in which the DHT functions. @@ -200,8 +92,7 @@ IP/ports but get no response. If we have sent 4 ping requests to 4 IP/ports that supposedly belong to the friend and get no response, then this is enough for toxcore to start the hole punching. The numbers 8 and 4 are used in-toxcore and where chosen based on feel alone and so may not be the best-numbers.+toxcore and were chosen based on feel alone and so may not be the best numbers. Before starting the hole punching, the peer will send a NAT ping packet to the friend via the peers that say they know the friend. If a NAT ping response@@ -236,7 +127,7 @@ common IP returned by the peers and to ignore the other IP/ports. In the case where the peers return the same IP and port it means that the other-friend is on a restricted cone NAT. These kind of NATs can be hole punched by+friend is on a restricted cone NAT. These kinds of NATs can be hole punched by getting the friend to send a packet to our public IP/port. This means that hole punching can be achieved easily and that we should just continue sending DHT ping packets regularly to that IP/port until we get a ping response. This
+ src/tox/Network/Tox/DHT/ClientList.lhs view
@@ -0,0 +1,156 @@+\section{Client Lists}++\begin{code}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE Safe #-}+module Network.Tox.DHT.ClientList where++import Control.Applicative ((<$>), (<*>))+import Control.Monad (join)+import Data.List (sort)+import Data.Map (Map)+import qualified Data.Map as Map+import Test.QuickCheck.Arbitrary (Arbitrary, arbitrary,+ arbitrarySizedNatural)+import Test.QuickCheck.Gen (Gen)+import qualified Test.QuickCheck.Gen as Gen++import Network.Tox.Crypto.Key (PublicKey)+import Network.Tox.DHT.ClientNode (ClientNode)+import qualified Network.Tox.DHT.ClientNode as ClientNode+import Network.Tox.DHT.Distance (Distance)+import qualified Network.Tox.DHT.Distance as Distance+import Network.Tox.NodeInfo.NodeInfo (NodeInfo)+import qualified Network.Tox.NodeInfo.NodeInfo as NodeInfo+import Network.Tox.Time (Timestamp)+++{-------------------------------------------------------------------------------+ -+ - :: Implementation.+ -+ ------------------------------------------------------------------------------}++\end{code}++A Client List of \textit{maximum size} \texttt{k} with a given public key as+\textit{base key} is an ordered set of at most \texttt{k} nodes close to the+base key. The elements are sorted by \href{#distance}{distance} from the base+key. Thus, the first (smallest) element of the set is the closest one to the+base key in that set, the last (greatest) element is the furthest away. The+maximum size and base key are constant throughout the lifetime of a Client+List.+++\begin{code}++data ClientList = ClientList+ { baseKey :: PublicKey+ , maxSize :: Int+ , nodes :: ClientNodes+ }+ deriving (Eq, Read, Show)++type ClientNodes = Map Distance ClientNode++nodeInfos :: ClientList -> [NodeInfo]+nodeInfos = map ClientNode.nodeInfo . Map.elems . nodes++empty :: PublicKey -> Int -> ClientList+empty publicKey size = ClientList+ { baseKey = publicKey+ , maxSize = size+ , nodes = Map.empty+ }++isEmpty :: ClientList -> Bool+isEmpty = Map.null . nodes++updateClientNodes :: (ClientNodes -> ClientNodes) -> ClientList -> ClientList+updateClientNodes f clientList@ClientList{ nodes } =+ clientList{nodes = f nodes}++lookup :: PublicKey -> ClientList -> Maybe NodeInfo+lookup publicKey _cl@ClientList{ baseKey, nodes } =+ ClientNode.nodeInfo <$> Distance.xorDistance publicKey baseKey `Map.lookup` nodes++\end{code}+++A Client List is \textit{full} when the number of nodes it contains is the+maximum size of the list.++A node is \textit{viable} for entry if the Client List is not \textit{full} or the+node's public key has a lower distance from the base key than the current entry+with the greatest distance.++If a node is \textit{viable} and the Client List is \textit{full}, the entry+with the greatest distance from the base key is removed to keep the size below+the maximum configured size.++Adding a node whose key already exists will result in an update of the Node+Info in the Client List. Removing a node for which no Node Info exists in the+Client List has no effect. Thus, removing a node twice is permitted and has the+same effect as removing it once.++\begin{code}++full :: ClientList -> Bool+full ClientList{ nodes, maxSize } =+ Map.size nodes >= maxSize++addNode :: Timestamp -> NodeInfo -> ClientList -> ClientList+addNode time nodeInfo clientList@ClientList{ baseKey, maxSize } =+ (`updateClientNodes` clientList) $+ mapTake maxSize+ . Map.insert+ (Distance.xorDistance (NodeInfo.publicKey nodeInfo) baseKey)+ (ClientNode.newNode time nodeInfo)+ where+ -- | 'mapTake' is 'Data.Map.take' in >=containers-0.5.8, but we define it+ -- for compatibility with older versions.+ mapTake :: Int -> Map k a -> Map k a+ mapTake n = Map.fromDistinctAscList . take n . Map.toAscList+++removeNode :: PublicKey -> ClientList -> ClientList+removeNode publicKey clientList =+ (`updateClientNodes` clientList) .+ Map.delete . Distance.xorDistance publicKey $ baseKey clientList++viable :: NodeInfo -> ClientList -> Bool+viable nodeInfo ClientList{ baseKey, maxSize, nodes } =+ let key = Distance.xorDistance (NodeInfo.publicKey nodeInfo) baseKey+ in (key `elem`) . take maxSize . sort $ key : Map.keys nodes++\end{code}++The iteration order of a Client List is in order of distance from the base+key. I.e. the first node seen in iteration is the closest, and the last node+is the furthest away in terms of the distance metric.++\begin{code}++foldNodes :: (a -> NodeInfo -> a) -> a -> ClientList -> a+foldNodes f x = foldl f x . nodeInfos++closeNodes :: PublicKey -> ClientList -> [ (Distance, NodeInfo) ]+closeNodes publicKey ClientList{ baseKey, nodes } =+ Map.toAscList . fmap ClientNode.nodeInfo $+ Map.mapKeys (Distance.rebaseDistance baseKey publicKey) nodes++{-------------------------------------------------------------------------------+ -+ - :: Tests.+ -+ ------------------------------------------------------------------------------}+++genClientList :: PublicKey -> Int -> Gen ClientList+genClientList publicKey size =+ foldl (flip $ uncurry addNode) (empty publicKey size) <$> Gen.listOf arbitrary+++instance Arbitrary ClientList where+ arbitrary = join $ genClientList <$> arbitrary <*> arbitrarySizedNatural+\end{code}
+ src/tox/Network/Tox/DHT/ClientNode.lhs view
@@ -0,0 +1,38 @@+\begin{code}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE Safe #-}+module Network.Tox.DHT.ClientNode where++import Control.Applicative ((<$>), (<*>))+import Test.QuickCheck.Arbitrary (Arbitrary, arbitrary)++import Network.Tox.NodeInfo.NodeInfo (NodeInfo)+import Network.Tox.Time (Timestamp)+++{-------------------------------------------------------------------------------+ -+ - :: Implementation.+ -+ ------------------------------------------------------------------------------}++data ClientNode = ClientNode+ { nodeInfo :: NodeInfo+ , lastCheck :: Timestamp+ , checkCount :: Int+ }+ deriving (Eq, Read, Show)++newNode :: Timestamp -> NodeInfo -> ClientNode+newNode time node = ClientNode node time 0++{-------------------------------------------------------------------------------+ -+ - :: Tests.+ -+ ------------------------------------------------------------------------------}++instance Arbitrary ClientNode where+ arbitrary = ClientNode <$> arbitrary <*> arbitrary <*> arbitrary++\end{code}
src/tox/Network/Tox/DHT/DhtPacket.lhs view
@@ -1,10 +1,10 @@ \section{DHT Packet} The DHT Packet contains the sender's DHT Public Key, an encryption Nonce, and-an encrypted payload. The payload is encrypted with the the DHT secret key of-the sender, the DHT public key of the receiver, and the nonce that is sent-along with the packet. DHT Packets are sent inside Protocol Packets with a-varying Packet Kind.+an encrypted payload. The payload is encrypted with the DHT secret key of the+sender, the DHT public key of the receiver, and the nonce that is sent along+with the packet. DHT Packets are sent inside Protocol Packets with a varying+Packet Kind. \begin{tabular}{l|l|l} Length & Type & \href{#protocol-packet}{Contents} \\@@ -27,22 +27,22 @@ {-# LANGUAGE Safe #-} module Network.Tox.DHT.DhtPacket where -import Control.Applicative ((<$>), (<*>))-import Data.Binary (Binary, get, put)-import Data.Binary.Get (getRemainingLazyByteString)-import Data.Binary.Put (putByteString, putByteString,- runPut)-import qualified Data.ByteString.Lazy as LazyByteString-import Data.MessagePack (MessagePack)-import Data.Typeable (Typeable)-import GHC.Generics (Generic)-import Network.Tox.Crypto.Box (CipherText, PlainText (..),- unCipherText)-import qualified Network.Tox.Crypto.Box as Box-import qualified Network.Tox.Crypto.CombinedKey as CombinedKey-import Network.Tox.Crypto.Key (Nonce, PublicKey)-import Network.Tox.Crypto.KeyPair (KeyPair (..))-import Test.QuickCheck.Arbitrary (Arbitrary, arbitrary)+import Control.Applicative ((<$>), (<*>))+import Data.Binary (Binary, get, put)+import Data.Binary.Get (getRemainingLazyByteString)+import Data.Binary.Put (putByteString, runPut)+import qualified Data.ByteString.Lazy as LazyByteString+import Data.MessagePack (MessagePack)+import Data.Typeable (Typeable)+import GHC.Generics (Generic)+import Network.Tox.Crypto.Box (CipherText, PlainText (..),+ unCipherText)+import qualified Network.Tox.Crypto.Box as Box+import Network.Tox.Crypto.Key (Nonce, PublicKey)+import Network.Tox.Crypto.Keyed (Keyed)+import qualified Network.Tox.Crypto.Keyed as Keyed+import Network.Tox.Crypto.KeyPair (KeyPair (..))+import Test.QuickCheck.Arbitrary (Arbitrary, arbitrary) @@ -74,14 +74,21 @@ encrypt :: KeyPair -> PublicKey -> Nonce -> PlainText -> DhtPacket-encrypt (KeyPair senderSecretKey senderPublicKey') receiverPublicKey nonce plainText =- DhtPacket senderPublicKey' nonce $ Box.encrypt combinedKey nonce plainText- where combinedKey = CombinedKey.precompute senderSecretKey receiverPublicKey+encrypt = (((Keyed.runNullKeyed .) .) .) . encryptKeyed +encryptKeyed :: Keyed m => KeyPair -> PublicKey -> Nonce -> PlainText -> m DhtPacket+encryptKeyed (KeyPair senderSecretKey senderPublicKey') receiverPublicKey nonce plainText =+ (\combinedKey -> DhtPacket senderPublicKey' nonce $+ Box.encrypt combinedKey nonce plainText) <$>+ Keyed.getCombinedKey senderSecretKey receiverPublicKey + encode :: Binary payload => KeyPair -> PublicKey -> Nonce -> payload -> DhtPacket-encode keyPair receiverPublicKey nonce =- encrypt keyPair receiverPublicKey nonce+encode = (((Keyed.runNullKeyed .) .) .) . encodeKeyed++encodeKeyed :: (Binary payload, Keyed m) => KeyPair -> PublicKey -> Nonce -> payload -> m DhtPacket+encodeKeyed keyPair receiverPublicKey nonce =+ encryptKeyed keyPair receiverPublicKey nonce . PlainText . LazyByteString.toStrict . runPut@@ -89,13 +96,19 @@ decrypt :: KeyPair -> DhtPacket -> Maybe PlainText-decrypt (KeyPair receiverSecretKey _) DhtPacket { senderPublicKey, encryptionNonce, encryptedPayload } =- Box.decrypt combinedKey encryptionNonce encryptedPayload- where combinedKey = CombinedKey.precompute receiverSecretKey senderPublicKey+decrypt = (Keyed.runNullKeyed .) . decryptKeyed +decryptKeyed :: Keyed m => KeyPair -> DhtPacket -> m (Maybe PlainText)+decryptKeyed (KeyPair receiverSecretKey _) DhtPacket { senderPublicKey, encryptionNonce, encryptedPayload } =+ (\combinedKey -> Box.decrypt combinedKey encryptionNonce encryptedPayload) <$>+ Keyed.getCombinedKey receiverSecretKey senderPublicKey + decode :: Binary payload => KeyPair -> DhtPacket -> Maybe payload-decode keyPair packet = decrypt keyPair packet >>= Box.decode+decode = (Keyed.runNullKeyed .) . decodeKeyed++decodeKeyed :: (Binary payload, Keyed m) => KeyPair -> DhtPacket -> m (Maybe payload)+decodeKeyed keyPair packet = (>>= Box.decode) <$> decryptKeyed keyPair packet {-------------------------------------------------------------------------------
+ src/tox/Network/Tox/DHT/DhtRequestPacket.lhs view
@@ -0,0 +1,70 @@+\section{DHT Request Packets}+DHT Request packets are used to route encrypted data from a sender to another+node, referred to as the addressee of the packet, via a third node.++A DHT Request Packet is sent as the payload of a Protocol Packet with the+corresponding Packet Kind. It contains the DHT Public Key of an addressee, and a+DHT Packet which is to be received by the addressee.++\begin{tabular}{l|l|l}+ Length & Type & \href{#protocol-packet}{Contents} \\+ \hline+ \texttt{32} & Public Key & Addressee DHT Public Key \\+ \texttt{[72,]} & DHT Packet & DHT Packet \\+\end{tabular}++\begin{code}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE Safe #-}+module Network.Tox.DHT.DhtRequestPacket where++import Control.Applicative ((<$>), (<*>))+import Data.Binary (Binary, get, put)+import Data.MessagePack (MessagePack)+import Data.Typeable (Typeable)+import GHC.Generics (Generic)++import Network.Tox.Crypto.Key (PublicKey)+import Network.Tox.DHT.DhtPacket (DhtPacket)++import Test.QuickCheck.Arbitrary (Arbitrary, arbitrary)++++{-------------------------------------------------------------------------------+ -+ - :: Implementation.+ -+ ------------------------------------------------------------------------------}+++data DhtRequestPacket = DhtRequestPacket+ { addresseePublicKey :: PublicKey+ , dhtPacket :: DhtPacket+ }+ deriving (Eq, Read, Show, Generic, Typeable)++instance MessagePack DhtRequestPacket+++instance Binary DhtRequestPacket where+ put packet = do+ put $ addresseePublicKey packet+ put $ dhtPacket packet++ get =+ DhtRequestPacket <$> get <*> get++{-------------------------------------------------------------------------------+ -+ - :: Tests.+ -+ ------------------------------------------------------------------------------}+++instance Arbitrary DhtRequestPacket where+ arbitrary =+ DhtRequestPacket <$> arbitrary <*> arbitrary+\end{code}
src/tox/Network/Tox/DHT/DhtState.lhs view
@@ -1,22 +1,41 @@ \section{DHT node state} \begin{code}-{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE Safe #-} module Network.Tox.DHT.DhtState where -import Control.Applicative ((<$>), (<*>), (<|>))-import Data.Map (Map)-import qualified Data.Map as Map-import qualified Data.Maybe as Maybe-import Test.QuickCheck.Arbitrary (Arbitrary, arbitrary, shrink)+import Control.Applicative (Applicative, Const (..),+ getConst, (<$>), (<*>), (<|>))+import Data.Functor.Identity (Identity (..))+import Data.List (nub, sortBy)+import Data.Map (Map)+import qualified Data.Map as Map+import qualified Data.Maybe as Maybe+import Data.Monoid (All (..), Monoid, getAll)+import Data.Ord (comparing)+import Data.Traversable (traverse)+import Lens.Family2 (Lens')+import Test.QuickCheck.Arbitrary (Arbitrary, arbitrary, shrink) -import Network.Tox.Crypto.Key (PublicKey)-import Network.Tox.Crypto.KeyPair (KeyPair)-import qualified Network.Tox.Crypto.KeyPair as KeyPair-import Network.Tox.DHT.KBuckets (KBuckets)-import qualified Network.Tox.DHT.KBuckets as KBuckets-import Network.Tox.NodeInfo.NodeInfo (NodeInfo)-import qualified Network.Tox.NodeInfo.NodeInfo as NodeInfo+import Network.Tox.Crypto.Key (PublicKey)+import Network.Tox.Crypto.KeyPair (KeyPair)+import qualified Network.Tox.Crypto.KeyPair as KeyPair+import Network.Tox.DHT.ClientList (ClientList)+import qualified Network.Tox.DHT.ClientList as ClientList+import Network.Tox.DHT.Distance (Distance)+import Network.Tox.DHT.KBuckets (KBuckets)+import qualified Network.Tox.DHT.KBuckets as KBuckets+import Network.Tox.DHT.NodeList (NodeList)+import qualified Network.Tox.DHT.NodeList as NodeList+import Network.Tox.DHT.PendingReplies (PendingReplies)+import qualified Network.Tox.DHT.Stamped as Stamped+import Network.Tox.NodeInfo.NodeInfo (NodeInfo)+import qualified Network.Tox.NodeInfo.NodeInfo as NodeInfo+import Network.Tox.Time (Timestamp) {-------------------------------------------------------------------------------@@ -43,13 +62,41 @@ \begin{code} +data ListStamp = ListStamp { listTime :: Timestamp, listBootstrappedTimes :: Int }+ deriving (Eq, Read, Show)+newListStamp :: Timestamp -> ListStamp+newListStamp t = ListStamp t 0+ data DhtState = DhtState- { dhtKeyPair :: KeyPair- , dhtCloseList :: KBuckets- , dhtSearchList :: Map PublicKey DhtSearchEntry+ { dhtKeyPair :: KeyPair+ , dhtCloseList :: KBuckets+ , dhtSearchList :: Map PublicKey DhtSearchEntry++ , dhtCloseListStamp :: ListStamp+ , dhtPendingReplies :: PendingReplies } deriving (Eq, Read, Show) +_dhtKeyPair :: Lens' DhtState KeyPair+_dhtKeyPair f d@DhtState{ dhtKeyPair = a } =+ (\a' -> d{ dhtKeyPair = a' }) <$> f a++_dhtCloseListStamp :: Lens' DhtState ListStamp+_dhtCloseListStamp f d@DhtState{ dhtCloseListStamp = a } =+ (\a' -> d{ dhtCloseListStamp = a' }) <$> f a++_dhtCloseList :: Lens' DhtState KBuckets+_dhtCloseList f d@DhtState{ dhtCloseList = a } =+ (\a' -> d{ dhtCloseList = a' }) <$> f a++_dhtSearchList :: Lens' DhtState (Map PublicKey DhtSearchEntry)+_dhtSearchList f d@DhtState{ dhtSearchList = a } =+ (\a' -> d{ dhtSearchList = a' }) <$> f a++_dhtPendingReplies :: Lens' DhtState PendingReplies+_dhtPendingReplies f d@DhtState{ dhtPendingReplies = a } =+ (\a' -> d{ dhtPendingReplies = a' }) <$> f a+ \end{code} A DHT node state is initialised using a Key Pair, which is stored in the state@@ -58,38 +105,64 @@ \begin{code} -empty :: KeyPair -> DhtState-empty keyPair =- DhtState keyPair (KBuckets.empty $ KeyPair.publicKey keyPair) Map.empty+empty :: Timestamp -> KeyPair -> DhtState+empty time keyPair =+ DhtState keyPair (KBuckets.empty $ KeyPair.publicKey keyPair)+ Map.empty (newListStamp time) Stamped.empty \end{code} \subsection{DHT Search Entry} -A DHT Search Entry contains a k-buckets instance, which serves the same purpose-as the Close List, but the base key is the searched node's Public Key. Once the-searched node is found, it is also stored in the Search Entry. Recall that-k-buckets never contain a node info for the base key, so it must be stored-outside the k-buckets instance.+A DHT Search Entry contains a Client List with base key the searched node's+Public Key. Once the searched node is found, it is also stored in the Search+Entry. +The maximum size of the Client List is set to 8.+(Must be the same or smaller than the bucket size of the close list to make+sure all the closest peers found will know the node being searched+(TODO(zugz): this argument is unclear.)).++A DHT node state therefore contains one Client List for each bucket index in+the Close List, and one Client List for each DHT Search Entry.+These lists are not required to be disjoint - a node may be in multiple Client+Lists simultaneously.+ \begin{code} data DhtSearchEntry = DhtSearchEntry- { searchNode :: Maybe NodeInfo- , searchKBuckets :: KBuckets+ { searchNode :: Maybe NodeInfo+ , searchStamp :: ListStamp+ , searchClientList :: ClientList } deriving (Eq, Read, Show) +_searchNode :: Lens' DhtSearchEntry (Maybe NodeInfo)+_searchNode f d@DhtSearchEntry{ searchNode = a } =+ (\a' -> d{ searchNode = a' }) <$> f a++_searchStamp :: Lens' DhtSearchEntry ListStamp+_searchStamp f d@DhtSearchEntry{ searchStamp = a } =+ (\a' -> d{ searchStamp = a' }) <$> f a++_searchClientList :: Lens' DhtSearchEntry ClientList+_searchClientList f d@DhtSearchEntry{ searchClientList = a } =+ (\a' -> d{ searchClientList = a' }) <$> f a++searchEntryClientListSize :: Int+searchEntryClientListSize = 8+ \end{code} A Search Entry is initialised with the searched-for Public Key. The contained-k-buckets instance is initialised to be empty.+Client List is initialised to be empty. \begin{code} -emptySearchEntry :: PublicKey -> DhtSearchEntry-emptySearchEntry =- DhtSearchEntry Nothing . KBuckets.empty+emptySearchEntry :: Timestamp -> PublicKey -> DhtSearchEntry+emptySearchEntry time publicKey =+ DhtSearchEntry Nothing (newListStamp time) $+ ClientList.empty publicKey searchEntryClientListSize \end{code} @@ -101,12 +174,12 @@ \begin{code} -addSearchKey :: PublicKey -> DhtState -> DhtState-addSearchKey searchKey dhtState@DhtState { dhtSearchList } =+addSearchKey :: Timestamp -> PublicKey -> DhtState -> DhtState+addSearchKey time searchKey dhtState@DhtState { dhtSearchList } = dhtState { dhtSearchList = updatedSearchList } where searchEntry =- Map.findWithDefault (emptySearchEntry searchKey) searchKey dhtSearchList+ Map.findWithDefault (emptySearchEntry time searchKey) searchKey dhtSearchList updatedSearchList = Map.insert searchKey searchEntry dhtSearchList @@ -128,90 +201,129 @@ \end{code} +\input{src/tox/Network/Tox/DHT/NodeList.lhs}+ The iteration order over the DHT state is to first process the Close List-k-buckets, then the Search List entry k-buckets. Each list itself follows the-iteration order in the k-buckets specification.+k-buckets, then the Search List entry Client Lists. Each of these follows the+iteration order in the corresponding specification. \begin{code} -foldBuckets :: (a -> KBuckets -> a) -> a -> DhtState -> a-foldBuckets f x DhtState { dhtCloseList, dhtSearchList } =- Map.foldl (\x' -> f x' . searchKBuckets) (f x dhtCloseList) dhtSearchList+traverseNodeLists :: Applicative f =>+ (forall l. NodeList l => l -> f l) -> DhtState -> f DhtState+traverseNodeLists f dhtState@DhtState{ dhtCloseList, dhtSearchList } =+ (\close' search' ->+ dhtState{ dhtCloseList = close', dhtSearchList = search' }) <$>+ f dhtCloseList <*>+ traverse traverseEntry dhtSearchList+ where+ traverseEntry entry =+ (\x -> entry{ searchClientList = x }) <$> f (searchClientList entry) +foldMapNodeLists :: Monoid m =>+ (forall l. NodeList l => l -> m) -> DhtState -> m+foldMapNodeLists f = getConst . traverseNodeLists (Const . f) -foldNodes :: (a -> NodeInfo -> a) -> a -> DhtState -> a-foldNodes =- foldBuckets . KBuckets.foldNodes+mapNodeLists :: (forall l. NodeList l => l -> l) -> DhtState -> DhtState+mapNodeLists f = runIdentity . traverseNodeLists (Identity . f) \end{code} +A node info is considered to be contained in the DHT State if it is contained+in the Close List or in at least one of the Search Entries.+ The size of the DHT state is defined to be the number of node infos it-contains. Node infos contained multiple times, e.g. as part of the close list-and as part of various search entries, are counted as many times as they-appear.+contains, counted with multiplicity: node infos contained multiple times, e.g.+in the close list and in various search entries, are counted as many times as+they appear. Search keys do not directly count towards the state size. So+the size of the state is the sum of the sizes of the Close List and the Search+Entries. -Search keys do not directly count towards the state size. The state size is-relevant to later pruning algorithms that decide when to remove a node info and-when to request a ping from stale nodes. Search keys, once added, are never-automatically pruned.+The state size is relevant to later pruning algorithms that decide when to+remove a node info and when to request a ping from stale nodes. Search keys,+once added, are never automatically pruned. \begin{code} size :: DhtState -> Int-size = foldNodes (flip $ const (1 +)) 0+size = NodeList.foldNodes (flip $ const (1 +)) 0 \end{code} -The bucket count of the state is the number of k-buckets instances. An empty-state contains one k-buckets instance. For each added search key, it contains-one additional k-buckets instance. Thus, the number of search keys is one less-than the bucket count.+Adding a Node Info to the state is done by adding the node to each Node List+in the state. +When adding a node info to the state, the search entry for the node's public+key, if it exists, is updated to contain the new node info. All k-buckets and+Client Lists that already contain the node info will also be updated. See the+corresponding specifications for the update algorithms. However, a node info+will not be added to a search entry when it is the node to which the search+entry is associated (i.e. the node being search for).+ \begin{code} -bucketCount :: DhtState -> Int-bucketCount = foldBuckets (flip $ const (1 +)) 0+addNode :: Timestamp -> NodeInfo -> DhtState -> DhtState+addNode time nodeInfo =+ updateSearchNode (NodeInfo.publicKey nodeInfo) (Just nodeInfo)+ . mapNodeLists addUnlessBase+ where+ addUnlessBase nodeList+ | NodeInfo.publicKey nodeInfo == NodeList.baseKey nodeList = nodeList+ addUnlessBase nodeList = NodeList.addNode time nodeInfo nodeList +removeNode :: PublicKey -> DhtState -> DhtState+removeNode publicKey =+ updateSearchNode publicKey Nothing+ . mapNodeLists (NodeList.removeNode publicKey) -updateSearchNode :: PublicKey -> Maybe NodeInfo -> DhtState -> DhtState-updateSearchNode publicKey nodeInfo dhtState@DhtState { dhtSearchList } =- dhtState- { dhtSearchList = Map.adjust update publicKey dhtSearchList- }- where- update entry = entry { searchNode = nodeInfo }+viable :: NodeInfo -> DhtState -> Bool+viable nodeInfo = getAll . foldMapNodeLists (All . NodeList.viable nodeInfo) +traverseClientLists ::+ Applicative f => (ClientList -> f ClientList) -> DhtState -> f DhtState+traverseClientLists f = traverseNodeLists $ NodeList.traverseClientLists f -mapBuckets :: (KBuckets -> KBuckets) -> DhtState -> DhtState-mapBuckets f dhtState@DhtState { dhtCloseList, dhtSearchList } =- dhtState- { dhtCloseList = f dhtCloseList- , dhtSearchList = Map.map updateSearchBucket dhtSearchList- }- where- updateSearchBucket entry@DhtSearchEntry { searchKBuckets } =- entry { searchKBuckets = f searchKBuckets }+closeNodes :: PublicKey -> DhtState -> [ (Distance, NodeInfo) ]+closeNodes publicKey =+ nub . sortBy (comparing fst) . foldMapNodeLists (NodeList.closeNodes publicKey) -\end{code}+-- | although it is not referred to as a Node List in the spec, we make DhtState+-- an instance of NodeList so we can use the traversal and folding functions.+instance NodeList DhtState where+ addNode = addNode+ removeNode = removeNode+ viable = viable+ baseKey = KeyPair.publicKey . dhtKeyPair+ traverseClientLists = traverseClientLists+ closeNodes = closeNodes -Adding a node info to the state is done by adding the node to each k-bucket in-the state, i.e. the close list and all the k-buckets in the search entries.+takeClosestNodesTo :: Int -> PublicKey -> DhtState -> [ NodeInfo ]+takeClosestNodesTo n publicKey = map snd . take n . closeNodes publicKey -When adding a node info to the state, the search entry for the node's public-key, if it exists, is updated to contain the new node info. All k-buckets that-already contain the node info will also be updated. See the k-buckets-specification for the update algorithm.+mapBuckets :: (KBuckets -> KBuckets) -> DhtState -> DhtState+mapBuckets f dhtState@DhtState { dhtCloseList } =+ dhtState+ { dhtCloseList = f dhtCloseList+ } -Recall that a k-buckets instance will never contain the node info for its base-key. Thus, when adding a node info for which a search entry exists, that node-info will not be added to the search entry's k-buckets instance.+mapSearchEntry :: (DhtSearchEntry -> DhtSearchEntry) -> DhtState -> DhtState+mapSearchEntry f dhtState@DhtState { dhtSearchList } =+ dhtState+ { dhtSearchList = Map.map f dhtSearchList+ } -\begin{code}+mapSearchClientLists :: (ClientList -> ClientList) -> DhtState -> DhtState+mapSearchClientLists f =+ mapSearchEntry $ \entry@DhtSearchEntry{ searchClientList } ->+ entry { searchClientList = f searchClientList } -addNode :: NodeInfo -> DhtState -> DhtState-addNode nodeInfo =- updateSearchNode (NodeInfo.publicKey nodeInfo) (Just nodeInfo)- . mapBuckets (KBuckets.addNode nodeInfo)+updateSearchNode :: PublicKey -> Maybe NodeInfo -> DhtState -> DhtState+updateSearchNode publicKey nodeInfo dhtState@DhtState { dhtSearchList } =+ dhtState+ { dhtSearchList = Map.adjust update publicKey dhtSearchList+ }+ where+ update entry = entry { searchNode = nodeInfo } \end{code} @@ -221,15 +333,9 @@ \begin{code} -removeNode :: PublicKey -> DhtState -> DhtState-removeNode publicKey =- updateSearchNode publicKey Nothing- . mapBuckets (KBuckets.removeNode publicKey)-- containsNode :: PublicKey -> DhtState -> Bool containsNode publicKey =- foldNodes (\a x -> a || NodeInfo.publicKey x == publicKey) False+ NodeList.foldNodes (\a x -> a || NodeInfo.publicKey x == publicKey) False {-------------------------------------------------------------------------------@@ -241,19 +347,19 @@ instance Arbitrary DhtState where arbitrary =- initialise <$> arbitrary <*> arbitrary <*> arbitrary+ initialise <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary where- initialise :: KeyPair -> [NodeInfo] -> [PublicKey] -> DhtState- initialise kp nis =- foldl (flip addSearchKey) (foldl (flip addNode) (empty kp) nis)+ initialise :: Timestamp -> KeyPair -> [(Timestamp, NodeInfo)] -> [(Timestamp, PublicKey)] -> DhtState+ initialise time kp nis =+ foldl (flip $ uncurry addSearchKey) (foldl (flip $ uncurry NodeList.addNode) (empty time kp) nis) shrink dhtState = Maybe.maybeToList shrunkNode ++ Maybe.maybeToList shrunkSearchKey where -- Remove the first node we can find in the state. shrunkNode = do- firstPK <- NodeInfo.publicKey <$> foldNodes (\a x -> a <|> Just x) Nothing dhtState- return $ removeNode firstPK dhtState+ firstPK <- NodeInfo.publicKey <$> NodeList.foldNodes (\a x -> a <|> Just x) Nothing dhtState+ return $ NodeList.removeNode firstPK dhtState shrunkSearchKey = Nothing
src/tox/Network/Tox/DHT/Distance.lhs view
@@ -1,15 +1,15 @@ \section{Distance} \begin{code}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE MagicHash #-}-{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE Trustworthy #-} module Network.Tox.DHT.Distance where import Control.Applicative ((<$>)) import Control.Arrow (first) import Data.Bits (xor) import Data.Monoid (Monoid, mappend, mempty)+import Data.Semigroup (Semigroup, (<>)) import GHC.Exts (Int (I#)) import GHC.Integer.Logarithms (integerLog2#) import Network.Tox.Crypto.Key (PublicKey)@@ -27,11 +27,9 @@ \end{code} A Distance is a positive integer. Its human-readable representation is a-base-16 number. Distance is an+base-16 number. Distance (type) is an \href{https://en.wikipedia.org/wiki/Ordered_semigroup}{ordered monoid} with the associative binary operator \texttt{+} and the identity element \texttt{0}.-When we speak of a "close node", we mean that their Distance to the node under-consideration is small compared to the Distance to other nodes. \begin{code} @@ -39,6 +37,9 @@ deriving (Eq, Ord) +instance Semigroup Distance where+ (Distance x) <> (Distance y) = Distance (x + y)+ instance Monoid Distance where mempty = Distance 0 mappend (Distance x) (Distance y) = Distance (x + y)@@ -58,19 +59,27 @@ \end{code} -The DHT needs a+The DHT uses a \href{https://en.wikipedia.org/wiki/Metric_(mathematics)}{metric} to determine-distance between two nodes. The Distance type is the co-domain of this metric.-The metric currently used by the Tox DHT is the \texttt{XOR} of the nodes'-public keys. The public keys are interpreted as Big Endian integers (see-\href{#key-1}{Crypto Numbers}).+the distance between two nodes. The Distance type is the co-domain of this+metric. The metric currently used by the Tox DHT is the \texttt{XOR} of the+nodes' public keys: \texttt{distance(x, y) = x XOR y}. For this computation,+public keys are interpreted as Big Endian integers (see \href{#key-1}{Crypto+Numbers}). +When we speak of a "close node", we mean that its Distance to the node under+consideration is small compared to the Distance to other nodes.+ \begin{code} xorDistance :: PublicKey -> PublicKey -> Distance xorDistance a b = Distance $ Key.keyToInteger a `xor` Key.keyToInteger b +-- | rebaseDistance a b (xorDistance a c) == xorDistance b c+rebaseDistance :: PublicKey -> PublicKey -> Distance -> Distance+rebaseDistance a b (Distance d) =+ Distance $ d `xor` Key.keyToInteger a `xor` Key.keyToInteger b {------------------------------------------------------------------------------- -@@ -80,7 +89,7 @@ instance Arbitrary Distance where- arbitrary = (Distance . abs) <$> arbitrary+ arbitrary = Distance . abs <$> arbitrary \end{code} An implementation is not required to provide a Distance type, so it has no
src/tox/Network/Tox/DHT/KBuckets.lhs view
@@ -10,20 +10,29 @@ {-# LANGUAGE Trustworthy #-} module Network.Tox.DHT.KBuckets where -import Control.Applicative ((<$>))+import Control.Applicative (Applicative, (<$>)) import Data.Binary (Binary)+import Data.Foldable (toList)+import Data.List (sortBy) import Data.Map (Map) import qualified Data.Map as Map+import Data.Maybe (isJust) import Data.Ord (comparing)+import Data.Traversable (Traversable, mapAccumR,+ traverse) import Data.Word (Word8) import Test.QuickCheck.Arbitrary (Arbitrary, arbitrary) import Test.QuickCheck.Gen (Gen) import qualified Test.QuickCheck.Gen as Gen import Network.Tox.Crypto.Key (PublicKey)+import Network.Tox.DHT.ClientList (ClientList)+import qualified Network.Tox.DHT.ClientList as ClientList+import Network.Tox.DHT.Distance (Distance) import qualified Network.Tox.DHT.Distance as Distance import Network.Tox.NodeInfo.NodeInfo (NodeInfo) import qualified Network.Tox.NodeInfo.NodeInfo as NodeInfo+import Network.Tox.Time (Timestamp) {-------------------------------------------------------------------------------@@ -34,16 +43,17 @@ \end{code} -A k-buckets is a map from small integers \texttt{0 <= n < 256} to a set of up-to \texttt{k} Node Infos. The set is called a bucket. \texttt{k} is called-the bucket size. The default bucket size is 8.+A k-buckets is a map from small integers \texttt{0 <= n < 256} to Client Lists+of maximum size $k$. Each Client List is called a (k-)bucket. A k-buckets is+equipped with a base key, and each bucket has this key as its base key.+\texttt{k} is called the bucket size. The default bucket size is 8.+A large bucket size was chosen to increase the speed at which peers are found. \begin{code} - data KBuckets = KBuckets { bucketSize :: Int- , buckets :: Map KBucketIndex KBucket+ , buckets :: Map KBucketIndex ClientList , baseKey :: PublicKey } deriving (Eq, Read, Show)@@ -56,11 +66,10 @@ empty :: PublicKey -> KBuckets empty = KBuckets defaultBucketSize Map.empty - \end{code} -The number \texttt{n} is the bucket index. It is positive integer with the-range \texttt{[0, 255]}, i.e. the range of an 8 bit unsigned integer.+The above number \texttt{n} is the bucket index. It is a non-negative integer+with the range \texttt{[0, 255]}, i.e. the range of an 8 bit unsigned integer. \begin{code} @@ -71,59 +80,18 @@ \end{code} -A bucket entry is an element of the bucket. The bucket is an ordered set, and-the entries are sorted by \href{#distance}{distance} from the base key. Thus,-the first (smallest) element of the set is the closest one to the base key in-that set, the last (greatest) element is the furthest away.--\begin{code}---newtype KBucket = KBucket- { bucketNodes :: Map PublicKey KBucketEntry- }- deriving (Eq, Read, Show)---emptyBucket :: KBucket-emptyBucket = KBucket Map.empty---bucketIsEmpty :: KBucket -> Bool-bucketIsEmpty = Map.null . bucketNodes---data KBucketEntry = KBucketEntry- { entryBaseKey :: PublicKey- , entryNode :: NodeInfo- }- deriving (Eq, Read, Show)--instance Ord KBucketEntry where- compare = comparing distance- where- distance entry =- Distance.xorDistance- (entryBaseKey entry)- (NodeInfo.publicKey $ entryNode entry)---entryPublicKey :: KBucketEntry -> PublicKey-entryPublicKey = NodeInfo.publicKey . entryNode---\end{code}- \subsection{Bucket Index} -The bucket index can be computed using the following function:-\texttt{bucketIndex(baseKey, nodeKey) = 255 - log_2(distance(baseKey,+The index of the bucket can be computed using the following function:+\texttt{bucketIndex(baseKey, nodeKey) = 255 - log\_2(distance(baseKey, nodeKey))}. This function is not defined when \texttt{baseKey == nodeKey},-meaning k-buckets will never contain a Node Info about the local node.+meaning k-buckets will never contain a Node Info about the base node. Thus, each k-bucket contains only Node Infos for whose keys the following holds: if node with key \texttt{nodeKey} is in k-bucket with index \texttt{n},-then \texttt{bucketIndex(baseKey, nodeKey) == n}.+then \texttt{bucketIndex(baseKey, nodeKey) == n}. Thus, n'th k-bucket consists+of nodes for which distance to the base node lies in range+\verb![2^n, 2^(n+1) - 1]!. The bucket index can be efficiently computed by determining the first bit at which the two keys differ, starting from the most significant bit. So, if the@@ -144,6 +112,11 @@ \subsection{Manipulating k-buckets} +TODO: this is different from kademlia's least-recently-seen eviction policy; why+the existing solution was chosen, how does it affect security, performance and+resistance to poisoning? original paper claims that preference of old live nodes+results in better persistence and resistance to basic DDoS attacks;+ Any update or lookup operation on a k-buckets instance that involves a single node requires us to first compute the bucket index for that node. An update involving a Node Info with \texttt{nodeKey == baseKey} has no effect. If the@@ -152,21 +125,21 @@ \begin{code} -updateBucketForKey :: KBuckets -> PublicKey -> (KBucket -> KBucket) -> KBuckets+updateBucketForKey :: KBuckets -> PublicKey -> (ClientList -> ClientList) -> KBuckets updateBucketForKey kBuckets key f = case bucketIndex (baseKey kBuckets) key of Nothing -> kBuckets Just index -> updateBucketForIndex kBuckets index f -updateBucketForIndex :: KBuckets -> KBucketIndex -> (KBucket -> KBucket) -> KBuckets-updateBucketForIndex kBuckets@KBuckets { buckets } index f =+updateBucketForIndex :: KBuckets -> KBucketIndex -> (ClientList -> ClientList) -> KBuckets+updateBucketForIndex kBuckets@KBuckets { buckets, baseKey, bucketSize } index f = let -- Find the old bucket or create a new empty one.- updatedBucket = f $ Map.findWithDefault emptyBucket index buckets+ updatedBucket = f $ Map.findWithDefault (ClientList.empty baseKey bucketSize) index buckets -- Replace old bucket with updated bucket or delete if empty. updatedBuckets =- if bucketIsEmpty updatedBucket+ if ClientList.isEmpty updatedBucket then Map.delete index buckets else Map.insert index updatedBucket buckets in@@ -175,59 +148,39 @@ \end{code} -A bucket is \textit{full} when the bucket contains the maximum number of-entries configured by the bucket size.--A node is \textit{viable} for entry if the bucket is not \textit{full} or the-node's public key has a lower distance from the base key than the current entry-with the greatest distance.--If a node is \textit{viable} and the bucket is \textit{full}, the entry with-the greatest distance from the base key is removed to keep the bucket size-below the maximum configured bucket size.--Adding a node whose key already exists will result in an update of the Node-Info in the bucket. Removing a node for which no Node Info exists in the-k-buckets has no effect. Thus, removing a node twice is permitted and has the-same effect as removing it once.+Adding a node to, or removing a node from, a k-buckets consists of performing+the corresponding operation on the Client List bucket whose index is that of+the node's public key, except that adding a new node to a full bucket has no+effect. A node is considered \textit{viable} for entry if the corresponding+bucket is not full. \begin{code} --addNode :: NodeInfo -> KBuckets -> KBuckets-addNode nodeInfo kBuckets =- updateBucketForKey kBuckets (NodeInfo.publicKey nodeInfo) $ \bucket ->+addNode :: Timestamp -> NodeInfo -> KBuckets -> KBuckets+addNode time nodeInfo kBuckets =+ updateBucketForKey kBuckets publicKey $ \clientList -> let- -- The new entry.- entry = KBucketEntry (baseKey kBuckets) nodeInfo+ full = ClientList.full clientList+ alreadyIn = isJust $ ClientList.lookup publicKey clientList in- -- Insert the entry into the bucket.- addNodeToBucket (bucketSize kBuckets) entry bucket---addNodeToBucket :: Int -> KBucketEntry -> KBucket -> KBucket-addNodeToBucket maxSize entry =- KBucket . truncateMap maxSize . Map.insert (entryPublicKey entry) entry . bucketNodes---truncateMap :: Ord a => Int -> Map k a -> Map k a-truncateMap maxSize m- | Map.size m <= maxSize = m- | otherwise =- -- Remove the greatest element until the map is small enough again.- truncateMap maxSize $ Map.deleteMax m---removeNodeFromBucket :: PublicKey -> KBucket -> KBucket-removeNodeFromBucket publicKey =- KBucket . Map.delete publicKey . bucketNodes-+ if not full || alreadyIn+ then ClientList.addNode time nodeInfo clientList+ else clientList+ where+ publicKey = NodeInfo.publicKey nodeInfo removeNode :: PublicKey -> KBuckets -> KBuckets removeNode publicKey kBuckets =- updateBucketForKey kBuckets publicKey $ \bucket ->- removeNodeFromBucket publicKey bucket+ updateBucketForKey kBuckets publicKey $ ClientList.removeNode publicKey +viable :: NodeInfo -> KBuckets -> Bool+viable nodeInfo KBuckets{ baseKey, buckets } =+ case bucketIndex baseKey $ NodeInfo.publicKey nodeInfo of+ Nothing -> False+ Just index -> case Map.lookup index buckets of+ Nothing -> True+ Just bucket -> not $ ClientList.full bucket+ \end{code} Iteration order of a k-buckets instance is in order of distance from the base@@ -236,11 +189,30 @@ \begin{code} -foldNodes :: (a -> NodeInfo -> a) -> a -> KBuckets -> a-foldNodes f x =- foldl f x . concatMap (map entryNode . Map.elems . bucketNodes) . Map.elems . buckets+traverseClientLists ::+ Applicative f => (ClientList -> f ClientList) -> KBuckets -> f KBuckets+traverseClientLists f kBuckets@KBuckets{ buckets } =+ (\x -> kBuckets{ buckets = x }) <$> traverse f (reverseT buckets)+ where+ reverseT :: (Traversable t) => t a -> t a+ reverseT t = snd (mapAccumR (\ (x:xs) _ -> (xs, x)) (toList t) t) +closeNodes :: PublicKey -> KBuckets -> [ (Distance, NodeInfo) ]+closeNodes publicKey KBuckets{ baseKey, buckets } =+ let+ (further, at, nearer) = case bucketIndex baseKey publicKey of+ Nothing -> (buckets, Nothing, Map.empty)+ Just index -> Map.splitLookup index buckets+ clientClose = ClientList.closeNodes publicKey+ bucketsClose = sortBy (comparing fst) . concatMap clientClose+ in+ concat+ [ maybe [] clientClose at+ , bucketsClose $ Map.elems nearer+ , bucketsClose $ Map.elems further+ ] + {------------------------------------------------------------------------------- - - :: Tests.@@ -250,12 +222,12 @@ getAllNodes :: KBuckets -> [NodeInfo] getAllNodes =- concatMap (map entryNode . Map.elems . bucketNodes) . Map.elems . buckets+ concatMap ClientList.nodeInfos . Map.elems . buckets genKBuckets :: PublicKey -> Gen KBuckets genKBuckets publicKey =- foldl (flip addNode) (empty publicKey) <$> Gen.listOf arbitrary+ foldl (flip $ uncurry addNode) (empty publicKey) <$> Gen.listOf arbitrary instance Arbitrary KBuckets where
+ src/tox/Network/Tox/DHT/NodeList.lhs view
@@ -0,0 +1,75 @@+The Close List and the Search Entries are termed the \texttt{Node Lists} of+the DHT State.++\begin{code}+module Network.Tox.DHT.NodeList where++import Control.Applicative (Applicative, Const (..),+ getConst)+import Control.Monad (guard)+import Data.Maybe (listToMaybe)+import Data.Monoid (Dual (..), Endo (..), Monoid,+ appEndo, getDual, mempty)++import Network.Tox.Crypto.Key (PublicKey)+import Network.Tox.DHT.ClientList (ClientList)+import qualified Network.Tox.DHT.ClientList as ClientList+import Network.Tox.DHT.Distance (Distance)+import Network.Tox.DHT.KBuckets (KBuckets)+import qualified Network.Tox.DHT.KBuckets as KBuckets+import Network.Tox.NodeInfo.NodeInfo (NodeInfo)+import Network.Tox.Time (Timestamp)++class NodeList l where+ addNode :: Timestamp -> NodeInfo -> l -> l++ removeNode :: PublicKey -> l -> l++ viable :: NodeInfo -> l -> Bool++ baseKey :: l -> PublicKey++ traverseClientLists ::+ Applicative f => (ClientList -> f ClientList) -> l -> f l++ -- | 'closeNodes pub' returns the (pub',node) pairs of the Node List in+ -- increasing order of distance of pub' from pub.+ closeNodes :: PublicKey -> l -> [(Distance, NodeInfo)]++ -- | copied from Data.Traversable.foldMapDefault+ foldMapClientLists :: Monoid m => (ClientList -> m) -> l -> m+ foldMapClientLists f = getConst . traverseClientLists (Const . f)++ -- | copied from Data.Foldable.foldl+ foldlClientLists :: (a -> ClientList -> a) -> a -> l -> a+ foldlClientLists f z t =+ appEndo (getDual (foldMapClientLists (Dual . Endo . flip f) t)) z++ nodeListList :: l -> [NodeInfo]+ nodeListList = foldMapClientLists ClientList.nodeInfos++ foldNodes :: (a -> NodeInfo -> a) -> a -> l -> a+ foldNodes = foldlClientLists . ClientList.foldNodes++ lookupPublicKey :: PublicKey -> l -> Maybe NodeInfo+ lookupPublicKey publicKey list = do+ (dist,node) <- listToMaybe $ closeNodes publicKey list+ guard (dist == mempty)+ Just node++instance NodeList ClientList where+ addNode = ClientList.addNode+ removeNode = ClientList.removeNode+ viable = ClientList.viable+ baseKey = ClientList.baseKey+ traverseClientLists = id+ closeNodes = ClientList.closeNodes++instance NodeList KBuckets where+ addNode = KBuckets.addNode+ removeNode = KBuckets.removeNode+ viable = KBuckets.viable+ baseKey = KBuckets.baseKey+ traverseClientLists = KBuckets.traverseClientLists+ closeNodes = KBuckets.closeNodes+\end{code}
src/tox/Network/Tox/DHT/NodesRequest.lhs view
@@ -30,7 +30,7 @@ ------------------------------------------------------------------------------} -data NodesRequest = NodesRequest+newtype NodesRequest = NodesRequest { requestedKey :: PublicKey } deriving (Eq, Read, Show, Generic, Typeable)
src/tox/Network/Tox/DHT/NodesResponse.lhs view
@@ -7,11 +7,11 @@ \texttt{[39, 204]} & Node Infos & Nodes in Packed Node Format \\ \end{tabular} -An IPv4 node is 39 bytes, an IPv6 node is 51 bytes, so the maximum size is-\texttt{51 * 4 = 204} bytes.+An IPv4 node is 39 bytes, an IPv6 node is 51 bytes, so the maximum size of the+packed Node Infos is \texttt{51 * 4 = 204} bytes. Nodes responses should contain the 4 closest nodes that the sender of the-response has in their list of known nodes.+response has in their lists of known nodes. \begin{code} {-# LANGUAGE DeriveDataTypeable #-}@@ -37,7 +37,7 @@ ------------------------------------------------------------------------------} -data NodesResponse = NodesResponse+newtype NodesResponse = NodesResponse { foundNodes :: [NodeInfo] } deriving (Eq, Read, Show, Generic, Typeable)
+ src/tox/Network/Tox/DHT/Operation.lhs view
@@ -0,0 +1,590 @@+\section{DHT Operation}++\begin{code}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE Safe #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeSynonymInstances #-}+module Network.Tox.DHT.Operation where++import Control.Applicative (Applicative, pure, (<$>),+ (<*>))+import Control.Monad (guard, msum, replicateM,+ unless, void, when)+import Control.Monad.Identity (Identity, runIdentity)+import Control.Monad.Random (RandT, evalRandT)+import Control.Monad.State (MonadState, StateT,+ execStateT, get, gets,+ modify, put, runStateT)+import Control.Monad.Trans (lift)+import Control.Monad.Trans.Maybe (MaybeT (..), runMaybeT)+import Control.Monad.Writer (MonadWriter, WriterT,+ execWriterT, tell)+import Data.Binary (Binary)+import Data.Foldable (for_)+import Data.Functor (($>))+import Data.Map (Map)+import qualified Data.Map as Map+import Data.Maybe (isNothing)+import Data.Traversable (traverse)+import Lens.Family2 (Lens')+import Lens.Family2.State (zoom, (%%=), (%=))+import System.Random (StdGen, mkStdGen)+import Test.QuickCheck.Arbitrary (Arbitrary, arbitrary)++import Network.Tox.Crypto.Key (PublicKey)+import Network.Tox.Crypto.Keyed (Keyed)+import Network.Tox.Crypto.KeyedT (KeyedT)+import qualified Network.Tox.Crypto.KeyedT as KeyedT+import qualified Network.Tox.Crypto.KeyPair as KeyPair+import Network.Tox.DHT.ClientList (ClientList)+import qualified Network.Tox.DHT.ClientList as ClientList+import Network.Tox.DHT.ClientNode (ClientNode)+import qualified Network.Tox.DHT.ClientNode as ClientNode+import qualified Network.Tox.DHT.DhtPacket as DhtPacket+import Network.Tox.DHT.DhtRequestPacket (DhtRequestPacket (..))+import Network.Tox.DHT.DhtState (DhtState)+import qualified Network.Tox.DHT.DhtState as DhtState+import Network.Tox.DHT.NodeList (NodeList)+import qualified Network.Tox.DHT.NodeList as NodeList+import Network.Tox.DHT.NodesRequest (NodesRequest (..))+import Network.Tox.DHT.NodesResponse (NodesResponse (..))+import qualified Network.Tox.DHT.PendingReplies as PendingReplies+import Network.Tox.DHT.PingPacket (PingPacket (..))+import Network.Tox.DHT.RpcPacket (RpcPacket (..))+import qualified Network.Tox.DHT.RpcPacket as RpcPacket+import qualified Network.Tox.DHT.Stamped as Stamped+import Network.Tox.Network.MonadRandomBytes (MonadRandomBytes)+import qualified Network.Tox.Network.MonadRandomBytes as MonadRandomBytes+import Network.Tox.Network.Networked (Networked)+import qualified Network.Tox.Network.Networked as Networked+import Network.Tox.NodeInfo.NodeInfo (NodeInfo)+import qualified Network.Tox.NodeInfo.NodeInfo as NodeInfo+import Network.Tox.Protocol.Packet (Packet (..))+import Network.Tox.Protocol.PacketKind (PacketKind)+import qualified Network.Tox.Protocol.PacketKind as PacketKind+import Network.Tox.Time (TimeDiff, Timestamp)+import qualified Network.Tox.Time as Time+import Network.Tox.Timed (Timed)+import qualified Network.Tox.Timed as Timed+import Network.Tox.TimedT (TimedT)+import qualified Network.Tox.TimedT as TimedT+++{-------------------------------------------------------------------------------+ -+ - :: Implementation.+ -+ ------------------------------------------------------------------------------}++class+ ( Networked m+ , Timed m+ , MonadRandomBytes m+ , MonadState DhtState m+ , Keyed m+ ) => DhtNodeMonad m where {}++data RequestInfo = RequestInfo+ { requestTo :: NodeInfo+ , requestSearch :: PublicKey+ }+ deriving (Eq, Read, Show)++sendDhtPacket :: (DhtNodeMonad m, Binary payload) =>+ NodeInfo -> PacketKind -> payload -> m ()+sendDhtPacket to kind payload = do+ keyPair <- gets DhtState.dhtKeyPair+ nonce <- MonadRandomBytes.randomNonce+ Networked.sendPacket to . Packet kind =<<+ DhtPacket.encodeKeyed keyPair (NodeInfo.publicKey to) nonce payload++sendRpcRequest :: (DhtNodeMonad m, Binary payload) =>+ NodeInfo -> PacketKind -> payload -> m ()+sendRpcRequest to packetKind payload = do+ requestId <- RpcPacket.RequestId <$> MonadRandomBytes.randomWord64+ time <- Timed.askTime+ DhtState._dhtPendingReplies %= PendingReplies.expectReply time to requestId+ sendDhtPacket to packetKind $+ RpcPacket payload requestId++sendNodesRequest :: DhtNodeMonad m => RequestInfo -> m ()+sendNodesRequest (RequestInfo to key) =+ sendRpcRequest to PacketKind.NodesRequest $ NodesRequest key++sendNodesResponse ::+ DhtNodeMonad m => NodeInfo -> RpcPacket.RequestId -> [NodeInfo] -> m ()+sendNodesResponse to requestId nodes =+ sendDhtPacket to PacketKind.NodesResponse $+ RpcPacket (NodesResponse nodes) requestId++sendPingRequest :: DhtNodeMonad m => NodeInfo -> m ()+sendPingRequest to =+ sendRpcRequest to PacketKind.PingRequest PingRequest++sendPingResponse ::+ DhtNodeMonad m => NodeInfo -> RpcPacket.RequestId -> m ()+sendPingResponse to requestId =+ sendDhtPacket to PacketKind.PingResponse $+ RpcPacket PingResponse requestId++modifyM :: MonadState s m => (s -> m s) -> m ()+modifyM = (put =<<) . (get >>=)++-- | adapted from michaelt's lens-simple:+-- zoom_ is like zoom but for convenience returns an mtl style+-- abstracted MonadState state, rather than a concrete StateT, recapturing+-- a bit more of the abstractness of Control.Lens.zoom+zoom_ :: MonadState s' m => Lens' s' s -> StateT s m a -> m a+-- full signature:+-- zoom_ :: MonadState s' m =>+-- LensLike' (Zooming m a) s' s -> StateT s m a -> m a+zoom_ l f = abstract $ zoom l f+ where+ abstract :: MonadState s m => StateT s m a -> m a+ abstract st = do+ (a,s') <- runStateT st =<< get+ put s'+ return a++\end{code}++\subsection{DHT Initialisation}+A new DHT node is initialised with a DHT State with a fresh random key pair, an+empty close list, and a search list containing 2 empty search entries searching+for randomly generated public keys.++\begin{code}++initRandomSearches :: Int+initRandomSearches = 2++initDht :: (MonadRandomBytes m, Timed m) => m DhtState+initDht = do+ dhtState <- DhtState.empty <$> Timed.askTime <*> MonadRandomBytes.newKeyPair+ time <- Timed.askTime+ (`execStateT` dhtState) $ replicateM initRandomSearches $ do+ publicKey <- MonadRandomBytes.randomKey+ DhtState._dhtSearchList %=+ Map.insert publicKey (DhtState.emptySearchEntry time publicKey)++bootstrapNode :: DhtNodeMonad m => NodeInfo -> m ()+bootstrapNode nodeInfo =+ sendNodesRequest . RequestInfo nodeInfo =<<+ KeyPair.publicKey <$> gets DhtState.dhtKeyPair++-- TODO+--loadDHT :: ??++\end{code}++\subsection{Periodic sending of Nodes Requests}+For each Nodes List in the DHT State, every 20 seconds we send a Nodes Request+to a random node on the list, searching for the base key of the list.++When a Nodes List first becomes populated with nodes, we send 5 such random+Nodes Requests in quick succession.++Random nodes are chosen since being able to predict which node a node will+send a request to next could make some attacks that disrupt the network+easier, as it adds a possible attack vector.++\begin{code}++randomRequestPeriod :: TimeDiff+randomRequestPeriod = Time.seconds 20++maxBootstrapTimes :: Int+maxBootstrapTimes = 5++randomRequests :: DhtNodeMonad m => WriterT [RequestInfo] m ()+randomRequests = do+ closeList <- gets DhtState.dhtCloseList+ zoom_ DhtState._dhtCloseListStamp $ doList closeList+ zoom_ DhtState._dhtSearchList .+ modifyM . traverse . execStateT $ do+ searchList <- gets DhtState.searchClientList+ zoom_ DhtState._searchStamp $ doList searchList+ where+ doList ::+ ( NodeList l+ , Timed m+ , MonadRandomBytes m+ , MonadState DhtState.ListStamp m+ , MonadWriter [RequestInfo] m+ ) => l -> m ()+ doList nodeList =+ case NodeList.nodeListList nodeList of+ [] -> return ()+ nodes -> do+ time <- Timed.askTime+ DhtState.ListStamp lastTime bootstrapped <- get+ when (time Time.- lastTime >= randomRequestPeriod+ || bootstrapped < maxBootstrapTimes) $ do+ node <- MonadRandomBytes.uniform nodes+ tell [RequestInfo node $ NodeList.baseKey nodeList]+ put $ DhtState.ListStamp time (bootstrapped + 1)++\end{code}++Furthermore, we periodically check every node for responsiveness by sending it a+Nodes Request: for each Nodes List in the DHT State, we send each node on the+list a Nodes Request every 60 seconds, searching for the base key of the list.+We remove from the DHT State any node from which we persistently fail to receive+Nodes Responses.++c-toxcore's implementation of checking and timeouts:+A Last Checked time is maintained for each node in each list. When a node is+added to a list, if doing so evicts a node from the list then the Last Checked+time is set to that of the evicted node, and otherwise it is set to 0. This+includes updating an already present node. Nodes from which we have not+received a Nodes Response for 122 seconds are considered Bad; they remain in the+DHT State, but are preferentially overwritten when adding to the DHT State, and+are ignored for all operations except the once-per-60s checking described above.+If we have not received a Nodes Response for 182 seconds, the node is not even+checked. So one check is sent after the node becomes Bad. In the special case+that every node in the Close List is Bad, they are all checked once more.)++hs-toxcore implementation of checking and timeouts:+We maintain a Last Checked timestamp and a Checks Counter on each node on each+Nodes List in the Dht State. When a node is added to a list, these are set+respectively to the current time and to 0. This includes updating an already+present node. We periodically pass through the nodes on the lists, and for each+which is due a check, we: check it, update the timestamp, increment the counter,+and, if the counter is then 2, remove the node from the list. This is pretty+close to the behaviour of c-toxcore, but much simpler. TODO: currently hs-toxcore+doesn't do anything to try to recover if the Close List becomes empty. We could+maintain a separate list of the most recently heard from nodes, and repopulate+the Close List with that if the Close List becomes empty.++\begin{code}++checkPeriod :: TimeDiff+checkPeriod = Time.seconds 60++maxChecks :: Int+maxChecks = 2++checkNodes :: forall m. DhtNodeMonad m => WriterT [RequestInfo] m ()+checkNodes = modifyM $ DhtState.traverseClientLists checkNodes'+ where+ checkNodes' :: ClientList -> WriterT [RequestInfo] m ClientList+ checkNodes' clientList =+ (\x -> clientList{ ClientList.nodes = x }) <$>+ traverseMaybe checkNode (ClientList.nodes clientList)+ where+ traverseMaybe :: Applicative f =>+ (a -> f (Maybe b)) -> Map k a -> f (Map k b)+ traverseMaybe f = (Map.mapMaybe id <$>) . traverse f++ checkNode :: ClientNode -> WriterT [RequestInfo] m (Maybe ClientNode)+ checkNode clientNode = Timed.askTime >>= \time ->+ if time Time.- lastCheck < checkPeriod+ then pure $ Just clientNode+ else (tell [requestInfo] $>) $+ if checkCount + 1 < maxChecks+ then Just $ clientNode+ { ClientNode.lastCheck = time+ , ClientNode.checkCount = checkCount + 1+ }+ else Nothing+ where+ nodeInfo = ClientNode.nodeInfo clientNode+ lastCheck = ClientNode.lastCheck clientNode+ checkCount = ClientNode.checkCount clientNode+ requestInfo = RequestInfo nodeInfo $ NodeList.baseKey clientList++doDHT :: DhtNodeMonad m => m ()+doDHT =+ execWriterT (randomRequests >> checkNodes) >>= mapM_ sendNodesRequest+++\end{code}++\subsection{Handling Nodes Response packets}+When we receive a valid Nodes Response packet, we first check that it is a reply+to a Nodes Request which we sent within the last 60 seconds to the node from+which we received the response, and that no previous reply has been received. If+this check fails, the packet is ignored. If the check succeeds, first we add to+the DHT State the node from which the response was sent. Then, for each node+listed in the response and for each Nodes List in the DHT State which does not+currently contain the node and to which the node is viable for entry, we send a+Nodes Request to the node with the requested public key being the base key of+the Nodes List.++An implementation may choose not to send every such Nodes Request.+(c-toxcore only sends so many per list (8 for the Close List, 4 for a Search+Entry) per 50ms, prioritising the closest to the base key).++\begin{code}++requireNodesResponseWithin :: TimeDiff+requireNodesResponseWithin = Time.seconds 60++handleNodesResponse ::+ DhtNodeMonad m => NodeInfo -> RpcPacket NodesResponse -> m ()+handleNodesResponse from (RpcPacket (NodesResponse nodes) requestId) = do+ isReply <- checkPending requireNodesResponseWithin from requestId+ when isReply $ do+ time <- Timed.askTime+ modify $ DhtState.addNode time from+ for_ nodes $ \node ->+ (>>= mapM_ sendNodesRequest) $ (<$> get) $ DhtState.foldMapNodeLists $+ \nodeList ->+ guard (isNothing (NodeList.lookupPublicKey+ (NodeInfo.publicKey node) nodeList)+ && NodeList.viable node nodeList) >>+ [ RequestInfo node $ NodeList.baseKey nodeList ]++\end{code}++\subsection{Handling Nodes Request packets}+When we receive a Nodes Request packet from another node, we reply with a Nodes+Response packet containing the 4 nodes in the DHT State which are the closest to+the public key in the packet. If there are fewer than 4 nodes in the state, we+reply with all the nodes in the state. If there are no nodes in the state, no+reply is sent.++We also send a Ping Request when this is appropriate; see below.++\begin{code}++responseMaxNodes :: Int+responseMaxNodes = 4++handleNodesRequest ::+ DhtNodeMonad m => NodeInfo -> RpcPacket NodesRequest -> m ()+handleNodesRequest from (RpcPacket (NodesRequest key) requestId) = do+ ourPublicKey <- gets $ KeyPair.publicKey . DhtState.dhtKeyPair+ when (ourPublicKey /= NodeInfo.publicKey from) $ do+ nodes <- gets (DhtState.takeClosestNodesTo responseMaxNodes key)+ unless (null nodes) $ sendNodesResponse from requestId nodes+ sendPingRequestIfAppropriate from++\end{code}++\subsection{Handling Ping Request packets}+When a valid Ping Request packet is received, we reply with a Ping Response.++We also send a Ping Request when this is appropriate; see below.++\begin{code}++handlePingRequest ::+ DhtNodeMonad m => NodeInfo -> RpcPacket PingPacket -> m ()+handlePingRequest from (RpcPacket PingRequest requestId) = do+ sendPingResponse from requestId+ sendPingRequestIfAppropriate from+handlePingRequest _ _ = return ()++\end{code}++\subsection{Handling Ping Response packets}+When we receive a valid Ping Response packet, we first check that it is a reply+to a Ping Request which we sent within the last 5 seconds to the node from+which we received the response, and that no previous reply has been received. If+this check fails, the packet is ignored. If the check succeeds, we add to the+DHT State the node from which the response was sent.++\begin{code}++requirePingResponseWithin :: TimeDiff+requirePingResponseWithin = Time.seconds 5++maxPendingTime :: TimeDiff+maxPendingTime = maximum+ [ requireNodesResponseWithin+ , requirePingResponseWithin+ ]++checkPending :: DhtNodeMonad m =>+ TimeDiff -> NodeInfo -> RpcPacket.RequestId -> m Bool+checkPending timeLimit from requestId = do+ oldTime <- (Time.+ negate maxPendingTime) <$> Timed.askTime+ DhtState._dhtPendingReplies %= Stamped.dropOlder oldTime+ recentCutoff <- (Time.+ negate timeLimit) <$> Timed.askTime+ DhtState._dhtPendingReplies %%=+ PendingReplies.checkExpectedReply recentCutoff from requestId++handlePingResponse ::+ DhtNodeMonad m => NodeInfo -> RpcPacket PingPacket -> m ()+handlePingResponse from (RpcPacket PingResponse requestId) = do+ isReply <- checkPending requirePingResponseWithin from requestId+ ourPublicKey <- gets $ KeyPair.publicKey . DhtState.dhtKeyPair+ when (isReply && ourPublicKey /= NodeInfo.publicKey from) $ do+ time <- Timed.askTime+ modify $ DhtState.addNode time from+handlePingResponse _ _ = return ()++\end{code}++\subsection{Sending Ping Requests}+When we receive a Nodes Request or a Ping Request, in addition to the handling+described above, we sometimes send a Ping Request.+Namely, we send a Ping Request to the node which sent the packet if the node is+viable for entry to the Close List and is not already in the Close List.+An implementation may (TODO: should?) choose not to send every such Ping+Request.+(c-toxcore sends at most 32 every 2 seconds, preferring closer nodes.)++\begin{code}++sendPingRequestIfAppropriate :: DhtNodeMonad m => NodeInfo -> m ()+sendPingRequestIfAppropriate from = do+ closeList <- gets DhtState.dhtCloseList+ when+ (isNothing (NodeList.lookupPublicKey (NodeInfo.publicKey from) closeList)+ && NodeList.viable from closeList) $+ sendPingRequest from++\end{code}++\input{src/tox/Network/Tox/DHT/DhtRequestPacket.lhs}+\subsection{Handling DHT Request packets}++A DHT node that receives a DHT request packet checks whether the addressee+public key is their DHT public key. If it is, they will decrypt and handle+the packet. Otherwise, they will check whether the addressee DHT public key+is the DHT public key of one of the nodes in their Close List. If it isn't,+they will drop the packet. If it is they will resend the packet, unaltered, to+that DHT node.++DHT request packets are used for DHT public key packets (see+\href{#onion}{onion}) and NAT ping packets.++\begin{code}++handleDhtRequestPacket :: DhtNodeMonad m => NodeInfo -> DhtRequestPacket -> m ()+handleDhtRequestPacket _from packet@(DhtRequestPacket addresseePublicKey dhtPacket) = do+ keyPair <- gets DhtState.dhtKeyPair+ if addresseePublicKey == KeyPair.publicKey keyPair+ then void . runMaybeT $ msum+ [ (MaybeT $ DhtPacket.decodeKeyed keyPair dhtPacket) >>= lift . handleNatPingPacket+ , (MaybeT $ DhtPacket.decodeKeyed keyPair dhtPacket) >>= lift . handleDhtPKPacket+ ]+ else void . runMaybeT $ do+ node :: NodeInfo <- MaybeT $+ NodeList.lookupPublicKey addresseePublicKey <$> gets DhtState.dhtCloseList+ lift . Networked.sendPacket node . Packet PacketKind.Crypto $ packet++\end{code}++\subsection{NAT ping packets}++A NAT ping packet is sent as the payload of a DHT request packet.++We use NAT ping packets to see if a friend we are not connected to directly is+online and ready to do the hole punching.++\subsubsection{NAT ping request}++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0xfe) \\+ \texttt{1} & \texttt{uint8\_t} (0x00) \\+ \texttt{8} & \texttt{uint64\_t} random number \\+\end{tabular}++\subsubsection{NAT ping response}++\begin{tabular}{l|l}+ Length & Contents \\+ \hline+ \texttt{1} & \texttt{uint8\_t} (0xfe) \\+ \texttt{1} & \texttt{uint8\_t} (0x01) \\+ \texttt{8} & \texttt{uint64\_t} random number (the same that was received in request) \\+\end{tabular}++TODO: handling these packets.++\begin{code}++-- | TODO+type NatPingPacket = ()+handleNatPingPacket :: DhtNodeMonad m => NatPingPacket -> m ()+handleNatPingPacket _ = return ()++-- | TODO+type DhtPKPacket = ()+handleDhtPKPacket :: DhtNodeMonad m => DhtPKPacket -> m ()+handleDhtPKPacket _ = return ()++\end{code}++\subsection{Effects of chosen constants on performance}+If the bucket size of the k-buckets were increased, it would increase the+amount of packets needed to check if each node is still alive, which would+increase the bandwidth usage, but reliability would go up. If the number of+nodes were decreased, reliability would go down along with bandwidth usage.+The reason for this relationship between reliability and number of nodes is+that if we assume that not every node has its UDP ports open or is behind a+cone NAT it means that each of these nodes must be able to store a certain+number of nodes behind restrictive NATs in order for others to be able to find+those nodes behind restrictive NATs. For example if 7/8 nodes were behind+restrictive NATs, using 8 nodes would not be enough because the chances of+some of these nodes being impossible to find in the network would be too high.++TODO(zugz): this seems a rather wasteful solution to this problem.++If the ping timeouts and delays between pings were higher it would decrease the+bandwidth usage but increase the amount of disconnected nodes that are still+being stored in the lists. Decreasing these delays would do the opposite.++If the maximum size 8 of the DHT Search Entry Client Lists were increased+would increase the bandwidth usage, might increase hole punching efficiency on+symmetric NATs (more ports to guess from, see Hole punching) and might increase+the reliability. Lowering this number would have the opposite effect.++The timeouts and number of nodes in lists for toxcore were picked by feeling+alone and are probably not the best values. This also applies to the behavior+which is simple and should be improved in order to make the network resist+better to sybil attacks.++TODO: consider giving min and max values for the constants.++\begin{code}++{-------------------------------------------------------------------------------+ -+ - :: Tests.+ -+ ------------------------------------------------------------------------------}++type TestDhtNodeMonad = KeyedT (TimedT (RandT StdGen (StateT DhtState (Networked.NetworkLogged Identity))))+instance DhtNodeMonad TestDhtNodeMonad++runTestDhtNode :: ArbStdGen -> Timestamp -> DhtState -> TestDhtNodeMonad a -> (a, DhtState)+runTestDhtNode seed time s =+ runIdentity+ . Networked.evalNetworkLogged+ . (`runStateT` s)+ . (`evalRandT` unwrapArbStdGen seed)+ . (`TimedT.runTimedT` time)+ . (`KeyedT.evalKeyedT` Map.empty)++evalTestDhtNode :: ArbStdGen -> Timestamp -> DhtState -> TestDhtNodeMonad a -> a+evalTestDhtNode seed time s = fst . runTestDhtNode seed time s+execTestDhtNode :: ArbStdGen -> Timestamp -> DhtState -> TestDhtNodeMonad a -> DhtState+execTestDhtNode seed time s = snd . runTestDhtNode seed time s++initTestDhtState :: ArbStdGen -> Timestamp -> DhtState+initTestDhtState seed time =+ runIdentity+ . (`evalRandT` unwrapArbStdGen seed)+ . (`TimedT.runTimedT` time)+ $ initDht++-- | wrap StdGen so the Arbitrary instance isn't an orphan+newtype ArbStdGen = ArbStdGen { unwrapArbStdGen :: StdGen }+ deriving (Read, Show)++instance Arbitrary ArbStdGen+ where arbitrary = ArbStdGen . mkStdGen <$> arbitrary++\end{code}+
+ src/tox/Network/Tox/DHT/PendingReplies.lhs view
@@ -0,0 +1,45 @@+\subsection{Replies to RPC requests}+A \textit{reply} to a Request packet is a Response packet with the Request ID in+the Response packet set equal to the Request ID in the Request packet. A+response is accepted if and only if it is the first received reply to a request+which was sent sufficiently recently, according to a time limit which depends on+the service.++\begin{code}+{-# LANGUAGE Safe #-}+module Network.Tox.DHT.PendingReplies where++import qualified Network.Tox.DHT.RpcPacket as RpcPacket+import Network.Tox.DHT.Stamped (Stamped)+import qualified Network.Tox.DHT.Stamped as Stamped+import Network.Tox.NodeInfo.NodeInfo (NodeInfo)+import Network.Tox.Time (Timestamp)++{-------------------------------------------------------------------------------+ -+ - :: Implementation.+ -+ ------------------------------------------------------------------------------}++type PendingReplies = Stamped (NodeInfo, RpcPacket.RequestId)++expectReply :: Timestamp -> NodeInfo -> RpcPacket.RequestId ->+ PendingReplies -> PendingReplies+expectReply time node requestId = Stamped.add time (node, requestId)++checkExpectedReply :: Timestamp -> NodeInfo -> RpcPacket.RequestId ->+ PendingReplies -> (Bool, PendingReplies)+checkExpectedReply cutoff node requestId pendingReplies =+ case filter (>= cutoff) $+ Stamped.findStamps (== (node, requestId)) pendingReplies+ of+ [] -> (False, pendingReplies)+ time:_ -> (True, Stamped.delete time (node, requestId) pendingReplies)++{-------------------------------------------------------------------------------+ -+ - :: Tests.+ -+ ------------------------------------------------------------------------------}++\end{code}
src/tox/Network/Tox/DHT/PingPacket.lhs view
@@ -1,6 +1,6 @@ \subsection{Ping Service} -The Ping Service is used to periodically check if another node is still alive.+The Ping Service is used to check if a node is responsive. \begin{code} {-# LANGUAGE DeriveDataTypeable #-}
src/tox/Network/Tox/DHT/RpcPacket.lhs view
@@ -13,7 +13,6 @@ import GHC.Generics (Generic) import Test.QuickCheck.Arbitrary (Arbitrary, arbitrary) - {------------------------------------------------------------------------------- - - :: Implementation.@@ -24,9 +23,7 @@ A DHT RPC Service consists of a Request packet and a Response packet. A DHT RPC Packet contains a payload and a Request ID. This ID is a 64 bit unsigned-integer that helps identify the response for a given request. The Request ID-in the response packet must be equal to the Request ID in the request it is-responding to.+integer that helps identify the response for a given request. \begin{code} @@ -37,13 +34,15 @@ \end{code} +\input{src/tox/Network/Tox/DHT/PendingReplies.lhs}+ DHT RPC Packets are encrypted and transported within DHT Packets. \begin{tabular}{l|l|l} Length & Type & \href{#dht-packet}{Contents} \\ \hline \texttt{[0,]} & Bytes & Payload \\- \texttt{8} & \texttt{uint64_t} & Request ID \\+ \texttt{8} & \texttt{uint64\_t} & Request ID \\ \end{tabular} The minimum payload size is 0, but in reality the smallest sensible payload@@ -82,5 +81,5 @@ The Request ID provides some resistance against replay attacks. If there were no Request ID, it would be easy for an attacker to replay old responses and-thus provide nodes with out-of-date information. The exact value of the-Request ID will be specified later in the DHT section.+thus provide nodes with out-of-date information. A Request ID should be+randomly generated for each Request which is sent.
+ src/tox/Network/Tox/DHT/Stamped.hs view
@@ -0,0 +1,54 @@+{-# LANGUAGE Safe #-}+module Network.Tox.DHT.Stamped where++import qualified Data.Foldable as F+import Data.List ((\\))+import Data.Map (Map)+import qualified Data.Map as Map++import Network.Tox.Time (Timestamp)++{-------------------------------------------------------------------------------+ -+ - :: Implementation.+ -+ ------------------------------------------------------------------------------}++-- | a collection of objects associated with a timestamp.+type Stamped a = Map Timestamp [a]++empty :: Stamped a+empty = Map.empty++-- | add a timestamped object. There is no requirement that the stamp be later+-- than that of previously added objects.+add :: Timestamp -> a -> Stamped a -> Stamped a+add time x = Map.insertWith (++) time [x]++delete :: Eq a => Timestamp -> a -> Stamped a -> Stamped a+delete time x = Map.adjust (\\ [x]) time++findStamps :: (a -> Bool) -> Stamped a -> [Timestamp]+findStamps p = Map.keys . Map.filter (any p)++dropOlder :: Timestamp -> Stamped a -> Stamped a+dropOlder time = Map.mapMaybeWithKey $+ \t x -> if t < time then Nothing else Just x++getList :: Stamped a -> [a]+getList = F.concat++popFirst :: Stamped a -> (Maybe (Timestamp, a), Stamped a)+popFirst stamped =+ case Map.toAscList stamped of+ [] -> (Nothing, stamped)+ assoc:assocs -> case assoc of+ (_, []) -> popFirst $ Map.fromAscList assocs+ (stamp, [a]) -> (Just (stamp, a), Map.fromAscList assocs)+ (stamp, a:as) -> (Just (stamp, a), Map.fromAscList $ (stamp, as):assocs)++{-------------------------------------------------------------------------------+ -+ - :: Tests.+ -+ ------------------------------------------------------------------------------}
+ src/tox/Network/Tox/Network/MonadRandomBytes.hs view
@@ -0,0 +1,105 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE Trustworthy #-}++module Network.Tox.Network.MonadRandomBytes where++import Control.Applicative (Applicative, (<$>))+import Control.Monad.Random (RandT, getRandoms)+import Control.Monad.Reader (ReaderT)+import Control.Monad.RWS (RWST)+import Control.Monad.State (StateT)+import Control.Monad.Trans.Class (lift)+import Control.Monad.Writer (WriterT)+import Data.Binary (get)+import Data.Binary.Get (Get, getWord16be, getWord32be,+ getWord64be, getWord8, runGet)+import Data.ByteString (ByteString, pack, unpack)+import Data.ByteString.Lazy (fromStrict)+import Data.Monoid (Monoid)+import Data.Proxy (Proxy (..))+import Data.Word (Word16, Word32, Word64, Word8)+import System.Entropy (getEntropy)+import System.Random (RandomGen)+++import Network.Tox.Crypto.Key (Key)+import qualified Network.Tox.Crypto.Key as Key+import Network.Tox.Crypto.KeyPair (KeyPair)+import qualified Network.Tox.Crypto.KeyPair as KeyPair++class (Monad m, Applicative m) => MonadRandomBytes m where+ randomBytes :: Int -> m ByteString++ newKeyPair :: m KeyPair+ newKeyPair = KeyPair.fromSecretKey <$> randomKey++instance (Monad m, Applicative m, RandomGen s) => MonadRandomBytes (RandT s m) where+ randomBytes n = pack . take n <$> getRandoms++-- | cryptographically secure random bytes from system source+instance MonadRandomBytes IO where+ randomBytes = getEntropy+ newKeyPair = KeyPair.newKeyPair++instance MonadRandomBytes m => MonadRandomBytes (ReaderT r m) where+ randomBytes = lift . randomBytes+ newKeyPair = lift newKeyPair+instance (Monoid w, MonadRandomBytes m) => MonadRandomBytes (WriterT w m) where+ randomBytes = lift . randomBytes+ newKeyPair = lift newKeyPair+instance MonadRandomBytes m => MonadRandomBytes (StateT s m) where+ randomBytes = lift . randomBytes+ newKeyPair = lift newKeyPair+instance (Monoid w, MonadRandomBytes m) => MonadRandomBytes (RWST r w s m) where+ randomBytes = lift . randomBytes+ newKeyPair = lift newKeyPair++randomBinary :: MonadRandomBytes m => Get a -> Int -> m a+randomBinary g len = runGet g . fromStrict <$> randomBytes len++randomKey :: forall m a. (MonadRandomBytes m, Key.CryptoNumber a) => m (Key a)+randomKey = randomBinary get $ Key.encodedByteSize (Proxy :: Proxy a)++randomNonce :: MonadRandomBytes m => m Key.Nonce+randomNonce = randomKey++randomWord64 :: MonadRandomBytes m => m Word64+randomWord64 = randomBinary getWord64be 8+randomWord32 :: MonadRandomBytes m => m Word32+randomWord32 = randomBinary getWord32be 4+randomWord16 :: MonadRandomBytes m => m Word16+randomWord16 = randomBinary getWord16be 2+randomWord8 :: MonadRandomBytes m => m Word8+randomWord8 = randomBinary getWord8 1++-- produces Int uniformly distributed in range [0,bound)+randomInt :: MonadRandomBytes m => Int -> m Int+randomInt bound | bound <= 1 = return 0+randomInt bound =+ let+ numBits = log2 bound+ numBytes = 1 + (numBits - 1 `div` 8)+ in do+ r <- (`mod` 2^numBits) . makeInt . unpack <$> randomBytes numBytes+ if r >= bound+ then randomInt bound+ else return r+ where+ log2 :: Int -> Int+ log2 = ceiling . logBase 2 . (fromIntegral :: Int -> Double)+ makeInt :: [Word8] -> Int+ makeInt = foldr (\w -> (fromIntegral w +) . (256*)) 0++-- produces Int uniformly distributed in range [low,high]+randomIntR :: MonadRandomBytes m => (Int,Int) -> m Int+randomIntR (low,high) = (low +) <$> randomInt (1 + high - low)++-- | produces uniformly random element of a list+uniform :: MonadRandomBytes m => [a] -> m a+uniform [] = error "empty list in uniform"+uniform as = (as!!) <$> randomInt (length as)++uniformSafe :: MonadRandomBytes m => [a] -> m (Maybe a)+uniformSafe [] = return Nothing+uniformSafe as = Just <$> uniform as+
+ src/tox/Network/Tox/Network/Networked.hs view
@@ -0,0 +1,61 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE Trustworthy #-}++-- | Abstraction layer for network functionality.+--+-- The intention is to+-- (i) separate the logic of the protocol from its binary encoding, and+-- (ii) allow a simulated network in place of actual network IO.+module Network.Tox.Network.Networked where++import Control.Applicative (Applicative, (<$>))+import Control.Monad.Random (RandT)+import Control.Monad.Reader (ReaderT)+import Control.Monad.State (MonadState, StateT)+import Control.Monad.Trans.Class (lift)+import Control.Monad.Writer (WriterT, execWriterT,+ runWriterT, tell)+import Data.Binary (Binary)+import Data.Monoid (Monoid)++import Network.Tox.Network.MonadRandomBytes (MonadRandomBytes)+import Network.Tox.NodeInfo.NodeInfo (NodeInfo)+import Network.Tox.Protocol.Packet (Packet (..))+import Network.Tox.Timed (Timed)++class Monad m => Networked m where+ sendPacket :: (Binary payload, Show payload) => NodeInfo -> Packet payload -> m ()++-- | actual network IO+instance Networked (StateT NetworkState IO) where+ -- | TODO+ sendPacket _ _ = return ()++-- | TODO: sockets etc+type NetworkState = ()++type NetworkEvent = String+newtype NetworkLogged m a = NetworkLogged (WriterT [NetworkEvent] m a)+ deriving (Monad, Applicative, Functor, MonadState s, MonadRandomBytes, Timed)++runNetworkLogged :: Monad m => NetworkLogged m a -> m (a, [NetworkEvent])+runNetworkLogged (NetworkLogged m) = runWriterT m+evalNetworkLogged :: (Monad m, Applicative m) => NetworkLogged m a -> m a+evalNetworkLogged = (fst <$>) . runNetworkLogged+execNetworkLogged :: Monad m => NetworkLogged m a -> m [NetworkEvent]+execNetworkLogged (NetworkLogged m) = execWriterT m++-- | just log network events+instance Monad m => Networked (NetworkLogged m) where+ sendPacket to packet = NetworkLogged $+ tell [">>> " ++ show to ++ " : " ++ show packet]++instance Networked m => Networked (ReaderT r m) where+ sendPacket = (lift .) . sendPacket+instance (Monoid w, Networked m) => Networked (WriterT w m) where+ sendPacket = (lift .) . sendPacket+instance Networked m => Networked (RandT s m) where+ sendPacket = (lift .) . sendPacket+instance Networked m => Networked (StateT s m) where+ sendPacket = (lift .) . sendPacket
src/tox/Network/Tox/NodeInfo/HostAddress.lhs view
@@ -44,7 +44,7 @@ data HostAddress = IPv4 Socket.HostAddress | IPv6 Socket.HostAddress6- deriving (Eq, Generic, Typeable)+ deriving (Eq, Ord, Generic, Typeable) instance Binary HostAddress instance MessagePack HostAddress
src/tox/Network/Tox/NodeInfo/NodeInfo.lhs view
@@ -10,7 +10,7 @@ \hline \texttt{1} bit & Transport Protocol & UDP = 0, TCP = 1 \\ \texttt{7} bit & Address Family & 2 = IPv4, 10 = IPv6 \\- \texttt{4 | 16} & IP address & 4 bytes for IPv4, 16 bytes for IPv6 \\+ \texttt{4 $|$ 16} & IP address & 4 bytes for IPv4, 16 bytes for IPv6 \\ \texttt{2} & Port Number & Port number \\ \texttt{32} & Public Key & Node ID \\ \end{tabular}@@ -35,8 +35,8 @@ The number \texttt{130} is used for an IPv4 TCP relay and \texttt{138} is used to indicate an IPv6 TCP relay. -The reason for these numbers is because the numbers on Linux for IPv4 and IPv6-(the \texttt{AF_INET} and \texttt{AF_INET6} defines) are \texttt{2} and+The reason for these numbers is that the numbers on Linux for IPv4 and IPv6+(the \texttt{AF\_INET} and \texttt{AF\_INET6} defines) are \texttt{2} and \texttt{10}. The TCP numbers are just the UDP numbers \texttt{+ 128}. \begin{code}@@ -71,7 +71,7 @@ , address :: SocketAddress , publicKey :: PublicKey }- deriving (Eq, Show, Read, Generic, Typeable)+ deriving (Eq, Ord, Show, Read, Generic, Typeable) instance MessagePack NodeInfo
src/tox/Network/Tox/NodeInfo/SocketAddress.lhs view
@@ -7,7 +7,6 @@ \begin{code} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE LambdaCase #-} {-# LANGUAGE Trustworthy #-} module Network.Tox.NodeInfo.SocketAddress where @@ -36,7 +35,7 @@ data SocketAddress = SocketAddress HostAddress PortNumber- deriving (Eq, Show, Read, Generic, Typeable)+ deriving (Eq, Ord, Show, Read, Generic, Typeable) instance Binary SocketAddress instance MessagePack SocketAddress
src/tox/Network/Tox/NodeInfo/TransportProtocol.lhs view
@@ -38,7 +38,7 @@ data TransportProtocol = UDP | TCP- deriving (Eq, Show, Read, Generic, Typeable)+ deriving (Eq, Ord, Show, Read, Generic, Typeable) instance Binary TransportProtocol instance MessagePack TransportProtocol
src/tox/Network/Tox/Protocol/Packet.lhs view
@@ -44,7 +44,6 @@ instance Binary payload => Binary (Packet payload) instance MessagePack payload => MessagePack (Packet payload) - {------------------------------------------------------------------------------- - - :: Tests.
src/tox/Network/Tox/Protocol/PacketKind.lhs view
@@ -56,31 +56,56 @@ data PacketKind- = PingRequest -- 0x00: Ping request- | PingResponse -- 0x01: Ping response- | NodesRequest -- 0x02: Nodes request- | NodesResponse -- 0x04: Nodes response- | CookieRequest -- 0x18: Cookie request- | CookieResponse -- 0x19: Cookie response- | CryptoHandshake -- 0x1a: Crypto handshake- | CryptoData -- 0x1b: Crypto data- | Crypto -- 0x20: Encrypted data- | LanDiscovery -- 0x21: LAN discovery- | OnionRequest0 -- 0x80: Initial onion request- | OnionRequest1 -- 0x81: First level wrapped onion request- | OnionRequest2 -- 0x82: Second level wrapped onion request- | AnnounceRequest -- 0x83: Announce request- | AnnounceResponse -- 0x84: Announce response- | OnionDataRequest -- 0x85: Onion data request- | OnionDataResponse -- 0x86: Onion data response- | OnionResponse3 -- 0x8c: Third level wrapped onion response- | OnionResponse2 -- 0x8d: Second level wrapped onion response- | OnionResponse1 -- 0x8e: First level wrapped onion response- | BootstrapInfo -- 0xf0: Bootstrap node info request and response+ = PingRequest+ | PingResponse+ | NodesRequest+ | NodesResponse+ | CookieRequest+ | CookieResponse+ | CryptoHandshake+ | CryptoData+ | Crypto+ | LanDiscovery+ | OnionRequest0+ | OnionRequest1+ | OnionRequest2+ | AnnounceRequest+ | AnnounceResponse+ | OnionDataRequest+ | OnionDataResponse+ | OnionResponse3+ | OnionResponse2+ | OnionResponse1+ | BootstrapInfo deriving (Eq, Read, Show, Bounded, Enum, Generic, Typeable) instance MessagePack PacketKind+++kindDescription :: PacketKind -> String+kindDescription = \case+ PingRequest -> "Ping request"+ PingResponse -> "Ping response"+ NodesRequest -> "Nodes request"+ NodesResponse -> "Nodes response"+ CookieRequest -> "Cookie request"+ CookieResponse -> "Cookie response"+ CryptoHandshake -> "Crypto handshake"+ CryptoData -> "Crypto data"+ Crypto -> "Encrypted data"+ LanDiscovery -> "LAN discovery"+ OnionRequest0 -> "Initial onion request"+ OnionRequest1 -> "First level wrapped onion request"+ OnionRequest2 -> "Second level wrapped onion request"+ AnnounceRequest -> "Announce request"+ AnnounceResponse -> "Announce response"+ OnionDataRequest -> "Onion data request"+ OnionDataResponse -> "Onion data response"+ OnionResponse3 -> "Third level wrapped onion response"+ OnionResponse2 -> "Second level wrapped onion response"+ OnionResponse1 -> "First level wrapped onion response"+ BootstrapInfo -> "Bootstrap node info request and response" kindToByte :: PacketKind -> Word8
src/tox/Network/Tox/Testing.lhs view
@@ -1,7 +1,7 @@ \chapter{Testing} The final part of the architecture is the test protocol. We use a-\href{http://msgpack.org}{MessagePack} based RPC protocol to expose language+\href{https://msgpack.org}{MessagePack} based RPC protocol to expose language agnostic interfaces to internal functions. Using property based testing with random inputs as well as specific edge case tests help ensure that an implementation of the Tox protocol following the architecture specified in this
+ src/tox/Network/Tox/Time.hs view
@@ -0,0 +1,54 @@+{-# LANGUAGE Safe #-}+module Network.Tox.Time where++import Control.Applicative ((<$>), (<*>))+import qualified System.Clock as Clock+import Test.QuickCheck.Arbitrary (Arbitrary, arbitrary)++{-------------------------------------------------------------------------------+ -+ - :: Implementation.+ -+ ------------------------------------------------------------------------------}++newtype Timestamp = Timestamp Clock.TimeSpec+ deriving (Eq, Ord, Show, Read)++newtype TimeDiff = TimeDiff Clock.TimeSpec+ deriving (Eq, Ord, Show, Read)++instance Num TimeDiff where+ TimeDiff t + TimeDiff t' = TimeDiff $ t Prelude.+ t'+ TimeDiff t - TimeDiff t' = TimeDiff $ t Prelude.- t'+ TimeDiff t * TimeDiff t' = TimeDiff $ t * t'+ negate (TimeDiff t) = TimeDiff $ negate t+ abs (TimeDiff t) = TimeDiff $ abs t+ signum (TimeDiff t) = TimeDiff $ signum t+ fromInteger = TimeDiff . fromInteger++seconds :: Integer -> TimeDiff+seconds s = TimeDiff $ Clock.TimeSpec (fromIntegral s) 0++milliseconds :: Integer -> TimeDiff+milliseconds = TimeDiff . Clock.TimeSpec 0 . (*10^(6::Integer)) . fromIntegral++getTime :: IO Timestamp+getTime = Timestamp <$> Clock.getTime Clock.Monotonic++(-) :: Timestamp -> Timestamp -> TimeDiff+Timestamp t - Timestamp t' = TimeDiff $ t Prelude.- t'++(+) :: Timestamp -> TimeDiff -> Timestamp+Timestamp t + TimeDiff t' = Timestamp $ t Prelude.+ t'++{-------------------------------------------------------------------------------+ -+ - :: Tests.+ -+ ------------------------------------------------------------------------------}++instance Arbitrary Timestamp+ where arbitrary = (Timestamp <$>) $ Clock.TimeSpec <$> arbitrary <*> arbitrary++instance Arbitrary TimeDiff+ where arbitrary = (TimeDiff <$>) $ Clock.TimeSpec <$> arbitrary <*> arbitrary
+ src/tox/Network/Tox/Timed.hs view
@@ -0,0 +1,31 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE Safe #-}+{-# LANGUAGE UndecidableInstances #-}++module Network.Tox.Timed where++import Control.Monad (Monad)+import Control.Monad.Random (RandT)+import Control.Monad.Reader (ReaderT)+import Control.Monad.RWS (RWST)+import Control.Monad.State (StateT)+import Control.Monad.Trans (lift)+import Control.Monad.Writer (WriterT)+import Data.Monoid (Monoid)++import Network.Tox.Time (Timestamp)++class Monad m => Timed m where+ askTime :: m Timestamp++instance Timed m => Timed (ReaderT r m) where+ askTime = lift askTime+instance (Monoid w, Timed m) => Timed (WriterT w m) where+ askTime = lift askTime+instance Timed m => Timed (StateT s m) where+ askTime = lift askTime+instance (Monoid w, Timed m) => Timed (RWST r w s m) where+ askTime = lift askTime+instance Timed m => Timed (RandT s m) where+ askTime = lift askTime
+ src/tox/Network/Tox/TimedT.hs view
@@ -0,0 +1,29 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE Trustworthy #-}++module Network.Tox.TimedT where++import Control.Applicative (Applicative)+import Control.Monad (Monad)+import Control.Monad.IO.Class (MonadIO)+import Control.Monad.Reader (ReaderT, ask, runReaderT)+import Control.Monad.State (MonadState)+import Control.Monad.Trans (MonadTrans)+import Control.Monad.Writer (MonadWriter)++import Network.Tox.Crypto.Keyed (Keyed)+import Network.Tox.Network.MonadRandomBytes (MonadRandomBytes)+import Network.Tox.Network.Networked (Networked)+import Network.Tox.Time (Timestamp)+import Network.Tox.Timed (Timed (..))++newtype TimedT m a = TimedT (ReaderT Timestamp m a)+ deriving (Monad, Applicative, Functor, MonadState s, MonadWriter w+ , MonadRandomBytes, MonadTrans, MonadIO, Networked, Keyed)++runTimedT :: TimedT m a -> Timestamp -> m a+runTimedT (TimedT m) = runReaderT m++instance Monad m => Timed (TimedT m) where+ askTime = TimedT ask
test/testsuite.hs view
@@ -1,17 +1,17 @@ module Main (main) where import Control.Concurrent.Async (cancel, wait, withAsync)-import System.Environment (withArgs)+import System.Environment (getArgs, withArgs) import Network.Tox.Testing (serve) import qualified ToxTestSuite main :: IO ()-main =+main = do+ args <- getArgs+ let client = withArgs (args ++ ["--print-cpu-time", "--color"]) ToxTestSuite.main withAsync serve $ \server -> withAsync client $ \res -> do wait res cancel server- where- client = withArgs ["--print-cpu-time", "--color"] ToxTestSuite.main